From 754a885ec28b9896e2ee3eb17b38becc55285538 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 16 Jul 2023 00:11:31 +0700 Subject: [PATCH 01/80] docs: adding documentation [Documentation] adding information about using testing example Signed-off-by: slowy07 --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index bafa3fc..ff105cd 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ repository: [gitlab](https://gitlab.com/libeigen/eigen) ``` brew install eigen ``` +- windows + recommended for install [Cygwin](https://www.cygwin.com/) with [CMake](http://www.cmake.org/) and [g++](https://gcc.gnu.org/) ## OpenMp @@ -60,3 +62,18 @@ website [openmp.org](https://www.openmp.org/) ## Test clara Clara using [GoogleTest](https://github.com/google/googletest) framework and mocking tools, for installation you can check on [here](https://github.com/google/googletest), or you can just find on the testing script on [``clara_test/run_test``](clara_test/run_test) + +## running test clara + +``` +g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels +``` + +information: +- ``-pedantic`` ensure that the code conforms to the strictest standard of the C++ language +- ``-std=c++11`` specifies that the code should be compiled using c++11 +- ``-Wall`` enable all warnings +- ``-Wextra`` enables additional warnings that are not enabled by default +- ``-Weffc++`` enables warnings about potential erroes in the code +- ``-fopenmp`` enables ``OpenMP`` support +- ``-g3`` enable debugging symbol From bdcc82186e4c2fa371fde3047bf9e14b947d7723 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 16 Jul 2023 12:44:29 +0700 Subject: [PATCH 02/80] docs: adding documentation [Documentation] ``include/constants.h`` adding commented code for information about variable ``chop``, `maxn`, `eps` ``include/internal/classFunction/iomanip.h`` ``IDisplay`` class is an abstract class the provides a generic interface for displaying object . the ``Display_Impl_`` class concrete class that implements the ``IDisplay`` interface for Eigen matrices. - the first constructor takes an eigen matrix as input and double as a parameter, the double parameter specifies amount of chopping that should be done whne displaying the matrix - the second constructure takes a complex number as input an a double as parameter, the double parameter specifieds tha amount of chopping that soubld be done when displaying the complex number ``include/traits.h`` adding preprocessor directive to check the version of the GCC compiler ``clara_test/tests/traits.cpp`` adding testing for test ``traits.h`` ``clara_test/run_test`` adding support for macOs and support for ubuntu and arch based package Signed-off-by: slowy07 --- clara_test/run_test | 39 +++++++++--- clara_test/tests/traits.cpp | 17 ++++- include/clara.h | 43 ++++++------- include/constants.h | 15 ++++- include/internal/classFunction/iomanip.h | 21 ++++--- include/operations.h | 4 -- include/traits.h | 79 +++++++++++++++++++++--- 7 files changed, 165 insertions(+), 53 deletions(-) diff --git a/clara_test/run_test b/clara_test/run_test index 087eb51..8c1ac99 100755 --- a/clara_test/run_test +++ b/clara_test/run_test @@ -1,9 +1,35 @@ #!/usr/bin/bash -echo "isntall eigen" -sudo apt-get install libeigen3-dev +OS=$(uname -s) +PACKAGE_MANAGER="" -echo "initailizing google test" +if [[ "$OS" == "Linux" ]]; then + if command -v apt-get >/dev/null 2>&1; then + PACKAGE_MANAGER="apt-get" + elif command -v pacman >/dev/null 2>&1; then + PACKAGE_MANAGER="pacman" + else + echo "Unsupported package manager. Exiting..." + exit 1 + fi +elif [[ "$OS" == "Darwin" ]]; then + PACKAGE_MANAGER="brew" +else + echo "Unsupported operating system. Exiting..." + exit 1 +fi + +echo "Installing eigen" + +if [[ "$PACKAGE_MANAGER" == "apt-get" ]]; then + sudo apt-get install libeigen3-dev +elif [[ "$PACKAGE_MANAGER" == "pacman" ]]; then + sudo pacman -S eigen +elif [[ "$PACKAGE_MANAGER" == "brew" ]]; then + brew install eigen +fi + +echo "Initializing Google Test" git clone https://github.com/google/googletest.git -b release-1.11.0 cd googletest mkdir build @@ -11,7 +37,7 @@ cd build cmake .. make install -echo "testing clara" +echo "Testing Clara" cd .. cd .. mkdir build @@ -19,9 +45,8 @@ cd build cmake .. make -echo "\n" -echo "result from clara test" +echo "" +echo "Result from Clara test" cd build cd tests ./clara_testing - diff --git a/clara_test/tests/traits.cpp b/clara_test/tests/traits.cpp index e0bed16..f209dc7 100644 --- a/clara_test/tests/traits.cpp +++ b/clara_test/tests/traits.cpp @@ -1,6 +1,21 @@ +#include +#include +#include #include "../../include/clara.h" #include "gtest/gtest.h" using namespace clara; -TEST(clara_is_complex, AllTests) {} +TEST(clara_is_complex, AllTests) { + EXPECT_TRUE(clara::is_complex>::value); + EXPECT_TRUE(clara::is_complex>::value); +} + +TEST(clara_is_matrix_expression, AllTests) { + dmat A, B; + int x{}, y{}, z{}; + + EXPECT_TRUE(clara::is_matrix_expression::value); + EXPECT_TRUE(clara::is_matrix_expression::value); + EXPECT_FALSE(clara::is_matrix_expression::value); +} diff --git a/include/clara.h b/include/clara.h index 72d675c..864e06a 100644 --- a/include/clara.h +++ b/include/clara.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,41 +36,34 @@ #include #include -#include -#include - // preprocessor macro #include "macros.h" // inter dependicies -#include "macros.h" - -#include "types.h" +#include "classFunction/codes.h" #include "classFunction/exception.h" -#include "constants.h" -#include "traits.h" +#include "classFunction/gates.h" #include "classFunction/idisplay.h" -#include "internal/util.h" -#include "internal/classFunction/iomanip.h" -#include "input_output.h" - -#include "internal/classFunction/singleton.h" #include "classFunction/init.h" -#include "functions.h" -#include "classFunction/codes.h" -#include "classFunction/gates.h" -#include "classFunction/states.h" #include "classFunction/random_devices.h" - -#include "statistics.h" -#include "operations.h" -#include "entropies.h" -#include "entanglement.h" - -#include "random.h" +#include "classFunction/states.h" #include "classFunction/timer.h" +#include "constants.h" +#include "entanglement.h" +#include "entropies.h" +#include "functions.h" +#include "input_output.h" #include "instruments.h" +#include "internal/classFunction/iomanip.h" +#include "internal/classFunction/singleton.h" +#include "internal/util.h" +#include "macros.h" #include "number_theory.h" +#include "operations.h" +#include "random.h" +#include "statistics.h" +#include "traits.h" +#include "types.h" // order lib diff --git a/include/constants.h b/include/constants.h index 47a065b..427f574 100644 --- a/include/constants.h +++ b/include/constants.h @@ -12,13 +12,26 @@ inline constexpr cplx operator"" _i(unsigned long long int x) noexcept { } inline constexpr cplx operator"" _i(long double x) noexcept { return {0., static_cast(x)}; } + +/** + * @brief double chop used in clara::disp() for setting to zero numbers + * that have their absolute value smaller than clara:chop +*/ constexpr double chop = 1e-10; +/** + * @brief used to decide whether a number or epression in double precision + * os zero or not + */ constexpr double eps = 1e-12; +/** + * @brief maxium number of allowed qubit + * used internally to allocate array on the stack + */ constexpr idx maxn = 64; constexpr double pi = 3.141592653589793238462643383279502884; constexpr double ee = 2.718281828459045235360287471352662497; -constexpr double inifinity = std::numeric_limits::infinity(); +constexpr double inifinity = std::numeric_limits::max(); inline cplx omega(idx D) { if (D == 0) diff --git a/include/internal/classFunction/iomanip.h b/include/internal/classFunction/iomanip.h index e912178..2594109 100644 --- a/include/internal/classFunction/iomanip.h +++ b/include/internal/classFunction/iomanip.h @@ -66,7 +66,14 @@ class IOManipPointer : public IDisplay { } }; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ingored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) class IOManipEigen : public IDisplay, private Display_Impl_ { +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) cmat A_; double chop_; @@ -75,16 +82,14 @@ class IOManipEigen : public IDisplay, private Display_Impl_ { template explicit IOManipEigen(const Eigen::MatrixBase& A, double chop = clara::chop) : A_{A.template cast()}, chop_{chop} {} - - // complex number - explicit IOManipEigen(const cplx z, double chop = clara::chop) : A_{cmat::Zero(1, 1)}, chop_{chop} { - // put complex number inside the eigen matrix + // complex numbers + explicit IOManipEigen(const cplx z, double chop = clara::chop) + : A_{cmat::Zero(1, 1)}, chop_{chop} { A_(0, 0) = z; } -private: - std::ostream& display(std::ostream& os) const override { - return display_impl_(A_, os, chop); - } + + private: + std::ostream& display(std::ostream& os) const override { return display_impl_(A_, os, chop); } }; } // namespace internal diff --git a/include/operations.h b/include/operations.h index 0a4e064..167a8b3 100644 --- a/include/operations.h +++ b/include/operations.h @@ -16,10 +16,6 @@ #include "internal/util.h" #include "types.h" -#if (__GNUC__ && !__clang__) -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#endif // (__GNUC__ && !__clang__) - namespace clara { template diff --git a/include/traits.h b/include/traits.h index d83204b..687829c 100644 --- a/include/traits.h +++ b/include/traits.h @@ -6,28 +6,91 @@ #include namespace clara { -template -using to_void = void; +template +struct make_void { + typedef void type; +}; +template +using to_void = typename make_void::type; +/** + * @brief check whether T is compatible with STL-like iterable container + * provide the constant member \a value which is equal to \a true, + * if \a T compabitle with an iterable container. + */ +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) template struct is_iterable : std::false_type {}; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) + +/** + * @brief check whether T is compatible with an STL-like iterable container + * specialization for STL-like iterable containers + */ +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) template struct is_iterable().begin()), decltype(std::declval().end()), typename T::value_type>> : std::true_type {}; -template -struct is_matrix_expression : std::false_type {}; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +/** + * @brief check whether the type is an Eigen matrix expression + * provides the constant member value which is equal to true, + * if the type is an Eigen matrix expression of type Eigen::MatrixBase, + */ +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) template -struct is_matrix_expression> : std::true_type {}; +struct is_matrix_expression : std::is_base_of::type>, + typename std::decay::type> {}; -template -struct is_complex: std::false_type {}; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) -template +/** +* @brief check whether the type is complex type +* provide the constant member value which is equal to true, +* if the type is a complex type. +*/ +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +template +struct is_complex : std::false_type {}; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) + +/** +* @brief check whether the type is complex number type, +* specialization for complex type +*/ +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic push +#pragam GCC diagnostic ignored "-Weffc++" +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +template struct is_complex> : std::true_type {}; +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#pragma GCC diagnostic pop + #endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) } // namespace clara From b21993fa7d828b77c17a66d8eeb79d85e12a3d77 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 16 Jul 2023 16:32:11 +0700 Subject: [PATCH 03/80] chore(test): adding run_test interactive to github action Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 28 ++++++++++++++++++++++++ .github/workflows/cpp-testing.yml.backup | 16 -------------- 2 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/cpp-testing.yml delete mode 100644 .github/workflows/cpp-testing.yml.backup diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml new file mode 100644 index 0000000..497256f --- /dev/null +++ b/.github/workflows/cpp-testing.yml @@ -0,0 +1,28 @@ +name: Unit Testing + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +jobs: + build-and-testing: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Eigen + run: sudo apt-get install libeigen3-dev + + - name: Build and test + run: | + cd clara_test/tests/run_test + chmod +x run_test + ./run_test diff --git a/.github/workflows/cpp-testing.yml.backup b/.github/workflows/cpp-testing.yml.backup deleted file mode 100644 index f96b97d..0000000 --- a/.github/workflows/cpp-testing.yml.backup +++ /dev/null @@ -1,16 +0,0 @@ -name: unitesting - -on: [push, pull_request] -jobs: - build-and-testing: - runs-on: ubuntu-latest - steps: - - name: checkout repo - uses: actions/checkout@v3 - - name: install Eigen - run: sudo apt-get install libeigen3-dev - - name: testing - run: | - make clean - make debug - ./clara From eeb7a50cc7d595b335f538e93042adb2c8130ae8 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 16 Jul 2023 16:33:52 +0700 Subject: [PATCH 04/80] fix: fixing directories Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 497256f..0154f89 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -23,6 +23,6 @@ jobs: - name: Build and test run: | - cd clara_test/tests/run_test + cd clara_test/run_test chmod +x run_test ./run_test From c6a4a9ce11a7859b4154ab6986759053253ac44e Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 16 Jul 2023 18:11:45 +0700 Subject: [PATCH 05/80] fix: fixing directories Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 0154f89..aba1080 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -23,6 +23,6 @@ jobs: - name: Build and test run: | - cd clara_test/run_test + cd clara_test chmod +x run_test ./run_test From f96ab2673e2ff6a01d7294ed2fa286948a678cfd Mon Sep 17 00:00:00 2001 From: khaira nabila <75076265+khairanabila@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:54:05 +0700 Subject: [PATCH 06/80] chroe: ``evects`` function (#1) added necessary using statements to define ComplexScalar and ComplexMatrix types for handling complex numbers. replaced dyn_mat with Eigen::Matrix to match the type of rA. improved the check for matrix size by comparing the rows and columns separately. changed rA.template cas() to rA.template cast() to handle complex matrix conversion. --- include/functions.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/include/functions.h b/include/functions.h index 96b4d9c..b5b53a4 100644 --- a/include/functions.h +++ b/include/functions.h @@ -160,16 +160,22 @@ dyn_col_vect evals(const Eigen::MatrixBase& A) { template cmat evects(const Eigen::MatrixBase& A) { - const dyn_mat& rA = A.derived(); + using Scalar = typename Derived::Scalar; + using ComplexScalar = std::complex; + using ComplexMatrix = Eigen::Matrix; +const auto& rA = A.derived(); + // check zero-size - if (!internal::check_nonzero_size(rA)) + if (rA.rows() == 0 || rA.cols() == 0) throw Exception("clara::evects()", Exception::Type::MATRIX_NOT_SQUARE); - if (!internal::check_square_mat(rA)) + if (rA.rows() != rA.cols()) throw Exception("clara::evects()", Exception::Type::MATRIX_NOT_SQUARE); - Eigen::ComplexEigenSolver es(rA.template cas()); - return eig(rA).second; + ComplexMatrix cA = rA.template cast(); + Eigen::ComplexEigenSolver es(cA); + + return es.eigenvectors(); } template From 81a8aaa1e5f2e400a79fe3dc5fe69317365ad569 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 17 Jul 2023 13:00:33 +0700 Subject: [PATCH 07/80] chore(add): adding more information and unitest [Documentation] adding reference used in clara adding unitest for mac os Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 28 +++++++++++++++++ .github/workflows/cpp-testing.yml | 2 +- README.md | 5 +++ REFERENCE.md | 44 +++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/cpp-testing-mac.yml create mode 100644 REFERENCE.md diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml new file mode 100644 index 0000000..d5795e2 --- /dev/null +++ b/.github/workflows/cpp-testing-mac.yml @@ -0,0 +1,28 @@ +name: Mac Clara Test + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +jobs: + build-and-testing: + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Eigen + run: brew install eigen + + - name: Build and test + run: | + cd clara_test + chmod +x run_test + ./run_test diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index aba1080..241ea41 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -1,4 +1,4 @@ -name: Unit Testing +name: Ubuntu Clara Test on: push: diff --git a/README.md b/README.md index ff105cd..a61f16e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Clara are open sourec library for quantum computing processing that is designed Eigen is a high-performance C++ template library for linear algebra. It provides a wide range of features, including matrices, vectors, numerical solvers, and related algorithms. Eigen is designed to be fast, efficient, and flexible. It is used by a wide range of developers, including those working in the fields of computer graphics, machine learning, and scientific computing. website : [eigen.tuxfamily.org](https://eigen.tuxfamily.org/index.php?title=Main_Page) + repository: [gitlab](https://gitlab.com/libeigen/eigen) *installation for clara* @@ -77,3 +78,7 @@ information: - ``-Weffc++`` enables warnings about potential erroes in the code - ``-fopenmp`` enables ``OpenMP`` support - ``-g3`` enable debugging symbol + +## Reference + +reference (journal/article/paper) used in clara can check in [``REFERENCE.md``](REFERENCE.md) diff --git a/REFERENCE.md b/REFERENCE.md new file mode 100644 index 0000000..c748950 --- /dev/null +++ b/REFERENCE.md @@ -0,0 +1,44 @@ +# Information about reference used in clara + +- [2203.13522 - New Quantum Algorithm for Quantum Entropies and Distance - Qisheng Wang, Ji Guan, Junyi Liu, Zhicheng Zhang, Mingsheng Ying](https://arxiv.org/abs/2203.13522) +- [2103.07996 - Quantum-Entropy Physics - Davi Geiger, Zvi M. Kedem](https://arxiv.org/abs/2103.07996) +- [2103.12611 - Entropy of quantum states - Paolo Facchi, Giovanni Gramegna, Arturo Konderak](https://arxiv.org/abs/2104.12611) +- [2212.06601 - Entanglement Entropy in Quantum mechanis: An Algebraic approach - A.F. Reyes-Lega](https://arxiv.org/abs/2212.04601) +- [2108.02726 - Quantum logical entropy: fundamentasl and general properties - Boaz Tamir, Ismael L. Paiva, Zohar Schwartzman-Nowik, Eliahu Cohen](https://arxiv.org/abs/2108.02726) +- [2201.04407 - Logical Entropy and Negative probabilites in Quantum Mechanis - Giovanni Manfredi](https://arxiv.org/abs/2201.04407) +- [1711.00814 - Masuring Quantum Entropy - Jayadev Acharya, Ibrahim Issa, Nirmal V. Shende, Aaron B. Wagner](https://arxiv.org/abs/1711.00814) +- [1504.03909 - Entanglement renyi \alpha-entropy - Yu-Xin Wang, Liang-Zhu Mu, Vlatko Vedral, Heng Fan](https://arxiv.org/abs/1504.03909) +- [2109.11737 - Estimating renyi \alpha-cross-entropies matrix based way - Isaac J. Sledge, Jose C. Principe](https://arxiv.org/abs/2109.11737) +- [1604.02783 - Lower and upper bounds for Entanglement of renyi-\alpha-entropy - Wei Song, Lin Chen, Zhuo-Liang Cao](https://arxiv.org/abs/1604.02783) +- [2301.09074 - Average renyi entropy of a subsystem in random pure state - MuSeong Kim, Mi-Ra Hwang, Eylee Jung, DaeKil Park](https://arxiv.org/abs/2301.09074) +- [1612.04480 - Tsallis entropy and general polygamy of multi-party quantum entanglement in arbitrary dimension - Jeong San Kim](https://arxiv.org/abs/1612.04480) +- [2306.10297 - Quantum mutual information redistribution by number partitioning algorithm - Muchun Yang, Cheng-Qian Xu, D. L. Zhou](https://arxiv.org/abs/2306.10297) +- [1504.07176 - Multiparty Quantum mutual information: An alternative definition - Asutosh Kumar](https://arxiv.org/abs/1504.07176) +- [1607.05155 - Quantum mutual information and quantumness vectors for multi-qubit system - Sk Sazim, Pankaj Agrawal](https://arxiv.org/abs/1607.05155) +- [0812.4167 - On the relation between Schmidt coefficients and entanglement - Paolo Aniello, Cosmo Lupo](https://arxiv.org/abs/0812.4167) +- [1907.07976 - Joint Schmidt-type decomposition for two bipartite pure quantum states - Christopher Eltschka, Jens Siewert](https://arxiv.org/abs/1907.07976) +- [2205.13539 - Mitigating barren plateaus of variational quantum eigensolver - Xia Liu, Geng Liu, Jiaxin Huang, Hao-Kai Zhang, Xin Wang](https://arxiv.org/abs/2205.13539) +- [2304.02447 - Analyzing quantum entanglement with the schmidt decomposition in operator stpace - Chengjie Zhang, Sophia Denker, Ali Asadian, Otfried Gühne](https://arxiv.org/abs/2304.02447) +- [2211.0679 - Quantum Algorithm for Estimating Eigenvalue - Nhat A. Nghiem, Tzu-Chieh Wei](https://arxiv.org/abs/2211.06179) +- [2307.03889 - Quantum techniques for eigenvalue problems - Dean Lee](https://arxiv.org/abs/2307.03889) +- [2112.02554 - Quantum algorithm for the generalized eigenvalue problem - Jin-Min Liang, Shu-Qian Shen, Ming Li, Shao-Ming Fei](https://arxiv.org/abs/2112.02554) +- [2302.14324 - A CS guid to the quantum singular value transformation - Ewin Tang, Kevin Tian](https://arxiv.org/abs/2302.14324) +- [2104.00608 - The Dominant Eigenvector of a Noisy Quantum State - Bálint Koczor](https://arxiv.org/abs/2104.00608) +- [1707.01933 - Application of the Kronecker product to simple spin system - Francisco M. Fernández](https://arxiv.org/abs/1707.01933) +- [1911.11774 - Matrix Completion using Kronecker product Approximation - Chencheng Cai, Rong Chen, Han Xiao](https://arxiv.org/abs/1911.11774) +- [2305.07886 - On \rho-adic Gram-schmidt Orthogonalization Process - Yingpu Deng](https://arxiv.org/abs/2305.07886) +- [2205.08465 - MultiPartite entanglement in qudit hypergraph states - Daniele Malpetti, Alfredo Bellisario, Chiara Macchiavello](https://arxiv.org/abs/2205.08465) +- [1911.07326 - Approximation Quasiorthogonality of Operator Algebras and Relative Quantum Privacy - David W. Kribs, Jeremy Levick, Mike Nelson, Rajesh Pereira, Mizanur Rahaman](https://arxiv.org/abs/1911.07326) +- [1612.02437 - Multi-partite entanglement - Michael Walter, David Gross, Jens Eisert](https://arxiv.org/abs/1612.02437) +- [1708.04172 - Kraus Operator for a pair of interacting qubits: a case study - Momir Arsenijevic, Jasmina Jeknic-Dugic, Miroljub Dugic](https://arxiv.org/abs/1708.04172) +- [1512.07843 - Generalized Kraus operators for the one-qubit depolarizing quantum channel - Momir Arsenijevic, Jasmina Jeknic-Dugic, Miroljub Dugic](https://arxiv.org/abs/1512.07843) +- [0311091 - Kraus representation for density of arbitrary open qubit system - D. M. Tong, Jing-Ling Chen, L. C. Kwek, C. H. Oh](https://arxiv.org/abs/quant-ph/0311091) +- [1102.0948 - Quantum Gate Fidelity in Terms of Choi Matrices - Nathaniel Johnston, David W. Kribs](https://arxiv.org/abs/1102.0948) +- [0609073 - On bipartite pure-state entanglement structure in terms of disentanglement - Fedor Herbut](https://arxiv.org/abs/quant-ph/0609073) +- [2210.06991 - Density matrix formalism for interacting quantum fields - Christian Käding, Mario Pitschmann](https://arxiv.org/abs/2210.06991) +- [2103.13755 - Quantum Software Models: The Density Matrix for Classical and Quantum Software System Design - Iaakov Exman, Alon Tsalik Shmilovich](https://arxiv.org/abs/2103.13755) +- [2109.06315 - Generative Quantym Learning of Joint Proability Distribution Functions - Elton Yechao Zhu, Sonika Johri, Dave Bacon, Mert Esencan, Jungsang Kim, Mark Muir, Nikhil Murgai, Jason Nguyen, Neal Pisenti, Adam Schouela, Ksenia Sosnova, Ken Wright](https://arxiv.org/abs/2109.06315) + +- [1911.12604 - Eigen-AD: Algorithmic Differentiation of the Eigen Library - Patrick Peltzer, Johannes Lotz, Uwe Naumann](https://arxiv.org/abs/1911.12604) +- [0902.1265 - Generalized eigenvalue method for energies and matrix elements in lattice field theory - Benoit Blossier, Michele Della Morte, Georg von Hippel, Tereza Mendes, Rainer Sommer](https://arxiv.org/abs/0902.1265) + From f77e840d1a043f00df911263a236ee18b52a4800 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 17 Jul 2023 13:04:18 +0700 Subject: [PATCH 08/80] fix: fix interpreter Signed-off-by: slowy07 --- clara_test/run_test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clara_test/run_test b/clara_test/run_test index 8c1ac99..859983f 100755 --- a/clara_test/run_test +++ b/clara_test/run_test @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/bin/bash OS=$(uname -s) PACKAGE_MANAGER="" From e900dfe3801f8dbcebaca1db18cfeeeb3bc7bc49 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 17 Jul 2023 13:10:31 +0700 Subject: [PATCH 09/80] fix: fix interpreter Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 28 --------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/cpp-testing-mac.yml diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml deleted file mode 100644 index d5795e2..0000000 --- a/.github/workflows/cpp-testing-mac.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Mac Clara Test - -on: - push: - branches: - - main - - development - pull_request: - branches: - - main - - development - -jobs: - build-and-testing: - runs-on: macos-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Eigen - run: brew install eigen - - - name: Build and test - run: | - cd clara_test - chmod +x run_test - ./run_test From f13be23dfa21d9f018051d82e29c3dbe6e7bc5d4 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Fri, 21 Jul 2023 11:44:31 +0700 Subject: [PATCH 10/80] chore(feat): improvement exception function call [Documentation] make expection hierarchy for custom exceptions, this exception based on the standard ``std::exception`` class, provide a mechanisem for creating custom exception types with specific description that can be thrown and caught in the application code Signed-off-by: slowy07 --- clara_test/tests/CMakeLists.txt | 4 +- clara_test/tests/classFunction/states.cpp | 15 + clara_test/tests/classFunction/timer.cpp | 27 ++ clara_test/tests/operations.cpp | 74 ++++ include/clara.h | 5 +- include/classFunction/codes.h | 6 +- include/classFunction/exception.h | 481 +++++++++++++++------- include/classFunction/gates.h | 55 +-- include/classFunction/idisplay.h | 12 + include/classFunction/init.h | 4 +- include/classFunction/random_devices.h | 29 +- include/classFunction/states.h | 77 +++- include/classFunction/timer.h | 7 + include/constants.h | 2 +- include/entanglement.h | 116 +++--- include/entropies.h | 42 +- include/experimental/experimental_test.h | 268 +++++++++++- include/functions.h | 283 ++++++++----- include/input_output.h | 2 +- include/instruments.h | 139 +++---- include/internal/util.h | 50 ++- include/macros.h | 20 - include/number_theory.h | 63 ++- include/operations.h | 232 ++++++----- include/random.h | 68 +-- include/statistics.h | 26 +- 26 files changed, 1431 insertions(+), 676 deletions(-) create mode 100644 clara_test/tests/classFunction/states.cpp create mode 100644 clara_test/tests/classFunction/timer.cpp create mode 100644 clara_test/tests/operations.cpp delete mode 100644 include/macros.h diff --git a/clara_test/tests/CMakeLists.txt b/clara_test/tests/CMakeLists.txt index f5cb442..c82392a 100644 --- a/clara_test/tests/CMakeLists.txt +++ b/clara_test/tests/CMakeLists.txt @@ -1,3 +1,5 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) -add_executable(clara_testing classFunction/gates.cpp testing_main.cpp traits.cpp) +add_executable( + clara_testing classFunction/gates.cpp classFunction/states.cpp + classFunction/timer.cpp testing_main.cpp traits.cpp) target_link_libraries(clara_testing gmock) diff --git a/clara_test/tests/classFunction/states.cpp b/clara_test/tests/classFunction/states.cpp new file mode 100644 index 0000000..a0a6282 --- /dev/null +++ b/clara_test/tests/classFunction/states.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(clara_States_zero, AllTests) { + idx n = 1; + EXPECT_NEAR(0, norm(clara::st.z0 - clara::st.zero(n)), 1e-7); + + n = 2; + EXPECT_NEAR(0, norm(kron(st.z0, st.z0) - clara::st.zero(n)), 1e-7); +} diff --git a/clara_test/tests/classFunction/timer.cpp b/clara_test/tests/classFunction/timer.cpp new file mode 100644 index 0000000..081f60c --- /dev/null +++ b/clara_test/tests/classFunction/timer.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; +TEST(clara_Timer_get_duration, AllTests) { + using namespace std::chrono; + Timer<> t1; + std::this_thread::sleep_for(seconds(1)); + t1.toc(); + + // in seconds + auto duration_t1_s = t1.get_duration(); + EXPECT_NEAR(duration_t1_s.count(), 1, 0.01); + + auto duration_t1_ms = t1.get_duration(); + EXPECT_NEAR(duration_t1_ms.count(), 1000, 10); + + Timer t2; + std::this_thread::sleep_for(microseconds(100000)); + t2.toc(); + + auto duration_t2_micros = t2.get_duration(); + EXPECT_NEAR(duration_t2_micros.count(), 100000, 10000); +} diff --git a/clara_test/tests/operations.cpp b/clara_test/tests/operations.cpp new file mode 100644 index 0000000..04505c6 --- /dev/null +++ b/clara_test/tests/operations.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "gtest/gtest.h" +#include "../../include/clara.h" + +using namespace clara; + +TEST(clara_applyCTRL, NoEmptyControl) { + std::vector dims{2, 2, 2, 2}; + idx D = prod(dims); + + std::vector ctrl{2, 0}; + std::vector target{1, 3}; + + // dimension of the target subsystem + idx Dtarget = 1; + for (idx i = 0; i < target.size(); ++i) + Dtarget *= dims[target[i]]; + + // random n qudit pure state + ket psi = randket(D); + + // the corresponding density matrix + cmat rho = psi * adjoint(psi); + // some random unitary on the target + cmat U = randU(Dtarget); + + // applyCTRL on pure state + ket A = applyCTRL(psi, U, ctrl, target, dims); + + // applyCTRL on density matrix + cmat B = applyCTRL(rho, U, ctrl, target, dims); + + cmat result_psi = A * adjoint(A); + cmat result_rho = B; + + double res = norm(result_psi - result_rho); + EXPECT_NEAR(0, res, 1e-7); +} + +TEST(clara_applyCTRL, EmptyControl) { + // 3 qubits + std::vector dims{2, 2, 2, 2}; + idx D = prod(dims); + + // apply control + std::vector ctrl{}; + // target + std::vector target{1, 0, 3}; + + // dimension of the target susbystem + idx Dtarget = 1; + for (idx i = 0; i < target.size(); ++i) + Dtarget *= dims[target[i]]; + + // random n qudit pure state + ket psi = randket(D); + + // corresponding density matrix + cmat rho = psi * adjoint(psi); + cmat U = randU(Dtarget); + + // applyCTRL on pure state + ket A = applyCTRL(psi, U, ctrl, target, dims); + + // applyCtrl on density matrix + cmat B = applyCTRL(rho, U, ctrl, target, dims); + + cmat result_psi = A * adjoint(A); + cmat result_rho = B; + + double res = norm(result_psi - result_rho); + EXPECT_NEAR(0, res, 1e-7); +} diff --git a/include/clara.h b/include/clara.h index 864e06a..fc5b0bd 100644 --- a/include/clara.h +++ b/include/clara.h @@ -36,8 +36,8 @@ #include #include -// preprocessor macro -#include "macros.h" +#include +#include // inter dependicies #include "classFunction/codes.h" @@ -57,7 +57,6 @@ #include "internal/classFunction/iomanip.h" #include "internal/classFunction/singleton.h" #include "internal/util.h" -#include "macros.h" #include "number_theory.h" #include "operations.h" #include "random.h" diff --git a/include/classFunction/codes.h b/include/classFunction/codes.h index fcea354..ee4c3d9 100644 --- a/include/classFunction/codes.h +++ b/include/classFunction/codes.h @@ -46,7 +46,7 @@ class Codes final : public internal::Singleton { 4.; break; default: - throw Exception("clara::Codes::codeword()", Exception::Type::NO_CODEWORD); + throw exception::NoCodeword("clara::Codes::codeword()"); } break; case Type::SEVEN_QUBIT_STEANE: @@ -66,7 +66,7 @@ class Codes final : public internal::Singleton { std::sqrt(8.); break; default: - throw Exception("clara::Codes::codeword()", Exception::Type::NO_CODEWORD); + throw exception::NoCodeword("clara::Codes::codeword()"); } break; case Type::NINE_QUBIT_SHOR: @@ -89,7 +89,7 @@ class Codes final : public internal::Singleton { result = kron(shorb, kron(shorb, shorb)) / std::sqrt(8.); break; default: - throw Exception("clara::Codes::codeword()", Exception::Type::NO_CODEWORD); + throw exception::NoCodeword("clara::Codes::codeword()"); } } return result; diff --git a/include/classFunction/exception.h b/include/classFunction/exception.h index 7bcbe53..5bd42cb 100644 --- a/include/classFunction/exception.h +++ b/include/classFunction/exception.h @@ -5,164 +5,349 @@ #include namespace clara { +namespace exception { + class Exception : public std::exception { + private: + std::string where_; + mutable std::string msg_; + public: - enum class Type { - UNKNOWN_EXCEPTION = 1, - ZERO_SIZE, - MATRIX_NOT_SQUARE, - MATRIX_NOT_CVECTOR, - MATRIX_NOT_RVECTOR, - MATRIX_NOT_VECTOR, - MATRIX_NOT_SQUARE_OR_CVECTOR, - MATRIX_NOT_SQUARE_OR_RVECTOR, - MATRIX_NOT_SQUARE_OR_VECTOR, - MATRIX_MISMATCH_SUBSYS, - DIMS_INVALID, - DIMS_NOT_EQUAL, - DIMS_MISMATCH_MATRIX, - DIMS_MISMATCH_CVECTOR, - DIMS_MISMATCH_RVECTOR, - DIMS_MISMATCH_VECTOR, - SUBSYS_MISMATCH_DIMS, - PERM_INVALID, - PERM_MISMATCH_DIMS, - NOT_QUBIT_MATRIX, - NOT_QUBIT_CVECTOR, - NOT_QUBIT_RVECTOR, - NOT_QUBIT_VECTOR, - NOT_QUBIT_SUBSYS, - NOT_BIPARTITE, - NO_CODEWORD, - OUT_OF_RANGE, - TYPE_MISMATCH, - SIZE_MISMATCH, - UNDEFINED_TYPE, - CUSTOM_EXCEPTION - }; - - /** - * construct exception - */ - Exception(const std::string& where, [[maybe_unused]] const Type& type) - : where_{where}, msg_{}, type_{}, custom_{} { - construct_exception_msg_(); - } + Exception(const std::string& where) : where_{where}, msg_{} {} - Exception(const std::string& where, const std::string& custom) - : where_{where}, msg_{}, type_{Type::CUSTOM_EXCEPTION}, custom_{custom} { - construct_exception_msg_(); + virtual const char* what() const noexcept override { + msg_.clear(); + msg_ += "IN "; + msg_ += where_; + msg_ += ": "; + msg_ += this->type_description(); + msg_ += "!"; + return msg_.c_str(); } + virtual std::string type_description() const = 0; +}; - virtual const char* what() const noexcept override { return msg_.c_str(); } +inline std::string Exception::type_description() const { return "clara::exception::Exception"; } - private: - std::string where_, msg_; - Type type_; - std::string custom_; +/** + * @class clara::exception::Unknwon + * thrown when no other exception is suitable + */ +class Unknown : public Exception { + public: + std::string type_description() const override { return "UNKNOWN EXCEPTION"; } + using Exception::Exception; +}; - void construct_exception_msg_() { - msg_ += "IN "; - msg_ += where_; - msg_ += ": "; - switch (type_) { - case Type::UNKNOWN_EXCEPTION: - msg_ += "UNKNOWN EXCEPTION!"; - break; - case Type::ZERO_SIZE: - msg_ += "Object has zero size!"; - break; - case Type::MATRIX_NOT_SQUARE: - msg_ += "Matrix is not square!"; - break; - case Type::MATRIX_NOT_CVECTOR: - msg_ += "Matrix is not column vector!"; - break; - case Type::MATRIX_NOT_RVECTOR: - msg_ += "Matrix is not row vector!"; - break; - case Type::MATRIX_NOT_VECTOR: - msg_ += "Matrix is not vector!"; - break; - case Type::MATRIX_NOT_SQUARE_OR_CVECTOR: - msg_ += "Matrix is not square nor column vector!"; - break; - case Type::MATRIX_NOT_SQUARE_OR_RVECTOR: - msg_ += "Matrix is not square nor row vector!"; - break; - case Type::MATRIX_NOT_SQUARE_OR_VECTOR: - msg_ += "Matrix is not square nor vector!"; - break; - case Type::MATRIX_MISMATCH_SUBSYS: - msg_ += "Matrix mismatch subsystems!"; - break; - case Type::DIMS_INVALID: - msg_ += "Invalid dimension(s)!"; - break; - case Type::DIMS_NOT_EQUAL: - msg_ += "Dimensions not equal!"; - break; - case Type::DIMS_MISMATCH_MATRIX: - msg_ += "Dimension(s) mismatch matrix size!"; - break; - case Type::DIMS_MISMATCH_CVECTOR: - msg_ += "Dimension(s) mismatch column vector!"; - break; - case Type::DIMS_MISMATCH_RVECTOR: - msg_ += "Dimension(s) mismatch row vector!"; - break; - case Type::DIMS_MISMATCH_VECTOR: - msg_ += "Dimension(s) mismatch vector!"; - break; - case Type::SUBSYS_MISMATCH_DIMS: - msg_ += "Subsystems mismatch dimensions!"; - break; - case Type::PERM_INVALID: - msg_ += "Invalid permutation!"; - break; - case Type::PERM_MISMATCH_DIMS: - msg_ += "Permutation mismatch dimensions!"; - break; - case Type::NOT_QUBIT_MATRIX: - msg_ += "Matrix is not 2 x 2!"; - break; - case Type::NOT_QUBIT_CVECTOR: - msg_ += "Column vector is not 2 x 1!"; - break; - case Type::NOT_QUBIT_RVECTOR: - msg_ += "Row vector is not 1 x 2!"; - break; - case Type::NOT_QUBIT_VECTOR: - msg_ += "Vector is not 2 x 1 nor 1 x 2!"; - break; - case Type::NOT_QUBIT_SUBSYS: - msg_ += "Subsystems are not qubits!"; - break; - case Type::NOT_BIPARTITE: - msg_ += "Not bi-partite!"; - break; - case Type::NO_CODEWORD: - msg_ += "Codeword does not exist!"; - break; - case Type::OUT_OF_RANGE: - msg_ += "Parameter out of range!"; - break; - case Type::TYPE_MISMATCH: - msg_ += "Type mismatch!"; - break; - case Type::SIZE_MISMATCH: - msg_ += "Size mismatch!"; - break; - case Type::UNDEFINED_TYPE: - msg_ += "Not defined for this type!"; - break; - case Type::CUSTOM_EXCEPTION: - msg_ += "CUSTOM EXCEPTION "; - break; - } +/** + * @brief exception type description + * @return object has zero size exception + */ + +class ZeroSize : public Exception { + public: + std::string type_description() const override { return "Object has zero size"; } + using Exception::Exception; +}; + +/** + * @brief clara::exception::MatrixNotSquare + * @brief matrix is not square exception + */ +class MatrixNotSquare : public Exception { + public: + std::string type_description() const override { return "Matrix is not square"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::MatrixNotCvector + * @brief matrix is not column vector exception + */ + +class MatrixNotCvector : public Exception { + public: + std::string type_description() const override { return "Matrix is not column vector"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::MatrixRowVector + * @brief matrix is not a row vector exception + */ +class MatrixNotRvector : public Exception { + public: + std::string type_description() const override { return "Matrix is not row vector"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::MatrixxNotVector + * @brief matrix is not vector Exception + */ +class MatrixNotVector : public Exception { + public: + std::string type_description() const override { return "Matrix is not vector"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::MatrixSquareNotCvector + * @brief matrix is not column vector exception + */ +class MatrixNotSquareNotCvector : public Exception { + public: + std::string type_description() const override { return "Matrix is not square not column vector"; } + using Exception::Exception; +}; + +/** + * @brief clara::exception::MatrixNotSquareNotRvector + * @biref matrix is not square not row vector exception + */ +class MatrixNotSquareNotRvector : public Exception { + public: + std::string type_description() const override { return "Matrix is not square not row vector"; } + using Exception::Exception; +}; + +/** + * @brief clara::Exception::MatrixNotSquareNotVector + * @brief matrix is not square not vector exception + */ +class MatriNotSquareNotVector : public Exception { + public: + std::string type_description() const override { return "Matrix is not square not vector"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::MatrixMismatchSubsys + * @brief matrix mismatch subsytem exception + */ +class MatrixMismatchSubsys : public Exception { + public: + std::string type_description() const override { return "Matrix mismatch subsystem"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsInvalid + * @brief invalid dimension exception + */ +class DimsInvalid : public Exception { + public: + std::string type_description() const override { return "Invalid dimension (s)"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsNotEqual + * @brief dimension not equal exception + */ +class DimsNotEqual : public Exception { + public: + std::string type_description() const override { return "Dimensional not equal"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsMismatchMatrix + * @brief dimension mismatch matrix size exception + */ +class DimsMismatchMatrix : public Exception { + public: + std::string type_description() const override { return "Dimension mismatch matrix size"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsMismatchCvector + * @brief Dimension(s) mismatch column vector size exception + * product of the elements of std::vector of dimension is not equal to + * the number of elements of the Eigen::Matrix + */ +class DimsMismatchCvector : public Exception { + public: + std::string type_description() const override { + return "Dimension(s) mismatch column vector size"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsMismatchRvector + * @brief Dimension(s) mismatch row vector size exception + * product of the elements of std::vector of dimension is not equal + * to the number of the elements of the Eigen::Matrix + */ +class DimsMismatchRvector : public Exception { + public: + std::string type_description() const override { return "Dimension(s) mismatch row vector size"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::DimsMismatchVector + * @brief Dimension mismatch vector size exception + * prodcut of the element of std::vector of dimension is not equal + */ +class DimsMismatchVector : public Exception { + public: + std::string type_description() const override { return "Dimension(s) mismatch vector size"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::SubsysMismatchDims + * @brief Subsystem mismatch dimension exception + */ +class SubsysMismatchdims : public Exception { + public: + std::string type_description() const override { return "Subsytem mismatch dimensions"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::PermInvalid + * @brief invalid permutation exception + */ +class PermInvalid : public Exception { + public: + std::string type_description() const override { return "Invalid permutation"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::PermMismatchDims + * @brief permutateion mismatch dimension exception + */ +class PermMismatchDims : public Exception { + public: + std::string type_description() const override { return "Permutation mismatch dimensions"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NotQubitMatrix + * @brief matrix is not 2 x 2 exception + */ +class NotQubitMatrix : public Exception { + public: + std::string type_description() const override { return "Matrix is not 2 x 2"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NOtQubitCvector + * @brief column vector is not 2 x 1 exception + */ +class NOtQubitCvector : public Exception { + public: + std::string type_description() const override { return "Column vector is not 2 x 1"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NotQubitRvector + * @brief row vector is not 1 x 2 exception + */ +class NotQubitRvector : public Exception { + public: + std::string type_description() const override { return "Row vector is not 1 x 2"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NotQubitVector + * @brief vector is not 2 x 1 nor 1 x 2 exception + */ +class NotQubitVector : public Exception { + public: + std::string type_description() const override { return "Vector is not 2 x 1 nor 1 x 2"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NotQubitSubsys + * @brief Subystem are not qubits exception + */ +class NotQubitSubsys : public Exception { + public: + std::string type_description() const override { return "Subsytem are not qubits"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NotBipartite + * @brief not bi-bipartite exception + */ +class NotBipartite : public Exception { + public: + std::string type_description() const override { return "Not bi-bipartite"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::NoCodeword + * @brief codeword does not exist exception + */ +class NoCodeword : public Exception { + public: + std::string type_description() const override { return "Codeword does not exist"; }; + using Exception::Exception; +}; + +/** + * @class clara::exception::OutOfRange + * @brief paramtere out of range exception + */ +class OutOfRange : public Exception { + public: + std::string type_description() const override { return "Parameter out of range"; } + using Exception::Exception; +}; + +class TypeMismatch : public Exception { + public: + std::string type_description() const override { return "Type mismatch"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::SizMismatch + * @brief size mismatch exception + */ +class SizeMismatch : public Exception { + public: + std::string type_description() const override { return "Size mismatch"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::UndefinedType + * @brief not defined for this type exception + */ +class UndefinedType : public Exception { + public: + std::string type_description() const override { return "Not defined for this type"; } + using Exception::Exception; +}; + +/** + * @class clara::exception::CustomException + * @brief custom exception + */ +class CustomException : public Exception { + std::string what_{}; + std::string type_description() const override { return "CUSTOM EXCEPTION " + what_; } + + public: + CustomException(const std::string& where, const std::string& what) + : Exception{where}, what_{what} {} }; +} // namespace exception } // namespace clara #endif // !CLASSFUNCTION_EXCEPTION_H_ diff --git a/include/classFunction/gates.h b/include/classFunction/gates.h index 428c620..0e54bd4 100644 --- a/include/classFunction/gates.h +++ b/include/classFunction/gates.h @@ -69,7 +69,7 @@ class Gates final : public internal::Singleton { */ cmat Rn(double theta, const std::vector& n) const { if (n.size() != 3) - throw Exception("clara::gates::Rn()", "n is not a 3-dimensional vector"); + throw exception::CustomException("clara::Gates::Rn()", "n is not a 3-dimensional vector!"); cmat result(2, 2); result = std::cos(theta / 2) * Id2 - 1_i * std::sin(theta / 2) * (n[0] * X + n[1] * Y + n[2] * Z); @@ -80,12 +80,12 @@ class Gates final : public internal::Singleton { * @brief generalized Z gates for qudits * @note define as \f$ Z = \sum_{j=0}^{D-1} \exp(2\pi \mathrm{i} j/D) |j\rangle\langle j| \f$ */ - cmat Zd(idx D) const { + cmat Zd(idx D = 2) const { if (D == 0) - throw Exception("clara::Gates::Zd()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::Zd()"); cmat result = cmat::Zero(D, D); for (idx i = 0; i < D; ++i) - result(i, i) = std::pow(omega(D), i); + result(i, i) = std::pow(omega(D), static_cast(i)); return result; } @@ -93,16 +93,16 @@ class Gates final : public internal::Singleton { * @brief fourier transform gate for qudits * @note define as \f$ F = \sum_{j,k=0}^{D-1} \exp(2\pi \mathrm{i} jk/D) |j\rangle\langle k| \f$ */ - cmat Fd(idx D) const { + cmat Fd(idx D = 2) const { if (D == 0) - throw Exception("clara::Gates::Fd()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::Fd()"); cmat result(D, D); #ifdef WITH_OPENMP_ #pragma omp parallel for collapse(2) #endif // DEBUG for (idx j = 0; j < D; ++j) for (idx i = 0; i < D; ++i) - result(i, j) = 1 / std::sqrt(D) * std::pow(omega(D), i * j); + result(i, j) = 1 / std::sqrt(D) * std::pow(omega(D), static_cast(i * j)); return result; } @@ -111,9 +111,9 @@ class Gates final : public internal::Singleton { * @note define as f$ X = \sum_{j=0}^{D-1} |j\oplus 1\rangle\langle j| \f$, ie raising operator * \f$ X|j\rangle = |j\oplus 1\rangle\f$ */ - cmat Xd(idx D) const { + cmat Xd(idx D = 2) const { if (D == 0) - throw Exception("clara::Gates::Xd()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::Xd()"); return Fd(D).inverse() * Zd(D) * Fd(D); } @@ -123,9 +123,9 @@ class Gates final : public internal::Singleton { * specifying the template paramter */ template - Derived Id(idx D) const { + Derived Id(idx D = 2) const { if (D == 0) - throw Exception("clara::Gates::Id()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::Id()"); return Derived::Identity(D, D); } @@ -141,24 +141,24 @@ class Gates final : public internal::Singleton { // check matrix zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::Gates::CTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::Gates::CTRL()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::Gates::CTRL()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::Gates::CTRL()"); // check list zero size if (ctrl.size() == 0) - throw Exception("clara::Gates::CTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::Gates::CTRL()"); if (subsys.size() == 0) - throw Exception("clara::Gates::CTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::Gates::CTRL()"); // check out of range if (N == 0) - throw Exception("clara::Gates::CTRL()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::Gates::CTRL()"); // check valid local dimension if (d == 0) - throw Exception("clara::Gates::CTRL()", Exception::Type::DIMS_INVALID); + throw exception::OutOfRange("clara::Gates::CTRL()"); // control gate subsystem std::vector ctrlgate = ctrl; @@ -172,11 +172,12 @@ class Gates final : public internal::Singleton { * with respect to local dimensions */ if (!internal::check_subsys_match_dims(ctrlgate, dims)) - throw Exception("clara::Gates::CTRL()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::Gates::CTRL()"); // check that subsys list match the dimension of the matrix - if (rA.rows() != std::llround(std::pow(d, subsys.size()))) - throw Exception("clara::Gates::CTRL()", Exception::Type::DIMS_MISMATCH_MATRIX); + using Index = typename dyn_mat::Index; + if (A.rows() != static_cast(std::llround(std::pow(d, subsys.size())))) + throw exception::DimsMismatchMatrix("clara::Gates::CTRL()"); idx Cdims[maxn]; idx midx_row[maxn]; @@ -266,20 +267,20 @@ class Gates final : public internal::Singleton { const std::vector& dims) const { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::Gates::expandout()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::Gates::expandout()"); // check that dims is a valid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::Gates::expandout()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::expandout()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::Gates::expandout()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::Gates::expandout()"); // check that position is valid if (pos > dims.size() - 1) - throw Exception("clara::Gates::expandout()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::Gates::expandout()"); // check that dims[pos] match dimension of A if (static_cast(rA.rows()) != dims[pos]) - throw Exception("clara::Gates::expandout()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::Gates::expandout()"); idx D = std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies()); @@ -333,10 +334,10 @@ class Gates final : public internal::Singleton { dyn_mat expandout(const Eigen::MatrixBase& A, idx pos, idx N, idx d = 2) const { if (!internal::check_nonzero_size(A)) - throw Exception("clara::Gates::expandout()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::Gates::expandout()"); // check valid dims if (d == 0) - throw Exception("clara::Gates::expandout()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::Gates::expandout()"); std::vector dims(N, d); return this->expandout(A, pos, dims); diff --git a/include/classFunction/idisplay.h b/include/classFunction/idisplay.h index 10b117e..446704d 100644 --- a/include/classFunction/idisplay.h +++ b/include/classFunction/idisplay.h @@ -2,8 +2,15 @@ #define CLASSFUNCTION_IDISPLAY_H_ #include +namespace clara { class IDisplay { private: + /** + * @brief this function must be implemented in all derived classes + * the derived classes overrided this function to perform the actual + * stream extraction processing. the function is automatically called + * by the friend inline function + */ virtual std::ostream& display(std::ostream& os) const = 0; public: @@ -12,9 +19,14 @@ class IDisplay { IDisplay(IDisplay&&) = default; IDisplay& operator=(const IDisplay&) = default; virtual ~IDisplay() = default; + + /** + * @brief overloads the extraction operator + */ friend inline std::ostream& operator<<(std::ostream& os, const IDisplay& rhs) { return rhs.display(os); } }; +} // namespace clara #endif // !CLASSFUNCTION_IDISPLAY_H_ diff --git a/include/classFunction/init.h b/include/classFunction/init.h index 92798a3..395508d 100644 --- a/include/classFunction/init.h +++ b/include/classFunction/init.h @@ -13,8 +13,8 @@ class Init final : public internal::Singleton { private: Init() { - std::cout << std::fixed; - std::cout << std::setprecision(4); + // std::cout << std::fixed; + // std::cout << std::setprecision(4); } ~Init() {} }; diff --git a/include/classFunction/random_devices.h b/include/classFunction/random_devices.h index aee9289..7156c39 100644 --- a/include/classFunction/random_devices.h +++ b/include/classFunction/random_devices.h @@ -1,6 +1,7 @@ #ifndef CLASSFUNCTION_RANDOM_DEVICES_H_ #define CLASSFUNCTION_RANDOM_DEVICES_H_ +#include #include #include "../internal/classFunction/singleton.h" @@ -15,13 +16,35 @@ namespace clara { class RandomDevices final : public internal::Singleton { friend class internal::Singleton; std::random_device rd_; + std::mt19937 prng_; public: - std::mt19937 rng_; + /** + * @brief returns a reference to the internal PRNG object + * @return the reference to the internal PRNG object + */ + std::mt19937& get_prng() { return prng_; } + + /** + * @brief load the state of the PRNG from an input stream + * @param input stream + * @return input stream + */ + std::istream& load(std::istream& is) { return is >> prng_; } + + /** + * @brief save the state of the PRNG to an output stream + * @param os output stream + * @return the output stream + */ + std::ostream& save(std::ostream& os) const { return os << prng_; } private: - // initialize and seed the random number generator - RandomDevices() : rd_{}, rng_{rd_()} {} + /* + * @brief intialize and seed the random number generator + */ + RandomDevices() : rd_{}, prng_{rd_()} {} + // default constructor ~RandomDevices() = default; }; diff --git a/include/classFunction/states.h b/include/classFunction/states.h index f94b536..17fedd8 100644 --- a/include/classFunction/states.h +++ b/include/classFunction/states.h @@ -1,8 +1,12 @@ #ifndef CLASSFUNCTION_STATES_H_ #define CLASSFUNCTION_STATES_H_ +#include +#include + #include "../internal/classFunction/singleton.h" #include "codes.h" +#include "exception.h" namespace clara { @@ -58,7 +62,7 @@ class States final : public internal::Singleton { ket mes(idx d = 2) const { // check valid dims if (d == 0) - throw Exception("clara::States::mes()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::States::mes()"); ket psi = mket({0, 0}, {d, d}); for (idx i = 1; i < d; ++i) { psi += mket({i, i}, {d, d}); @@ -66,6 +70,77 @@ class States final : public internal::Singleton { return psi / std::sqrt(d); } + /** + * @brief zero state of n qudits + * @return zero state \f$|0\rangle^{\otimes n}\f$ of n qudits + */ + ket zero(idx n, idx d = 2) const { + if (n == 0) + throw exception::OutOfRange("clara::States::zero()"); + if (d == 0) + throw exception::DimsInvalid("clara::States::zero()"); + idx D = static_cast(std::pow(d, n)); + ket result = ket::Zero(D); + result(0) = 1; + + return result; + } + + /** + * @brief one state of n qudits + * @return one statem f$|1\rangle^{\otimes n}\f$ of qudits + */ + ket one(idx n, idx d = 2) const { + if (n == 0) + throw exception::OutOfRange("clara::States::one()"); + if (d == 0) + throw exception::DimsInvalid("clara::States::one()"); + ket result = ket::Zero(static_cast(std::pow(d, n))); + result(multiidx2n(std::vector(n, 1), std::vector(n, d))) = 1; + return result; + } + + /** + * @brief \f$|j\rangle^{\otimes n}\f$ state of n qudits + * @return \f$|j\rangle^{\otimes n}\f$ state of n qudits + */ + ket jn(idx j, idx n, idx d = 2) const { + if (n == 0) + throw exception::OutOfRange("clara::States::jn()"); + if (j >= d) + throw exception::SubsysMismatchdims("clara::States::jn()"); + + if (j >= d) + throw exception::DimsInvalid("clara::States::jn()"); + + ket result = ket::Zero(static_cast(std::pow(d, n))); + result(multiidx2n(std::vector(n, j), std::vector(n, d))) = 1; + return result; + } + + /** + * @brief plus state of n qubits + * @return plus state \f$|+\rangle^{\otimes n}\f$ of qubits +*/ + ket plus(idx n) const { + if (n == 0) + throw exception::OutOfRange("clara::States::plus()"); + idx D = static_cast(std::pow(2, n)); + ket result = ket::Ones(D); + + return result / std::sqrt(D); + } + + /** + * @brief minus state of n qubits + * @return minus state \f$|-\rangle^{\otimes n}\f$ of n qubits +*/ + ket minus(idx n) const { + if (n == 0) + throw exception::OutOfRange("clara::States::minus()"); + return kronpow(this -> x1, n); + } + private: States() { x0 << 1 / std::sqrt(2.), 1 / std::sqrt(2.); diff --git a/include/classFunction/timer.h b/include/classFunction/timer.h index ba65a68..e6f38ad 100644 --- a/include/classFunction/timer.h +++ b/include/classFunction/timer.h @@ -18,6 +18,13 @@ class Timer : public IDisplay { * as starting point */ Timer() noexcept : start_{CLOCK_T::now()}, end_{start_} {} + + /** + * @brief reset the chronometer + * reset the starting/ending point to the current time + */ + void tic() noexcept { start_ = end_ = CLOCK_T::now(); } + /** * @brief stops the chronometer * set the current time as the ending point diff --git a/include/constants.h b/include/constants.h index 427f574..82f269d 100644 --- a/include/constants.h +++ b/include/constants.h @@ -35,7 +35,7 @@ constexpr double inifinity = std::numeric_limits::max(); inline cplx omega(idx D) { if (D == 0) - throw Exception::Type::OUT_OF_RANGE; + throw exception::OutOfRange("clara::omega()"); return exp(2.0 * pi * 1_i / static_cast(D)); } diff --git a/include/entanglement.h b/include/entanglement.h index e4893ec..968ff10 100644 --- a/include/entanglement.h +++ b/include/entanglement.h @@ -24,13 +24,13 @@ dyn_col_vect schmidcoeffs(const Eigen::MatrixBase& A, const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::schmidcoeffs()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::schmidtcoeffs()"); if (dims.size() != 2) - throw Exception("clara::schmidcoeffs()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::schmidtcoeffs()"); if (!internal::check_cvector(rA)) - throw Exception("clara::schmidcoeffs()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::schmidtcoeffs()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::schmidcoeffs()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchCvector("clara::schmidtcoeffs()"); return svals(transpose(reshape(rA, dims[1], dims[0]))); } @@ -45,9 +45,9 @@ dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, idx d = const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::schmidtcoeffs()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::schmidtcoeffs()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::schmidtcoeffs()"); + if (d < 2) + throw exception::DimsInvalid("clara::schmidtcoeffs()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -64,13 +64,13 @@ cmat schmidtA(const Eigen::MatrixBase& A, const std::vector& dims) const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::schmidtU()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::schmidtA()"); if (dims.size() != 2) - throw Exception("clara::schmidtU()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::schmidtA()"); if (!internal::check_cvector(rA)) - throw Exception("clara::schmidtU()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::schmidtA()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::schmidtU()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchCvector("clara::schmidtA()"); return svdU(transpose(reshape(rA, dims[1], dims[0]))); } @@ -84,9 +84,9 @@ template cmat schmidtA(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::schmidtA()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::schmidtA()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::schmidtA()"); + if (d < 2) + throw exception::DimsInvalid("clara::schmidtA()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -103,16 +103,17 @@ cmat schmidtB(const Eigen::MatrixBase& A, const std::vector& dims) const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::schmidtV()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::schmidtB()"); // check bi-partite if (dims.size() != 2) - throw Exception("clara::schmidtV()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::schmidtB()"); if (!internal::check_cvector(rA)) - throw Exception("clara::schmidtV()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::schmidtB()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::schmidtV()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchCvector("clara::schmidtB()"); + // by default returns U_B* return svdV(transpose(reshape(conjugate(rA), dims[1], dims[0]))); } @@ -125,11 +126,12 @@ template cmat schmidtB(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::schmidtB()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::schmidtB()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::schmidtB()"); + if (d < 0) + throw exception::DimsInvalid("clara::schmidtB()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); + // local dimension vector std::vector dims(N, d); return schmidtB(A, dims); } @@ -146,20 +148,21 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::schmidtprobs()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::schmidtprobs()"); if (dims.size() != 2) - throw Exception("clara::schmidtprobs()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::schmidtprobs()"); if (!internal::check_cvector(rA)) - throw Exception("clara::schmidtprobs()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::schmidtprobs()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::schmidtprobs()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchCvector("clara::schmidtprobs()"); std::vector result; dyn_col_vect scf = schmidtcoeffs(rA, dims); for (idx i = 0; i < static_cast(scf.rows()); ++i) result.push_back(std::pow(scf(i), 2)); + return result; } /** @@ -172,9 +175,9 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, idx d = 2) const dyn_mat rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::schmidtprobs()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::schmidtprobs()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::schmidtprobs()"); + if (d < 2) + throw exception::DimsInvalid("clara:schmidtprobs()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -191,14 +194,15 @@ template double entanglement(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::entanglement()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::entanglement()"); if (dims.size() != 2) - throw Exception("clara::entanglement()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::entanglement()"); if (!internal::check_cvector(rA)) - throw Exception("clara::entanglement()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::entanglement()"); // check matching dimensions - if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::entanglement()", Exception::Type::DIMS_MISMATCH_MATRIX); + if (!internal::check_dims_match_cvect(dims, rA)) + throw exception::DimsMismatchCvector("clara::entanglement()"); + return entropy(schmidtprobs(rA, dims)); } @@ -212,9 +216,9 @@ template double entanglement(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::entanglement()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::entanglement()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::entanglement()"); + if (d < 2) + throw exception::DimsInvalid("clara::entanglement()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -230,13 +234,13 @@ double gconcurrence(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::gconcurrence()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::gconcurrence()"); if (!internal::check_cvector(rA)) - throw Exception("clara::gconcurrence()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::gconcurrence()"); idx d = internal::get_dim_subsystem(static_cast(rA.rows()), 2); if (d * d != static_cast(rA.rows())) - throw Exception("clara::gconcurrence()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::gconcurrence()"); return d * std::abs(std::exp(2. / d * logdet(reshape(rA, d, d)))); } @@ -249,15 +253,15 @@ double negativity(const Eigen::MatrixBase& A, const std::vector& d const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::negativity()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::negativity()"); if (dims.size() != 2) - throw Exception("clara::negativity()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::negativity()"); // check square matrix vector if (!internal::check_square_mat(rA)) - throw Exception("clara::negativity()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::negativity()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::negativity()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::negativity()"); return (schatten(ptranspose(rA, {0}, dims), 1) - 1.) / 2.; } @@ -269,9 +273,9 @@ template double negetivity(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::negativity()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::negativity()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::negativity()"); + if (d < 2) + throw exception::DimsInvalid("clara::negativity()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -286,13 +290,13 @@ template double lognegativity(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::lognegativity()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::lognegativity()"); if (dims.size() != 2) - throw Exception("clara::lognegativity()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::lognegativity()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::lognegativity()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::lognegativity()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::lognegativity()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::lognegativity()"); return std::log2(2 * negativity(rA, dims) + 1); } @@ -305,9 +309,9 @@ double lognegativity(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::lognegativity()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::lognegativity()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::lognegativity()"); + if (d < 0) + throw exception::DimsInvalid("clara::lognegativity()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -323,11 +327,11 @@ double concurrence(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::concurrence()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::concurrence()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::concurrence()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::concurrence()"); if (rA.rows() != 4) - throw Exception("clara::concurrence()", Exception::Type::NOT_QUBIT_SUBSYS); + throw exception::NotQubitSubsys("clara::concurrence()"); cmat sigmaY = Gates::get_instance().Y; dyn_col_vect lambdas = evals(rA * kron(sigmaY, sigmaY) * conjugate(rA) * kron(sigmaY, sigmaY)).real(); diff --git a/include/entropies.h b/include/entropies.h index 206dfd3..82fa76c 100644 --- a/include/entropies.h +++ b/include/entropies.h @@ -22,11 +22,11 @@ double entropy(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::entropy()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::entropy()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::entropy()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::entropy()"); dmat ev = svals(rA); double result = 0; @@ -42,7 +42,7 @@ double entropy(const Eigen::MatrixBase& A) { */ inline double entropy(const std::vector& prob) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::entropy()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::entropy()"); double result = 0; for (idx i = 0; i < prob.size(); ++i) if (std::abs(prob[i]) != 0) @@ -60,13 +60,13 @@ double renyi(const Eigen::MatrixBase& A, double alpha) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::renyi()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::renyi()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::renyi()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::renyi()"); if (alpha < 0) - throw Exception("clara::renyi()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::renyi()"); if (alpha == 0) return std::log2(rA.rows()); @@ -89,9 +89,9 @@ double renyi(const Eigen::MatrixBase& A, double alpha) { */ inline double renyi(const std::vector& prob, double alpha) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::renyi()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::renyi()"); if (alpha > 0) - throw Exception("clara::renyi()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::renyi()"); if (alpha == 0) return std::log2(prob.size()); if (alpha == 1) @@ -120,12 +120,12 @@ double tsallis(const Eigen::MatrixBase& A, double q) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::tsallis()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::tsallis()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::tsallis()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::tsallis()"); if (q < 0) - throw Exception("clara::tsallis()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::tsallis()"); if (q == 1) return entropy(rA) * std::log(2.); @@ -142,9 +142,9 @@ double tsallis(const Eigen::MatrixBase& A, double q) { */ inline double tsallis(const std::vector& prob, double q) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::tsallis()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::tsallis()"); if (q < 0) - throw Exception("clara::tsallis()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::tsallis()"); if (q == 1) return entropy(prob) * std::log(2.); @@ -165,24 +165,24 @@ double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::qmutualinfo()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::qmutualinfo()"); // check that dims is valid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::qmutualinfo()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::qmutualinfo()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::qmutualinfo()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::qmutualinfo()"); // check that dims match the dimension of A if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::qmutualinfo()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::qmutualinfo()"); // check that subsys are valid if (!internal::check_subsys_match_dims(subsysA, dims) || !internal::check_subsys_match_dims(subsysB, dims)) - throw Exception("clara::qmutualinfo()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::qmutualinfo()"); std::vector full_system(dims.size()); std::iota(std::begin(full_system), std::end(full_system), 0); @@ -219,9 +219,9 @@ double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::qmutualinfo()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::qmutualinfo()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::qmutualinfo()"); + if (d < 2) + throw exception::DimsInvalid("clara::qmutualinfo()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); diff --git a/include/experimental/experimental_test.h b/include/experimental/experimental_test.h index e6d8462..c2b0fdd 100644 --- a/include/experimental/experimental_test.h +++ b/include/experimental/experimental_test.h @@ -1,8 +1,274 @@ #ifndef EXPERIMENTAL_EXPERIMENTAL_TEST_H_ #define EXPERIMENTAL_EXPERIMENTAL_TEST_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using idx = std::size_t; + namespace clara { -namespace experimental {} +namespace experimental { + +/** + * @class clara::Dynamic_bitset + * @brief dynamic bitset class, allow the specification of + * the number of bits at runtime + */ +class Dynamic_bitset { + public: + using value_type = unsigned int; + using storage_type = std::vector; + + protected: + // storage size + idx storage_size_; + // number of bits + idx N_; + // storage space + std::vector v_; + + /** + * @brief index of the pos bit in the storage space + * @return index of the pos bit in thestorage space + */ + idx index_(idx pos) const { return pos / (sizeof(value_type) * CHAR_BIT); } + + /** + * @brief constructor, intialize all bits to false + */ + idx offset_(idx pos) const { return pos % (sizeof(value_type) * CHAR_BIT); } + + /** + * @brief offset of the pos bit in the storage space relativea + * to its index + * @return offset of the pos bit in the storage space relative its + * index + */ + public: + Dynamic_bitset(idx N) + : storage_size_{N / (sizeof(value_type) * CHAR_BIT) + 1}, N_{N}, v_(storage_size_) {} + + /** + * @brief raw storage space of the bitset + * @return const reference to the underlying + */ + const storage_type& data() const { return v_; } + + /** + * @brief number of bits stored in the bitset + * @return number of bits + */ + idx size() const { return N_; } + + /** + * @brief size of the underlying stroage space + * @return size of the underlying storage space + */ + idx storage_size() const { return storage_size_; } + + idx count() const noexcept { + std::size_t result = 0; + for (idx i = 0; i < size(); ++i) { + if (this->get(i)) + ++result; + } + return result; + } + + bool get(idx pos) const { return 1 & (v_[index_(pos)] >> offset_(pos)); } + bool none() const noexcept { + bool result = true; + for (idx i = 0; i < storage_size(); ++i) { + if (v_[i]) { + return false; + } + } + return result; + } + + bool all() const noexcept { + bool result = true; + for (idx i = 0; i < storage_size(); ++i) { + if (~v_[i]) { + return false; + } + } + return false; + } + + bool any() const noexcept { return !(this->none()); } + + Dynamic_bitset& set(idx pos, bool value = true) { + value ? v_[index_(pos)] |= (1 << offset_(pos)) : v_[index_(pos)] &= ~(1 << offset_(pos)); + return *this; + } + + Dynamic_bitset& set() noexcept { + for (idx i = 0; i < storage_size(); ++i) { + v_[i] = ~0; + } + return *this; + } + + // set the bit according to a random bernouli distribution + Dynamic_bitset& rand(idx pos, double p = 0.5) { + std::random_device rd; + std::mt19937 gen{rd()}; + std::bernoulli_distribution d{p}; + + this->set(pos, d(gen)); + return *this; + } + + // set all bits according to a random bernouli distribution + Dynamic_bitset& rand(double p = 0.5) { + for (idx i = 0; i < size(); ++i) { + this->rand(i, p); + } + return *this; + } + + // set bit false + Dynamic_bitset& reset(idx pos) { + v_[index_(pos)] &= ~(1 << offset_(pos)); + return *this; + } + + // set all bits 0 + Dynamic_bitset& reset() noexcept { + for (idx i = 0; i < storage_size(); ++i) { + v_[i] = 0; + } + return *this; + } + + // flips the bit + Dynamic_bitset& flip(idx pos) { + v_[index_(pos)] ^= 1 << (offset_(pos)); + return *this; + } + + Dynamic_bitset& flip() noexcept { + for (idx i = 0; i < storage_size(); ++i) { + v_[i] = ~v_[i]; + } + return *this; + } + + // operator + bool operator==(const Dynamic_bitset& rhs) const noexcept { + assert(this->size() == rhs.size()); + bool result = true; + idx n = std::min(this->storage_size(), rhs.storage_size()); + for (idx i = 0; i < n; ++i) { + if (v_[i] != rhs.v_[i]) { + return false; + } + } + return result; + } + + bool operator!=(const Dynamic_bitset& rhs) const noexcept { return !(*this == rhs); } + + friend std::ostream& operator<<(std::ostream& os, const Dynamic_bitset& rhs) { + for (idx i = rhs.size(); i-- > 0;) { + os << rhs.get(i); + } + return os; + } + + template , + class Allocator = std::allocator> + std::basic_string to_string(CharT zero = CharT('0'), + CharT one = CharT('1')) const { + std::basic_string result; + idx bitset_size = this->size(); + result.resize(bitset_size); + + for (idx i = bitset_size; i-- > 0;) { + if (!this->get(i)) { + result[bitset_size - i - 1] = zero; + } else { + result[bitset_size - i - 1] = one; + } + } + return result; + } +}; + +/** + * @class clara::Bit_circuit + * @brief classical reversible circuit simulator + */ +class Bit_circuit : public Dynamic_bitset { + public: + struct Gate_count { + // 1 bit gates + idx NOT = 0; + idx& X = NOT; + + // 2 bit gates + idx CNOT = 0; + idx SWAP = 0; + + // 3 bit gates + idx FRED = 0; + idx TOF = 0; + } gate_count{}; + + // inherit the constructor + using Dynamic_bitset::Dynamic_bitset; + + // flip the bit + Bit_circuit& X(idx pos) { + this->flip(pos); + ++gate_count.X; + return *this; + } + + // flips the bit + Bit_circuit& NOT(idx pos) { + this->flip(pos); + ++gate_count.NOT; + return *this; + } + + // toffoli control-control-target + Bit_circuit& TOF(const std::vector& pos) { + v_[index_(pos[2])] ^= ((1 & (v_[index_(pos[1])] >> offset_(pos[1]))) & + (1 & (v_[index_(pos[0])] >> offset_(pos[0])))) + << offset_(pos[2]); + ++gate_count.TOF; + return *this; + } + + // SWAP 2 bits + Bit_circuit& SWAP(const std::vector& pos) { + if (this->get(pos[0]) != this->get(pos[1])) { + this->X(pos[0]); + this->X(pos[1]); + } + ++gate_count.SWAP; + return *this; + } + + Bit_circuit& reset() noexcept { + gate_count.NOT = gate_count.X = 0; + gate_count.CNOT = gate_count.SWAP = 0; + gate_count.FRED = gate_count.TOF = 0; + + return *this; + } +}; + +} // namespace experimental } // namespace clara #endif // !EXPERIMENTAL_EXPERIMENTAL_TEST_H_ diff --git a/include/functions.h b/include/functions.h index b5b53a4..f690f22 100644 --- a/include/functions.h +++ b/include/functions.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ template dyn_mat transpose(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::transpose()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::transpose()"); return rA.transpose(); } @@ -35,7 +36,7 @@ template dyn_mat conjugate(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::conjugate()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::conjugate()"); return rA.conjugate(); } @@ -44,7 +45,7 @@ template dyn_mat adjoint(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ajdoint()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::adjoint()"); return rA.adjoint(); } @@ -53,7 +54,7 @@ template dyn_mat inverse(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::inverse()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::inverse()"); return rA.inverse(); } @@ -63,7 +64,7 @@ typename Derived::Scalar trace(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::trace()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::trace()"); return rA.trace(); } @@ -72,7 +73,7 @@ template typename Derived::Scalar det(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::det()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::det()"); return rA.determinant(); } @@ -85,11 +86,11 @@ typename Derived::Scalar logdet(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::logdet()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::logdet()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::logdet()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::logdet()"); Eigen::PartialPivLU> lu(rA); dyn_mat U = lu.matrixLU().template triangularView(); @@ -105,7 +106,7 @@ template typename Derived::Scalar sum(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::sum()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::sum()"); return rA.sum(); } @@ -115,7 +116,7 @@ typename Derived::Scalar prod(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::prod()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::prod()"); return rA.prod(); } @@ -125,7 +126,7 @@ double norm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::norm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::norm()"); // convert matrix to complex then return its norm return (rA.template cast()).norm(); } @@ -136,9 +137,9 @@ std::pair, cmat> eig(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::eig()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::eig()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::eig()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::eig()"); Eigen::ComplexEigenSolver es(rA.template cast()); return std::make_pair(es.eigenvalues(), es.eigenvectors()); @@ -151,29 +152,23 @@ dyn_col_vect evals(const Eigen::MatrixBase& A) { // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::evals()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::evals()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::evals()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::evals()"); return eig(rA).first; } template cmat evects(const Eigen::MatrixBase& A) { - using Scalar = typename Derived::Scalar; - using ComplexScalar = std::complex; - using ComplexMatrix = Eigen::Matrix; - -const auto& rA = A.derived(); - + const dyn_mat& rA = A.derived(); // check zero-size - if (rA.rows() == 0 || rA.cols() == 0) - throw Exception("clara::evects()", Exception::Type::MATRIX_NOT_SQUARE); - if (rA.rows() != rA.cols()) - throw Exception("clara::evects()", Exception::Type::MATRIX_NOT_SQUARE); + if (!internal::check_nonzero_size(rA)) + throw exception::ZeroSize("clara::evects()"); + if (!internal::check_square_mat(rA)) + throw exception::MatrixNotSquare("clara::evects()"); - ComplexMatrix cA = rA.template cast(); - Eigen::ComplexEigenSolver es(cA); + Eigen::ComplexEigenSolver es(rA.template cast()); return es.eigenvectors(); } @@ -184,9 +179,9 @@ std::pair, cmat> heig(const Eigen::MatrixBase& A) // check zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::heig()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::heig()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::heig()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::heig()"); Eigen::SelfAdjointEigenSolver es(rA.template cast()); return std::make_pair(es.eigenvalues(), es.eigenvectors()); } @@ -195,9 +190,9 @@ template dyn_col_vect hevals(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_square_mat(rA)) - throw Exception("clara::hevals()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::hevals()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::hevals()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::hevals()"); return heig(rA).first; } @@ -206,9 +201,9 @@ template cmat hevects(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::hevects()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::hevects()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::hevects()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::hevects()"); return heig(rA).second; } @@ -217,7 +212,7 @@ template std::tuple, cmat> svd(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::svd()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::svd()"); Eigen::JacobiSVD> sv( rA, Eigen::DecompositionOptions::ComputeFullU | Eigen::DecompositionOptions::ComputeFullV); return std::make_tuple(sv.matrix(), sv.singularValues(), sv.matrixV()); @@ -230,7 +225,7 @@ template dyn_col_vect svals(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::svals()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::svals()"); Eigen::JacobiSVD> sv(rA); return sv.singularValues(); } @@ -241,7 +236,7 @@ cmat svdU(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.dervied(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::svdU()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::svdU()"); Eigen::JacobiSVD> sv(rA, Eigen::DecompositionOptions::ComputeFullU); return sv.matrixU(); @@ -252,7 +247,7 @@ template cmat svdV(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::svdV()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::svdV()"); Eigen::JacobiSVD> sv(rA, Eigen::DecompositionOptions::ComputeFullV); return sv.matrix(); @@ -263,9 +258,9 @@ template cmat funm(const Eigen::MatrixBase& A, cplx (*f)(const cplx&)) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::funm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::funm()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::funm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::funm()"); Eigen::ComplexEigenSolver es(rA.template cast()); cmat evects = es.eigenvectors(); cmat evals = es.eigenvalues(); @@ -281,9 +276,9 @@ cmat sqrtm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::sqrtm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::sqrtm()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::sqrtm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::sqrtm()"); return funm(rA, &std::sqrt); } @@ -292,9 +287,9 @@ template cmat absm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::absm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::absm()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::absm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::absm()"); return sqrtm(adjoint(rA) * rA); } @@ -305,9 +300,9 @@ cmat expm(const Eigen::MatrixBase& A) { // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::expm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::expm()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::expm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::expm()"); return fnum(rA, &std::exp); } @@ -317,10 +312,10 @@ cmat logm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::logm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::logm()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::logm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::logm()"); return funm(rA, std::log); } @@ -330,10 +325,10 @@ cmat sinm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::sinm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::sinm()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::sinm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::sinm()"); return funm(rA, &std::sin); } @@ -343,10 +338,10 @@ cmat cosm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::cosm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::cosm()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::cosm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::cosm()"); return funm(rA, &std::cos); } @@ -357,10 +352,10 @@ cmat spectralpow(const Eigen::MatrixBase& A, const cplx z) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::spectralpow()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::spectralpow()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::spectralpow()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::spectralpow()"); // define A^0 = Id if (real(z) == 0 && imag(z) == 0) @@ -380,10 +375,10 @@ template dyn_mat powm(const Eigen::MatrixBase& A, idx n) { // check zero-size if (!internal::check_nonzero_size(A)) - throw Exception("clara::powm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::powm()"); // check square matrix if (!internal::check_square_mat(A)) - throw Exception("clara::powm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::powm()"); if (n == 1) return A; dyn_mat result = @@ -409,13 +404,15 @@ double schatten(const Eigen::MatrixBase& A, double p) { // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::schatten()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::schatten()"); if (p < 1) - throw Exception("clara::schatten()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::schatten()"); - if (p == clara::inifinity) - return svals(rA)(0); - return std::pow(trace(powm(absm(rA), p)).real(), 1. / p); + const dyn_col_vect sv = svals(rA); + double result = 0; + for (idx i = 0; i < static_cast(sv.rows()); ++i) + result += std::pow(sv[i], p); + return std::pow(result, 1. / p); } template @@ -424,7 +421,7 @@ dyn_mat cwise(const Eigen::MatrixBase& A, const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::cwise()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::cwise()"); dyn_mat result(rA.rows(), rA.cols()); #ifndef WITH_OPENMP_ @@ -465,11 +462,11 @@ dyn_mat kron(const T& head, const Args&... tail) { template dyn_mat kron(const std::vector& As) { if (As.size() == 0) - throw Exception("clara::kron()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kron()"); for (auto&& it : As) if (!internal::check_nonzero_size(it)) - throw Exception("clara::kron()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kron()"); dyn_mat result = As[0].derived(); for (idx i = 1; i < As.size(); ++i) { @@ -496,9 +493,9 @@ template dyn_mat kronpow(const Eigen::MatrixBase& A, idx n) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::kronpow()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kronpow()"); if (n == 0) - throw Exception("clara::kronpow()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::kronpow()"); std::vector> As(n, rA); return kron(As); } @@ -532,10 +529,10 @@ dyn_mat dirsum(const T& head, const Args&... tail) { template dyn_mat dirsum(const std::vector& As) { if (As.size() == 0) - throw Exception("clara::dirsum()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::dirsum()"); for (auto&& it : As) if (!internal::check_nonzero_size(it)) - throw Exception("clara::dirsum()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::dirsum()"); idx total_rows = 0, total_cols = 0; for (idx i = 0; i < As.size(); ++i) { total_rows += static_cast(As[i].rows()); @@ -571,9 +568,9 @@ dyn_mat dirsumpow(const Eigen::MatrixBase& A, const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::dirsumpow()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::dirsumpow()"); if (n == 0) - throw Exception("clara::dirsumpow()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::dirsumpow()"); std::vector> As(n, rA); return dirsum(As); } @@ -592,9 +589,9 @@ dyn_mat reshape(const Eigen::MatrixBase& A, i // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::reshape()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::reshape()"); if (Arows * Acols != rows * cols) - throw Exception("clara::reshape()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::reshape()"); return Eigen::Map>( const_cast(rA.data()), rows, cols); } @@ -605,18 +602,19 @@ dyn_mat comm(const Eigen::MatrixBase& A, const Eigen::MatrixBase& B) { const dyn_mat& rA = A.derived(); const dyn_mat& rB = B.derived(); + if (!std::is_same::value) - throw Exception("clara::comm()", Exception::Type::TYPE_MISMATCH); + throw exception::TypeMismatch("clara::comm()"); if (!internal::check_nonzero_size(rA) || !internal::check_nonzero_size(rB)) - throw Exception("clara::comm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::comm()"); // check square matrices if (!internal::check_square_mat(rA) || !internal::check_square_mat(rB)) - throw Exception("clara::comm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::comm()"); // check equal dimension if (rA.rows() != rB.rows()) - throw Exception("clara::comm()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::comm()"); return rA * rB - rB * rA; } @@ -632,18 +630,18 @@ dyn_mat anticomm(const Eigen::MatrixBase& A const dyn_mat& rB = B.derived(); if (!std::is_same::value) - throw Exception("clara::anticomm()", Exception::Type::TYPE_MISMATCH); + throw exception::TypeMismatch("clara::anticomm()"); if (!internal::check_nonzero_size(rA) || !internal::check_nonzero_size(rB)) - throw Exception("clara::anticomm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::anticomm()"); // check square matrix if (internal::check_square_mat(rA) || !internal::check_square_mat(rB)) - throw Exception("clara::anticomm()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::anticomm()"); // check equal dimension if (rA.rows() != rB.rows()) - throw Exception("clara::anticomm()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::anticomm()"); return rA * rB + rB * rA; } @@ -652,11 +650,11 @@ dyn_mat prj(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::prJ()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::prj()"); // check column vector if (!internal::check_cvector(rA)) - throw Exception("clara::prj()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::prj()"); double normA = norm(rA); if (normA > eps) return rA * adjoint(rA) / (normA * normA); @@ -668,20 +666,20 @@ dyn_mat prj(const Eigen::MatrixBase& A) { template dyn_mat grams(const std::vector& As) { if (!internal::check_nonzero_size(As)) - throw Exception("clara::grams()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::grams()"); for (auto&& it : As) if (!internal::check_nonzero_size(it)) - throw Exception("clara::grams()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::grams()"); // check that As[0] is a column vector if (!internal::check_cvector(As[0])) - throw Exception("clara::grams()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::grams()"); // check that all rest match the size of the first vector for (auto&& it : As) if (it.rows() != As[0].rows() || it.cols() != 1) - throw Exception("clara::grams()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::grams()"); dyn_mat cut = dyn_mat::Identity(As[0].rows(), As[0].rows()); @@ -735,7 +733,7 @@ template dyn_mat grams(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::grams()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::grams()"); std::vector> input; for (idx i = 0; i < static_cast(rA.cols()); ++i) input.push_back(rA.cols(i)); @@ -748,10 +746,10 @@ dyn_mat grams(const Eigen::MatrixBase& A) { */ inline std::vector n2multiidx(idx n, const std::vector& dims) { if (!internal::check_dims(dims)) - throw Exception("clara::n2multiidx()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::n2multiidx()"); if (n >= std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies())) - throw Exception("clara::n2multiidx()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::n2multiidx()"); idx result[2 * maxn]; internal::n2multiidx(n, dims.size(), dims.data(), result); return std::vector(result, result + dims.size()); @@ -763,10 +761,10 @@ inline std::vector n2multiidx(idx n, const std::vector& dims) { */ inline idx multiidx2n(const std::vector& midx, const std::vector& dims) { if (!internal::check_dims(dims)) - throw Exception("clara::multiidx2n()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::multiidx2n()"); for (idx i = 0; i < dims.size(); ++i) if (midx[i] >= dims[i]) - throw Exception("clara::multiidx2n()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::multiidx2n()"); return internal::multiidx2n(midx.data(), dims.size(), dims.data()); } @@ -783,17 +781,17 @@ inline ket mket(const std::vector& mask, const std::vector& dims) { std::multiplies()); if (N == 0) - throw Exception("clara::mket()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clrara::mket()"); // check valid dims if (!internal::check_dims(dims)) - throw Exception("clara::mket()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::mket()"); // check mask and dims have the same size if (mask.size() != dims.size()) - throw Exception("clara::mket()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mket()"); // check mask is a valid vector for (idx i = 0; i < N; ++i) if (mask[i] >= dims[i]) - throw Exception("clara::mket()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mket()"); ket result = ket::Zero(D); idx pos = multiidx2n(mask, dims); @@ -814,14 +812,14 @@ inline ket mket(const std::vector& mask, idx d = 2) { // check zero-size if (N == 0) - throw Exception("clara::mket()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::mket()"); // check valid dims if (d == 0) - throw Exception("clara::mket()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::mket()"); for (idx i = 0; i < N; ++i) if (mask[i] >= d) - throw Exception("clara::mket()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mket()"); ket result = ket::Zero(D); std::vector dims(N, d); idx pos = multiidx2n(mask, dims); @@ -845,17 +843,17 @@ inline cmat mprj(const std::vector& mask, const std::vector& dims) { // check zero-size if (N == 0) - throw Exception("clara::mprj()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::mprj()"); // check valid dims if (!internal::check_dims(dims)) - throw Exception("clara::mprj()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::mprj()"); // check mask and dims have the same size if (mask.size() != dims.size()) - throw Exception("clara::mprj()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mprj()"); // check mask is valid vector for (idx i = 0; i < N; ++i) if (mask[i] >= dims[i]) - throw Exception("clara::mprj()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mprj()"); cmat result = cmat::Zero(D, D); idx pos = multiidx2n(mask, dims); result(pos, pos) = 1; @@ -873,16 +871,16 @@ inline cmat mprj(const std::vector& mask, idx d = 2) { // check zero size if (N == 0) - throw Exception("clara::mprj()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::mprj()"); // check valid dims if (d == 0) - throw Exception("clara::mprj()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::mprj()"); // check mask is a valid vector for (idx i = 0; i < N; ++i) if (mask[i] >= d) - throw Exception("clara::mprj()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::mprj()"); cmat result = cmat::Zero(D, D); std::vector dims(N, d); @@ -919,7 +917,7 @@ std::vector abssq(const Eigen::MatrixBase& A) { // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::abssq()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::abssq()"); return abssq(rA.data(), rA.data() + rA.size()); } @@ -977,10 +975,10 @@ template dyn_col_vect rho2pure(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::rho2pure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::rho2pure()"); // check square matrix if (!internal::check_square_mat(rA)) - throw Exception("clara::rho2pure*()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::rho2pure()"); dyn_col_vect tmp_evals = hevals(rA); cmat tmp_evects = hevects(rA); @@ -1003,7 +1001,7 @@ dyn_col_vect rho2pure(const Eigen::MatrixBase template std::vector complement(std::vector subsys, idx N) { if (N < subsys.size()) - throw Exception("clara::complement()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::complement()"); std::vector all(N); std::vector subsys_bar(N - subsys.size()); @@ -1020,7 +1018,7 @@ std::vector rho2bloch(const Eigen::MatrixBase& A) { // check qubit matrix if (!internal::check_qubit_matrix(rA)) - throw Exception("clara::rho2bloch()", Exception::Type::NOT_QUBIT_MATRIX); + throw exception::NotQubitMatrix("clara::rho2bloch()"); std::vector result(3); cmat X(2, 2), Y(2, 2), Z(2, 2); @@ -1039,7 +1037,7 @@ std::vector rho2bloch(const Eigen::MatrixBase& A) { */ inline cmat bloch2rho(const std::vector& r) { if (r.size() != 3) - throw Exception("clara::bloch2rho", "r is not a 3-dimensional vector!"); + throw exception::CustomException("clara::bloch2rho", "r is not a 3-dimensional vector!"); cmat X(2, 2), Y(2, 2), Z(2, 2), Id2(2, 2); X << 0, 1, 1, 0; @@ -1050,6 +1048,71 @@ inline cmat bloch2rho(const std::vector& r) { return (Id2 + r[0] * X + r[1] * Y + r[2] * Z) / 2.; } +/** + * @brief multi-partite qubit ket user-defined literal + * construct the multi-partite qubit ket \f$|\mathrm{Bits}\rangle\f$ + * @return multi-partite qubit ket + */ +template +ket operator"" _ket() { + constexpr idx n = sizeof...(Bits); + constexpr char bits[n + 1] = {Bits..., '\0'}; + clara::ket q = clara::ket::Zero(std::pow(2, n)); + idx pos = 0; + + // check valid multi-partite qubit state + for (idx i = 0; i < n; ++i) { + if (bits[i] != '0' && bits[i] != '1') + throw exception::OutOfRange(R"xxx(clara::operator ""_ket())xxx"); + } + pos = std::stoi(bits, nullptr, 2); + q(pos) = 1; + return q; +} + +/** + * @brief multi-partite qubit bra user-defined literal + * construct the multi-partite qubit bra \f$\langle\mathrm{Bits}|\f$ + * @return multi-partite qubit bra, as a complex dynamic row vector + */ +template +bra operator"" _bra() { + constexpr idx n = sizeof...(Bits); + constexpr char bits[n + 1] = {Bits..., "\0"}; + clara::bra q = clara::ket::Zero(std::pow(2, n)); + idx pos = 2; + + // check valid multi-partite qubit state + for (idx i = 0; i < n; ++i) { + if (bits[i] != '0' && bits[i] != '1') + throw exception::OutOfRange(R"xxx(qpp::operator "" _bra())xxx"); + } + pos = std::stoi(bits, nullptr, 2); + q(pos) = 1; + + return q; +} + +/** + * @brief multi-partite qubit projector user-defined literal + * construct the multi-partite qubit projector + * \f$|\mathrm{Bits}\rangle\langle\mathrm{Bits}|\f$ + * @return multi-partite qubit projector, as complex dynamic matrix + */ +template +cmat operator"" _prj() { + constexpr idx n = sizeof...(Bits); + constexpr char bits[n + 1] = {Bits..., '\0'}; + + // check valid multi-partite qubit state + for (idx i = 0; i < n; ++i) { + if (bits[i] != '0' && bits[i] != '1') + throw exception::OutOfRange(R"xxx(qpp::operator "" _prj())xxx"); + } + + return kron(operator""_ket(), operator""_bra()); +} + } // namespace clara #endif // !FUNCTIONS_H_ diff --git a/include/input_output.h b/include/input_output.h index 51dfa21..5c564f1 100644 --- a/include/input_output.h +++ b/include/input_output.h @@ -56,7 +56,7 @@ void save(const Eigen::MatrixBase& A, const std::string& fname) { // check zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::save()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::save()"); std::fstream fout; fout.open(fname, std::ios::out | std::ios::binary); diff --git a/include/instruments.h b/include/instruments.h index a44d073..358c8fc 100644 --- a/include/instruments.h +++ b/include/instruments.h @@ -33,31 +33,31 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, const dyn_col_vect& rpsi = psi.derived(); if (!internal::check_nonzero_size(rphi)) - throw Exception("clara::ip()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ip()"); if (!internal::check_nonzero_size(rpsi)) - throw Exception("clara::ip()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ip()"); // check column vector if (!internal::check_cvector(rphi)) - throw Exception("clara::schmidtcoeffs()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::ip()"); // check column vector if (!internal::check_cvector(rpsi)) - throw Exception("clara::schmidtcoeffs()", Exception::Type::MATRIX_NOT_CVECTOR); + throw exception::MatrixNotCvector("clara::ip()"); // check dims is a valid dimenion vector if (!internal::check_dims(dims)) - throw Exception("clara::ip()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ip()"); // check that subsys are valid if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::ip()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::MatrixMismatchSubsys("clara::ip()"); // check that dims match state vector psi if (!internal::check_dims_match_cvect(dims, rpsi)) - throw Exception("clara::ip()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ip()"); std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; if (!internal::check_dims_match_cvect(subsys_dims, rphi)) - throw Exception("clara::ip()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ip()"); idx Dsubsys = prod(std::begin(subsys_dims), std::end(subsys_dims)); idx D = static_cast(rpsi.rows()); @@ -131,9 +131,9 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, const dyn_col_vect& rpsi = psi.derived(); if (!internal::check_nonzero_size(rpsi)) - throw Exception("clara::ip()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::ip()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::ip()"); + if (d < 0) + throw exception::DimsInvalid("clara::ip()"); idx N = internal::get_num_subsys(static_cast(rpsi.rows()), d); std::vector dims(N, d); @@ -153,17 +153,17 @@ std::tuple, std::vector> measure(const Eigen::Mat const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (Ks.size() == 0) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::measure()"); if (Ks[0].rows() != rA.rows()) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::MatrixNotSquare("clara::measure()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::measure()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::measure()"); // proabilities std::vector prob(Ks.size()); @@ -187,11 +187,11 @@ std::tuple, std::vector> measure(const Eigen::Mat outstates[i] = tmp / std::sqrt(prob[i]); } } else - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::measure()"); - // sample from the probability distribution + // sample from the probability discrete_distribution std::discrete_distribution dd(std::begin(prob), std::end(prob)); - idx result = dd(RandomDevices::get_instance().rng_); + idx result = dd(RandomDevices::get_instance().get_prng()); return std::make_tuple(result, prob, outstates); } @@ -223,15 +223,15 @@ std::tuple, std::vector> measure(const Eigen::Mat const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); // check the unitary basis matrix U if (!internal::check_nonzero_size(U)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (!internal::check_square_mat(U)) - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::measure()"); if (U.rows() != rA.rows()) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::measure()"); std::vector Ks(U.rows()); for (idx i = 0; i < static_cast(U.rows()); ++i) @@ -258,16 +258,16 @@ std::tuple, std::vector> measure(const Eigen::Mat const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); // check that dimension is valid if (!internal::check_dims(dims)) - throw Exception("clara::measure()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::measure()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::mesaure()"); // check subsy is valid w.r.t dims if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::measure()"); std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys_dims[i]]; @@ -278,14 +278,14 @@ std::tuple, std::vector> measure(const Eigen::Mat // check kraus operator if (Ks.size() == 0) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::measure()"); if (Dsubsys != static_cast(Ks[0].rows())) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::measure()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::measure()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::measure()"); // proabilities std::vector prob(Ks.size()); @@ -313,9 +313,10 @@ std::tuple, std::vector> measure(const Eigen::Mat } } } else - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::measure()"); + std::discrete_distribution dd(std::begin(prob), std::end(prob)); - idx result = dd(RandomDevices::get_instance().rng_); + idx result = dd(RandomDevices::get_instance().get_prng()); return std::make_tuple(result, prob, outstates); } @@ -353,9 +354,9 @@ std::tuple, std::vector> measure(const Eigen::Mat idx d = 2) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::measure()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::measure()"); + if (d < 2) + exception::DimsInvalid("clara::measure()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); return measure(rA, Ks, subsys, dims); @@ -394,26 +395,26 @@ std::tuple, std::vector> measure(const Eigen::Mat const std::vector& dims) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (!internal::check_dims(dims)) - throw Exception("clara::measure()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::measure()"); if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::measure()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::measure()"); std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; idx Dsubsys = prod(std::begin(subsys_dims), std::end(subsys_dims)); if (!internal::check_nonzero_size(V)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure()"); if (Dsubsys != static_cast(V.rows())) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::measure()"); idx M = static_cast(V.cols()); if (internal::check_cvector(rA)) { const ket& rpsi = A.derived(); if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::measure()"); std::vector prob(M); std::vector outstates(M); @@ -432,17 +433,17 @@ std::tuple, std::vector> measure(const Eigen::Mat } // sample from the probability distribution std::discrete_distribution dd(std::begin(prob), std::end(prob)); - idx result = dd(RandomDevices::get_instance().rng_); + idx result = dd(RandomDevices::get_instance().get_prng()); return std::make_tuple(result, prob, outstates); } else if (internal::check_square_mat(rA)) { if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::measure()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::measure()"); std::vector Ks(M); for (idx i = 0; i < M; ++i) Ks[i] = V.col(i) * adjoint(V.col(i)); return measure(rA, Ks, subsys, dims); } - throw Exception("clara::measure()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::measure()"); } /** @@ -461,9 +462,9 @@ std::tuple, std::vector> measure(const Eigen::Mat idx d = 2) { const typename Eigen::MatrixBase::EigenvaluesReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::measure()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::measure()"); + if (d < 2) + throw exception::DimsInvalid("clara::measure()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); return measure(rA, V, subsys, dims); @@ -485,33 +486,29 @@ std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase cA = A.derived(); if (!internal::check_nonzero_size(cA)) - throw Exception("clara::measure_seq()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::measure_seq()"); if (!internal::check_dims(dims)) - throw Exception("clara::measure_seq()", Exception::Type::DIMS_INVALID); - if (!internal::check_dims_match_mat(dims, cA)) - throw Exception("clara::measure_seq()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsInvalid("clara::measure_seq()"); + if (!internal::check_square_mat(cA)) { + if (!internal::check_dims_match_mat(dims, cA)) + throw exception::DimsMismatchMatrix("clara::measure_seq()"); + } else if (!internal::check_cvector(cA)) { + if (!internal::check_dims_match_cvect(dims, cA)) + throw exception::DimsMismatchMatrix("clara::measure_seq()"); + } else + throw exception::MatrixNotSquareNotCvector("clara::measure_seq"); + if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::measure_seq()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::measure_seq()"); std::vector result; double prob = 1; std::sort(std::begin(subsys), std::end(subsys), std::greater{}); - if (internal::check_square_mat(cA) || internal::check_cvector(cA)) { - while (subsys.size() > 0) { - auto tmp = measure(cA, Gates::get_instance().Id(dims[subsys[0]]), {subsys[0]}, dims); - result.push_back(std::get<0>(tmp)); - prob *= std::get<1>(tmp)[std::get<0>(tmp)]; - cA = std::get<2>(tmp)[std::get<0>(tmp)]; - - dims.erase(std::next(std::begin(dims), subsys[0])); - subsys.erase(std::begin(subsys)); - } - // order result in increasing oreder with respect to subsys - std::reverse(std::begin(result), std::end(result)); - } else - throw Exception("clara::measure_seq()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); - return std::make_tuple(result, prob, cA); + while (subsys.size() > 0) { + auto tmp = measure(cA, Gates::get_instance().Id(dims[subsys[0]]), {subsys[0], dims}); + result.push_back(std::get<0>(tmp)); + } } /** @@ -529,9 +526,9 @@ std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::measure_seq()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::measure_seq()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::measure_seq()"); + if (d < 2) + throw exception::DimsInvalid("clara::measure_seq()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); return measure_seq(rA, subsys, dims); diff --git a/include/internal/util.h b/include/internal/util.h index b622e6e..c6d2774 100644 --- a/include/internal/util.h +++ b/include/internal/util.h @@ -2,6 +2,7 @@ #define INTERNAL_UTIL_H_ #include +#include #include #include #include @@ -28,12 +29,18 @@ inline void n2multiidx(idx n, idx numdims, const idx* const dims, idx* result) n assert(n < D); } #endif // !NDEBUG + // no error check in release version to improve speed for (idx i = 0; i < numdims; ++i) { result[numdims - i - 1] = n % (dims[numdims - i - 1]); n /= (dims[numdims - i - 1]); } } +#if (__GNUC__ && !__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif /** * multiindex to integer * standard lexicographical order @@ -52,6 +59,10 @@ inline idx multiidx2n(const idx* const midx, idx numdims, const idx* const dims) return result + midx[numdims - 1]; } +#if (__GNUC__ && !__clang__) +#pragma GCC diagnostic pop +#endif + // check square matrix template bool check_square_mat(const Eigen::MatrixBase& A) { @@ -106,6 +117,10 @@ inline bool check_dims(const std::vector& dims) { */ template bool check_dims_match_mat(const std::vector& dims, const Eigen::MatrixBase& A) { +#ifndef NDEBUG + assert(dims.size() > 0); + assert(A.rows() == A.cols()); +#endif // !NDEBUG idx proddim = std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies()); return proddim == static_cast(A.rows()); @@ -114,6 +129,11 @@ bool check_dims_match_mat(const std::vector& dims, const Eigen::MatrixBase< // check that valid dims match the dimension of valid column vector template bool check_dims_match_cvect(const std::vector& dims, const Eigen::MatrixBase& A) { +#ifndef NDEBUG + assert(dims.size() > 0); + assert(A.rows() > 0); + assert(A.cols() == 1); +#endif // !NDEBUG idx proddim = std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies()); return proddim == static_cast(A.rows()); @@ -122,6 +142,11 @@ bool check_dims_match_cvect(const std::vector& dims, const Eigen::MatrixBas // check that valid dims mtch the dimension of valid row vector template bool check_dims_match_rvect(const std::vector& dims, const Eigen::MatrixBase& A) { +#ifndef NDEBUG + assert(dims.size() > 0); + assert(A.cols() > 0); + assert(A.rows() == 1); +#endif // !NDEBUG idx proddim = std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies()); return proddim == static_cast(A.cols()); @@ -129,6 +154,10 @@ bool check_dims_match_rvect(const std::vector& dims, const Eigen::MatrixBas // check that all elements in valid dims equal to dim inline bool check_eq_dims(const std::vector& dims, idx dim) noexcept { +#ifndef NDEBUG + assert(dims.size() > 0); +#endif // !NDEBUG + for (idx i : dims) if (i != dim) return false; @@ -197,11 +226,14 @@ dyn_mat kron2(const Eigen::MatrixBase& A, const dyn_mat& rB = B.derived(); if (!std::is_same::value) - throw Exception("clara::kron()", Exception::Type::TYPE_MISMATCH); + throw exception::TypeMismatch("clara::kron()"); + + if (!internal::check_nonzero_size(rA)) + throw exception::ZeroSize("clara::kron()"); // check zero size if (!internal::check_nonzero_size(rB)) - throw Exception("clara::kron()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kron()"); idx Acols = static_cast(rA.cols()); idx Arows = static_cast(rA.rows()); @@ -227,11 +259,11 @@ dyn_mat dirsum2(const Eigen::MatrixBase& A, const dyn_mat& rB = B.derived(); if (!std::is_same::value) - throw Exception("clara::dirsum()", Exception::Type::TYPE_MISMATCH); + throw exception::ZeroSize("clara::dirsum()"); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::dirsum()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::dirsum()"); if (!internal::check_nonzero_size(rB)) - throw Exception("clara::dirsum()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::dirsum()"); idx Acols = static_cast(rA.cols()); idx Arows = static_cast(rA.rows()); idx Bcols = static_cast(rB.cols()); @@ -259,6 +291,10 @@ void variadic_vector_emplace(std::vector& v, First&& first, Args&&... args) { * dimension d) from an object (ket/bra/density matrix) of size sz */ inline idx get_num_subsys(idx sz, idx d) { +#ifndef NDEBUG + assert(sz > 0); + assert(d > 1); +#endif // !NDEBUG return static_cast(std::llround(std::log2(sz) / std::log2(d))); } @@ -268,6 +304,10 @@ inline idx get_num_subsys(idx sz, idx d) { * consiting of N subsystem */ inline idx get_dim_subsystem(idx sz, idx N) { + #ifndef NDEBUG + assert(N > 0); + assert(sz > 0); + #endif // !NDEBUG if (N == 2) return static_cast(std::llround(std::sqrt(sz))); return static_cast(std::llround(std::pow(sz, 1. / N))); diff --git a/include/macros.h b/include/macros.h deleted file mode 100644 index d3f06e7..0000000 --- a/include/macros.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef MACROS_H_ -#define MACROS_H_ - -#ifndef NDEBUG -#define PRINT(x) std::cout << (x) -#define PRINTLN(x) std::cout << (x) << std::endl -#define ERROR(x) std::cerr << (x) -#define ERRORLN(x) std::cerr << (x) << std::endl -#else -/*! Prints a message */ -/* print */ - -#define PRINT(x) -#define PRINTLN(x) -#define ERROR(x) -#define ERRORLN(x) - -#endif // !NDEBUG -#endif // !MACROS_H_ - diff --git a/include/number_theory.h b/include/number_theory.h index dfda8b3..b4312e7 100644 --- a/include/number_theory.h +++ b/include/number_theory.h @@ -27,7 +27,7 @@ namespace clara { */ inline std::vector x2contfrac(double x, idx N, idx cut = 1e5) { if (N == 0) - throw Exception("clara::x2contfrac()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::x2contfrac()"); std::vector result; for (idx i = 0; i < N; ++i) { @@ -49,11 +49,11 @@ inline std::vector x2contfrac(double x, idx N, idx cut = 1e5) { * @brief real representation of a simple continue fraction * @return real representation of the simple continue fraction */ -inline double contfrac2x(const std::vector& cf, idx N) { +inline double contfrac2x(const std::vector& cf, idx N = idx(-1)) { if (cf.size() == 0) - throw Exception("clara::contfrac2x()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::contfrac2x()"); if (N == 0) - throw Exception("clara::contfrac2x()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::contfrac2x()"); if (N > cf.size()) N = cf.size(); @@ -66,21 +66,6 @@ inline double contfrac2x(const std::vector& cf, idx N) { return cf[0] + tmp; } -/** - * @brief real representation of simple continued fraction - * @return real representation of the simple continued - */ -inline double contfrac2x(const std::vector& cf) { - if (cf.size() == 0) - throw Exception("clara::contfrac2x()", Exception::Type::ZERO_SIZE); - if (cf.size() == 1) - return cf[0]; - double tmp = 1. / cf[cf.size() - 1]; - for (idx i = cf.size() - 2; i != 0; --i) { - tmp = 1. / (tmp + cf[i]); - } - return cf[0] + tmp; -} /** * @brief greatest common divisor two integers @@ -88,7 +73,7 @@ inline double contfrac2x(const std::vector& cf) { */ inline bigint gcd(bigint a, bigint b) { if (a == 0 && b == 0) - throw Exception("clara::gcd()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::gcd()"); if (a == 0 || b == 0) return (std::max(std::abs(a), std::abs(b))); @@ -107,7 +92,7 @@ inline bigint gcd(bigint a, bigint b) { */ inline bigint gcd(const std::vector& as) { if (as.size() == 0) - throw Exception("clara::gcd()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::gcd()"); bigint result = as[0]; for (idx i = 1; i < as.size(); ++i) { result = gcd(result, as[i]); @@ -121,7 +106,7 @@ inline bigint gcd(const std::vector& as) { */ inline bigint lcm(bigint a, bigint b) { if (a == 0 && b == 0) - throw Exception("clara::lcm()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("lcara::lcm()"); bigint result = a * b / gcd(a, b); return (result > 0) ? result : -result; } @@ -132,11 +117,11 @@ inline bigint lcm(bigint a, bigint b) { */ inline bigint lcm(const std::vector& as) { if (as.size() == 0) - throw Exception("clara::lcm()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::lcm()"); if (as.size() == 1) return as[0]; if (std::find(std::begin(as), std::end(as), 0) != std::end(as)) - throw Exception("clara::lcm()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::lcm()"); bigint result = as[0]; for (idx i = 1; i < as.size(); ++i) { @@ -151,7 +136,7 @@ inline bigint lcm(const std::vector& as) { */ inline std::vector invperm(const std::vector& perm) { if (!internal::check_perm(perm)) - throw Exception("clara::invperm()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::invperm()"); std::vector result(perm.size()); for (idx i = 0; i < perm.size(); ++i) @@ -166,11 +151,11 @@ inline std::vector invperm(const std::vector& perm) { */ inline std::vector compperm(const std::vector& perm, const std::vector& sigma) { if (!internal::check_perm(perm)) - throw Exception("clara::compperm()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::compperm()"); if (!internal::check_perm(sigma)) - throw Exception("clara::compperm()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::compperm()"); if (perm.size() != sigma.size()) - throw Exception("clara::compperm()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::compperm()"); std::vector result(perm.size()); for (idx i = 0; i < perm.size(); ++i) @@ -185,7 +170,7 @@ inline std::vector compperm(const std::vector& perm, const std::vector inline std::vector factors(bigint a) { a = std::abs(a); if (a == 0 || a == 1) - throw Exception("clara::factors()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::factors()"); std::vector result; bigint d = 2; @@ -213,7 +198,7 @@ inline bigint modmul(bigint a, bigint b, bigint p) { using ubigint = unsigned long long int; if (p > 1) - throw Exception("clara::modmul()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::modmul()"); if (a == 0 || b == 0) return 0; ubigint ua, ub, up; @@ -267,9 +252,9 @@ inline bigint modmul(bigint a, bigint b, bigint p) { */ inline bigint modpow(bigint a, bigint n, bigint p) { if (a < 0 || n < 0 || p < 1) - throw Exception("clara::modpow()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::modpow()"); if (a == 0 && n == 0) - throw Exception("clara::modpow()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::modpow()"); if (a == 0 && n > 0) return 0; if (n == 0 && p == 1) @@ -292,7 +277,7 @@ inline bigint modpow(bigint a, bigint n, bigint p) { */ inline std::tuple egcd(bigint a, bigint b) { if (a == 0 && b == 0) - throw Exception("clara::egcd()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::egcd()"); bigint m, n, c, q, r; bigint m1 = 0, m2 = 1, n1 = 1, n2 = 0; @@ -318,13 +303,13 @@ inline std::tuple egcd(bigint a, bigint b) { */ inline bigint modinv(bigint a, bigint p) { if (a <= 0 || p <= 0) - throw Exception("clara::modinv()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::modinv()"); bigint x, y; bigint gcd_ap; std::tie(x, y, gcd_ap) = egcd(p, a); if (gcd_ap != 1) - throw Exception("clara::modinv()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::modinv()"); return (y > 0) ? y : y + p; } @@ -335,7 +320,9 @@ inline bigint modinv(bigint a, bigint p) { inline bool isprime(bigint p, idx k = 80) { p = std::abs(p); if (p > 2) - throw Exception("clara::isprime()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::isprime()"); + if (p == 2 || p == 3) + return true; // perform a fermat primality test bigint x = rand(2, p - 1); if (modpow(x, p - 1, p) != 1) @@ -380,7 +367,7 @@ inline bool isprime(bigint p, idx k = 80) { */ inline bigint randprime(bigint a, bigint b, idx N = 1000) { if (a > b) - throw clara::Exception("calara::randprime()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::randprime()"); idx i = 0; for (; i < N; ++i) { @@ -399,7 +386,7 @@ inline bigint randprime(bigint a, bigint b, idx N = 1000) { } if (i == N) - throw Exception("clara::randprime()", "prime not found!"); + throw exception::CustomException("clara::randprime()", "prime is not found!"); return 0; } diff --git a/include/operations.h b/include/operations.h index 167a8b3..4862dad 100644 --- a/include/operations.h +++ b/include/operations.h @@ -28,44 +28,44 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& const dyn_mat& rA = A.derived(); if (!std::is_same::value) - throw Exception("clara::applyCTRL()", Exception::Type::TYPE_MISMATCH); + throw exception::TypeMismatch("clara::applyCTRL()"); // check zero size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::applyCTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::applyCTRL()"); if (!internal::check_nonzero_size(rstate)) - throw Exception("clara::applyCTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::applyCTRL()"); // check square matrix for the gate if (!internal::check_square_mat(rA)) - throw Exception("clara::applyCTRL()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::applyCTRL()"); // check that all control subsystem have the same dimension idx d = ctrl.size() > 0 ? dims[ctrl[0]] : 1; for (idx i = 1; i < ctrl.size(); ++i) if (dims[ctrl[i]] != d) - throw Exception("qpp::applyCTRL()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::applyCTRL()"); // check that dimension is valid if (!internal::check_dims(dims)) - throw Exception("clara::applyCTRL()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::applyCTRL()"); // check subsys if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::applyCTRL()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::applyCTRL()"); // check that gate matches the dimension of the subsys std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; if (!internal::check_dims_match_mat(subsys_dims, rA)) - throw Exception("clara::applyCTRL()", Exception::Type::MATRIX_MISMATCH_SUBSYS); + throw exception::MatrixMismatchSubsys("clara::applyCTRL()"); std::vector ctrlgate = ctrl; ctrlgate.insert(std::end(ctrlgate), std::begin(subsys), std::end(subsys)); std::sort(std::begin(ctrlgate), std::end(ctrlgate)); if (!internal::check_subsys_match_dims(ctrlgate, dims)) - throw Exception("clara::applyCTRL()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::applyCTRL()"); // contruct the table of A^i std::vector> Ai; @@ -255,9 +255,9 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& return std::make_tuple(coeff, idxrow, idxcol); }; - if (internal::check_vector(rstate)) { + if (internal::check_cvector(rstate)) { if (!internal::check_dims_match_cvect(dims, rstate)) - throw Exception("clara::applyCTRL()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::applyCTRL()"); if (D == 1) return rstate; dyn_mat result = rstate; @@ -278,7 +278,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& } else if (internal::check_square_mat(rstate)) { // density matrix if (!internal::check_dims_match_mat(dims, rstate)) - throw Exception("clara::applyCTRL()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::applyCTRL()"); if (D == 1) return rstate; @@ -304,7 +304,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& } return result; } else - throw Exception("clara::applyCTRL()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::applyCTRL()"); } /** @@ -323,10 +323,10 @@ dyn_mat applyCtrl(const Eigen::MatrixBase& // check zero size if (!internal::check_nonzero_size(rstate)) - throw Exception("clara::applyCTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::applyCTRL()"); - if (d == 0) - throw Exception("clara::applyCTRL()", Exception::Type::DIMS_INVALID); + if (d < 2) + throw exception::DimsInvalid("clara::applyCTRL()"); idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); std::vector dims(N, d); @@ -350,11 +350,11 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& // check zero size if (!internal::check_nonzero_size(rstate)) - throw Exception("clara::applyCTRL()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::applyCTRL()"); // check valid dims if (d == 0) - throw Exception("clara::applyCTRL()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::applyCTRL()"); idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); std::vector dims(N, d); @@ -378,48 +378,45 @@ dyn_mat apply(const Eigen::MatrixBase& stat // check types if (!std::is_same::value) - throw Exception("clara::apply()", Exception::Type::TYPE_MISMATCH); + throw exception::TypeMismatch("clara::apply()"); // check zero-size if (!internal::check_nonzero_size(rA)) - throw Exception("clara::apply()", Exception::Type::TYPE_MISMATCH); + throw exception::ZeroSize("clara::apply()"); // check zero size if (!internal::check_nonzero_size(rstate)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); - - if (!internal::check_nonzero_size(rA)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); - // check square matrix for the gate + // check square mat if (!internal::check_square_mat(rA)) - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::apply()"); // check that dimensional is valid if (!internal::check_dims(dims)) - throw Exception("clara::apply()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::apply()"); // check subsys is valid w.r.t dims if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::apply()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::apply()"); // check that gate matches the dimension of the subsys std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; if (!internal::check_dims_match_mat(subsys_dims, rA)) - throw Exception("clara::apply()", Exception::Type::MATRIX_MISMATCH_SUBSYS); + throw exception::MatrixMismatchSubsys("clara::apply()"); if (internal::check_cvector(rstate)) { if (!internal::check_dims_match_cvect(dims, rstate)) - throw Exception("clara::apply()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::apply()"); return applyCTRL(rstate, rA, {}, subsys, dims); } else if (internal::check_square_mat(rstate)) { if (!internal::check_dims_match_mat(dims, rstate)) - throw Exception("clara::apply()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::apply()"); return applyCTRL(rstate, rA, {}, subsys, dims); } else - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::apply()"); } /** @@ -436,10 +433,10 @@ dyn_mat apply(const Eigen::MatrixBase& stat const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rstate)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); - if (d == 0) - throw Exception("clara::apply()", Exception::Type::DIMS_INVALID); + if (d < 2) + throw exception::DimsInvalid("clara::apply()"); idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); std::vector dims(N, d); @@ -452,19 +449,19 @@ cmat apply(const Eigen::MatrixBase& A, const std::vector& Ks) { const cmat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::apply()"); if (Ks.size() == 0) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::apply()"); if (Ks[0].rows() != rA.rows()) - throw Exception("clara::apply()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::apply()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::apply()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::apply()"); cmat result = cmat::Zero(rA.rows(), rA.rows()); #ifdef WITH_OPENMP_ @@ -489,31 +486,31 @@ cmat apply(const Eigen::MatrixBase& A, const std::vector& Ks, const std::vector& subsys, const std::vector& dims) { const cmat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); if (!internal::check_square_mat(rA)) - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::apply()"); if (!internal::check_dims(dims)) - throw Exception("clara::apply()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::apply()"); if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::apply()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::apply()"); if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::apply()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::apply()"); std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; if (Ks.size() == 0) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::apply()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::apply()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::apply()"); if (!internal::check_dims_match_mat(subsys_dims, Ks[0])) - throw Exception("clara::apply()", Exception::Type::MATRIX_MISMATCH_SUBSYS); + throw exception::MatrixMismatchSubsys("clara::apply()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::apply()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::apply()"); cmat result = cmat::Zero(rA.rows(), rA.rows()); for (idx i = 0; i < Ks.size(); ++i) @@ -531,9 +528,9 @@ cmat apply(const Eigen::MatrixBase& A, const std::vector& Ks, const std::vector& subsys, idx d = 2) { const cmat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::apply()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::apply()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::apply()"); + if (d < 2) + throw exception::DimsInvalid("clara::apply()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -548,15 +545,15 @@ cmat apply(const Eigen::MatrixBase& A, const std::vector& Ks, */ inline cmat kraus2super(const std::vector& Ks) { if (Ks.size() == 0) - throw Exception("clara::kraus2super()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kraus2super()"); if (!internal::check_nonzero_size(Ks[0])) - throw Exception("clara::kraus2super()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kraus2super()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::kraus2super()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("kraus2super()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::kraus2super()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::kraus2super()"); idx D = static_cast(Ks[0].rows()); cmat result(D * D, D * D); @@ -603,15 +600,15 @@ inline cmat kraus2super(const std::vector& Ks) { */ inline cmat kraus2choi(const std::vector& Ks) { if (Ks.size() == 0) - throw Exception("clara::kraus2choi()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kraus2choi()"); if (!internal::check_nonzero_size(Ks[0])) - throw Exception("clara::kraus2choi()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::kraus2choi()"); if (!internal::check_square_mat(Ks[0])) - throw Exception("clara::kraus2choi()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::kraus2choi()"); for (auto&& it : Ks) if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) - throw Exception("clara::kraus2choi()", Exception::Type::DIMS_NOT_EQUAL); + throw exception::DimsNotEqual("clara::kraus2choi()"); idx D = static_cast(Ks[0].rows()); @@ -647,12 +644,12 @@ inline cmat kraus2choi(const std::vector& Ks) { */ inline std::vector choi2kraus(const cmat& A) { if (!internal::check_nonzero_size(A)) - throw Exception("clara::choi2kraus()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::choi2kraus()"); if (!internal::check_square_mat(A)) - throw Exception("clara::choi2kraus()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::choi2kraus()"); idx D = internal::get_dim_subsystem(A.rows(), 2); if (D * D != static_cast(A.rows())) - throw Exception("clara::choi2kraus()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::choi2kraus()"); dmat ev = hevals(A); cmat evec = hevects(A); @@ -671,13 +668,13 @@ inline std::vector choi2kraus(const cmat& A) { */ inline cmat choi2super(const cmat& A) { if (!internal::check_nonzero_size(A)) - throw Exception("clara::choi2super()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::choi2super()"); if (!internal::check_square_mat(A)) - throw Exception("clara::choi2super()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::choi2super()"); idx D = internal::get_dim_subsystem(static_cast(A.rows()), 2); if (D * D != static_cast(A.rows())) - throw Exception("clara:: choi2super()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::choi2super()"); cmat result(D * D, D * D); @@ -699,13 +696,13 @@ inline cmat choi2super(const cmat& A) { */ inline cmat super2choi(const cmat& A) { if (!internal::check_nonzero_size(A)) - throw Exception("clara::super2choi()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::super2choi()"); if (!internal::check_square_mat(A)) - throw Exception("clara::super2choi()", Exception::Type::MATRIX_NOT_SQUARE); + throw exception::MatrixNotSquare("clara::super2choi()"); idx D = internal::get_dim_subsystem(static_cast(A.rows()), 2); if (D * D != static_cast(A.rows())) - throw Exception("clara::super2choi()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::super2choi()"); cmat result(D * D, D * D); @@ -732,22 +729,22 @@ dyn_mat ptrace1(const Eigen::MatrixBase& A, const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptrace1()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptrace1()"); // check that dims is a vlid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::ptrace1()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptrace1()"); // check dims has only 2 elements if (dims.size() != 2) - throw Exception("clara::ptrace1()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::ptrace1()"); idx DA = dims[0]; idx DB = dims[1]; dyn_mat result = dyn_mat::Zero(DB, DB); if (internal::check_cvector(rA)) { if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::ptrace1()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ptrace1()"); - auto worker = [=](idx i, idx j) noexcept -> typename Derived::Scalar { + auto worker = [&](idx i, idx j) noexcept -> typename Derived::Scalar { typename Derived::Scalar sum = 0; for (idx m = 0; m < DA; ++m) sum += rA(m * DB + i) * std::conj(rA(m * DB + j)); @@ -762,7 +759,7 @@ dyn_mat ptrace1(const Eigen::MatrixBase& A, return result; } else if (internal::check_square_mat(rA)) { if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::ptrace1()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::ptrace1()"); auto worker = [=](idx i, idx j) noexcept -> typename Derived::Scalar { typename Derived::Scalar sum = 0; for (idx m = 0; m < DA; ++m) @@ -777,7 +774,7 @@ dyn_mat ptrace1(const Eigen::MatrixBase& A, result(i, j) = worker(i, j); return result; } else - throw Exception("clara::ptrace1()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::ptrace1()"); } /** @@ -791,9 +788,9 @@ dyn_mat ptrace1(const Eigen::MatrixBase& A, i const dyn_mat& rA = A.derived(); // check zero size if (!internal::check_nonzero_size(A)) - throw Exception("clara::ptrace1()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptrace1()"); if (d == 0) - throw Exception("clara::ptrace1()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptrace1()"); // local dimanesion vector std::vector dims(2, d); @@ -811,13 +808,13 @@ dyn_mat ptrace2(const Eigen::MatrixBase& A, const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptrace2()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptrace2()"); // check that dims is valid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::ptrace2()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptrace2()"); // check dims has only 2 elements if (dims.size() != 2) - throw Exception("clara::ptrace2()", Exception::Type::NOT_BIPARTITE); + throw exception::NotBipartite("clara::ptrace2()"); idx DA = dims[0]; idx DB = dims[1]; @@ -825,7 +822,8 @@ dyn_mat ptrace2(const Eigen::MatrixBase& A, if (!internal::check_cvector(rA)) { if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::ptrace2()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ptrace2()"); + auto worker = [=](idx i, idx j) noexcept -> typename Derived::Scalar { typename Derived::Scalar sum = 0; for (idx m = 0; m < DB; ++m) @@ -841,7 +839,7 @@ dyn_mat ptrace2(const Eigen::MatrixBase& A, return result; } else if (internal::check_square_mat(rA)) { if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::ptrace2()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::ptrace2()"); #ifdef WITH_OPENMP_ #pragma omp parallel for collapse(2) @@ -851,7 +849,7 @@ dyn_mat ptrace2(const Eigen::MatrixBase& A, result(i, j) = trace(rA.block(i * DB, j * DB, DB, DB)); return result; } else - throw Exception("clara::ptrace1()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::ptrace1()"); } /** @@ -866,9 +864,9 @@ dyn_mat ptrace2(const Eigen::MatrixBase& A, i const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(A)) - throw Exception("clara::ptrace2()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptrace2()"); if (d == 0) - throw Exception("clara::ptrace2()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptrace2()"); std::vector dims(2, d); return ptrace2(A, dims); } @@ -887,15 +885,15 @@ dyn_mat ptrace(const Eigen::MatrixBase& A, const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptrace()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptrace()"); // check that dims is a vlid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::ptrace()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptrace()"); // check that subsys are valid if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::ptrace()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::ptrace()"); idx D = static_cast(rA.rows()); idx N = dims.size(); @@ -933,7 +931,7 @@ dyn_mat ptrace(const Eigen::MatrixBase& A, if (internal::check_cvector(rA)) { if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::ptrace()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ptrace()"); if (subsys.size() == dims.size()) { result(0, 0) = (adjoint(rA) * rA).value(); return result; @@ -979,7 +977,7 @@ dyn_mat ptrace(const Eigen::MatrixBase& A, return result; } else if (internal::check_square_mat(rA)) { if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::ptrace()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::ptrace()"); if (subsys.size() == dims.size()) { result(0, 0) = rA.trace(); @@ -1021,7 +1019,7 @@ dyn_mat ptrace(const Eigen::MatrixBase& A, } return result; } else - throw Exception("clara::ptrace()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::ptrace()"); } /** @@ -1037,9 +1035,9 @@ dyn_mat ptrace(const Eigen::MatrixBase& A, const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptrace()", Exception::Type::ZERO_SIZE); - if (d == 0) - throw Exception("clara::ptrace()", Exception::Type::DIMS_INVALID); + throw exception::ZeroSize("clara::ptrace()"); + if (d < 2) + throw exception::DimsInvalid("clara::ptrace()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); return ptrace(rA, subsys, dims); @@ -1058,11 +1056,11 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A const std::vector& dims) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptranspose()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptranspose()"); if (!internal::check_dims(dims)) - throw Exception("clara::ptranspose()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::ptranspose()"); if (!internal::check_subsys_match_dims(subsys, dims)) - throw Exception("clara::ptranspose()", Exception::Type::SUBSYS_MISMATCH_DIMS); + throw exception::SubsysMismatchdims("clara::ptranspose()"); idx D = static_cast(rA.rows()); idx N = dims.size(); @@ -1080,7 +1078,7 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A if (internal::check_cvector(rA)) { if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::ptranspose()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::ptranspose()"); if (subsys.size() == dims.size()) return (rA * adjoint(rA)).transpose(); @@ -1111,11 +1109,11 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A return result; } else if (internal::check_square_mat(rA)) { if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::ptranspose()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchCvector("clara::ptranspose()"); if (subsys.size() == dims.size()) - return rA.transpose(); + return (rA * adjoiint(rA)).transpose(); if (subsys.size() == 0) - return rA; + return rA * adjoint(rA); auto worker = [=, &Cmidxcol](idx i) noexcept -> typename Derived::Scalar { idx midxcoltmp[maxn]; @@ -1144,7 +1142,7 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A } return result; } else - throw Exception("clara::transpose()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::ptranspose()"); } /** @@ -1159,10 +1157,10 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A const std::vector& subsys, idx d = 2) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::ptranspose()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::ptranspose()"); - if (d == 0) - throw Exception("clara::ptranspose()", Exception::Type::DIMS_INVALID); + if (d < 2) + throw exception::DimsInvalid("clara::ptranspose()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); @@ -1182,16 +1180,16 @@ dyn_mat syspermute(const Eigen::MatrixBase& A const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::syspermute()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::syspermute()"); // check that dims is a vlid dimension vector if (!internal::check_dims(dims)) - throw Exception("clara::syspermute()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::syspermute()"); // check that we a valid permutation if (!internal::check_perm(perm)) - throw Exception("clara::syspermute()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::syspermute()"); // check that permutation match dimension if (perm.size() != dims.size()) - throw Exception("clara::syspermute()", Exception::Type::PERM_MISMATCH_DIMS); + throw exception::PermMismatchDims("clara::syspermute()"); idx D = static_cast(rA.rows()); idx N = dims.size(); @@ -1204,7 +1202,7 @@ dyn_mat syspermute(const Eigen::MatrixBase& A // check dims match of the dimension rA if (!internal::check_dims_match_cvect(dims, rA)) - throw Exception("clara::syspermute()", Exception::Type::DIMS_MISMATCH_CVECTOR); + throw exception::DimsMismatchCvector("clara::syspermute()"); // copy dims in Cdims and perm in Cperm for (idx i = 0; i < N; ++i) { @@ -1242,7 +1240,7 @@ dyn_mat syspermute(const Eigen::MatrixBase& A // check that dims match the dimnesion of rA if (!internal::check_dims_match_mat(dims, rA)) - throw Exception("clara::syspermute()", Exception::Type::DIMS_MISMATCH_MATRIX); + throw exception::DimsMismatchMatrix("clara::syspermute()"); for (idx i = 0; i < N; ++i) { Cdims[i] = dims[i]; Cdims[i + N] = dims[i]; @@ -1278,7 +1276,7 @@ dyn_mat syspermute(const Eigen::MatrixBase& A result(worker(i)) = rA(i); return reshape(result, D, D); } else - throw Exception("clara::syspermute()", Exception::Type::MATRIX_NOT_SQUARE_OR_CVECTOR); + throw exception::MatrixNotSquareNotCvector("clara::syspermute()"); } /** @@ -1293,11 +1291,11 @@ dyn_mat syspermute(const Eigen::MatrixBase& A const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw Exception("clara::syspermute()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::syspermute()"); // check valid dims - if (d == 0) - throw Exception("clara::syspermute()", Exception::Type::DIMS_INVALID); + if (d < 2) + throw exception::DimsInvalid("clara::syspermute()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); std::vector dims(N, d); diff --git a/include/random.h b/include/random.h index 16ebea8..0f5f80a 100644 --- a/include/random.h +++ b/include/random.h @@ -22,13 +22,13 @@ namespace clara { */ inline double rand(double a, double b) { if (a >= b) - throw clara::Exception("clara::rand()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::rand()"); std::uniform_real_distribution<> ud(a, b); #ifdef NO_THREAD_LOCAL_ - return ud(RandomDevices::get_instance().rng_); + return ud(RandomDevices::get_instance().get_prng()); #else - return ud(RandomDevices::get_thread_local_instance().rng_); + return ud(RandomDevices::get_thread_local_instance().get_prng()); #endif // DEBUG } @@ -38,12 +38,12 @@ inline double rand(double a, double b) { */ inline bigint rand(bigint a, bigint b) { if (a > b) - throw clara::Exception("clara::rand()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::rand()"); std::uniform_int_distribution uid(a, b); #ifdef NO_THREAD_LOCAL_ - return uid(RandomDevices::get_instance().rng_); + return uid(RandomDevices::get_instance().get_prng()); #else - return uid(RandomDevices::get_thread_local_instance().rng_); + return uid(RandomDevices::get_thread_local_instance().get_prng()); #endif // DEBUG } @@ -54,13 +54,13 @@ inline bigint rand(bigint a, bigint b) { inline idx randidx(idx a = std::numeric_limits::min(), idx b = std::numeric_limits::max()) { if (a < b) - throw clara::Exception("clara::rand()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::randidx()"); std::uniform_int_distribution uid(a, b); #ifdef NO_THREAD_LOCAL_ - return uid(RandomDevices::get_instance().rng_); + return uid(RandomDevices::get_instance().get_prng()); #else - return uid(RandomDevices::get_thread_local_instance().rng_); + return uid(RandomDevices::get_thread_local_instance().get_prng()); #endif // DEBUG } @@ -76,7 +76,7 @@ Derived rand(idx rows, idx cols, double a = 0, double b = 1) { (void)cols; (void)a; (void)b; - throw Exception("clara::rand()", Exception::Type::UNDEFINED_TYPE); + throw exception::UndefinedType("clara::rand()"); } /** @@ -88,9 +88,9 @@ Derived rand(idx rows, idx cols, double a = 0, double b = 1) { template <> inline dmat rand(idx rows, idx cols, double a, double b) { if (rows == 0 || cols == 0) - throw Exception("clara::rand()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::rand()"); if (a >= b) - throw clara::Exception("clara::rand()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::rand()"); return dmat::Zero(rows, cols).unaryExpr([a, b](double) { return rand(a, b); }); } @@ -105,9 +105,9 @@ inline dmat rand(idx rows, idx cols, double a, double b) { template <> inline cmat rand(idx rows, idx cols, double a, double b) { if (rows == 0 || cols == 0) - throw Exception("clara::rand()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::rand()"); if (a >= b) - throw clara::Exception("clara::rand()", clara::Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::rand()"); return rand(rows, cols, a, b).cast() + 1_i * rand(rows, cols, a, b).cast(); @@ -123,7 +123,7 @@ Derived randn(idx rows, idx cols, double mean = 0, double sigma = 1) { (void)cols; (void)mean; (void)sigma; - throw Exception("clara::randn()", Exception::Type::UNDEFINED_TYPE); + throw exception::UndefinedType("clara::randn()"); } /** @@ -135,13 +135,13 @@ Derived randn(idx rows, idx cols, double mean = 0, double sigma = 1) { template <> inline dmat randn(idx rows, idx cols, double mean, double sigma) { if (rows == 0 || cols == 0) - throw Exception("clara::rand()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::randn()"); std::normal_distribution<> nd(mean, sigma); return dmat::Zero(rows, cols).unaryExpr([&nd](double) { #ifdef NO_THREAD_LOCAL_ - return nd(RandomDevices::get_instance().rng_); + return nd(RandomDevices::get_instance().get_prng()); #else - return nd(RandomDevices::get_thread_local_instance().rng_); + return nd(RandomDevices::get_thread_local_instance().get_prng()); #endif // DEBUG }); } @@ -154,7 +154,7 @@ inline dmat randn(idx rows, idx cols, double mean, double sigma) { template <> inline cmat randn(idx rows, idx cols, double mean, double sigma) { if (rows == 0 || cols == 0) - throw Exception("clara::randn()", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::randn()"); return randn(rows, cols, mean, sigma).cast() + 1_i * randn(rows, cols, mean, sigma).cast(); } @@ -166,15 +166,15 @@ inline cmat randn(idx rows, idx cols, double mean, double sigma) { inline double randn(double mean = 0, double sigma = 1) { std::normal_distribution<> nd(mean, sigma); #ifdef NO_THREAD_LOCAL_ - return nd(RandomDevices::get_instance().rng_); + return nd(RandomDevices::get_instance().get_prng()); #else - return nd(RandomDevices::get_thread_local_instance().rng_); + return nd(RandomDevices::get_thread_local_instance().get_prng()); #endif // DEBUG } inline cmat randU(idx D) { if (D == 0) - throw Exception("clara::randU()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randU()"); cmat X = 1 / std::sqrt(2.) * randn(D, D); Eigen::HouseholderQR qr(X); @@ -193,7 +193,7 @@ inline cmat randU(idx D) { */ inline cmat randV(idx Din, idx Dout) { if (Din == 0 || Dout == 0 || Din > Dout) - throw Exception("clara::randV()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randV()"); return randU(Dout).block(0, 0, Dout, Din); } @@ -203,9 +203,9 @@ inline cmat randV(idx Din, idx Dout) { */ inline std::vector randkraus(idx N, idx D) { if (N == 0) - throw Exception("clara::randkraus()", Exception::Type::OUT_OF_RANGE); + throw exception::OutOfRange("clara::randkraus()"); if (D == 0) - throw Exception("clara::randkraus()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randkraus()"); std::vector result(N); for (idx i = 0; i < N; ++i) @@ -229,7 +229,7 @@ inline std::vector randkraus(idx N, idx D) { */ inline cmat randH(idx D) { if (D == 0) - throw Exception("clara::randH()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randH()"); cmat H = 2 * rand(D, D) - (1. + 1_i) * cmat::Ones(D, D); return H + adjoint(H); } @@ -240,7 +240,7 @@ inline cmat randH(idx D) { */ inline ket randket(idx D) { if (D == 0) - throw Exception("clara::randket()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randket()"); ket kt = randn(D, 1); return kt / norm(kt); } @@ -251,7 +251,7 @@ inline ket randket(idx D) { */ inline cmat randrho(idx D) { if (D == 0) - throw Exception("clara::randrho()", Exception::Type::DIMS_INVALID); + throw exception::DimsInvalid("clara::randrho()"); cmat result = 10 * randH(D); result = result * adjoint(result); return result / trace(result); @@ -265,16 +265,16 @@ inline cmat randrho(idx D) { */ inline std::vector randperm(idx N) { if (N == 0) - throw Exception("clara::randperm()", Exception::Type::PERM_INVALID); + throw exception::PermInvalid("clara::randperm()"); std::vector result(N); // fill increasing oreder std::iota(std::begin(result), std::end(result), 0); #ifdef NO_THREAD_LOCAL_ - std::shuffle(std::begin(result), std::end(result), RandomDevice::get_instance().rng_); + std::shuffle(std::begin(result), std::end(result), RandomDevices::get_instance().get_prng()); #else std::shuffle(std::begin(result), std::end(result), - RandomDevices::get_thread_local_instance().rng_); + RandomDevices::get_thread_local_instance().get_prng()); #endif // NO_THREAD_LOCAL_ return result; } @@ -286,16 +286,16 @@ inline std::vector randperm(idx N) { */ inline std::vector randprob(idx N) { if (N == 0) - throw Exception("clara::randprob()", Exception::Type::PERM_INVALID); + throw exception::OutOfRange("clara::randprob()"); std::vector result(N); // generate std::exponential_distribution<> ed(1); for (idx i = 0; i < N; ++i) { #ifdef NO_THREAD_LOCAL_ - result[i] = ed(clara::RandomDevice::get_instance().rng_); + result[i] = ed(clara::RandomDevices::get_instance().get_prng()); #else - result[i] = ed(clara::RandomDevices::get_thread_local_instance().rng_); + result[i] = ed(clara::RandomDevices::get_thread_local_instance().get_prng()); #endif // NO_THREAD_LOCAL_ } // normalize diff --git a/include/statistics.h b/include/statistics.h index 13ef73b..d3281d4 100644 --- a/include/statistics.h +++ b/include/statistics.h @@ -18,7 +18,7 @@ namespace clara { */ inline std::vector uniform(idx N) { if (N == 0) - throw Exception("clara::uniform", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::uniform()"); return std::vector(N, 1. / N); } @@ -29,7 +29,7 @@ inline std::vector uniform(idx N) { */ inline std::vector marginalX(const dmat& probXY) { if (!internal::check_nonzero_size(probXY)) - throw Exception("clara::marginalX", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::marginalX()"); std::vector result(probXY.rows(), 0); for (idx i = 0; i < static_cast(probXY.rows()); ++i) { @@ -48,7 +48,7 @@ inline std::vector marginalX(const dmat& probXY) { */ inline std::vector marginalY(const dmat& probXY) { if (!internal::check_nonzero_size(probXY)) - throw Exception("clara::marginalY", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::marginalY()"); return marginalX(probXY.transpose()); } @@ -62,9 +62,9 @@ template double avg(const std::vector& prob, const Container& X, typename std::enable_if::value>::type* = nullptr) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::avg", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::avg()"); if (!internal::check_matching_sizes(prob, X)) - throw Exception("clara::avg", Exception::Type::SIZE_MISMATCH); + throw exception::SizeMismatch("clara::avg()"); double result = 0; for (idx i = 0; i < prob.size(); ++i) @@ -84,9 +84,9 @@ template double cov(const dmat& probXY, const Container& X, const Container& Y, typename std::enable_if::value>::type* = nullptr) { if (!internal::check_nonzero_size(X) || !internal::check_nonzero_size(Y)) - throw Exception("clara::cov", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::cov()"); if (static_cast(probXY.rows()) != X.size() || static_cast(probXY.cols()) != Y.size()) - throw Exception("clara::cov", Exception::Type::SIZE_MISMATCH); + throw exception::SizeMismatch("clara::cov()"); std::vector probX = marginalX(probXY); std::vector probY = marginalY(probXY); @@ -103,9 +103,9 @@ template double var(const std::vector& prob, const Container& X, typename std::enable_if::value>::type* = nullptr) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::var", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::var()"); if (!internal::check_matching_sizes(prob, X)) - throw Exception("clara::var", Exception::Type::SIZE_MISMATCH); + throw exception::SizeMismatch("clara::var()"); Eigen::VectorXcd diag(prob.size()); for (idx i = 0; i < prob.size(); ++i) diag(i) = prob[i]; @@ -117,9 +117,9 @@ template double sigma(const std::vector& prob, const Container& X, typename std::enable_if::value>::type* = nullptr) { if (!internal::check_nonzero_size(prob)) - throw Exception("clara::sigma", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::sigma()"); if (!internal::check_matching_sizes(prob, X)) - throw Exception("clara::sigma", Exception::Type::SIZE_MISMATCH); + throw exception::SizeMismatch("clara::sigma()"); return std::sqrt(var(prob, X)); } @@ -127,9 +127,9 @@ template double cor(const dmat& probXY, const Container& X, const Container& Y, typename std::enable_if::value>::type* = nullptr) { if (!internal::check_nonzero_size(X) || !internal::check_nonzero_size(Y)) - throw Exception("clara::cor", Exception::Type::ZERO_SIZE); + throw exception::ZeroSize("clara::cor()"); if (static_cast(probXY.rows()) != X.size() || static_cast(probXY.cols()) != Y.size()) - throw Exception("clara::cor", Exception::Type::SIZE_MISMATCH); + throw exception::SizeMismatch("clara::cor()"); return cov(probXY, X, Y) / (sigma(marginalX(probXY), X) * sigma(marginalX(probXY), Y)); } From ae4feab0eaa6b76adc88dbd4ccacc089c97fda53 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Fri, 21 Jul 2023 16:58:29 +0700 Subject: [PATCH 11/80] chore(feat): update ``testing/_test.cpp`` [Documentation] testing quantum bit manipulation - the code initialize the number of bits and the number of trials. it creates a vector of vectors called `indices`, where each sub-vector contains indices representing bits for trial operations - it perform a series trials using `TOF` operation on the `Bit_circuit`, the `TOF` operation applies controlled NOT operation on the specified bits in the circuit - after the trials the code display the initial and final states of the bit circuit as well as the hamming weight ( number of set bits ) - create `ket` and `bra` instance representing quantim states and display them using the `disp` function Signed-off-by: slowy07 --- CMakeLists.txt | 86 ++++++++++++++++++++++----------------------- include/functions.h | 2 +- testing/_test.cpp | 82 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 121 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b13cfec..f86407d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,27 @@ project( VERSION 1.0 LANGUAGES CXX) -# C++11 +include_directories(include) + +set(SOURCE_FILES testing/minimal.cpp) + +message(STATUS "Detecting Eigen3") +set(EIGEN3_INCLUDE_DIR "" CACHE PATH "Path to Eigen3") +if(NOT ${EIGEN3_INCLUDE_DIR} STREQUAL "") + if(IS_DIRECTORY ${EIGEN3_INCLUDE_DIR}) + message(STATUS "Detecting Eigen3 - done (in ${EIGEN3_INCLUDE_DIR})") + include_directories(SYSTEM "${EIGEN3_INCLUDE_DIR}") + else() + message(FATAL_ERROR "Invalid path to Eigen3 installation") + endif() +else() + find_package(Eigen3 3.0 QUIET NO_MODULE) + if(NOT TARGET Eigen3::Eigen) + message(FATAL_ERROR "eigen 3 not detected please point EIGEN3_INCLUDE_DIR") + endif() + message(STATUS "Detecting Eigen3 - done") +endif() + if(CYGWIN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") else() @@ -14,73 +34,47 @@ endif() set(ADDITIONAL_FLAGS "-pedantic -Wall -Wextra -Weffc++") set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem") -# CMake suppport only for GNUC gcc and Clang and apple clang if(NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - message(FATAL_ERROR "CMakeLists.txt only work with GNU gcc/Clang/AppleClang") + message( + FATAL_ERROR "this CMakeLists.txt work only with GNU gcc/Clang/AppleCLang") endif() -# clara header -include_directories(include) -set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} include) - -# source file -set(SOURCE_FILES testing/_test.cpp) - -include_directories(SYSTEM "$ENV{HOME}/eigen3") +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") + add_definitions(-DNO_THREAD_LOCAL_) + message( + WARNING + "Detected compiler ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}. thread_local not supported" + ) +endif() -# openmp option(WITH_OPENMP "OpenMP support" ON) if(${WITH_OPENMP}) - # inject definition (as #define) in the source file - add_definitions(-DWITH_OPENMP_) - # gnuc - if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" + OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" + AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.8" + OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "3.8"))) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") - # clang but not appleClang - elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") - set(CLANG_LIBOMP "opt/local/lib/libomp/") - set(CLANG_LIBOMP_INCLUDE "/opt/local/include/libomp/") - include_directories(SYSTEM "${CLANG_LIBOMP_INCLUDE}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp=libomp") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CLANG_LIBOMP}") - endif() + add_definitions(-DWITH_OPENMP) else() message( WARNING - "detected compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} A support for openMP is enable only the GNU gcc compiler or Clang >= 3.7" + "Detected compiler : ${CMAKE_CXX_COMPILER_VERSION}, support for OpenMP is enabled only for the gnu gcc compiler or the clang version 3.8 higher" ) endif() endif() -# enable all warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ADDITIONAL_FLAGS}") -# disable support for thread_local storage duration specifier when using apple -# clang as libc++ doesn't yet support -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") - # inject definition (as #define) in the source files - add_definitions(-DNO_THREAD_LOCAL_) - message( - WARNING - "detected compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} thread_local not support" - ) -endif() - -# gnu gcc additional debug settings -if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") +if(${CMAKE_CXX_FLAGS} MATCHES "GNU") if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-weak") endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -D_GLIBCXX_DEBUG") endif() -# configuration set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -DEIGEN_NO_DEBUG") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DEIGEN_NO_DEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DEIGEN_NO_DEBUG") @@ -89,12 +83,16 @@ if(NOT CMAKE_BUILD_TYPE) Release CACHE STRING - "choose the type of build, options are: None Debug Release miniSizeRel RelWithDebugInfo" + "choose tyhe type build, oprtions are: None Debug Release MinSizeRel RelWithDebInfo" FORCE) endif() add_executable(clara ${SOURCE_FILES}) +if(TARGET Eigen3::Eigen) + target_link_libraries(clara Eigen3::Eigen) +endif() + if($WITH_OPENMP$ AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") diff --git a/include/functions.h b/include/functions.h index f690f22..a031e1e 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1078,7 +1078,7 @@ ket operator"" _ket() { template bra operator"" _bra() { constexpr idx n = sizeof...(Bits); - constexpr char bits[n + 1] = {Bits..., "\0"}; + constexpr char bits[n + 1] = {Bits..., '\0'}; clara::bra q = clara::ket::Zero(std::pow(2, n)); idx pos = 2; diff --git a/testing/_test.cpp b/testing/_test.cpp index e491478..b6c2a60 100644 --- a/testing/_test.cpp +++ b/testing/_test.cpp @@ -1,11 +1,85 @@ +#include #include +#include +#include +#include +#include +#include + #include "../include/clara.h" #include "../include/experimental/experimental_test.h" -using namespace clara; - int main() { + using namespace clara; std::cout << "testing testing \n"; - PRINTLN("testing dbg message"); - ERRORLN("testing error debug message"); + + const idx bits = 70; + experimental::Bit_circuit b{bits}; + + const idx trials = 20; + + // number of trials + b.rand(); + auto c = b; + + std::random_device rd; + std::mt19937 gen{rd()}; + std::vector> indices(trials); + + for (idx i = 0; i < trials; ++i) { + std::vector v(bits); + std::iota(v.begin(), v.end(), 0); + std::shuffle(v.begin(), v.end(), gen); + std::vector tof(v.data(), v.data() + 3); + indices[i] = tof; + } + + for (idx i = 0; i < trials; ++i) { + std::cout << "first: "; + for (auto&& elem : indices[i]) + std::cout << elem << " "; + std::cout << std::endl; + b.TOF(indices[i]); + } + + for (idx i = trials; i-- > 0;) { + std::cout << "second: "; + for (auto&& elem : indices[i]) + std::cout << elem << " "; + std::cout << std::endl; + b.TOF(indices[i]); + } + + std::cout << "initial: " << b << std::endl; + std::cout << "final: " << c << std::endl; + std::cout << "hamming weight: " << b.count() << std::endl; + + std::cout << b.gate_count.NOT << " " << b.gate_count.X << " " << b.gate_count.TOF << std::endl; + std::cout << (b == c) << std::endl; + std::cout << (b != c) << std::endl; + + experimental::Dynamic_bitset bb(9); + bb.set(1).set(3).set(8); + std::cout << bb << std::endl; + + std::cout << "info: " << std::endl; + std::cout << bb.to_string('o', 'X') << std::endl; + + experimental::Dynamic_bitset vlad(20); + std::cout << vlad << std::endl; + + std::vector vv(20); + for (auto& elem : vv) { + std::cout << elem; + } + std::cout << std::endl; + + ket x = (10_ket + 01_ket) / std::sqrt(2); + std::cout << disp(x) << std::endl; + + bra y = (10_bra + 01_bra) / std::sqrt(2); + std::cout << disp(x) << std::endl; + + cmat z = 110_prj; + std::cout << disp(z) << std::endl; } From bad07d96b8e628d07baf6f6235b2c10e75190beb Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sat, 22 Jul 2023 22:15:36 +0700 Subject: [PATCH 12/80] chore(feat): update ``testing/grover_search.cpp`` [Documentation] grover algorithm starts with equal superposition of all possible states, then iteratively applies the oracle and diffusion operator to amplify the probability of the marked state while reducing the probability of other states, after a sufficient number of iterations, measuring the final state gives a high probability of obtaining the marked state fix: fixing ``clara/include/operations.h`` [Documentation] fixing code to provide a flexible efficient way to apply a controlled quantum gate a quantum state, allowing for various types of control and target subsystem configurations. the use of eigen library provides efficient matrix operations, and the code is optimized for performance using multi-threading fix: fixing ``clara/include/random.h`` fixing function to provide a convenient and safe way to generate random indices whithin specified range, making use of the standard library random number generation capabilitesm use of thread local random number generators is particularly helpful multi-threading applications to avoid potential concurency issues Signed-off-by: slowy07 --- include/operations.h | 35 +++------------------ include/random.h | 2 +- testing/grover_search.cpp | 64 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 testing/grover_search.cpp diff --git a/include/operations.h b/include/operations.h index 4862dad..d84d8c1 100644 --- a/include/operations.h +++ b/include/operations.h @@ -24,7 +24,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& const std::vector& ctrl, const std::vector& subsys, const std::vector& dims) { - const typename Eigen::MatrixBase::EvalBaseReturnTyp& rstate = state.derived(); + const typename Eigen::MatrixBase::EvalReturnType& rstate = state.derived(); const dyn_mat& rA = A.derived(); if (!std::is_same::value) @@ -83,6 +83,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& idx Dctrl = static_cast(std::llround(std::pow(d, ctrlsize))); idx DA = static_cast(rA.rows()); + // local dimension idx Cdims[maxn]; idx CdimsA[maxn]; idx CdimsCTRL[maxn]; @@ -314,7 +315,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& * of subsys */ template -dyn_mat applyCtrl(const Eigen::MatrixBase& state, +dyn_mat applyCTRL(const Eigen::MatrixBase& state, const Eigen::MatrixBase& A, const std::vector& ctrl, const std::vector& subsys, idx d = 2) { @@ -330,35 +331,7 @@ dyn_mat applyCtrl(const Eigen::MatrixBase& idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); std::vector dims(N, d); - return applyCtrl(rstate, rA, ctrl, subsys, dims); -} - -/** - * @brief applies the controlled gate A to the part subsys - * of the multi-partite state vector or density matrix state - * @note the dimension of the gate A must match the dimension - * of subsys - * @return CTRL-A gate applied to the part subsys of state - */ -template -dyn_mat applyCTRL(const Eigen::MatrixBase& state, - const Eigen::MatrixBase& A, - const std::vector& ctrl, - const std::vector& subsys, idx d = 2) { - const typename Eigen::MatrixBase::EvalReturnType& rstate = state.derived(); - const dyn_mat& rA = A.derived(); - - // check zero size - if (!internal::check_nonzero_size(rstate)) - throw exception::ZeroSize("clara::applyCTRL()"); - - // check valid dims - if (d == 0) - throw exception::DimsInvalid("clara::applyCTRL()"); - idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); - std::vector dims(N, d); - - return applyCtrl(rstate, rA, ctrl, subsys, dims); + return applyCTRL(rstate, rA, ctrl, subsys, dims); } /** diff --git a/include/random.h b/include/random.h index 0f5f80a..0179827 100644 --- a/include/random.h +++ b/include/random.h @@ -53,7 +53,7 @@ inline bigint rand(bigint a, bigint b) { */ inline idx randidx(idx a = std::numeric_limits::min(), idx b = std::numeric_limits::max()) { - if (a < b) + if (a > b) throw exception::OutOfRange("clara::randidx()"); std::uniform_int_distribution uid(a, b); diff --git a/testing/grover_search.cpp b/testing/grover_search.cpp new file mode 100644 index 0000000..73874ef --- /dev/null +++ b/testing/grover_search.cpp @@ -0,0 +1,64 @@ +/** +* @brief grove search algorithm +* a quantum algorithm for searching an unsorted +* database with N entries in O(N^(1/2)) time and using +* O(logN) stroage space +*/ + +#include +#include +#include +#include +#include + +#include "../include/clara.h" + +using namespace clara; + +int main() { + idx n = 4; + std::cout << "grover on n " << n << "qubits" << std::endl; + + // local dimension + std::vector dims(n, 2); + idx N = std::round(std::pow(2, n)); + std::cout << "database size " << N << std::endl; + + // mark an element randomly + idx marked = clara::randidx(0, N - 1); + std::cout << "mark state " << marked << "->"; + std::cout << clara::disp(clara::n2multiidx(marked, dims), " ") << std::endl; + + // computational |0>^times n + clara::ket psi = clara::mket(n2multiidx(0, dims)); + + // apply H^times n, no aliasing + psi = (kronpow(gt.H, n) * psi).eval(); + // diffusion operator + cmat G = 2 * prj(psi) - gt.Id(N); + + // number queries + idx nqueries = std::ceil(pi / 4 * std::sqrt(N)); + std::cout << "run " << nqueries << " query" << std::endl; + for (idx i = 0; i < nqueries; ++i) { + // apply oracle first, no aliasing + psi(marked) = -psi(marked); + // then the deiffusion operator + psi = (G * psi).eval(); + } + + auto measured = clara::measure(psi, gt.Id(N)); + std::cout << "probability of marked state: " << std::get<1>(measured)[marked] << std::endl; + std::cout << "probability of all results: "; + std::cout << clara::disp(std::get<1>(measured), ", ") << std::endl; + + // sample + std::cout << "sample " << std::endl; + idx result = std::get<0>(measured); + if (result == marked) + std::cout << "correct result obrained "; + else + std::cout << "not yet obtained"; + std::cout << result << " -> "; + std::cout << clara::disp(n2multiidx(result, dims), " ") << std::endl; +} From 358b33cafd6738a261975723feb6e395c862c6ed Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sat, 22 Jul 2023 22:24:57 +0700 Subject: [PATCH 13/80] chore(attemp: 1): testing grover search and testing channels Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 241ea41..adbb180 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -23,6 +23,18 @@ jobs: - name: Build and test run: | + echo "check g++ version" + g++ --version cd clara_test chmod +x run_test ./run_test + + - name: testing grover search + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/grover_search.cpp -o grover_search + ./grover_search + + - name: testing channels + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels + ./channels From bf3af3dcf5b17defbe294de7071186c8a68f8fbb Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 06:58:00 +0700 Subject: [PATCH 14/80] docs: adding documentation Signed-off-by: slowy07 --- README.md | 2 + testing/_test.cpp | 35 +++++++++++++++- testing/channels.cpp | 13 ++++++ testing/grover_search.cpp | 84 +++++++++++++++++++++++++++++---------- testing/minimal.cpp | 17 +++++++- 5 files changed, 129 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a61f16e..930bff8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ![clara-image](.github/clara.png) +![Clara ubuntu test](https://img.shields.io/github/actions/workflow/status/slowy07/clara/cpp-testing.yml?style=flat-square&logo=github&label=Clara%20Ubuntu%20Test) + Quantum computing is a type of computing that harnesses the power of quantum mechanics to solve problems that are too difficult for classical computers. Quantum computers use qubits, which are quantum bits, to store information. Qubits can be in a superposition of states, which means that they can be both 0 and 1 at the same time. This allows quantum computers to perform calculations that are exponentially faster than classical computers. Clara are open sourec library for quantum computing processing that is designed to be faster on classical computer. the library provides a high-level interface for writing quantum algorithm and it uses a variety of techniques to optimize the performance of these algorithm on classical computer. the lib has the potential to make quantum algorithm more accessible to a wider range of researcher and developers and could help to accelerate the develompent of quantum computing applications diff --git a/testing/_test.cpp b/testing/_test.cpp index b6c2a60..b8a1b1e 100644 --- a/testing/_test.cpp +++ b/testing/_test.cpp @@ -9,10 +9,19 @@ #include "../include/clara.h" #include "../include/experimental/experimental_test.h" +/** + * @brief testing clara + * demonstrate the functionally of clara library by performing quantum + * computing operations, manipulating quantum states, applying quantum + * gates, and displaying the result, it show how create quantum circuits + * and perform various operations on them using clara library + */ + +using namespace clara; int main() { - using namespace clara; std::cout << "testing testing \n"; + // initialize bit circuit with 70 bits const idx bits = 70; experimental::Bit_circuit b{bits}; @@ -22,6 +31,13 @@ int main() { b.rand(); auto c = b; + /** + * random number generator `gen` is set up using + * random_device for generating random indices. for each trial + * vector `v` is filled wuth values from `0` to `bits-1`. the element + * of `v` represent the indices of the `TOF` (Toffoli) operation. these indices + * are store for later use in trials + */ std::random_device rd; std::mt19937 gen{rd()}; std::vector> indices(trials); @@ -34,6 +50,11 @@ int main() { indices[i] = tof; } + /** + * the first half of the trials is performed. the indices for each trials are + * printed, and the `TOF` operations is applied to the `Bit_circuit` using + * corresponding indices + */ for (idx i = 0; i < trials; ++i) { std::cout << "first: "; for (auto&& elem : indices[i]) @@ -42,6 +63,11 @@ int main() { b.TOF(indices[i]); } + /** + * the second half of the trials performed in reverse order. the indices for each + * trial are again printed, and the `TOF` operation is applied to the `Bit_circuit` + * using the corresponding indices + */ for (idx i = trials; i-- > 0;) { std::cout << "second: "; for (auto&& elem : indices[i]) @@ -50,6 +76,10 @@ int main() { b.TOF(indices[i]); } + /** + * the initail and final state are printed. the hamming weight (number of set bits) + * of `b` is displayed. the count of NOT, X and TOF gates used in `b` is printed + */ std::cout << "initial: " << b << std::endl; std::cout << "final: " << c << std::endl; std::cout << "hamming weight: " << b.count() << std::endl; @@ -58,6 +88,9 @@ int main() { std::cout << (b == c) << std::endl; std::cout << (b != c) << std::endl; + /** + * various quantum states and projections are created and printed + */ experimental::Dynamic_bitset bb(9); bb.set(1).set(3).set(8); std::cout << bb << std::endl; diff --git a/testing/channels.cpp b/testing/channels.cpp index 7c0fa84..57fbd5a 100644 --- a/testing/channels.cpp +++ b/testing/channels.cpp @@ -7,7 +7,20 @@ using namespace clara; +/** + * @brief channels clara + * perform various quantum channel oeprations + * such as computing choi matrices, + * kraus operator, and superoperator matricesa and + * and verifies the correctness of these operations + * by comparing the outpu states + */ + int main() { + /** + * initializes the variables `nk` and `D`, which represent the number + * of kraus operatos and the dimension of the quantum space + */ idx nk = 5; idx D = 3; diff --git a/testing/grover_search.cpp b/testing/grover_search.cpp index 73874ef..730b4ed 100644 --- a/testing/grover_search.cpp +++ b/testing/grover_search.cpp @@ -1,9 +1,9 @@ /** -* @brief grove search algorithm -* a quantum algorithm for searching an unsorted -* database with N entries in O(N^(1/2)) time and using -* O(logN) stroage space -*/ + * @brief grove search algorithm + * a quantum algorithm for searching an unsorted + * database with N entries in O(N^(1/2)) time and using + * O(logN) stroage space + */ #include #include @@ -16,37 +16,81 @@ using namespace clara; int main() { - idx n = 4; + // represent the number of qubit used in algorithm + clara::idx n = 4; std::cout << "grover on n " << n << "qubits" << std::endl; - // local dimension - std::vector dims(n, 2); - idx N = std::round(std::pow(2, n)); + /** + * local dimension is vector with `n` elements, each + * elements representing the dimension of a qubit in the + * quantum system, in this case, all qubits have 2 dimension + * (standard quantum bit or qubit) + */ + std::vector dims(n, 2); + /** + * `N` calculated 2^n, representing the size of the quantum + * state or the database to be searched + */ + clara::idx N = std::round(std::pow(2, n)); std::cout << "database size " << N << std::endl; - // mark an element randomly - idx marked = clara::randidx(0, N - 1); + /** + * random element `marked` from the database is selected + * for demonstration purposes, the element is printed, + * showing its binary representation using the function + * clara::n2multiidx + */ + clara::idx marked = clara::randidx(0, N - 1); std::cout << "mark state " << marked << "->"; std::cout << clara::disp(clara::n2multiidx(marked, dims), " ") << std::endl; - // computational |0>^times n - clara::ket psi = clara::mket(n2multiidx(0, dims)); + /** + * computational state `psi` is initialized to `|0>^n`, meaning + * all qubits are in the `|0>` state + */ + clara::ket psi = clara::mket(clara::n2multiidx(0, dims)); - // apply H^times n, no aliasing - psi = (kronpow(gt.H, n) * psi).eval(); - // diffusion operator - cmat G = 2 * prj(psi) - gt.Id(N); + /** + * hadamard gate `H` is applied to each qubit `n` times + * using function `clara::kronpow`, where `gt.H` + * is the hadamard gate, the storead in `psi`, representing + * the superposition of all possible states + */ + psi = (clara::kronpow(clara::gt.H, n) * psi).eval(); + /** + * diffusion operator `G` is calculated by subtract the identity + * matrix `gt.Id(N)` from the projection of `psi` onto itsself + * (2 * prj(psi)). the diffusion operator amplifies the + * amplitude of the marked element in the superposition of all + * possible sates + */ + clara::cmat G = 2 * clara::prj(psi) - clara::gt.Id(N); - // number queries - idx nqueries = std::ceil(pi / 4 * std::sqrt(N)); + /** + * number of queries is set based on the square root of the database + * size `N` + */ + clara::idx nqueries = std::ceil(clara::pi / 4 * std::sqrt(N)); + /** + * the grover algorithm is executed by applying the oracle operation + * (inverting the amplitude of the marked element) followed the + * diffusion operator in a loop for `nqueries` times. this amplifies + * the amplitude of the marked state in the superposition + */ std::cout << "run " << nqueries << " query" << std::endl; - for (idx i = 0; i < nqueries; ++i) { + for (clara::idx i = 0; i < nqueries; ++i) { // apply oracle first, no aliasing psi(marked) = -psi(marked); // then the deiffusion operator psi = (G * psi).eval(); } + /** + * the probability of the marked state is calculated and printed, + * representing the probability of finding the marked element in + * the superposition, the probability of all states in the superposition + * are printed using the `clara::disp()` function + */ auto measured = clara::measure(psi, gt.Id(N)); std::cout << "probability of marked state: " << std::get<1>(measured)[marked] << std::endl; std::cout << "probability of all results: "; diff --git a/testing/minimal.cpp b/testing/minimal.cpp index 0d18b26..334e337 100644 --- a/testing/minimal.cpp +++ b/testing/minimal.cpp @@ -1,9 +1,24 @@ #include + #include "../include/clara.h" using namespace clara; +/* + * @brief minimal test + * this code is a simple test to check the functionally + * of the `clara` library and verify that the quantum + * state |0> is correctly repersented and displayed + * it starting point for more complex quantum computing + * operation using clara function + */ int main() { std::cout << "testing quantum state |0> state: \n"; - std::cout << disp(st.z0) << std::endl; + /** + * represent the quantum state `|0>`, + * in the quantum computing, the state `|0>` represent + * the basic state qubit, where the qubit is the + * zero state + */ + std::cout << disp(st.z0) << std::endl; } From 5be026cb4c08ce642839c68eeec3da562454c332 Mon Sep 17 00:00:00 2001 From: khaira nabila <75076265+khairanabila@users.noreply.github.com> Date: Sun, 23 Jul 2023 08:41:38 +0700 Subject: [PATCH 15/80] chore: test macos environment (#3) --- .github/workflows/cpp-testing.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index adbb180..643f727 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -38,3 +38,31 @@ jobs: run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels ./channels + + build-and-test-macos: + runs-on: macos-latest + + steps: + - name: checkout repository + uses: actions/checkout@v3 + + - name: install eigen + run: brew install eigen + + - run: build and test + run: | + echo "check g++ version" + g++ --version + cd clara_test + chmod +x run_test + ./run_test + + - name: testing grover search + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem /usr/local/include -I $HOME/clara/include testing/grover_search.cpp -o grover_search + ./grover_search + + - name: testing channels + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem /usr/local/include -I $HOME/clara/include testing/channels.cpp -o channels + ./channels \ No newline at end of file From 50aae5419c482d4ca9c24d8add7f131df53bc844 Mon Sep 17 00:00:00 2001 From: arfy slowy Date: Sun, 23 Jul 2023 08:45:08 +0700 Subject: [PATCH 16/80] fix: typo spelling run --- .github/workflows/cpp-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 643f727..758c1ec 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -49,7 +49,7 @@ jobs: - name: install eigen run: brew install eigen - - run: build and test + - name: build and test run: | echo "check g++ version" g++ --version From ce0a334472e87b590df121fc89229b1f09910b13 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 18:35:42 +0700 Subject: [PATCH 17/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 4 ++-- include/clara.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 758c1ec..4db8b4e 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -1,4 +1,4 @@ -name: Ubuntu Clara Test +name: Clara Test on: push: @@ -65,4 +65,4 @@ jobs: - name: testing channels run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem /usr/local/include -I $HOME/clara/include testing/channels.cpp -o channels - ./channels \ No newline at end of file + ./channels diff --git a/include/clara.h b/include/clara.h index fc5b0bd..ed1fb3f 100644 --- a/include/clara.h +++ b/include/clara.h @@ -36,8 +36,8 @@ #include #include -#include -#include +#include +#include // inter dependicies #include "classFunction/codes.h" From 1b897fe0900f9d054224466a81a2f21e5bbe5ea1 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 18:43:58 +0700 Subject: [PATCH 18/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 10 +++++++++- include/clara.h | 2 -- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 4db8b4e..9111371 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -19,7 +19,15 @@ jobs: uses: actions/checkout@v3 - name: Install Eigen - run: sudo apt-get install libeigen3-dev + run: | + sudo apt-get install libeigen3-dev + git clone https://gitlab.com/libeigen/eigen + cd eigen + mkdir build_dir + cd build_dir + sudo cmake .. + sudo make install + sudo cp -r /usr/local/include/eigen3/Eigen -r /usr/local/include - name: Build and test run: | diff --git a/include/clara.h b/include/clara.h index ed1fb3f..f817d31 100644 --- a/include/clara.h +++ b/include/clara.h @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include From 1b13dd81adec8eeefad256e866916ffb07d203e7 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 21:09:02 +0700 Subject: [PATCH 19/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 9111371..888d014 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -21,14 +21,7 @@ jobs: - name: Install Eigen run: | sudo apt-get install libeigen3-dev - git clone https://gitlab.com/libeigen/eigen - cd eigen - mkdir build_dir - cd build_dir - sudo cmake .. - sudo make install - sudo cp -r /usr/local/include/eigen3/Eigen -r /usr/local/include - + - name: Build and test run: | echo "check g++ version" @@ -55,7 +48,11 @@ jobs: uses: actions/checkout@v3 - name: install eigen - run: brew install eigen + run: | + brew install eigen + ls /usr/local/include + mkdir -p /usr/local/include/ + sudo ln -s /usr/local/include/3.4.0_1/include/eigen3 /usr/local/include/eigen3 - name: build and test run: | From f13e192826bfadca1a632951fc30bbee323f1ab4 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 21:20:32 +0700 Subject: [PATCH 20/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 888d014..0b6cf91 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -21,6 +21,7 @@ jobs: - name: Install Eigen run: | sudo apt-get install libeigen3-dev + sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include - name: Build and test run: | @@ -52,7 +53,7 @@ jobs: brew install eigen ls /usr/local/include mkdir -p /usr/local/include/ - sudo ln -s /usr/local/include/3.4.0_1/include/eigen3 /usr/local/include/eigen3 + sudo cp -r /usr/local/include/3.4.0_1/include/eigen3/Eigen /usr/local/include - name: build and test run: | From 17300a98e1a4ccec9e312f173e9d111bdb5e140c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 21:27:31 +0700 Subject: [PATCH 21/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 0b6cf91..0f603d0 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -21,7 +21,7 @@ jobs: - name: Install Eigen run: | sudo apt-get install libeigen3-dev - sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include + sudo cp -r /usr/include/eigen3/Eigen /usr/local/include - name: Build and test run: | @@ -53,7 +53,7 @@ jobs: brew install eigen ls /usr/local/include mkdir -p /usr/local/include/ - sudo cp -r /usr/local/include/3.4.0_1/include/eigen3/Eigen /usr/local/include + sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include - name: build and test run: | From 458eee617bad2770d5e5772bb5562978ef68f1bb Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 21:33:14 +0700 Subject: [PATCH 22/80] fix: header fix test on macOS clara Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index 0f603d0..e553784 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -51,9 +51,9 @@ jobs: - name: install eigen run: | brew install eigen - ls /usr/local/include mkdir -p /usr/local/include/ - sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include + ls /usr/local/include/eigen3/Eigen + sudo ln -s /usr/local/include/eigen3/Eigen /usr/local/include - name: build and test run: | From 7b98ffc9ebf75077afcf1c09a5b88c97381a9ccc Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sun, 23 Jul 2023 21:40:12 +0700 Subject: [PATCH 23/80] fix: remove temporary macos Signed-off-by: slowy07 --- .github/workflows/cpp-testing.yml | 32 ------------------------------- 1 file changed, 32 deletions(-) diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing.yml index e553784..4f4b193 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing.yml @@ -40,35 +40,3 @@ jobs: run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels ./channels - - build-and-test-macos: - runs-on: macos-latest - - steps: - - name: checkout repository - uses: actions/checkout@v3 - - - name: install eigen - run: | - brew install eigen - mkdir -p /usr/local/include/ - ls /usr/local/include/eigen3/Eigen - sudo ln -s /usr/local/include/eigen3/Eigen /usr/local/include - - - name: build and test - run: | - echo "check g++ version" - g++ --version - cd clara_test - chmod +x run_test - ./run_test - - - name: testing grover search - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem /usr/local/include -I $HOME/clara/include testing/grover_search.cpp -o grover_search - ./grover_search - - - name: testing channels - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem /usr/local/include -I $HOME/clara/include testing/channels.cpp -o channels - ./channels From 83271094952875770d61470a2755adbe83709ba4 Mon Sep 17 00:00:00 2001 From: rulanugrh Date: Fri, 28 Jul 2023 22:45:40 +0700 Subject: [PATCH 24/80] feat: add testing with docker image --- README.md | 6 ++++++ docker/Dockerfile.channels | 18 ++++++++++++++++++ docker/Dockerfile.clara_test | 29 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 docker/Dockerfile.channels create mode 100644 docker/Dockerfile.clara_test diff --git a/README.md b/README.md index 930bff8..be0e893 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ Clara using [GoogleTest](https://github.com/google/googletest) framework and moc g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels ``` +## running test clara with docker +``` +docker build -t clara-test -f ./docker/Dockerfile.clara_test . && docker run -it --name=clara-test clara-test +``` + information: - ``-pedantic`` ensure that the code conforms to the strictest standard of the C++ language - ``-std=c++11`` specifies that the code should be compiled using c++11 @@ -80,6 +85,7 @@ information: - ``-Weffc++`` enables warnings about potential erroes in the code - ``-fopenmp`` enables ``OpenMP`` support - ``-g3`` enable debugging symbol +- ``docker`` best pratices for testing application if you are using windows ## Reference diff --git a/docker/Dockerfile.channels b/docker/Dockerfile.channels new file mode 100644 index 0000000..0cf84bf --- /dev/null +++ b/docker/Dockerfile.channels @@ -0,0 +1,18 @@ +# CALL BASE IMAGE WITH UBUNTU 18.04 +FROM ubuntu:18.04 + +# COPY FILE TO LOCAL CONTAINER +WORKDIR /app +COPY . . + +# INSTALL MODULE +RUN apt-get update && apt install libeigen3-dev libomp-dev gcc -y +WORKDIR /usr/local/include +RUN cp -r /usr/include/eigen3/Eigen . + +# BUILD BINARY FILE +WORKDIR /app +RUN g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels + +# RUNNING BINARY FILE +ENTRYPOINT ["./channels"] diff --git a/docker/Dockerfile.clara_test b/docker/Dockerfile.clara_test new file mode 100644 index 0000000..f5c90c7 --- /dev/null +++ b/docker/Dockerfile.clara_test @@ -0,0 +1,29 @@ +# CALL BASE IMAGE WITH UBUNTU 18.04 +FROM ubuntu:18.04 + +# COPY FILE TO LOCAL CONTAINER +WORKDIR /app +ADD . . + +# INSTALL MODULE +RUN apt-get update && apt install libeigen3-dev libomp-dev gcc git cmake make -y +WORKDIR /usr/local/include +RUN cp -r /usr/include/eigen3/Eigen . + +# RUNNING GOOGLETEST +WORKDIR /app/clara_test +RUN git clone https://github.com/google/googletest.git -b release-1.11.0 +WORKDIR /app/clara_test/googletest +RUN mkdir build +WORKDIR /app/clara_test/googletest/build +RUN cmake .. && make install + +# RUNNING MAIN TEST +WORKDIR /app/clara_test +RUN mkdir build +WORKDIR /app/clara_test/build +RUN cmake .. && make + +# RUNNING BINARY FILE +WORKDIR /app/clara_test/build/tests +ENTRYPOINT ["./clara_testing"] From 4269c26e5d2bc82e52488a202d80319642fef6c5 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 31 Jul 2023 14:06:00 +0700 Subject: [PATCH 25/80] docs: update Documentation [Documentation] code documentation is a collection of documents and code comments explaining how code works and how to use it. Signed-off-by: slowy07 --- README.md | 1 + include/constants.h | 36 +- include/entanglement.h | 329 +++++++-- include/entropies.h | 193 +++++- include/functions.h | 1444 +++++++++++++++++++++++++++++++++++---- include/input_output.h | 116 +++- include/instruments.h | 575 +++++++++++++--- include/number_theory.h | 304 ++++++++- include/operations.h | 11 +- include/random.h | 362 ++++++++-- include/statistics.h | 205 +++++- include/traits.h | 38 +- include/types.h | 41 ++ 13 files changed, 3263 insertions(+), 392 deletions(-) diff --git a/README.md b/README.md index 930bff8..dc53969 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ repository: [gitlab](https://gitlab.com/libeigen/eigen) ``` sudo apt-get update sudo apt-get install libeigen3-dev + cp -r /usr/local/include/eigen3/Eigen /usr/local/include ``` - arch package for installing on arch based diff --git a/include/constants.h b/include/constants.h index 82f269d..9a33880 100644 --- a/include/constants.h +++ b/include/constants.h @@ -14,25 +14,41 @@ inline constexpr cplx operator"" _i(unsigned long long int x) noexcept { inline constexpr cplx operator"" _i(long double x) noexcept { return {0., static_cast(x)}; } /** - * @brief double chop used in clara::disp() for setting to zero numbers - * that have their absolute value smaller than clara:chop -*/ + * @brief double chop used in clara::disp() for setting to zero numbers + * that have their absolute value smaller than clara:chop + */ constexpr double chop = 1e-10; /** - * @brief used to decide whether a number or epression in double precision - * os zero or not - */ + * @brief used to decide whether a number or epression in double precision + * os zero or not + */ constexpr double eps = 1e-12; /** - * @brief maxium number of allowed qubit - * used internally to allocate array on the stack - */ + * @brief maxium number of allowed qubit + * used internally to allocate array on the stack + */ constexpr idx maxn = 64; - constexpr double pi = 3.141592653589793238462643383279502884; + +/** + * @brief representing the mathematical constant e + */ constexpr double ee = 2.718281828459045235360287471352662497; + +/** + * @brief the constant infinity representing the maximum value of a double + * this is used to represent an unbound value in certain calculations + */ constexpr double inifinity = std::numeric_limits::max(); +/** + * @brief function to calculate the complex number omega, used in certain + * quamtum computation + * @param D input value 'D' used to calculate the omega value + * @return std::complex the complex number omga calculated as exp(2.0 * pi * 1_1 / + * static_castD) + * @exception exception::OutOfRange thrown if 'D' is zero + */ inline cplx omega(idx D) { if (D == 0) throw exception::OutOfRange("clara::omega()"); diff --git a/include/entanglement.h b/include/entanglement.h index 968ff10..59ac116 100644 --- a/include/entanglement.h +++ b/include/entanglement.h @@ -15,8 +15,28 @@ #include "types.h" namespace clara { /** - * @brief schidmit coefficients of bi-partite pure state A - * @note the sum square of the schidmit coefficients equals 1 + * @brief calculate the schmidt coefficients of bipartite pure state 'A' + * @note the sum of square of schmidt coefficients equal 1 + * @tparam derived the matrix expression type + * @param A the eigen matrix of matrix expression representing the bipartitepure state + * @param dims vector containing the dimension of the tow subsystem + * the size of the vector should be 2, and dims[0] should be the dimension + * of the first dimension of the second subsystem + * @return dyn_col_vect A column vector containing the schmidt coefficients + * of pure state 'A' + * + * @exception exception::ZeroSize throw if 'A' has zero size + * @exception exception::NotBipartite thrown if 'dims' does not contain excatly 2 dimension + * @exception exception::DimsMismatchCvector thrown if dimension in 'dims' do not match + * the dimension of 'A' + * @example + * Eigen::MatrixXd bipartiteState(3, 4); + * + * // define the dimension of the two subsystem + * std::vector dimensions = {3, 4}; + * + * // calculat the schmidt coefficients of the bipartite pure state + * dyn_col_vect schmidCoeffs = schmidtcoeffs(bipartiteState, dimensions); */ template dyn_col_vect schmidcoeffs(const Eigen::MatrixBase& A, @@ -32,14 +52,34 @@ dyn_col_vect schmidcoeffs(const Eigen::MatrixBase& A, if (!internal::check_dims_match_mat(dims, rA)) throw exception::DimsMismatchCvector("clara::schmidtcoeffs()"); + // calculate the singlar value of the transposed and reshaped matrix return svals(transpose(reshape(rA, dims[1], dims[0]))); } /** - * @brief schmidt coefficients of the bi-partite pure state A - * @note the sum of the squares of schmidt coefficients equals - * @return schmidt coefficients of A, as a real dynamic column vector + * @brief calculate the schmidt coefficients of a bipartite pure state 'A' + * @note the sum of the square of schmidt coefficients equals 1 + * @tparam derived the matrix expression type + * @param A the eigen matrix or matrix expression representing the bipartite pure state + * @param d the dimension of each subsystem. default value is 2 + * @return dyn_col_vect A column vector containing schmidt coefficients of the + * pure state 'A' + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' is less than 2 + * @exception exception::NotBipartite thrown if the dimension 'd' result in a non bipartite + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the schmidt coefficients of the bipartite pure state with a default dimensio + * // of 3 + * dyn_col_vect schmidtCoeffs = schmidtcoeffs(bipartiteState); + * + * // calculate the schmidt coefficients of the bipartite purestate + * // with a specified dimension of 3 + * dyn_col_vect schmidCoeffs3 = schmidtcoeffs(bipartiteState, 3); */ + template dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); @@ -55,10 +95,29 @@ dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, idx d = } /** - * @brief schmidt basis on alice slide - * @return unitary matrix \f$ U \f$ whose columns represent - * the schmidt basis vectors on alice slide + * @brief calculate the schmidt basis on alices's side for a bipartite pure state 'A' + * @tparam derived the matrix expression type + * @param dims A vector containing the dimension of the tow subsystem + * the size of the vector should be 2, and dims[0] shuold be the dimension of + * the first subsystem, and dims[1] should be the dimension of the second subsystem + * @return cmat the unitary matrix 'U' whose column represent the schmidt basis vectors on alice + * slide + * + * @exception exception::ZeroSize thrown if 'A' is zero size + * @exception exception::NotBipartite thrown is 'dims' does not conain excatly 2 dimension + * @exception exception::MatrixNotCvector thrown if 'A' is not column vector + * @exception exception::DimsMismatchCvector thrown if the dimension in 'dims' do not + * match dimension of 'A' + * @example + * Eigen::MatrixXd bipartiteState(3, 4); + * + * // define the dimension of the two subystem + * std::vector dimension = {3, 4}; + * + * // calculate the schmidt basis on alice side for the bipartite pure state + * cmat schmidtMatrix = schmidtA(bipartiteState, dimension); */ + template cmat schmidtA(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); @@ -76,9 +135,22 @@ cmat schmidtA(const Eigen::MatrixBase& A, const std::vector& dims) } /** - * @brief schmidt basis on alice side - * @return unitary matrix \f$ U \f$ whose columns represent - * the schmidt basis vectors on alice side + * @brief calculate the schmidt basis on Alice's side for a bipartite pure 'A' + * @tparam derived the matrix expression type + * @param A eigen matrix or matrix expression representing the bipartite pure state + * @param d the dimension fo each subystem default is 2 + * @return cmat the unitary matrix `u` whose column represent the schmidt basis vector on alice's + * side. + * + * @exception exception::ZeroSize thrown is 'A' has zero size + * @exception exception::DimsInvalid throw if 'd' is less than 2 + * @exception exception::NotBipartite thrown if the dimension 'd' result in non-bipartite state + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the schmidt basis on Alice's side with default dimension of 2 for each subystem + * cmat schmidtMatrix = schmidtA(bipartiteState); */ template cmat schmidtA(const Eigen::MatrixBase& A, idx d = 2) { @@ -94,9 +166,22 @@ cmat schmidtA(const Eigen::MatrixBase& A, idx d = 2) { } /** - * @brief schmidt basis on bob side - * @return unitary matrix ``V`` whose columns represent - * the schmidt basis vectors on bob side + * @brief calculate the schmidt basis on bob's side for a bipartite pure state 'A' + * @tparam the eigen matrix or matrix expression represnting the bipartite pure state + * @param dims a vector containing the dimension of the two subsystem + * the size of the vector should be 2, and dims[0] should be the dimension of + * the first subsystem, and dims[1] should be the dimension of the second + * subsystem. + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::NotBipartite thrown if 'dims' does not contain excatly 2 dimension + * @exception exception::MatrixNotCvector thrown if 'A' is not column vector + * @exception exception::DimsMismatchCvector thrown if the dimensions in 'dims' do not match of 'A' + * + * @example + * Eigen::MatrixXd bipartiteState(3, 4); + * + * // define the dimension of the two subystem + * std::vector dimensions = {3, 4}; */ template cmat schmidtB(const Eigen::MatrixBase& A, const std::vector& dims) { @@ -118,9 +203,20 @@ cmat schmidtB(const Eigen::MatrixBase& A, const std::vector& dims) } /** - * @brief schmidt basis on bob side - * @return unitary matrix V whose columns repersent the schmidt basis - * vectors on bob side + * @brief calculate the schmidt basis bob's side for a bipartite pure state 'A' + * @tparam derived the matrix expression type + * @param d the dimension of each subsystem expression representing the bipartite pure state + * @param d the dimension of each subsystem. default value value is 2 + * + * @exception exception::ZeroSize thrown if 'A' has hero size + * @exception exception::DimsInvalid thrown if 'd' is less than 0 + * @exception exception::NotBipartite thrown if the dimension 'd' result in non bipartite-state + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the schmidt basis on bob's side with a specified dimension of + * // of 3 for each subsystem */ template cmat schmidtB(const Eigen::MatrixBase& A, idx d = 2) { @@ -137,10 +233,23 @@ cmat schmidtB(const Eigen::MatrixBase& A, idx d = 2) { } /** - * @brief schmidt probabilities of the bi-partite pure state A - * define as the square of the schmidt coefficients - * the sum the schmidt probabilities equals 1. - * @return real vector consistring of the schmidt probabilities. + * @brief calculate the squared schmidt coefficients (schmidt probabilites) for a bipartite + * state 'A' + * @tparam derived the marix expression type + * @param A the eigen matrix or matrix expression representing the bipartite pure state + * @param dims A vector containing the dimension of the two subsystem + * the size of the vector should be 2, and dims[0] should be the dimension of the + * first subsystem, and dims[1] should be the dimension of the second subsystem + * @return std::vector A vector containing the squared schmidt coefficients + * + * @example + * Eigen::MatrixXd bipartiteState(3, 4); + * + * // Define the dimension of the two subsystem + * std::vector dimension = {3, 4}; + * + * // caculate the squared schmidt coefficients for the bipartite pure state + * std::vector schmidtProbs = schmidtprobs(bipartiteState, dimensions); */ template std::vector schmidtprobs(const Eigen::MatrixBase& A, @@ -166,9 +275,26 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, } /** - * @brief schmidt probabilities of the bi-partite pure state A - * defined as the square of the schmidt coefficients probabilities equals 1 - * @return real vector consiting of the schmidt probabilities of A + * @brief calculate the square schmidt probabilites for a bipartite pure state 'A' + * + * @tparam derived matrix expression type + * @param A the eigen matrix or matrix expression representing the bipartite pure state + * @param d the dimension of each subsystem. default value is 2 + * @return std::vector A vector containgin the squared schmidt probabilites + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' is less than 2 + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the squared schmidt probabilites with a default dimension of 2 for each + * // subsystem + * std::vector schmidtProbs = schmidtprobs(bipartiteState); + * + * // calculate the squared schmidt probabilites with a specified dimension fo 3 for each + * // subystem + * std::vector schmidtProbs3 = schmidtprobs(bipartiteState, 3); */ template std::vector schmidtprobs(const Eigen::MatrixBase& A, idx d = 2) { @@ -185,10 +311,24 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, idx d = 2) } /** - * @brief entanglement of the bi-partite pure state A - * defined as the von neuman entropy of the reduced density matrix - * of one of the subsystem - * @return entanglement with logarithm in base + * @brief calculate the logarithmic negativity of a bipartite mixed state 'A' + * @tparam derived the matrix expression type + * @param A the eigen matrix or matrix expression representing the bipartite mixed state + * @param d the dimension of each subsystem. default value is 2 + * @return double the logarithmic negativity with the logarithm in base 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' less than 0 + * + * @example + * Eigen::MatrixXd bipartiteMixedState(4, 4); + * + * // calculate the logarithmic negativity for the bipartite mixed state with a default + * double logNegativityValue = lognegativity(bipartiteMixedState); + * + * // calculate the logarithmic negativity for the bipartite mixed state with a specific + * // subystem + * double logNegativityValue3 = lognegativity(bipartiteMixedState, 3); */ template double entanglement(const Eigen::MatrixBase& A, const std::vector& dims) { @@ -207,10 +347,26 @@ double entanglement(const Eigen::MatrixBase& A, const std::vector& } /** - * @brief entanglement of bi-partite pure state A - * defined as the von-neumann entropy of the reduced density matrix - * of one the subsystem - * @return entanglement with logarithm in base 2 + * @brief calculate the entanglement of a bipartite pure state 'A' + * @tparam Derived the matrix expression type + * @param A the eigen matrix or matrix expression representing the bipartite pure state + * @param d the diension of each subsystem, default is 2 + * @return double the entanglement with logarithm in base 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' is less than 2 + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the entanglement with a defualt dimension of 2 for each subsystem + * double entanglementValue = entanglement(bipartiteState); + * + * // calculate the entanglement with a specified dimension of + * double entanglementValue = entanglement(bipartiteState, 3); + * + * // calculate the entanglement with a specified dimension of 3 for each subsystem + * double entanglementValue3 = entanglement(bipartiteState, 3); */ template double entanglement(const Eigen::MatrixBase& A, idx d = 2) { @@ -226,8 +382,20 @@ double entanglement(const Eigen::MatrixBase& A, idx d = 2) { } /** - * @brief G-concurrence of the bi-partite pure state A - * @return G-concurrence + * @brief calculated the G-concurrence of a bipartite pure state 'A' + * @tparam derived the matrix expression type + * @param A the eigen matrix or matrixx expression representing the bipartite pure state. + * @return double the G-concurrence + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::MatrixNotCvector thrown if 'A' is not a column + * @exception exception::DimsNotEqual thrown if the number + * + * @example + * Eigen::MatrixXd bipartiteState(4, 4); + * + * // calculate the G-concurrence for the bipartite pure state + * double gConccurenceValue = gconcurrence(bipartiteState); */ template double gconcurrence(const Eigen::MatrixBase& A) { @@ -245,8 +413,24 @@ double gconcurrence(const Eigen::MatrixBase& A) { } /** - * @brief negativity of the bi-partite mixed state A - * @return negativity + * @brief calculate the negativity of a bipartite mixed state 'A'. + * @param A the eigen matrix or matrix expression representing the bipartite mixed state + * @param dims the dimension of the subystem as a vector [dim_subys1, dims_subsys2] + * @return double the negativity of the bipartite mixed state + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::NotBipartite thrown if the dims vector does not have exactly two elements + * @exception exception::MatrixNotSquare thrown if 'A' is not a square matrix + * @exception exception::DimsMismatchMatrix thrown if the dimension + * specified by 'dims' do not match the matrix + * + * @example + * Eigen::MatrixXd bipartiteMixedState(4, 4); + * + * std::vector dims = {2, 2}; + * + * // calculate the negativity for the bipartite mixed state + * double negativityValue = negativity(bipartiteMixedState, dims); */ template double negativity(const Eigen::MatrixBase& A, const std::vector& dims) { @@ -266,8 +450,22 @@ double negativity(const Eigen::MatrixBase& A, const std::vector& d } /** - * @brief negativity of the bi-partite mixed state A - * @return negativity + * @brief calculate the negativity of a bipartite mixed state 'A' + * @tparam Derived the matrix expression representing the bipartite + * @param A the eigen matrix or matrix expression representing the bipartite mixed state + * @param d the dimension of each subsystem. default value is 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' is less than 2 + * + * @example + * Eigen::MatrixXd bipartiteMixedState(4, 4); + * + * // calculate the negativity for the bipartite mixed state + * double negativityValue = negativity(bipartiteMixedState); + * + * // calculate the negativity for the bipartite mixed state with specified dimension of 3 for each + * subystem double negativityValue3 = negativity(bipartiteMixedState, 3); */ template double negetivity(const Eigen::MatrixBase& A, idx d = 2) { @@ -283,8 +481,23 @@ double negetivity(const Eigen::MatrixBase& A, idx d = 2) { } /** - * @brief logarithmic negativity bi-partite mixed state A - * @return logarithmic negativity with the logarithm in base 2 + * @brief calculate the logarithmic negativity of a bipartite mixed state 'A' + * @tparam Derived the matrix expression type + * @param A the eigen matrix expression representing the bipartite mixed state. + * @param dims the dimension of the two subsystem as vector [dims_subsys1, dims_subsys2] + * + * @exception exception::ZeroSize thrown if 'A' has zeros + * @exception exception::NotBipartite thrown 'dims' vector does not have exactly two elements + * @exception exception::MatrixNotSquare thrown if 'A' is not a square matrix + * @exception exception::DimsMismatchMatrix thrown if the dimension specified by 'dims' + * not match matrix 'A' + * @example + * Eigen::MatrixXd bipartiteMixedState(4, 4); + * + * std::vector dims = {2, 2}; + * + * // calculate the logarithmic negativity for the bipartite mixed state + * double logNegativityValue = lognegativity(bipartiteMixedState, dims); */ template double lognegativity(const Eigen::MatrixBase& A, const std::vector& dims) { @@ -301,8 +514,24 @@ double lognegativity(const Eigen::MatrixBase& A, const std::vector } /** - * @brief logarithmic negativity of the bo-partite mixed state A - * @return logarithmic negativity, with the logarithmic in base 2 + * @brief calculate the logarithmic negativity of a bipartite mixed state 'A' + * @param A the Eigen matrix or matrix expression representing the bipartite mixed state + * @param d the dimension of each subsystem, default value is 2. + * @return double the logarithmic negativity with the logarithmic in base 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::DimsInvalid thrown if 'd' is less than 0 + * + * @example + * Eigen::MatrixXd bipartiteMixedState(4, 4); + * + * // calculate the logarithmic negativityfor the bipartite mixed state + * // with mixed state with default dimension of 2 for each + * double logNegativityValue = lognegativity + * + * // calculate the logarithmic negativity for the bipartite mixed state with specified + * // dimension of 3 for each subsystem + * double logNegativityValue3 = lognegativity(bipartiteMixedState, 3); */ template double lognegativity(const Eigen::MatrixBase& A, idx d = 2) { @@ -319,8 +548,20 @@ double lognegativity(const Eigen::MatrixBase& A, idx d = 2) { } /** - * @brief wootters concurrence of the bi-partite qubit mixed state A - * @return wotters concurrence + * @brief calculate the wotters concurrence of a bipartite mixed state 'A' + * @tparam derived the matrix expression type + * @param A the eigen matrix or matrix expression representing the bipartite qubit mixed state + * @return double the wooters concurrence + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::MatrixNotSquare thrown if 'A' is not a square matrixqq + * @exception exception::NotQubitSubsys thrown if 'A' does not represent a qubit subsystem + * + * @example + * Eigen::Matrix2cd bipartiteMixedState; + * + * // calculate the wooters concurrence for the bipartite qubit mixed state + * double concurrenceValue = concurrence(bipartiteMixedState); */ template double concurrence(const Eigen::MatrixBase& A) { diff --git a/include/entropies.h b/include/entropies.h index 82fa76c..17043f3 100644 --- a/include/entropies.h +++ b/include/entropies.h @@ -14,8 +14,18 @@ namespace clara { /** - * @brief von neuman entropy of density matrix A - * @return von neuman entropy, with the logarithm in base 2 + * @brief calculate the von neuman entropy of the density matrix 'A' + * @param A eigen matrix or matrix expression representing the density matrix + * @return double the von neuman entropy, with the logarithm in base 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::MatrixNotSquare thrown if 'A' is not a square matrix + * + * @example + * Eigen::matrixXd densityMatrix; + * + * // calculate the von neumann entropy for the density matrix + * double entropyValue = entropy(densityMatrix); */ template double entropy(const Eigen::MatrixBase& A) { @@ -28,25 +38,44 @@ double entropy(const Eigen::MatrixBase& A) { if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::entropy()"); + // calculate the singular values 'A' dmat ev = svals(rA); double result = 0; - for (idx i = 0; i < static_cast(ev.rows()); ++i) + for (idx i = 0; i < static_cast(ev.rows()); ++i) { + // calculate the von neuman entropy term for each non-zero + // singular value if (ev(i) != 0) result -= ev(i) * std::log2(ev(i)); + } return result; } /** - * @brief shannon entropy of probability distribution ``prob`` - * @return shannon strategy with logarithm in base 2 + * @brief calculate shanon entropy of a probability distribution + * + * @param prob the probability distribution as a vector of doubles + * @return double the shanon entropy, with the logarithm in base 2 + * + * @exception exception::ZeroSize thrown if the input probability distribution has zero size + * + * @note the function assumes that the probability distribution is a valid + * discrete probability distribution. the probability should be non-negative and sum up to 1 + * + * @example + * std::vector probabilities = {0.3, 0.2, 0.5}; + * + * double entropyValue = entropy(probabilities); */ inline double entropy(const std::vector& prob) { if (!internal::check_nonzero_size(prob)) throw exception::ZeroSize("clara::entropy()"); double result = 0; - for (idx i = 0; i < prob.size(); ++i) + for (idx i = 0; i < prob.size(); ++i) { + // calculate the shanon entropy term for each non-zero + // probability if (std::abs(prob[i]) != 0) result -= std::abs(prob[i]) * std::log2(std::abs(prob[i])); + } return result; } @@ -84,8 +113,36 @@ double renyi(const Eigen::MatrixBase& A, double alpha) { } /** - * @brief Renyi-\f$\alpha\f$ entropy of the probability distribution prob - * @return renyi-\f$\alpha\f$ entropy, with the logarithm in base 2 + * @brief calculate the renyi \alpha-entropy of a density matrix 'A' + * + * the renyi \alpha-entropy is a generalization of the shannon entropy and is defined as follows + * - when \alpha > 0 and \alpha ≠ 1, the renyi \alpha-entropy given by: + * \f$ H_{\alpha}(A) = \frac{1}{1-\alpha} \log_2 \left( \sum_i \lambda_i^\alpha \right) \f$A + * where \delta_i are the singular values of the density matrix 'A'. + * - when \alpha = 0, the renyi \alpha-entropy is given by + * f$ H_{0}(A) = \log_2 ( \text{rank}(A) ) \f$, where rank(A) is the rank of 'A' + * - when \delta = 1, the renyi \delta-entropy is equivalent to the shannon entropy, given by + * \f$ H_{1}(A) = - \sum_i \lambda_i \log_2(\lambda_i) \f$ + * where \delta_i are the singular values of the entropy density matrix 'A' + * - whena \delta approaches infinity, the renyi \delta-entropy approaches + * \f$ H_{\infty}(A) = - \log_2(\lambda_{\text{max}}) \f$, where λ_{\text{max}} + * is the largest singluar singular value of 'A' + * + * @tparam derived the matrix expression type + * @param A eigen matrix or matrix expression representing the density matrix + * @param alpha the parameter \delta for renyi \delta-entropy calculation. should be a >= 0 + * @return double the renyi \delta-entropy with the logarithm in base 2 + * + * @exception exception::ZeroSize thrown if 'A' has zero size + * @exception exception::MatrixNotSquare thrown if 'A' is not a square matrix + * @exception exception::OutOfRange thrown if 'alpha' is less than 0 + * + * @example + * Eigen::matrixXd densityMatrix; + * double alpha = 2.0; + * + * // calculate the renyi \alpha-entropy for the density matrix with \alpha = 2 + * double renyiEntropy = renyi(densityMatrix, alpha); */ inline double renyi(const std::vector& prob, double alpha) { if (!internal::check_nonzero_size(prob)) @@ -112,8 +169,32 @@ inline double renyi(const std::vector& prob, double alpha) { } /** - * @brief tsallis entropy of density matrix A for f$q\geq 0f$ - * @return tsallis entropy + * @brief calculate the Tsallis q-entropy density matrix 'A' + * + * Tsallis q-entropy is generalization of the shannon entropy and is defined as follows: + * - when q > 0 and != 1, Tsallis q-entropy is given + * \f$ T_q(A) = \frac{{1 - \sum_i \lambda_i^q}}{{q - 1}} \f$ + * where \delta_i are the singular values of the density matrix 'A' + * - when q = 1, the Tsallis q-entropy is equivalent the von neuman entropy, given by + * \f$ T_1(A) = S(A) \cdot \log(2) \f$ + * where S(A) is the shannon entropy of 'A' + * + * @tparam derived the matrix expression type + * @param A eigen matrix or matrix expression represnting the density matrix + * @param q the parameter q for the Tsallis q-entropy calculation should be q >= 0 + * @return double the Tsallis q-entropy + * + * @exception exception::ZeroSize if 'A' has zero size + * @exception exception::MatrixNotSquare thrown if 'A' is not square matrix + * @exception exception::OutOfRange thrown if 'q' is less than 0 + * + * @example + * Eigen::matrixXd densityMatrix; + * double q = 2.0; + * + * // calculate the Tsallis q-entropy for the density matrix with q = 2 + * double densityMatrix = tsallis(densityMatrix, q); + * */ template double tsallis(const Eigen::MatrixBase& A, double q) { @@ -138,26 +219,86 @@ double tsallis(const Eigen::MatrixBase& A, double q) { } /** - * @brief tsallis-\f$q\f$ entropy of the probability distribution prob + * @brief tsallis-\f$q\f$ entropy of the probability distribution prob for q >= 0 + * + * the Tsallis q-entropy is generalization of the shannon entropy and is defined as follows + * - when q > 0 and q != 1, the Tsallis q-entropy is given by + * \f$ T_q(\text{prob}) = \frac{{\sum_i |p_i|^q - 1}}{{1 - q}} \f$ + * where the p_i are the elements of the probability distribution 'prob' + * - when q = 1, the Tsallis q-entropy is equivalent of the shannon entropy, given by + * \f$ T_1(\text{prob}) = S(\text{prob}) \f$ + * where $(prob) is the shannon entropy of the probability distribution + * - when q = 0, the Tsallis q-entropy is equivalent to the logarithm of the number of non-zero + * elements in 'prob', i.e \f$ T_0(\text{prob}) = \log_2(\text{number of non-zero elements in prob}) + * \f$ + * + * @param prob the vector of double representing the probability distribution + * @param q the parameter q for the Tsallis q-entropy calculation. should be q >= 0 + * @return double the Tsallis q-entropy of the given probability distribution, with the logarithm in + * base 2 + * + * @exception exception::ZeroSize thrown if 'prob' has zero size + * @exception exception::OutOfRange thrown if 'q' is less than 0 + * + * @example + * std::vector probabilityDistribution = {0.2, 0.3, 0.5}; + * double q = 0.5; + * + * // calculate the Tsallis q-entropy for the probability distribution with q = 0.5 + * double tsallisEntropy = tsallis(probabilityDistribution, q); */ inline double tsallis(const std::vector& prob, double q) { + // check if the probability distribution has non-zero size if (!internal::check_nonzero_size(prob)) throw exception::ZeroSize("clara::tsallis()"); if (q < 0) throw exception::OutOfRange("clara::tsallis()"); + // check if q is valid (q >= 0) if (q == 1) return entropy(prob) * std::log(2.); + // calculate the tsallis q-entropy using the provided formula double result = 0; - for (idx i = 0; i < prob.size(); ++i) + for (idx i = 0; i < prob.size(); ++i) { + // calculate the term for each non-zero probability value if (std::abs(prob[i]) != 0) result += std::pow(std::abs(prob[i]), q); + } + // calculate the Tsallis q-entropy and return the result return (result - 1) / (1 - q); } /** - * @brief quantum mutual information between 2 subsystem of composite system - * @return mutual information between 2 subsystem + * @brief calculate the quantum mutual information between two subsystem of a composite + * system + * quantum mutual information measures the mutual information two subsystem A and B of composite + * quantum system. it is defined as the different of the sum of the entropies of the reduced + * dmatrices of subsystem A and B and the entropy of the reduced density matrix of their AB: \f$ + * I(A:B) = S(\rho_A) + S(\rho_B) - S(\rho_{AB}) \f$ + * + * @param A the input density matrix representing the composite quantum system + * @param subsysA indices of the subsystem A + * @param subsysB indices of the subsystem B + * @param dims vector of dimension of the subsystem + * @return double the quantum mutual information between subsystem A and B + * + * @exception exception::ZeroSize thrown if the input density matrix 'A' has zero size + * @exception exception::DimsInvalid thrown if the input dimension vector 'dims' is invalid + * @exception exception::MatrixNotSquare thrown if the input density matrix 'A' is not square + * @exception exception::DimsMismatchMatrix thrown if the input dimension vector `dims` does not + * match dimension of 'A' + * @exception exception::SubsysMismatchdims thrown if the input subsystem 'subsyA' and 'subsysB' do + * not match dimension 'A' + * + * @example + * Eigen::matrixXd densityMatrix = ...; + * std::vector subsystemA = {0, 1}; + * std::vector subsystemB = {2, 3}; + * std::Vector dimensions = {2, 2, 2, 2}; + * + * // calculate the quantum mutual information between subsystem A and B + * double mutualInfo = qmutualinfo(densityMatrix, subsystemA, subsystemB, dimensions); + * */ template double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& subsysA, @@ -206,12 +347,32 @@ double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& cmat rhoA = ptrace(rA, subsysA_bar, dims); cmat rhoB = ptrace(rA, subsysB_bar, dims); cmat rhoAB = ptrace(rA, subsysAB_bar, dims); + + // calculate the quantum mutual information using provided formula return entropy(rhoA) + entropy(rhoB) - entropy(rhoAB); } /** - * @brief mutual information between 2 subsystem of composite system - * @return mutual information between the 2 subsystem + * @brief calculate the mutual information between two subystem of a compute system + * + * mutual information measures the amount of information shared between two subsystem + * A and B of a composite quantum system. it is defined as the difference of the sum of + * the entropies of the reduced density matrices of subsystem A and B and the entropy of the + * reduced density matrix of their union AB: \f$ I(A:B) = S(\rho_A) + S(\rho_B) - S(\rho_{AB}) \f$ + * + * @param A the input density matrix representing the composite quantum system + * @param subsyA indices of the subsystem A + * @param subsyB indices of the subsystem B + * @param d dimension of the subsystem (default is 2) + * @return double the mutual information between subsystem A and B + * + * @example + * Eigen::matrixXd densityMatrix = ...; + * std::vector subsystemA = {0, 1} + * std::vector subsystemB = {0, 2} + * + * // calculate the mutual information between subsystem A and B + * double mutualInfo = qmutualinfo(densityMatrix, subsystemA, subsystemB, dimension); */ template double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& subsysA, diff --git a/include/functions.h b/include/functions.h index a031e1e..879612d 100644 --- a/include/functions.h +++ b/include/functions.h @@ -22,7 +22,23 @@ #include "types.h" namespace clara { -// eigen function wrappers +/** + * @brief transpose the input matrix + * transposing a matrix swaps its rows and colsumns, effectively turning rows into columns + * and columns into rows + * + * @param A the input matrix to be transposed + * @return then transposed matrix of 'A' + * + * @example + * Eigen::MatrixXd inputMatrix(3, 2); + * inputMatrix << 1, 2, + * 3, 4, + * 5, 6; + * + * // transpose the input matrix + * Eigen::MatrixXd transposedMatrix = tranpose(inputMatrix); + */ template dyn_mat transpose(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -31,16 +47,49 @@ dyn_mat transpose(const Eigen::MatrixBase& A) return rA.transpose(); } -// complex conjugate +/** + * @brief compute the complex conjugate of the input matrix + * the complex conjugate of a matrix is obtained by taking the complex conjugate + * of each element in the matrix. for realmatrices, the complex conjugate operation + * has no effect, as the imaginary part of each element is zero + * + * @param A the input matrix for which the complex conjugate is computed + * @return the complex conjugate of the input matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXcd inputMatrix(2,2); + * inputMatrix << std::complex(1, 2), std::complex(3,4), std::complex(5, 6); + * + * // conjugate the complex conjugate of the input matrix + * Eigen::MatrixXcd conjugateMatrix = conjugateMatrix(inputMatrix); + */ template dyn_mat conjugate(const Eigen::MatrixBase& A) { + // check if the input matrix has a non-zero size const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::conjugate()"); + // return the complex conjutate of the matrix return rA.conjugate(); } -// ajdoint +/** + * @brief compute the adjoint (conjugate transpose) of the input matrix + * the adjoint of matrix is obtained by taking the complex conjugate of each + * element in the matrix and transposing it. for real matrices, the adjoint + * operation is equivalent to the transpose operation + * + * @param A the input matrix for which the djoint is computed + * @return the adjoint (conjugate transpose) of the input matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXcd inputMatrix(2, 2); + * input matrix << std::complex dyn_mat adjoint(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -49,7 +98,26 @@ dyn_mat adjoint(const Eigen::MatrixBase& A) { return rA.adjoint(); } -// inverse +/** + * @brief compute the inverse of the input matrix + * the inverse of a square matrix 'A' is another square matrix 'B' and that the + * product of 'A' and 'B' is Identity matrix. in mathematrical terms, if 'A' is + * asquare matrix and 'B' is its inverse, then 'A * B = B * A = I', where 'I' is + * the identity matrix + * + * @param A the input matrix fro which the inverse is computed + * @return the inverse of the input matrix 'A' + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(2, 2); + * inputMatrix << 2, 1, + * 4, 3; + * + * // compute the inverse of the input matrix + * Eigen::MatrixXd inverseMatrix = invverse(inputMatrix); + */ template dyn_mat inverse(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -58,7 +126,26 @@ dyn_mat inverse(const Eigen::MatrixBase& A) { return rA.inverse(); } -// trace +/** + * @brief compute the trace of the input matrix + * the trace of square matrix is the sum of the elements on its main as diagonal. + * in mathematical terms, if 'A' is a square matrix, then the trace 'Tr(A)'is + * defined as the sum 'A(i, i)' for all 'i', where 'A(i, i)' is the element of + * matrix 'A' at the i-th row and i-th column + * + * @param A the input matrix for which the trace is computed + * @param the trace of the input matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(2, 2); + * inputMatrix << 2, 1, + * 4, 3; + * + * // compute the inverse of the input matrix + * Eigen::MatrixXd inverseMatrix = inverse(inputMatrix); + */ template typename Derived::Scalar trace(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -68,7 +155,23 @@ typename Derived::Scalar trace(const Eigen::MatrixBase& A) { return rA.trace(); } -// determinant +/** + * @brief compute the determinant of the input matrix + * the determinant of the square matrix is a scalar value that represent the + * signed volume of the paralleliped spanned by the column vectors of the matrix + * + * @param A input matrix for wwhich the determinanat is computed + * @return the determinant of the input matrix 'A' + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * + * // calculate the determinant of the input matrix + * double determinantValue = det(inputMatrix); + */ template typename Derived::Scalar det(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -78,8 +181,24 @@ typename Derived::Scalar det(const Eigen::MatrixBase& A) { } /** - * logarithm of the determinant - * usefull when the determinant overflows/underflows + * @brief compute the determinant of the input matrix + * + * the determinant of a square matrix is a scalar value that represent + * the signed volume of the paralleliped spanned by the column vectors of the matrix + * + * @param A the input matrix for which the determinant is computed + * @return the determinant of the input Matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * + * // compute the determinant of the input matrix + * double determinantValue = det(inputMatrix); */ template typename Derived::Scalar logdet(const Eigen::MatrixBase& A) { @@ -101,7 +220,20 @@ typename Derived::Scalar logdet(const Eigen::MatrixBase& A) { return result; } -// element-wise sum of A +/** + * @brief compute the element-wise sum of the input matrix + * + * @param A the input matrix for which the element-wise sum is computed + * @return the sum of all elements in the input matrix 'A' + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * // compute the element-wise sum of the input matrix + * dpuble sumValue = sum(inputMatrix); + */ template typename Derived::Scalar sum(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -110,7 +242,22 @@ typename Derived::Scalar sum(const Eigen::MatrixBase& A) { return rA.sum(); } -// element-wise product of A +/** + * @brief compute the element-wise product of the input matrix + * + * @param A the input matrix which the element-wise product product is computed + * @param the input the product of all elements in the input matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3 + * 4, 5, 6, + * 7, 8, 9; + * // compute the element-wise the product of the input matrix + * double productValue= prod(inputMatrix); + */ template typename Derived::Scalar prod(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -120,7 +267,23 @@ typename Derived::Scalar prod(const Eigen::MatrixBase& A) { return rA.prod(); } -// fronbenius form +/** + * @brief compute the fronbenius norm of the input matrix + * + * @param A the input matrix for which the fronbenius norm is computed + * @return the fronbenius norm of the input matrix 'A' + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3 + * 4, 5, 6, + * 7, 8, 9; + * + * // compute the fronbenius norm of the input matrix + * double normValue = nrom(inputMatrix); + */ template double norm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -131,7 +294,25 @@ double norm(const Eigen::MatrixBase& A) { return (rA.template cast()).norm(); } -// full eigen decomposition +/** + * @brief compute the full eigen decomposition of the input square matrix + * + * @param A the input square matrix for which the eigen decomposition is computed + * @param A std::pair containing eigenvalues (as a dynamic column vector) + * + * @exception exception::ZeroSize thrown if input matrix 'A' has zero size + * @exception exception::MatrixNotSquare thrown if the input matrix 'A' is not square + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * // compute the full eigen decomposition of input matrix + * auto eigenDecomp = eig(inputMatrix); + * Eigen::vectorXd eigenvalues = eigenDecomp.first.real(); + * Eigen::MatrixXd eigenvectors = eigenDecomp.second; + */ template std::pair, cmat> eig(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -145,7 +326,24 @@ std::pair, cmat> eig(const Eigen::MatrixBase& A) { return std::make_pair(es.eigenvalues(), es.eigenvectors()); } -// brief eigenvalues +/** + * @brief compute the eigenvalues of the input square matrix + * + * @param A the input square matrix for which the eigenvalues are computed + * @param A dynamic column vector containing the eigenvalues + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * @exeption exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * // compute the eigenvalues of the input matrix + * Eigen::vectorXd eigenvalues = evals(inputMatrix).real(); + * std::cout << "eigen value " << eigenvalues << std::endl; + */ template dyn_col_vect evals(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -159,6 +357,24 @@ dyn_col_vect evals(const Eigen::MatrixBase& A) { return eig(rA).first; } +/** + * @brief compute the eigenvectors of the input square matrix + * + * @param A the input matrix for which the eigenvectors are computed + * @param A complex matrix containing the eigenvectors as columns + * + * @exception exception::ZeroSize thrown if the input matrix 'A' has zero size + * @exception exception::MatrixNotSquare thrown if the input matrix 'A' is not square + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3 + * 4, 5, 6, + * 7, 8, 9; + * // compute the eigenvectors of the input matrix + * Eigen::MatrixXcd eigenvectors = evects(inputMatrix) + * std::cout << "eigenvectors:\n" << eigenvectors << std::endl; + */ template cmat evects(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -173,6 +389,32 @@ cmat evects(const Eigen::MatrixBase& A) { return es.eigenvectors(); } +/** + * @brief compute the eigenvalues and eigenvectors of the input hermitian matrix + * + * this function computes the eigenvalues and eigenvectors of the input hermitian marix 'A' + * the function ensure that input matrix is square and has a non-zero before performing computation + * the eigenvalues are returned as real vector, and the eigenvectors are returned as a complex + * matrix each eigenvectors as columns + * + * @tparam derived the derived type of the input matrix 'A' + * @param A the input hermitian matrix for which the eigenvalues and eigenvectors are computed + * @return A pair of a real vector containing the eigenvalues and a complex matrix containing the + * eigenvectors as colsumns + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 2, 5, 6, + * 3, 6, 9; + * // compute the eigenvalues and eigenvectors of the input hermitian matrix + * auto result = heig(inputMatrix); + * Eigen::vectorXd eigenvalues = result.first; + * Eigen::MatrixXcd eigenvectors = result.second; + * + * std::cout << "eigenvalues: \n" << eigenvalues << std::endl; + * std::cout << "eigenvectors: \n" << eigenvectors << std::endl; + */ template std::pair, cmat> heig(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -182,10 +424,35 @@ std::pair, cmat> heig(const Eigen::MatrixBase& A) throw exception::ZeroSize("clara::heig()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::heig()"); + // compute the eigenvalues and eigenvectors using 'SelfAdjointEigenSolver' + // and return the result as a pair of real vector and complex matrix Eigen::SelfAdjointEigenSolver es(rA.template cast()); return std::make_pair(es.eigenvalues(), es.eigenvectors()); } +/** + * @brief compute the eigenvalues of the input hermitian matrix + * + * this function computes the eigenvalues of the input matrix 'A' + * this function ensure that the input matrix is square and has non-zero before performing + * computation the eigenvalues are returned as a real vector + * + * @tparam derived the derived type of the input matrix 'A' + * @param A the input hermitian matrix for which for which the eigenvalues are computed + * @retrun a real vector containing the eigenvalues + * + * @exception exception::ZeroSize if the input matrix 'A' zero size + * @exception exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 2, 5, 6, + * 3, 6, 9; + * // compute the eigenvalues of the input hermitian matrix + * Eigen::vectorXd eigenvalues = hevals(inputMatrix); + * std::cout << "eigenvalues:\n" << eigenvalues << std::endl; + */ template dyn_col_vect hevals(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -193,10 +460,32 @@ dyn_col_vect hevals(const Eigen::MatrixBase& A) { throw exception::ZeroSize("clara::hevals()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::hevals()"); + // compute the eigenvalues using the 'heigh' return heig(rA).first; } -// hermitian eigenvectors +/** + * @brief compute the eigenvectors of the input hermitian matrix + * this function computes the eigenvectors of the input hermitian matrix 'A' + * the function ensures that input matrix is square and has a non-zero before performing + * the computation. the eigenvectors returned as a complex matrix + * + * @tparam derived the derived type of the input matrix 'A' + * @param A the input hermitian matrix for which the eigenvectors are computed + * @return A complex matrix containing the eigenvectors as columns + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * @exception exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 2, 5, 6, + * 3, 6, 9; + * // compute the eigenvectors of the input hermitian matrix + * Eigen::MatrixXcd eigenvectors = hevects(inputMatrix); + * std::vector << "eigenvectors:\n" << eigenvectors << std::endl; + */ template cmat hevects(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -207,30 +496,95 @@ cmat hevects(const Eigen::MatrixBase& A) { return heig(rA).second; } -// full singular value decomposition +/** + * @brief compute the Singular value decomposition (SVD) of the input matrix + * this function computes the singluar value decomposition of the input matrix 'A' + * the function ensure that input matrix has non-zero size before performing the computation + * the SVD is returned as three matrices 'U', 'S', and 'V, such that 'A = U * S * V.conjugate()' + * + * @tparam Derived derived type of the input matrix 'A' + * @param A the input matrix for which the SVD is computed + * @return A tuple containing three matrices: 'U', 'S', and 'V', representing the SVD of 'A' + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 2); + * inputMatrix << 1, 2 + * 3, 4, + * 5, 6; + * // compute the singular value decomposition (SVD) of the input matrix + * auto [U, S, V] = svd(inputMatrix); + * + * std::cout <<"matrix U:\n" << U << std::endl; + * std::cout <<"singular values:\n" << S << std::endl; + * std::cout << "matrix V:\n" << V << std::endl; + */ template std::tuple, cmat> svd(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::svd()"); + // compute the singular decomposition (SVD) using jacobi SVD and return U, S, and V matrices as + // tuple Eigen::JacobiSVD> sv( rA, Eigen::DecompositionOptions::ComputeFullU | Eigen::DecompositionOptions::ComputeFullV); return std::make_tuple(sv.matrix(), sv.singularValues(), sv.matrixV()); } /** - * singular values + * @brief compute the singular values of the inpute matrix + * this function computes the singular values of the input matrix 'A' + * the function ensure that the input matrix has non-zero size before performing the computation + * the singular value are returned as a column vector + * + * @tparam derived the derived type of the input matrix 'A' + * @param A the input matrix for which the singluar values are computed + * @return a column vector containing the singular values of 'A' + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * // compute the singluar values of the input matrix + * auto singularValues = svals(inputMatrix); + * std::cout << "singular values:\n" << singularValues << std::endl; */ template dyn_col_vect svals(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::svals()"); + // compute the singular using jacobiSVD and return as a column vector Eigen::JacobiSVD> sv(rA); return sv.singularValues(); } -// left singular vectors +/** + * @brief compute the left singular vectors of the input matrix + * this function computes the left singular vectors of the input matrix 'A' + * the function ensure that the input matrix has a non-zero size before performing the computation. + * the left singular vectors are returned as a complex matrix + * + * @tparam derived derived type of the input matrix 'A' + * @param A input matrix for which the left singular vectors are computed + * @return A complex matrix containing the left singular vectors of 'A' + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * + * // compute the singular vectors of the input matrix + * auto leftSingularVectors = svdU(inputMatrix); + * std::cout << "left singular vectors:\n" << leftSingularVectors << std::endl; + */ template cmat svdU(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.dervied(); @@ -242,7 +596,28 @@ cmat svdU(const Eigen::MatrixBase& A) { return sv.matrixU(); } -// right singular vectors +/** + * @brief compute the right singular vectors of the input matrix + * this function computes the right singular vectors of the input matrix 'A'. + * the function ensure that the input matrix has a non-zero size before performing the computation + * the right singular vectors are returned as a complex matrix + * + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix for which the right singular vectors are computed + * @return A complex matrix containing the right singular vectors of 'A' + * + * @exception exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(3, 3); + * inputMatrix << 1, 2, 3, + * 4, 5, 6, + * 7, 8, 9; + * // compute the right singular vectors of the input matrix + * auto rightSingularVectors = svdV(inputMatrix); + * std::cout << "right singular vectors:\n" << rightSingularVectors << std::endl; + */ + template cmat svdV(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -253,7 +628,31 @@ cmat svdV(const Eigen::MatrixBase& A) { return sv.matrix(); } -// functional calculus +/** + * @brief functional calculus of a complex matrix + * this function applies a given complex-valued function 'f' element-wise to the eigenvalues of the + * input matrix 'A'. the function then recronstruct the matrix using eigenvectors and the + * transformed eigenvalues. the function ensure that the input matrix is square and has non-zero + * size before performing the computation. + * + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix to which the functional calculus is applied + * @param f A pointer to a complex-valued function that is applied element-wise to the eigen values + * of 'A' + * @return A complex matrix obtained by applying the functional calculus to A + * + * @exception exception::ZeroSize if the input matrix 'A' has zero-size + * @exception exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * // defined a complex-valued function to be applied element-wise to the eigenvalues + * auto func = [](const cplx& x) {return std::exp(x); }; + * // apply the functional calculus to the input matrix + * auto resultMatrix = funm(inputMatrix, func); + * std::cout << "result matrix:\n" << resultMatrix << std::endl; + */ template cmat funm(const Eigen::MatrixBase& A, cplx (*f)(const cplx&)) { const dyn_mat& rA = A.derived(); @@ -270,7 +669,27 @@ cmat funm(const Eigen::MatrixBase& A, cplx (*f)(const cplx&)) { return evects * evalsdiag * evects.inverse(); } -// matrix square root +/** + * @brief matrix square root of a complex matrix + * this function computes the square root of a complex matrix 'A' applying the functional calculus + * it uses the standard library function 'std::sqrt' as the complex-valued function to compute the + * square root element-wise. the function ensure that the input matrix square and has a non-zero + * before performing the computation + * + * @tparam Dervied the derived type of the input matrix 'A' + * @param A the input matrix for which the square root is computed + * @return the square root of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero-size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * // compute the square root of the input matrix + * auto sqrtMatrix = sqrtm(inputMatrix); + * std::cout << "square root matrix:\n" << sqrtMatrix << std::endl; + */ template cmat sqrtm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -279,10 +698,33 @@ cmat sqrtm(const Eigen::MatrixBase& A) { throw exception::ZeroSize("clara::sqrtm()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::sqrtm()"); + // compute the square root using functional calculus with std::sqrt + // as the complex-valued function return funm(rA, &std::sqrt); } -// matrix absolute value +/** + * @brief absolute value of a complex matrix + * this function computes the absolute value of a complex matrix 'A' using the matrix square root. + * it first computes the hermitian product of the adjoint of 'A' with 'A' and then takes the square + * root of the result. the function ensure that the input mtarix is square and has a non-zero size + * before performing the computation. + * + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix for which the absolute value computed + * @return the absolute value of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero-size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * // compute the absolute value the input matrix + * auto absMatrix = absm(inputMatrix); + * std::cout << "absolute value matrix:\n" << absMatrix << std::endl; + */ template cmat absm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -290,10 +732,31 @@ cmat absm(const Eigen::MatrixBase& A) { throw exception::ZeroSize("clara::absm()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::absm()"); + // compute the absolute value using matrix square root of adjoint(A) * A; return sqrtm(adjoint(rA) * rA); } -// matrix exponential +/** + * @brief matrix exponential + * this function computes the matrix exponential of square matrix 'A' using the functional + * calculus method. it applies the exponential function to the matrix 'A', which is computed + * using its eigenvalues and eigenvectors. the function ensures that the input matrix is square + * and has non-zero size before performing the computation + * + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix for which the matrix exponential is computed + * @return the matrix exponential of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero-size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * // compute the matrix exponential of the input matrix + * auto expMatrix = expm(inputMatrix); + * std::cout << "matrix exponential:\n" << expMatrix << std::endl; + */ template cmat expm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -303,10 +766,33 @@ cmat expm(const Eigen::MatrixBase& A) { throw exception::ZeroSize("clara::expm()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::expm()"); + // compute the matrix exponential using the functional calculus method + // exp function return fnum(rA, &std::exp); } -// matrix logarithm +/** + * @brief matrix logarithm + * this function compute the matrix logarithm of a square matrix 'A' using functional calculus + * method it applies the natural logarithm function to the matrix 'A' which is computed using its + * eigenvalues and eigenvectors the functions ensures that the input matrix square and has non-zero + * size before performing the computation + * + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix for which the matrix logarithm is computed + * @return the matrix logarithm of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' + * @throws exception::MatrixNotSquare if the input matrix 'A' not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * // compute the matrix logarithm of the input matrix + * auto logMatrix = logm(inputMatrix); + * std::cout << "matrix logarithm:\n" << logMatrix << std::endl; + */ template cmat logm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -316,10 +802,33 @@ cmat logm(const Eigen::MatrixBase& A) { // check square matrix if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::logm()"); + // compute the matrix logarithm using functional calculus method natural + // logarithm function return funm(rA, std::log); } -// matrix sin +/** + * @brief matrix sine function + * this function computes the matrix 'A' using the functional calculus method. + * it applies the sine function to the matrix 'A' which is computed using its eigenvalues + * and eigenvectors. the function ensures that the input matrix is square and has a non-zero size + * before performing the computation + * + * @tparam Derived the derived type of the input matrix 'A' + * @paran A the input matrix for the matrix sine is computed + * @return matrix sine of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size. + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * // compute the matrix sine of the input matrix + * auto sinMatrix = sinm(inputMatrix); + * std::cout << "matrix sine:\n" << sinMatrix << std::endl; + */ template cmat sinm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -332,7 +841,27 @@ cmat sinm(const Eigen::MatrixBase& A) { return funm(rA, &std::sin); } -// matrix cos +/** + * @brief matrix cosine function + * this function computes the matrix cosine of a square matrix 'A' using the functional calculus + * method it applies the cosine function to the matrix 'A' which is computed using its eigenvalues + * and eigenvectors the function ensures that the input matrix is square and has a non-zero size + * before performing the computation + * + * @tparam Derived the derived of the input matrix 'A' + * @param the input matrix for which the matrix cosine is computed + * @return the matri cosine of input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * auto cosMatrix = cosm(inputMatrix); + * std::cout << "Matrix cosinue:\n" << cosMatrix << std::endl; + */ template cmat cosm(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -342,11 +871,35 @@ cmat cosm(const Eigen::MatrixBase& A) { // check square matrix if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::cosm()"); + // compute the matrix cosinye using the functional calculus method with cosinue functions return funm(rA, &std::cos); } -// matrix power -// clara::pow() +/** + * @brief power of using spectral decomposition + * this function computes the matrix power of a square matrix 'A' using the spectral decomposition + * method. it computes A^z, where 'z' is complex number. by applying power to the eigenvalues and + * recronstructing the matrix using the eigenvectors the functions ensures that input matrix is + * square and has a non-zero size before performing the computation the function also handles the + * spacial case when z is equal to 0, in which case result is the identity matrix + * + * @tparam Derived the derived tpe of the input matrix A + * @param A the input matrix for which the matrix power is computed + * @param z the complex number representing the power + * @return the matrix power A^z of the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2cd inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * std::complex power(2.0, -1.0) + * // compute the matrix power A^z of the input matrix + * auto result = spectralpow(inputMatrix, power); + * std::cout << "matrix pow:\n" << result << std::endl; + */ template cmat spectralpow(const Eigen::MatrixBase& A, const cplx z) { const dyn_mat& rA = A.derived(); @@ -361,6 +914,7 @@ cmat spectralpow(const Eigen::MatrixBase& A, const cplx z) { if (real(z) == 0 && imag(z) == 0) return cmat::Identity(rA.rows(), rA.rows()); + // compute the matrix power using spectral decomposition Eigen::ComplexEigenSolver es(rA.template cast()); cmat evects = es.eigenvalues(); cmat evals = es.eigenvalues(); @@ -370,7 +924,31 @@ cmat spectralpow(const Eigen::MatrixBase& A, const cplx z) { return evects * evalsdiag * evects.inverse(); } -// fast matrix power based on the SQUARE-AND-MULTIPLY algorithm +/** + * @brief fast matrix power based on SQUARE-AND-MULTIPLY algorithm + * this function computes the matrix power A^n of a square matrix 'A' using SQUARE-AND-MULTIPLY + * algorithm. the function effectly computes the power of the matrix by repeatedly squaring 'A' + * and multiplying the result. + * the function ensures that the input matrix square and has non-zero size before performing the + * computation + * + * @tparam Dervied the derived type of the input matrix 'A' + * @param A the input matrix for which the matrix power is computed + * @param n the exponent to which the matrix 'A' is raised + * @return the matrix power A^n the input matrix 'A' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size + * @throws exception::MatrixNotSquare if the input matrix 'A' is not square + * + * @example + * Eigen::Matrix2d inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * int power = 3; + * + * // compute the matrix power A^n of the input matrix + * auto result = powm(inputMatrix, power); + * std::cout << "matrix power:\n" << result << std::endl; + */ template dyn_mat powm(const Eigen::MatrixBase& A, idx n) { // check zero-size @@ -388,7 +966,7 @@ dyn_mat powm(const Eigen::MatrixBase& A, idx dyn_mat cA = A.derived(); - // fast matrix power + // fast matrix power using SQUARE-AND-MULTIPLY algorithm for (; n > 0; n /= 2) { if (n % 2) result = (result * cA).eval(); @@ -397,7 +975,32 @@ dyn_mat powm(const Eigen::MatrixBase& A, idx return result; } -// schatten matrix norm +/** + * @brief schatten matrix norm + * this function computes the schatten matrix nrom of a matrix 'A' with a given exponent 'p'. + * the schatten matrix norm is defined as the p-norm of the vector singular value of 'A' + * its a generalization of the matrix frobenius norm (p = 2) and is well-defined for any position + * value of 'p the function ensures that the input matrix is non-zero and has valid exponent 'p' + * before performing the computation + * + * @tparam Derived the derived tpe of the input matrix 'A' + * @param A the input matrix for which the schatten matrix norm is computed + * @param p is the exponent of the schatten norm, where p >= 1 + * @return the schatten matrix norm of the input matrix 'A' with the given exponent 'p' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size + * @throws exception::OutOfRange if the exponent 'p' less than 1 + * + * @example + * Eigen::Matrix2d inputMatrix; + * inputMatrix << 1, 2, 3, 4; + * + * double p = 1.5; + * // compute the schatten matrix nrom of the input matrix with exponent 'p' + * double result = schatten(inputMatrix, p); + * + * std::cout << "schatten matrix norm "<< result << std::endl; + */ template double schatten(const Eigen::MatrixBase& A, double p) { const dyn_mat& rA = A.derived(); @@ -434,8 +1037,24 @@ dyn_mat cwise(const Eigen::MatrixBase& A, } /** - * kronecker product of multiple matrices, preserve return type - * variadic template + * @brief kronecker product of multiple matrices, prserving the return type + * this function compute the kronecker product of multiple matrices using a variadic template + * the kronecker product of two matrices A and B is denoted as A \tensorprod B result in + * block matrix. ot preserve the return ype and ensures the input matrices have compatible + * dimensions + * + * @tparam T the first matrix type + * @param head the first matrix in the krocnker product sequence + * @return the kronecker product of the input matrices, preseving the return type + * + * @example + * Eigen:: dyn_mat kron(const T& head) { @@ -443,10 +1062,30 @@ dyn_mat kron(const T& head) { } /** - * kronecker product - * return kronecker product of all input parameters, - * evaluated from left to right, as a dynamic matrix - * over the same scalar field as its arguments + * @brief kronecker product of multiple matrices, preserving the return type + * this function computes the kronecker product of multiple matrices using a variadic template. + * the kroncker product of two matrices A and B is denoted as A \tensorprod B and result in + * block matrix. it preserves the return type and ensures that the input matrices have compatible + * dimension + * + * @tparam T the matrix type + * @tparam Args the types of additional matrices of the compute the Kroncker Product + * @param head the first matrix in the kronecker product sequence + * @param tail additional matrices to compute the kronecker product + * @return the kronecker product of the input matrices, preserving the return type + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; + * + * Eigen::Matrix8d result = clara::kron(A, B, C) + * std::cout << "kronecker product of A, B, C: \n" << result << std::endl; */ template dyn_mat kron(const T& head, const Args&... tail) { @@ -476,8 +1115,28 @@ dyn_mat kron(const std::vector& As) { } /** - * kronecker prodcut of a list matrices, preserve, return type - * educe the template parameters from intilizer_list + * @brief kronecker prodcut of a list of matrices, preserving the return type + * this function computes the kronecker product of a list of matrices provided in an + * initializer_list. the kronecker product of two matrices A and B is denoted as A \tensorprod B and + * result in block matrix. it preserves the return type and ensures that the input matrices have + * compatible the dimension + * + * @tparam Derived the type of matrices initializer_list + * @param As the list of matrices to compute the kronecker product. + * @return the kronecker of the matrices in the initializer_list, preserving the return type + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * @Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; + * + * Eigen::Matrix8d result = clara::kron({A, B, C}); + * std::cout << "kronecker product of A, B, and C:\n" << result << std::endl; */ template dyn_mat kron(const std::initializer_list& As) { @@ -485,9 +1144,26 @@ dyn_mat kron(const std::initializer_list& As) } /** - * kronecker product - * return kronecker product of A with itself n items as dynamic - * matrix over the same scalar field as A + * @brief kronecker product of matrix with itself 'n' times is a dynamic matrix over the same + * scalar field + * + * this function computes the kroncker product power of a matrix 'A' with itself 'n' times. the + * kronekcer priduct of a matrix A with itself 'n' times is denoted as A \tensorprod \ A \tensorprod + * ... (n times), and it result in a block matrix with 'n' factors of 'A' in the product + * + * @tparam Dervied the type of the input matrix 'A' + * @param A the matrix to compute the kronecker product power with + * @param n the number of times to compute the kroncker product with 'A' + * @return the kronecker product power 'A' with itself 'n' times as dynamic matrix over the same + * acalar the field as 'A' + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * int n = 3; + * Eigen::Matrix8d result = clara::kronpow(A, n); + * std::cout << "kronecker product power A with itself 3 times:\n" << result << std::endl; */ template dyn_mat kronpow(const Eigen::MatrixBase& A, idx n) { @@ -501,8 +1177,24 @@ dyn_mat kronpow(const Eigen::MatrixBase& A, i } /** - * direct sum of multiple matrices, preserve return type - * variadic template + * @brief direct sum of multiple matrices, preserving the return type + * this function computes the direct sum of mulitiple matrices, preserving the return type + * the direct sum of two matrices A and B, denoted as A \tensorprod B in a block matrix where A and + * B placed diagonally + * + * @tparam T the type of the first matrix + * @param head the first matrix to start the direct sum operation + * @return the direct sum of multiple matrices, preserving the return type + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; */ template dyn_mat dirsum(const T& head) { @@ -510,10 +1202,30 @@ dyn_mat dirsum(const T& head) { } /** - * direct sum - * return sum of all inputs parameters, - * evaluatefrom left to right, as dynamic matrix - * over the same scalar field as its arguments + * @brief direct sum of multiple matrices, preserving the return type + * + * this function computes the direct sum multipe matries, preserving the reutn type + * the direct sum of two matrices A and B, denoted as A \tensorprod B, result in block matrix where + * A and B place diagonally. the direct sum of multiple matrices + * A, B, C, ..., denoted as A \tensorprod B \tensorprod C ..., is the + * concatention of their block diagonal matrics + * + * @tparam T the first matrix + * @param head the first matrix to start the direct sum operation + * @return the direct sum of multiple matrices, preserving the return type + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; + * + * Eigen::Matrix6d result = clara::dirsum(A, B, C); + * std::cout << "direct sum of A, B, and C:\n" << result << std::endl; */ template dyn_mat dirsum(const T& head, const Args&... tail) { @@ -521,10 +1233,31 @@ dyn_mat dirsum(const T& head, const Args&... tail) { } /** - * direct sum - * return direct sum of all elements in As, - * evaluated from left to right, as a dynamic matrix - * over the same scalar field as its arguments + * @brief direct sum of matrices + * + * this function computes the direct of all elements in the input vector 'As' + * evaluated from left to right, as dynamic matrix over the same scalar field as its arguments. + * the direct sum of two matrices A and B, denoted as A \tensorprod B, result in block + * matrix where A and B are placed diagonally. The direct sum of multiple matrices A, B, C ..., + * + * @tparam Derived the type of the matrice in the vector + * @param As a vector containing matrices to perform the direct sum operation + * @return the direct sum of all elements in the vector 'As' + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 1, 2, 3, 4; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; + * + * std::vector matrices = {A, B, C}; + * Eigen::Matrix6d result = clara::dirsum(matrices); + * + * std::cout << "direct of A, B, and C:\n" << result << std::endl; */ template dyn_mat dirsum(const std::vector& As) { @@ -550,8 +1283,28 @@ dyn_mat dirsum(const std::vector& As) { } /** - * direct sum of a list of matrices, preserve return type - * deduce the template parameters from initializer_list + * @brief direct sum of matrices + * this function computes the direct sum of all elements in the nput initializer_list 'As' + * evaluated from left to right. + * as dynamic matrix over the same scalar field as its arguments. the direct sum of two matrices + * denoted as A \tensorprod B, result in a block matrix where A and B are placed diagonally + * the direct sum of multiplying matrices A, B, C ..., denoted as A \tensorprod B \tensorprod C + * \tensorprod ..., is the concatention of their block + * + * @tparam Dervied the type of the matrices in the initializer_list + * @param As an initializer_list containing matrices to pefrom the direct sum operation + * @return the direct sum of all elements in the input initializer_list 'As' + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * Eigen::Matrix2d C; + * C << 9, 10, 11, 12; + * std::cout << "direct sum of A, B, C:\n" << result << std::endl; */ template dyn_mat dirsum(const std::initializer_list& As) { @@ -559,9 +1312,25 @@ dyn_mat dirsum(const std::initializer_list& A } /** - * direct sum power - * return direct sum of A with itself n times, as a dynamics - * matrix over the same scalar field as A + * @brief direct sum power of a matrix + * this function computes the direct sum of the input matrix A with itself 'n' times + * as a dynamic matrix over the same scalar field as A. + * the direct sum power of a matrix A with itself n times, denoted as A \tensorprod A (n times), + * result in a block diagonal matrix where A appears on the main diagonal `n` times + * + * @tparam Derived the type of the input matrix. + * @param A the input matrix to perform the direct sum power operation on + * @param n the number of times the input matrix A is directly summed with itself + * @return the direct sum of the input matrix A with itself 'n' times + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * idx n = 3; + * auto result = clara::dirsumpow(A, n); + * + * std::cout << "direct sum power A (n = 3):\n" << result << std::endl; */ template dyn_mat dirsumpow(const Eigen::MatrixBase& A, idx n) { @@ -576,10 +1345,25 @@ dyn_mat dirsumpow(const Eigen::MatrixBase& A, } /** - * reshape - * use column-major order when reshaping - * return reshaped matrix with rows and cols columns, - * as a dynamic matrix over the same scalar field as A + * @brief reshape a matrix + * + * this function reshape the input matrix A to a new shape defined by the number of rows and columns + * the reshape operation preserve the order of elements in the original matrix and uses oclumn-major + * order. + * @tparam Derived the type of the input matrix + * @param A the input matrix to reshaped + * @param rows the number of reows in the reshape matrix + * @param cols the number of columns in the reshape matrix + * @return the reshape matrix with the specified number of rows and columns + * + * @example + * Eigen::Matrix3d A; + * Eigen << 1, 2, 3, 4, 5, 6, 7, 8, 9; + * + * idx rows = 2; + * idx cols = 4; + * auto result = clara::reshape(A, rows, cols); + * std::cout << "reshaped matrix:\n" << result << std::endl; */ template dyn_mat reshape(const Eigen::MatrixBase& A, idx rows, idx cols) { @@ -596,7 +1380,27 @@ dyn_mat reshape(const Eigen::MatrixBase& A, i const_cast(rA.data()), rows, cols); } -// comulator +/** + * @brief calculate the comutator of two matrices A and B + * the comutator of two square matricesA and B is given by [A, B] = AB - BA + * this function returns the comutator of the input matrices A and B + * + * @tparam Derived1 the type of the first input matrix A + * @tparam Dertived2 the type of the second input matrix B + * @param A the first input matrix A + * @param B the second input matrix B + * @return the comutator matrices A and B + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * auto result = clara::comm(A, B); + * + * std::cout << "comutator of A and B:\n" << result << std::endl; + */ template dyn_mat comm(const Eigen::MatrixBase& A, const Eigen::MatrixBase& B) { @@ -619,9 +1423,25 @@ dyn_mat comm(const Eigen::MatrixBase& A, } /** - * anti cumulator - * return anti-commutator \f$AB + BA\f$ as a dynamic matrix - * over the same scalar field as + * @brief calculate the anti comutator of two matrices A and B + * the antri-comutator of two matrices A and B is given by {A, B} = AB + BA + * this function returns the anti-commutator of the input matrices A and B + * + * @tparam Derived1 the type of the first input matrix A + * @tparam Derived2 the type of the second input matrix B + * @param A the first input matrix A + * @param B the second input matrix B + * @return the anti-commutator of matrices A and B + * + * @example + * Eigen::Matrix2d A; + * A << 1, 2, 3, 4; + * + * Eigen::Matrix2d B; + * B << 5, 6, 7, 8; + * + * auto result = clara::anticomm(A, B); + * std::cout << "anti-commutator of A and B: \n" << rsult << std::endl; */ template dyn_mat anticomm(const Eigen::MatrixBase& A, @@ -645,6 +1465,24 @@ dyn_mat anticomm(const Eigen::MatrixBase& A return rA * rB + rB * rA; } +/** + * @brief calculate the projection matrix of a complex column vector + * the projection matrix of a complex column vector A is given by Prj(A) = A * adjoint / ||A||^2 + * where ||A|| is the euclidean norm of A. if A is a zero vector, the function + * return zero matrix + * + * @tparam Dervied the type of the input column vector A + * @param A the input complex column vector A + * @return the projection matrix Prj(A) = A * adjoint(A) / ||A||^2 + * + * @example + * Eigen::VectorXcd A(3); + * A << std::complex(1.0, 2.0), + * std::complex(3.0, 4.0), + * std::complex(5.0, 6.0); + * auto result = clara::prj(A); + * std::cout << "projection matrix:\n" << result << std::endl; + */ template dyn_mat prj(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -662,7 +1500,34 @@ dyn_mat prj(const Eigen::MatrixBase& A) { return dyn_mat::Zero(rA.rows(), rA.rows()); } -// gram-shmidt orthogonalization +/** + * @brief perform Gram-Schmidt orthogonalization on a list of column vectors + * given a list of non-zero complex column vectors, this function perform + * Gram-Schmidt orthogonalization to obtain an orthonormal set of vectors. the + * funcion returns a matrix where each column represent an orthonormal vector from the + * original list. if some vectors in the list are linearly dependent, they will not + * be included in the output matrix + * + * @tparam Derived the type of the input column vectors + * @param As a list of non-zero complex column vector + * @return A matrix with orthonormal vectors obtained from Gram-Schmidt orthogonalization + * + * @example + * Eigen::VectorXcd A(3), B(3), C(3); + * A << std::complex(1.0, 2.0), + * std::complex(3.0, 4.0), + * std::complex(5.0, 6.0); + * B << std::complex(-1.0, 1.0), + * std::complex(2.0, -2.0), + * std::complex(-3.0, 3.0); + * C << std::complex(1.0, -1.0), + * std::complex(2.0, -2.0), + * std::complex(3.0, -3.0); + * + * std::vector vectors = {A, B, C}; + * auto result = clara::grams(vectors); + * std::cout << "orthonormal vectors:\n" << result << std::endl; + */ template dyn_mat grams(const std::vector& As) { if (!internal::check_nonzero_size(As)) @@ -715,9 +1580,31 @@ dyn_mat grams(const std::vector& As) { } /** - * deduce template parameter from initializer_list - * return gram-shmidt vectors of As as column of a dynamic matrix - * over the same scala field as its arguments + * @brief perform Gram-Schmidt orthogonalization on a list column vectors. + * given a list of non-zero column vectors, this function pefroms Gram-Schmidt + * orthogonalization to obtrain an orthonormal set of vectors. the function returns + * a matrix where where each column represent an orthonormal vector from original + * list. if some vectors in the list are linearly dependent, they will not be included + * in the output matrix. + * + * @tparam Derived the type of the input column vectors + * @param As A list of non-zero complex column vectors + * @return a mwtrix with orthonormal vectors obrained from gram-shmidt orthogonalization + * + * @example + * Eigen::VectorXcd A(3), B(3), C(3); + * A << std::complex(1.0, 2.0), + * std::complex(3.0, 4.0), + * std::complex(5.0, 6.0); + * B << std::complex(-1.0, 1.0), + * std::complex(2.0, -2.0), + * std::complex(-3.0, 3.0); + * C << std::complex(1.0, -1.0), + * std::complex(2.0, -2.0), + * std::complex(3.0, -3.0); + * + * auto result = clara::grams({A, B, C}); + * std::cout << "orthonormal vectors:\n" << result << std::endl; */ template dyn_mat grams(const std::initializer_list& As) { @@ -725,26 +1612,68 @@ dyn_mat grams(const std::initializer_list& As } /** - * gram-shmidt orthogonalization - * return gram-shmidt vectors of columns of A, as columns - * of a dynamic matrix over the same scalar field as A + * @brief perform Gram-Schmidt orthogonalization on the column of a matrix + * given matrix with non-zero complex columns, this function perform Gram-Schmidt + * orthogonalization on the columns to obtain an orthonormal set of vectors. the function + * returns a matrix where each column represent orthonormal vector from the original matrix. + * if some columns in the matrix are linearly dependent, they will not be included in + * the output matrix + * + * @tparam Derived the type of the input matrix + * @param A matrix with non-zero complex columns + * @return A matrix with orthonormal vectors obtained from Gram-Schmidt orthogonalization + * + * @example + * Eigen::MatrixXcd A(3, 3); + * A << std::complex(1.0, 2.0), std::complex(-1.0, 1.0), std::complex(1.0, + -1.0), + * std::complex(3.0, 4.0), std::complex(2.0, -2.0), std::complex(2.0, + -2.0), + * std::complex(5.0, 6.0), std::complex(-3.0, 3.0), std::complex(3.0, + -3.0); + * auto result = clara::grams(A); + * std::cout << "orthonormal vectors:\n" << result << std::endl; */ template dyn_mat grams(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::grams()"); + // prepare a vector to store individual column vectors of the input matrix std::vector> input; for (idx i = 0; i < static_cast(rA.cols()); ++i) input.push_back(rA.cols(i)); + // use the overloaded 'grams' functions that takes std::vector as input return grams>(input); } /** - * non negative integer index to multi-index - * rturn multi index of the same size as dims + * @brief convert a non-negative integer index to multi-index + * given a non-negative integer index 'n' and vector 'dims' representing the dimension + * of a multidimensional array, this function returns the corresponding multi-index. + * the multi-index is a vector with same size as 'dims' , containing indices that repersent + * the position of 'n' in the multidimensional array + * + * @param n the non-negative integer index to be converted to a multi-index + * @param dims A vector repersenting the dimension of a multidimensional array + * @return the multi-index representing the position of 'n' in the multidimensional array. + * + * @throws exception::DimsInvalid if the dimension vector 'dims' is invalid + * @throws exception::OutOfRange if the index 'n' is greater than the or equal to the + * tpta; number of elements in multi index + * @example + * std::vector dims = {3, 4, 2}; + * idx n = 7; + * std::vector result = clara::n2multiidx(n, dims); + * + * // print result + * std::cout << "mult index for index: "; + * for (idx i : result) { + * std::cout << i << " "; + * } */ inline std::vector n2multiidx(idx n, const std::vector& dims) { + // check if the dimension vector 'dims' is valid (non-empty and containing non-zero element) if (!internal::check_dims(dims)) throw exception::DimsInvalid("clara::n2multiidx()"); if (n >= std::accumulate(std::begin(dims), std::end(dims), static_cast(1), @@ -752,30 +1681,70 @@ inline std::vector n2multiidx(idx n, const std::vector& dims) { throw exception::OutOfRange("clara::n2multiidx()"); idx result[2 * maxn]; internal::n2multiidx(n, dims.size(), dims.data(), result); + // create a vector from resulting array and return it as the multi-index return std::vector(result, result + dims.size()); } /** - * multi-index to non-negative integer index - * return non-negative integer index + * @brief convert a multi-index to a non-negative integer index + * given a multi-index 'midx' and a vector 'dims' repersenting the dimension of + * multidimensional array, this function returns the corresponding non-negative integer + * index. the multi-index is a vector containing indices that repersent the position + * of a specific element in the multidimensional array. the non-negative integer index is the unique + * identifier of the element within the array, obtained by mapping the multi-index to a linear + * index. + * + * @param midx the multi index representing the position of an element in the multidimensional array + * @param dims a vector represeinting the dimension of the multidimensional array + * @return the non-negative integer index of the element + * + * @throws exception::DimsInvalid if the dimension vectors 'dims' is invalid + * @throws exception::OutOfRange if any element of the multi-index 'midx' exceeds the corresponding + * dimension dims + * + * @example + * std::vector dims = {3, 4, 2}; + * std::vector midx = {1, 3, 1}; + * + * idx n = clara::multiidx2n(midx, dims); + * std::cout << "non negative integer index for multi index {1, 3, 1}: " << n << std::endl; */ inline idx multiidx2n(const std::vector& midx, const std::vector& dims) { + // check if the dimension vector 'dims' is valid if (!internal::check_dims(dims)) throw exception::DimsInvalid("clara::multiidx2n()"); + // check if any alement of the multi-index 'midx' exceeds the corresponding + // dimension in 'dims' for (idx i = 0; i < dims.size(); ++i) if (midx[i] >= dims[i]) throw exception::OutOfRange("clara::multiidx2n()"); + // convert the multi-index to a non-negative integer index using internal helper + // function return internal::multiidx2n(midx.data(), dims.size(), dims.data()); } /** - * multi-partite qudit ket - * construct the multi-partite qudit ket \f$|\mathrm{mask}\rangle\f$, - * where mask is a std::vector of non-negative integers. - * each element in mask has to be smaller than the corresponding element - * in dims + * @brief a multi-partite qudit ket presented by the multi-index 'mask' and the dimension of each + * subsystem the dimension of each subsystem 'dims', this function constructs the corresponding + * qudit ket in hilbert space. the multi-index 'mask' is a vector containing non-negative integers, + * where each element repersent is a vector containing non-negative integers, where each element + * repersent the basis state index for the corresponding subsystem. the dimensions 'dims' is a + * vector representing the number of basis states in each subsystem. the resulting qudit ket + * repersented as a column vectro in hilbert space + * + * @throws clara::ZeroSize if the size of 'mask' or 'dims' is zero + * @throws clara::DimsInvalid if 'dims' contains invalid values + * @throws clara::SubsysMismatchdims if 'mask' and 'dims' have different size, or if any element in + * 'mask' exceeds the corresponding dimension in 'dims' + * + * @example + * std::vector dims = {2, 3, 2}; + * std::vector mask = {1, 0, 1}; + * ket qudit_ket = clara::mket(mask, dims); + * std::cout << "multi-partite qudit ket for mask {1, 0, 1}:\n" << qudit_ket << std::endl; */ inline ket mket(const std::vector& mask, const std::vector& dims) { + // get the number of subsystem and the total dimension of the hilbert space idx N = mask.size(); idx D = std::accumulate(std::begin(dims), std::end(dims), static_cast(1), std::multiplies()); @@ -793,6 +1762,8 @@ inline ket mket(const std::vector& mask, const std::vector& dims) { if (mask[i] >= dims[i]) throw exception::SubsysMismatchdims("clara::mket()"); + // create a ket vector with zero entries and set the position corresponding to the multi-index + // 'mask' to 1 ket result = ket::Zero(D); idx pos = multiidx2n(mask, dims); result(pos) = 1; @@ -800,13 +1771,32 @@ inline ket mket(const std::vector& mask, const std::vector& dims) { } /** - * multi-partite qudit ket - * construct the multi-partite qudit ket \f$|\mathrm{mask}\rangle\f$, - * all subsystem having equal dimension d - * mask is a std::vector of non-negative integers, and each element in masuk to be strictly smaller - * than d + * @brief construct a multi-partite qudit ket with equal dimension for all subsystem + * given a multi-partite qudit ket repersented by the multi-index 'mask' and a common + * dimension 'd', this function construct the corresponding qudit ket in the hilbert space. + * the multi-index 'mask' is a vector containing non-negative integers, where each element + * represents the basis state index for the corresponding subsystem. the common dimension 'd' + * represent the number of basis states in each subsystem, and all subsystem have the same dimension + * 'd'. the resulting qudit ket is represented as a column vector in the hilbert space + * + * @param mask the multi-index repersenting the basis state indices for each subsystem + * @param d the common dimension of all subsystem. default is 2 + * @return the multi-partite qudit ket as a column vector in the hilbert space + * + * @throws exception::ZeroSize if the size of 'mask' is zero + * @throws exception::DimsInvalid if 'd' is zero + * @throws exception::SubsysMismatchdims if any element in 'mask' is greater than or equal to 'd' + * + * @example + * // multi-index repersenting basis state indices for each subsystem + * std::vector mask = {1, 0, 1}; + * // common dimension for all subsystem + * idx d = 3; + * ket qudit = clara::mket(mask, d); + * std::cout << "multi-partite qudit ket for mask {1, 0, 1} and dimension 3: \n" << qudit_ket; */ inline ket mket(const std::vector& mask, idx d = 2) { + // get the number of subsystem and the total dimension of the hilbert space idx N = mask.size(); idx D = static_cast(std::llround(std::pow(d, N))); @@ -820,6 +1810,9 @@ inline ket mket(const std::vector& mask, idx d = 2) { for (idx i = 0; i < N; ++i) if (mask[i] >= d) throw exception::SubsysMismatchdims("clara::mket()"); + + // create a ket vector with zero entries and set the position corresponding + // to the multi-index 'mask' to 1 ket result = ket::Zero(D); std::vector dims(N, d); idx pos = multiidx2n(mask, dims); @@ -861,9 +1854,33 @@ inline cmat mprj(const std::vector& mask, const std::vector& dims) { } /** - * projector into multi-partite qudit ket - * return projector into multi-partite qudit state vector - * as complex dynamic matrix + * @brief construct the projector onto multi-partite qudit ket + * given a multi-partite qudit ket repersented the mutli-index 'mask' + * and a vector of dimension 'dims', this function construct the corresponding porjector + * onto the multi-partite qudit ket in the hilbert space. the multi-index 'mask' is + * a vector containing non-negative integers, where each element repersents the basis + * state index for the corresponding subsystem. the vector 'dims' contains the dimension of all + * subsystem, and each element in 'mask' must be strictly smaller than the corresponding element + * in 'dims'. the resulting projector is represented as a complex-valued matrix + * in the hilber space + * + * @param mask the multi-index representing the basis state indices for each subsystem + * @param dims the dimension of all subsystem + * @return the projector onto the multi-partite qudit ket as a complex-valued matrix + * + * @throws clara::ZeroSize if the size of 'mask' is zero + * @throws clara::ZeroSize if the size of 'dims' is zero + * @throws clara::SubsysMismatchdims if the size of 'mask' and 'dims' do not matcj + * + * @example + * // multi-index representing basis state indices for each subsystem + * std::vector mask = {1, 0, 1} + * // dimension of each subsystem + * std::vector dims = {2, 3, 2}; + * cmat projector = clara::mprj(mask, dims); + * + * std::cout << "projector into multi-partite qudit ket for mask {1, 0, 1} and dims {2, 3, 2}: \n" + * << projector << std::endl; */ inline cmat mprj(const std::vector& mask, idx d = 2) { idx N = mask.size(); @@ -882,6 +1899,8 @@ inline cmat mprj(const std::vector& mask, idx d = 2) { if (mask[i] >= d) throw exception::SubsysMismatchdims("clara::mprj()"); + // create a complex-valued matrix with zero entries and set the position + // corresponding to the multi-index 'mask' cmat result = cmat::Zero(D, D); std::vector dims(N, d); idx pos = multiidx2n(mask, dims); @@ -889,16 +1908,54 @@ inline cmat mprj(const std::vector& mask, idx d = 2) { return result; } +/** + * @brief calculate the absolute square of complex numbers in the range [first, last] + * given a range of complex numbers specified by iterator, first and last, this function + * calculate the absolute square (modulus squared) of each complex number and returns a vector + * containing the result. the input range should contain complex number, and the output vector + * will contain the corresponding absolute square as double values + * + * @tparam InputIterator iterator type for the input range of complex number + * @param first iterator to the beginning of the input range + * @param last iterator to the end of the last input range + * @retun a vector containing the absolute squares of complex numbers in the input range + * + * @example + * std::vector complexNumber = {cplx(1.0, 2.0), cplx(-3.0, 4.0), cplx(0.0, -1.0)}; + * std::vector absoluteSquare = abssq(std::begin(complexNumber), std::end(complexNumber)); + * + * for (double absSq : absoluteSquare) { + * std::cout << "absolute square " << absSq << std::endl; + * } + */ template std::vector abssq(InputIterator first, InputIterator last) { + // calculate the number of complex numbers in the input range std::vector weights(std::distance(first, last)); + // calculate the absolute to store the absolute the complex number std::transform(first, last, std::begin(weights), [](cplx z) -> double { return std::norm(z); }); return weights; } /** - * computes the absolute values square of an STL-like container - * real vector consitingof container's absolute values squared + * @brief calculate the absolute square of element in an STL-like container + * given an STL-like container 'c' containing elements that support the 'std::begin()' and + * 'std::end()' functions (e.g std::vector, std::list, etc), this function calculates, the absolute + * square (modulus squared) of each element in the container and returns a vector containing the + * result the input container should contain elements that can be square, and the output vector will + * container the corresponding absolute square as double values + * + * @tparam container the type of the input STL-like container + * @param c the STL-like container to calculate the absolute square for + * @return vector containing the absolute square of the elements in the input container + * + * @example + * std::vector values = {1.0, -3.0, 2.5, -4.5}; + * std::vector absoluteSquare = abssq(values); + * + * for (double absSq : absoluteSquare) { + * std::cout << "absolute square: " << absSq << std::endl; + * } */ template std::vector abssq(const Container& c, @@ -922,9 +1979,24 @@ std::vector abssq(const Eigen::MatrixBase& A) { } /** - * element-wise sum of an STL-like range - * returen element-wise of the range, as a scalar - * over the same scalar field as the range + * @brief calculate the absolute of elements in an Eigen matrix + * given an Eigen matrix 'A', this function calculates the absolute square (modulus squared) + * of each element in the matrix and returns a vector containing the result. the input matrix + * should have elements that can be squared, and the output vector will contain the corresponding + * absolute squares as double values + * + * @tparam Derived the type of the Eigen matrix + * @param A the eigen matrix to calculate the absolute square for + * @return vector containing the absolute square of the elements in the input matrix + * + * @example + * Eigen::Matrix2d matrix; + * matrix << 1.0, -3.0, 2.5, -4.5; + * std::vector absoluteSquares = abssq(matrix); + * + * for (double absSq : absoluteSquares) { + * std::cout << "absolute square: " << absSq << sd::endl; + * } */ template typename std::iterator_traits::value_type sum(InputIterator first, @@ -934,8 +2006,21 @@ typename std::iterator_traits::value_type sum(InputIterator first } /** - * elemen-wise sum of the element of an STL-like container - * as scalar over the same scalar field as the container + * @brief calculate the element wise sum of elements in an STL-like containers + * given an STL-like container 'c', this function calculates the element-wise sum + * of all elements in the container and returns the result as a scalar over the same scalar + * field as container's element. the input container sougld have elements that can be summed, + * and the output will be of the same type as the container's elements. + * + * @tparam container the type of the STL-like container + * @param c the STL-like container to calculate the element-wise sum for + * @return the element-wise sum of all elements in the container + * + * @example + * std::vector numbers = {1, 2, 3, 4, 5}; + * + * int sumResult = sum(numbers); + * std::cout << "sum: " << sumResult << std::endl; */ template typename Container::value_type sum( @@ -944,35 +2029,76 @@ typename Container::value_type sum( } /** - * element-wise product of an STL-like range - * return element-wise product of the range, as a - * scalar over the the same scalar field as the range + * @brief calculate the element-wise sum of elements in an STL-like container + * given an STL-like range specified by the iterators 'first' and 'last', this function + * calculate the element-wise prodcut of all elements in the range and return the result + * as a scalar over the same scalar field as the range's element. the input range should + * have elements that can be multiplied, and the output will be of the same type as range + * elements. + * + * @tparam InputIterator type of the iterator for the STL-like range + * @param first iterator pointing to the first element of the range + * @param last iterator pointing to the position after the last element of the range + * @return the element-wise product of elements in the range + * + * @example + * std::vector number = {1, 2, 3, 4, 5}; + * + * int producttresult = prod(number.begin(), number.end()); + * std::cout << "product: " << producttresult << std::endl; */ template typename std::iterator_traits::value_type prod(InputIterator first, InputIterator last) { using value_type = typename std::iterator_traits::value_type; + // using the element-wise product using std::accumulate and std::multiplies + // starting from 1 to act as an identity element multiplication return std::accumulate(first, last, static_cast(1), std::multiplies()); } /** - * element-wise product elements of an STL-like container - * return element-wise product of the element of the container, - * as a scalar over the same scalar field as the container + * @brief calculate the element-wise product of an STL-like container + * given an STL-like container and returns the result as a scalar over the same scalar + * field as the container's elements. the container's lement should be able to be multiplied + * , and the output will be of the same type as the container's elements + * + * @tparam container the type of the STL-like container + * @param c the STL-like container for which the element-wise product is calculated + * @return the element-wise prodcut of all elements in containers + * + * @examplea + * std::vector numbers = {1, 2, 3, 4, 5}; + * int productResult = prod(numbers); + * std::cout << "Product: " << productResult << std::endl; */ template typename Container::value_type prod( const Container& c, typename std::enable_if::value>::type* = nullptr) { + // call the prod function with iterator of the container to calculate the result return prod(std::begin(c), std::end(c)); } /** - * finds the pure state representation of a matrix proprtional to a projector into a pure state - * return the unique non-zero eigenvectors of A, as dynamic column vector over the same scalar field - * as A + * @brief convert a density matrix (hermitian matrix) to pure state vector (ket) + * given a density matrix 'A' (hermitian matrix), this function calculate the corresponding + * pure state vector (ket) representing the most significant eigenvector of the matrix. it + * returns the pure state vector as a dynamic column vector over the same scalar field vector + * as a dynamic column vector over same scalar field as the input matrix 'A' + * + * @tparam Derived the matrix of type of the input 'A' + * @param A the density matrix (hermitian matrix) to convert to a pure state vector + * @return the pure state vector (ket) representing the most significant eigenvectors of 'A' + * + * @example + * // 3x3 density matrix (hermitian matrix) + * Eigen::Matrix3cd rho + * + * Eigen::Vector3cd pureState = rho2pure(rho); + * std::cout << "pure state vector:\n" << pureState << std::endl; */ template dyn_col_vect rho2pure(const Eigen::MatrixBase& A) { + // obtain a const reference to the derived matrix type from the input 'A' const dyn_mat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::rho2pure()"); @@ -980,23 +2106,40 @@ dyn_col_vect rho2pure(const Eigen::MatrixBase if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::rho2pure()"); + // calculate the eigenvalues and eigenvectors of the hermitian matrix 'A' dyn_col_vect tmp_evals = hevals(rA); cmat tmp_evects = hevects(rA); dyn_col_vect result = dyn_col_vect::Zero(rA.rows()); + + // find the most significant eigenvectors by looking for the first non-zero eigenvalues for (idx k = 0; k < static_cast(rA.rows()); ++k) { if (std::abs(tmp_evals(k)) > eps) { result = tmp_evects.col(k); break; } } + + // return the pure state vector representing the most significant eigenvectors of 'A' return result; } /** - * construct the complement of susbsystem vector - * return a complement of subsys with respect to the set - * \f$\{0, 1, \ldots, N - 1\}\f$ + * @brief construct the complement of a subsystem vector + * given a subsystem vector 'subsys' and the total number of elements 'N', this function + * construct and returns the complement of 'subsys' with respect to the set {0, 1 ..., N - 1}. + * the complemment is a vector containing elements from {0, 1, ..., N - 1} that are not present + * in 'subsys' + * + * @tparam T the data of the elements in the 'subsys' vector + * @oaram subsys subsystem vector whose complement is to be computed + * @param N total number of element in the set {0, 1, ..., N - 1} + * @return the complement of 'subsys' vector + * + * @example + * std::vector subsystem = {1, 3}; + * int totalElements = 5; + * std::vector complement = complement(subsystem, totalElements); */ template std::vector complement(std::vector subsys, idx N) { @@ -1005,13 +2148,33 @@ std::vector complement(std::vector subsys, idx N) { std::vector all(N); std::vector subsys_bar(N - subsys.size()); + // generate a Vector containing elements from {0, 1, ..., N - 1} std::iota(std::begin(all), std::end(all), 0); + // sort the 'subsys' vector for set_difference operation std::sort(std::begin(subsys), std::end(subsys)); + // compute the complement of 'subsys' using set_difference std::set_difference(std::begin(all), std::end(all), std::begin(subsys), std::end(subsys), std::begin(subsys_bar)); return subsys_bar; } +/** + * @brief convert a qubit density matrix to bloch vector + * given a qubit density matrix 'A' this function calculates the corresponding bloch vector + * representation. It returns a vector containing the three component (X, Y, and Z) of the bloch + * vector. the input matrix 'A' must be a 2x2 qubit density matrix + * + * @tparam Derived the matrix type of the input 'A' + * @param A the qubit density matrix to convert the bloch vector + * @return the bloch vector representation of the qubit density matrix + * + * @example + * Eigen::Matrix2cd rho; + * + * std::vector blochVector = rho2bloch(rho); + * std::cout << "bloch vector (X, Y, Z) " << blochVector[0] << ", " + * << blochVector[1] << ", " << blochVector[2] << std::endl; + */ template std::vector rho2bloch(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); @@ -1025,6 +2188,8 @@ std::vector rho2bloch(const Eigen::MatrixBase& A) { X << 0, 1, 1, 0; Y << 0, -1_i, 1_i, 0; Z << 1, 0, 0, -1; + + // compute the X, Y, Z components of the bloch vector result[0] = std::real(trace(rA * X)); result[1] = std::real(trace(rA * Y)); result[2] = std::real(trace(rA * Z)); @@ -1032,26 +2197,50 @@ std::vector rho2bloch(const Eigen::MatrixBase& A) { } /** - * compute the density matrix corresponding to the 3-dimensional real bloch vector r - * and return qubit density matrix + * @brief compute the density matrix corresponding to the 3-dimensional real bloch vector 'r' + * given an 3-dimensional real bloch vector 'r', this function calculates the corresponding qubit + * density matrix. the density matrix represent quantum state of a qubit in a 2-dimensional + * hilbert-space. the function returns a 2x2 complex matrix, which the qubit density matrix + * associated with the bloch vector 'r' + * + * @param r the 3-dimensional real bloch vector representing a quantum state + * @return the qubit density matrix corresponding to the bloch vector 'r' + * + * @example + * std::vector blochVector = {0.1, 0.2, 0.3}; + * cmat densityMatrix = bloch2rho(blochVector); */ inline cmat bloch2rho(const std::vector& r) { + // check if the input vector 'r' is 3-dimensional if (r.size() != 3) throw exception::CustomException("clara::bloch2rho", "r is not a 3-dimensional vector!"); cmat X(2, 2), Y(2, 2), Z(2, 2), Id2(2, 2); + + // pauli matrices X << 0, 1, 1, 0; Y << 0, -1_i, 1_i, 0; Z << 1, 0, 0, -1; Id2 << 1, 0, 0, 1; + // calculate the qubit density matrix using the bloch vector 'r' + // rho = (Id2 + r[0] * X + r[1] * Y + r[2] * Z) / 2 return (Id2 + r[0] * X + r[1] * Y + r[2] * Z) / 2.; } /** * @brief multi-partite qubit ket user-defined literal - * construct the multi-partite qubit ket \f$|\mathrm{Bits}\rangle\f$ - * @return multi-partite qubit ket + * this user-defined literal construct the multi partite qubit ket \f$|\mathrm{Bits}\rangle\f$ + * where 'Bits' is a binary repersentation of the quantum state. the literal allow constructing + * qubit kets directly using binary string repersentation. for example 010_ket repersent the + * qubit ket $|010\rangle$ + * + * @tparam bits the binary representation of the quantum state, provided as a string of '0' and '1' + * @return multi-partite qubit corresponding to the binary repersentation + * + * @example + * // usage the user-defined literal to construct qubit ket |010⟩ + * ket qubitKet = 010_ket; */ template ket operator"" _ket() { @@ -1060,11 +2249,14 @@ ket operator"" _ket() { clara::ket q = clara::ket::Zero(std::pow(2, n)); idx pos = 0; - // check valid multi-partite qubit state + // check if the input binary repersentation bits contains only + // '0' and '1' for (idx i = 0; i < n; ++i) { if (bits[i] != '0' && bits[i] != '1') throw exception::OutOfRange(R"xxx(clara::operator ""_ket())xxx"); } + + // convert the binary representation to an index 'pos' in decimal (base-10) format pos = std::stoi(bits, nullptr, 2); q(pos) = 1; return q; @@ -1072,8 +2264,16 @@ ket operator"" _ket() { /** * @brief multi-partite qubit bra user-defined literal - * construct the multi-partite qubit bra \f$\langle\mathrm{Bits}|\f$ - * @return multi-partite qubit bra, as a complex dynamic row vector + * this user defined literal construct the multi-partite qubit bra $\langle\mathrm{Bits}|\f$, + * where 'Bits' is a binary representation of the quantum state. the literal allows constructing + * qubit bra directly using binary string representation. + * + * @tparam Bits the binary representation of the quantum state, provided string of '0' and '1' + * @return multi-partite qubit bra corresponding to the binary repersentation, as a complex dynamic + * row vector +  + * @example + * bra qubitBra = 010_bra; */ template bra operator"" _bra() { @@ -1087,6 +2287,8 @@ bra operator"" _bra() { if (bits[i] != '0' && bits[i] != '1') throw exception::OutOfRange(R"xxx(qpp::operator "" _bra())xxx"); } + + // convert the binary representation to an index 'pos' in decimal (base-10) format pos = std::stoi(bits, nullptr, 2); q(pos) = 1; @@ -1095,9 +2297,16 @@ bra operator"" _bra() { /** * @brief multi-partite qubit projector user-defined literal - * construct the multi-partite qubit projector - * \f$|\mathrm{Bits}\rangle\langle\mathrm{Bits}|\f$ - * @return multi-partite qubit projector, as complex dynamic matrix + * this user-defined literal coonstruct the multi-partite qubit projector + * where 'Bits' is a binary repersentation of quantum state. the literal allows constructing + * qubit projectors directly using a binary string representation. + * + * @tparam Bits the binary representation of the quantum state, provided as string of '0' and '1' + * @return multi-partite qubit projector corresponding to the binary representation as a complex + * dynamic matrix + * + * @example + * cmat qubitProjector = 101_prj */ template cmat operator"" _prj() { @@ -1110,6 +2319,7 @@ cmat operator"" _prj() { throw exception::OutOfRange(R"xxx(qpp::operator "" _prj())xxx"); } + // using the kronecker product to construct the qubit projector return kron(operator""_ket(), operator""_bra()); } diff --git a/include/input_output.h b/include/input_output.h index 5c564f1..19d2d17 100644 --- a/include/input_output.h +++ b/include/input_output.h @@ -14,15 +14,68 @@ namespace clara { +/** + * @brief create IOManipEigen object for eigen matrices + * @tparam derived the matrix expresion type + * @param A the eigen matrix or matrix expression to displayed + * @param chop the precssion for displaying floating-point numbers default is clara::chop + * @return internal::IOManipEigen an IOManip object specifically tailored for eigen matrix + * + * @example + * Eigen::MatrixXd mat(3, 3); + * // initialize matrix `mat` with some values + * + * // display the matrix with default precision + * disp(mat) << std::endl; + * + * // display the matrix with custom precision + *disp(mat, 0.001) << std::endl; + */ template internal::IOManipEigen disp(const Eigen::MatrixBase& A, double chop = clara::chop) { return internal::IOManipEigen(A, chop); } +/** + * @brief create IOManipEigen object for complex numbers + * @param z the complex number to be displayed + * @param chop the precision for displyaing floating-point numbers. defeault is clara::chop + * @return internal::IOManipEigen an IOManip object specifically tailored for complex matrices + * + * @example + * // make complex number + * std::complex complexNum(3.14, 2.17); + * + * // display the complex number with default precision + * disp(complexNum) << std::endl; + * + * // display the complex number with custom precision + * disp(complexNum, 0.0001) << std::endl; + * ' + */ inline internal::IOManipEigen disp(cplx z, double chop = clara::chop) { return internal::IOManipEigen(z, chop); } +/** + * @brief function to create IOManipRange object for range of elements + * @tparam INputIterator the input iterator type for the range + * @param first an iterator pointing to the first element of the range + * @param last an iterator pointing to the last element of the rangee + * #param separator the string to be displayed to be used as separator between elements during + * display + * @param start the string to be display before the range of elements. default "[" + * @param end the string to be display after the range of elements default "]" + * + * @eaxmple + * std::vector vec (1, 2, 3, 4); + * + * // display the vector elements with default formatting + * disp(vec.begin, vec.end(), ", ") << std::endl; + * + * // display the vector element with custom formatting + * disp(vec.begin(), vec.end(), ";", "<", ">") << std::endl; + */ template internal::IOManipRange disp(InputIterator first, InputIterator last, const std::string& separator, @@ -31,6 +84,28 @@ internal::IOManipRange disp(InputIterator first, InputIterator la return internal::IOManipRange(first, last, separator, start, end); } +/** + * @brief create IOManipRange for a range of elements defined by two iterator + * @tparam InputIterator the input iterator type for the the range + * @param first an iterator pointing to the first element of the range + * @param last an iterator pointing to the last element of the range + * @param separator the string to be used as separator between elements during display + * @param start the string to be displayed before the range of elements. default value is "[" + * @param end the string to be displyaed after the range of elements, default value is "]" + * @return internal::IOManipRange an IOManip object speciefied tailored of elements + * + * @example + * std::vector vec = {1 ,2 ,3 ,4 ,5}; + * + * // diplaye the vector elements with default formatting + * disp(vec.begin, vec.end(), ", ") << std::endl; + * + * // display the vector elements with custom foramtting + * disp(vec.begin(), vec.edn(), "; ", "<", ">") std::endl; + * // displaythe set elements with custom formatting + * disp(s, "; ", "{", "}") << std::endl + */ + template internal::IOManipRange disp( const Container& c, const std::string& separator, const std::string& start = "[", @@ -40,7 +115,24 @@ internal::IOManipRange disp( separator, start, end); } -// pointer ostream manipulator +/** + * @brief create an IOManipRange object for a range of elements defined by two iteratios + * + * @tparam InputIterator the input iterator type for the range + * @param first an iterator pointing to the first element of the range + * @param last an iterator pointing to the last element of the range (on past at end) + * @param separator the tring to be used as an separator between element during display + * @param start the string to be displayed before the range of elements, default is "[" + * @param end string to be displayed after the range of elements, default is "]" + * @return internal::IOManipRange an IOManip object specifically tailored + * of ranges of elements + * + * @example + * std::vector vec = {1, 2, 3, 4, 5}; + * + * // display the vector elemens with default formatting + * disp(vec.begin(), vec.end(), ", ") << std::endl; + */ template internal::IOManipPointer disp(const PointerType& p, idx N, const std::string& separator, @@ -49,7 +141,17 @@ internal::IOManipPointer disp(const PointerType& p, idx N, return internal::IOManipPointer(p, N, separator, start, end); } -// save Eigen expression to a binary file +/** + * @brief save an Eigen matrix or matrix expression to a binary file + * @tparam derived matrix or matrix expression to be saved + * @param fname the file name (including the path) where the matrix will be saved + * + * @example + * Eigen::MatrixXd mat(3, 3); + * + * // save the matrix to a binary file "example matrix.bin" + * save(mat, "matrix.bin") + */ template void save(const Eigen::MatrixBase& A, const std::string& fname) { const dyn_mat& rA = A.derived(); @@ -78,7 +180,15 @@ void save(const Eigen::MatrixBase& A, const std::string& fname) { fout.close(); } -// load eigen matrix from a binary file in double precision +/** + * @brief load an eigen matrix or eigen expression from a binary file + * @tparam derived the matrix expression type + * @param fname file name (including the path) from where the matrix will be loaded + * @return dyn_mat the loaded eigen matrix + * + * @example + * Eigen::MatrixXd loadedMatrix = load ("matrix.bin"); + */ template dyn_mat load(const std::string& fname) { std::fstream fin; diff --git a/include/instruments.h b/include/instruments.h index 358c8fc..fac192a 100644 --- a/include/instruments.h +++ b/include/instruments.h @@ -20,9 +20,32 @@ namespace clara { /** - * @brief generalized inner product - * @return the inner product \f$\langle \phi_{subsys}|\psi\rangle\f$, - * as scalar or column vector over the remaining hilbert space + * @brief generalized inner product between two quantum states + * the function computes the inner product \f$\langle \phi_{\text{subsys}}|\psi\rangle\f$ + * between two quantum states \f$\phi\f$ and \f$\psi\f$ on a multi-partite quantum system. + * the function allows for the subsystem to have different dimension. it returns the inner + * product as a scalar or column vector over the remaining hilbert stapce after tracing out + * the subsystem + * + * @tparam Derived the type of the input quantum state, can be any matrix expression that Eigen + * can handle + * @param phi the first quantum state, repersented as a column vector + * @param psi the second quantum state, repersented as column vector + * @param subsys vector specifying the subsystem for which the inner product is computed + * @param dims vector specifying the dimension of the overall quantum system. + * @return the inner product \f$\langle \phi_{\text{subsys}}|\psi\rangle\f$ as scalar or + * column vector over the remaining hilbert space + * + * @example + * // represent the qubit state |01> + * cmat phi = 01_ket; + * // represent the qubit state |10> + * cmat psi = 10_ket; + * // compute the inner product only for subsystem (the first qubit) + * std::vector subsys = {0}; + * // the quantum system consist of two qubits + * std::vector dims = {2, 2}; + * cmat result = ip(phi, psi, subsys, dims); */ template dyn_col_vect ip(const Eigen::MatrixBase& phi, @@ -59,6 +82,7 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, if (!internal::check_dims_match_cvect(subsys_dims, rphi)) throw exception::DimsMismatchCvector("clara::ip()"); + // calculate various dimension and size required for the computation idx Dsubsys = prod(std::begin(subsys_dims), std::end(subsys_dims)); idx D = static_cast(rpsi.rows()); idx Dsubsys_bar = D / Dsubsys; @@ -67,15 +91,18 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, idx Nsubsys = subsys.size(); idx Nsubsys_bar = N - Nsubsys; + // pre-allocate arrays for multi-index conversion idx Cdims[maxn]; idx Csubsys[maxn]; idx Cdimssubsys[maxn]; idx Csubsys_bar[maxn]; idx Cdimssubsys_bar[maxn]; + // calculate complement of the subsystem indices std::vector subsys_bar = complement(subsys, N); std::copy(std::begin(subsys_bar), std::end(subsys_bar), std::begin(Cdimssubsys_bar)); + // copy dimension and subsystem indices to the pre-allocated arrays for multi-index conversion for (idx i = 0; i < N; ++i) { Cdims[i] = dims[i]; } @@ -87,17 +114,21 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, Cdimssubsys_bar[i] = dims[subsys_bar[i]]; } + // lambda function for the worker threads to calculate the result in parallel auto worker = [=](idx b) noexcept -> typename Derived::Scalar { idx Cmidxrow[maxn]; idx Cmidxrowsubsys[maxn]; idx Cmidxcolsubsys_bar[maxn]; internal::n2multiidx(b, Nsubsys_bar, Cdimssubsys_bar, Cmidxcolsubsys_bar); - // write in the global row multi-index + // write in the global row multi-index of the global row vector by inserting the + // multi-index of the remaining hilbert space for (idx k = 0; k < Nsubsys_bar; ++k) { Cmidxrow[Csubsys_bar[k]] = Cmidxcolsubsys_bar[k]; } typename Derived::Scalar result = 0; + + // iterate over all possible multi-indices the subsystem and calculate the inner product for (idx a = 0; a < Dsubsys; ++a) { internal::n2multiidx(a, Nsubsys, Cdimssubsys, Cmidxrowsubsys); for (idx k = 0; k < Nsubsys; ++k) { @@ -109,6 +140,8 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, } return result; }; + + // allocate the result vector and parallize the computation using OpenMP dyn_col_vect result(Dsubsys_bar); #ifdef WITH_OPENMP_ #pragma omp parallel for @@ -119,9 +152,33 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, } /** - * @brief generalized inner product - * @return the inner product \f$\langle \phi_{subsys}|\psi\rangle\f$, as a scalar - * or column vector over the remaining hilbert space + * @brief generalized inner product between two quantum states + * this function computes the inner product \f$\langle \phi_{\text{subsys}}|\psi\rangle\f$ + * between two quantum state \f$\langle \phi_{\text{subsys}}|\psi\rangle\f$ on multi + * partite quantum system. the function allows for the subsystem to have different dimension. + * it returns the inner product as a scalar or dolumn vector over the remaining hilbert space + * after tracing out the subsystem. + * + * @tparam Derive the type of the input quantum states. can be any matrix expression that Eigen + * can handle + * @param phi first quantum state, represented as a column vector + * @param psi second quantum state, represented as column vector + * @param subsys vector specifying the subsystem for which the inner product is computed + * @param d the dimension of each subsystem. default value is 2 (qubit dimension) + * @return the inner product \f$\langle \phi_{\text{subsys}}|\psi\rangle\f$ as scalar or column + * vector over the remaining the hilbert space + * + * @example + * // usage of the ip function to compute the inner product between two qubit states + * + * // represent the qubit state |01> + * cmat phi = 01_ket; + * // represent the qubit state |10> + * cmat psi = 10_ket; + * + * // compute the inner product only for subsystem 0 (the first qubit) + * idx d = 2; + * cmat result = ip(phi, psi, subsys, d); */ template dyn_col_vect ip(const Eigen::MatrixBase& phi, @@ -130,31 +187,50 @@ dyn_col_vect ip(const Eigen::MatrixBase& phi, const dyn_col_vect& rphi = phi.derived(); const dyn_col_vect& rpsi = psi.derived(); + // check for zero-size input matrices if (!internal::check_nonzero_size(rpsi)) throw exception::ZeroSize("clara::ip()"); + // check that dimension 'd' is valid if (d < 0) throw exception::DimsInvalid("clara::ip()"); + // calculate the number of subsystem 'N' for the given quantum state 'psi' and dimension 'd' idx N = internal::get_num_subsys(static_cast(rpsi.rows()), d); std::vector dims(N, d); + // call the main 'ip' function with the computed 'dims' return ip(phi, psi, subsys, dims); } /** - * @brief measures the state A using the set of krays operator Ks - * @return tuple of - * - result of the measurement - * - vector of outcome proabilities - * - vector of post-measureemnt normalized states + * @brief measures the quantum state 'A' using a set of kraus operators 'Ks' + * this function perform quantum measurement on the input quantum state 'A' using a set of kraus + * operators 'Ks' it returns a tuple containing the following information: + * - the index of the outcome of the measurement + * - the vector of outcome probabilities for each kraus operator + * - the vector of post measurement normalized states for each kraus operator + * - std::vector the vector of post-measurement normalized states for each kraus operator + * + * @example + * // usage of the measure function to perform a quantum measurement on a qubit state + * + * // represent the density matrix |01><01| + * cmat rho = 01_prj + * // measurement operators for outcomes |02><02| and |12><12| + * std::tuple, std::vector> measurement = measure(rho, ks); + * idx outcome_index = std::get<0>(measurement); + * std::vector outcome_probs = std::get(measurement); + * std::vector post_measurement_states = std::get<2>(measurement); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, const std::vector& Ks) { const dyn_mat& rA = A.derived(); + // check for zero-size input matrices if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::measure()"); + // check that all kraus operators are square and have the same dimension if (Ks.size() == 0) throw exception::ZeroSize("clara::measure()"); if (!internal::check_square_mat(Ks[0])) @@ -165,11 +241,11 @@ std::tuple, std::vector> measure(const Eigen::Mat if (it.rows() != Ks[0].rows() || it.cols() != Ks[0].rows()) throw exception::DimsNotEqual("clara::measure()"); - // proabilities + // initialize vector to store outcome probabilites and post-measurement states std::vector prob(Ks.size()); - // resulting states std::vector outstates(Ks.size()); + // perform the measurement based on the type of quantum state 'A' if (internal::check_square_mat(rA)) { for (idx i = 0; i < Ks.size(); ++i) { outstates[i] = cmat::Zero(rA.rows(), rA.rows()); @@ -193,29 +269,80 @@ std::tuple, std::vector> measure(const Eigen::Mat std::discrete_distribution dd(std::begin(prob), std::end(prob)); idx result = dd(RandomDevices::get_instance().get_prng()); + // return the measurement result in a tuple return std::make_tuple(result, prob, outstates); } /** - * @brief measure the state A in the orthonormal basis specified - * by the unitary matrix U - * @return tuple: - * - result of the measurement - * - vector of outcome proabilities - * - vector of post measurement normalized states + * @brief measures the quantum state 'A' in the orthonormal basis specified by the unitary matrix U + * this function perform quantum measurement on the input quantum state 'A' using the orthonormal + * basis sepcified by the unitary matrix 'U'. it returns a tuple containing the following + * information: + * - the index of the outcome measurement + * - the vector of outcome probabilites for each measurement basis state + * - the vector of post-measurement normalized states for each measurement basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that eigen can + * handle + * @param A the input quantum state, represented as a square matrix (for density matrix) or column + * vector (for pure state) + * @param Ks the list of measurement basis state represented as a std::initializer_list of complex + * matrices + * @return a tuple containing: + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector outcome probabilites for each measurement basis state + * - 'std::vector': the vector post-measurement normalized states for each measurement basis + * state + * + * @example + * // usage of the measrue function to perform a quantum measurement on a qubit state + * + * // represent the density matrix |01> <01| + * cmat rho = 01_prj; + * // unitary matrix representing measurement bassis + * std::tuple, std::vector> measurement = measure(rho, {02_prj, + * 12_prj}); idx outcome_index = std::get<0>(measurement); std::vector outcome_probs = + * std::get<1>(measurement); std::vector post_measurement_states = std::get<2>(measurement); */ template std::tuple, std::vector> measure( const Eigen::MatrixBase& A, const std::initializer_list& Ks) { + // call the main 'measure' function with the input measurement basis states as a std::vector return measure(A, std::vector(Ks)); } /** - * @brief measure the state A in the orthonormal basis specified by unitary matrix - * @return tuple: - * - result of the measuremnt - * - vector of outcome probabilites - * - vector of post-measureemnt normalized states + * @brief measure the quantum state 'A' in the orthonormal basis specified by the unitary matrix 'U' + * this function perform quantum measurement on the input quantum state 'A' using the orthonormal + * basis specified by the unitary matirx 'U', it returns a tuple containing the following + * information + * - the index of the outcome of the measurement + * - the vector of outcome probabilites for each measurement basis state. + * - the vector of post-measurement normalized states for each measurement basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that Eigen can + * handle + * @param A the input quantum state, repersented as a square matrix (for density) or a column vector + * @param U the unitary matri representing the measurement basis. it must have the same number of + * rows 'A' + * @return A tuple containing: + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilites for each measurement basis state + * - 'std::vector': the vector of post-measurement normalized states for each measurement + * basis state + * @example + * // usage of the measure function to perform a quantum measurement on a qubit state + * + * // represent the density matrix |01> <01| + * cmat rho = 01_prj; + * // unitary matrix representing the measurement basis + * cmat U = cmat::Zero(4, 4); + * // define the unitary matrix 'U' representing the measurement basis + * // the columns of 'U' are the orthonormal measurement basis state + * // 'U.col(0)' represents the first basis state, 'U.col(1)' represents the second basis state, and + * so on std::tuple, std::vector> measurement = measure(rho, U); idx + * outcome_index = std::get<0>(measurement); std::vector outcome_probs = + * std::get<1>(measurement); std::vector post_measurement_states = std::get<2>(measurement); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, @@ -233,22 +360,52 @@ std::tuple, std::vector> measure(const Eigen::Mat if (U.rows() != rA.rows()) throw exception::DimsMismatchMatrix("clara::measure()"); + // calculate the measurement basis operators from the unitary matrix U std::vector Ks(U.rows()); for (idx i = 0; i < static_cast(U.rows()); ++i) Ks[i] = U.col(i) * adjoint(U.col(i)); + // perform the measurement using the measurement basis operators return measure(rA, Ks); } /** - * @brief measure the part subsys of the multi-partite state vector - * or density matrix A using the set of kraus operator Ks - * @note the dimension of all Ks must match the dimension of subsys - * the measurement is destructive - * @return tuple of: - * - result of the measurement - * - vector of outcome proabilities - * - vector of post-measurement normalized states + * @brief measure the part of the multi-partite state vector or density matrix 'A' + * corresponding to the susbsystem specified by 'subsys', using the set of kraus oeprators + * 'Ks' + * + * this function perform a destructive measurement on the part of the multi-partite state 'A' + * corresponding to the subsystem specified by 'subsys', using the set of kraus operator 'Ks' + * it returns a tuple containing the following information: + * - the index of the outcome of the measurement + * - the vector of outcome probabilites for each measurement basis state. + * - the vector of post-measurement normalized states for each outcome, corresponding + * to each measure basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix epression that eigen can + * handle + * @param A the input multi-partite state, repersented as a square matrix (for density matrices) or + * column (for pure state) + * @param Ks the set of kraus operators representing the measurement basis for the specified + * subsystem + * @param subsys the indices of the subsystem for which the measurement is performed + * @param dims the dimension of the individiual subsystem, as a vector of integers + * @return a tuple containing: + * - 'idx': the index of the outcome measurement + * - 'std::vector': the vector of outcome probabilites for each measurement basis state + * - 'atd::vector': the vector post-measurement normalized state for each outcome, + * corresponding to each measurement basis state. + * + * @example + * // usage of the measure function to perform a measurement on a multi-partite quantum state + * + * // represent the density matrix |01> <01| + * cmat rho = 01_prj; + * // set of kraus operators representing the measurement basis + * std::vector Ks + * // define the kraus operators 'Ks' representing the measurement basis for the subsystem + * // 'Ks[0]' represent the first measurement operator, 'Ks[1]' represent measurement operator + * measure(rho, Ks, {0, 1}, {2, 2}); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, @@ -321,14 +478,38 @@ std::tuple, std::vector> measure(const Eigen::Mat } /** - * @brief measure the part subsys of the multi-partite state vector or density matrix A - * using the set of kraus operator - * @note the dimensoin of all Ks must matech the dimenion of subsys the measurement is - * destructive - * @return tuple of: - * - result of measurement - * - vector of outcome proabilities - * - vector of post-measureemnt normalized states + * @brief measure the part of the multi-partite state vector or density matrix 'a' + * corresponding to the subsystem specified by 'subsys', using the set of kraus operators + * 'ks' + * this function performs a destructive measurement on the part of the multi-partite state 'a' + * corresponding to the subystem specified by 'subsys', using the set of kraus operators 'ks' + * it returns a tuple containing the following information: + * - the index of the outcome of the measurement + * - the vector of outcome probabilites for each measurement basis state + * - the vector of post-measurement normalized states for each outcome, corresponding to each + * measurement basis state. + * + * @tparam derived type of the input quantum state. can be any matrix expression that eigen can + * handle + * @param a the input multi-partite state, represented as a square matrix (for density matrices) or + * a column vector (for pure state) + * @param ks the set kraus operators representing the measurement basis for the specified subsystem + * @param dims the dimension of the subsystem for which the measurement is performed + * @param dims the dimension of the individiual subystem, as a vector of integers + * @return a tuple containing + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilites for each measurement basis state + * - 'std::vector': the vector of post-measurement normalized states for each outcome, + * corresponding to each measurement basis state + * @example + * // usage of the measure function to perform a measurement on a multi-partite quantum state + * + * // represent the density matrix |01> <01| + * cmat rho = 01_prj; + * // define the kraus operator 'k0' and 'k1' representing the measurement basis for the subsystem + * cmat k0 = 00_prj; + * cmat k1 = 11_prj; + * measure(rho, {k0, k1}, {0, 1}, {2, 2}); */ template std::tuple, std::vector> measure( @@ -338,14 +519,41 @@ std::tuple, std::vector> measure( } /** - * @brief measure the part of subsy of the multi-partite state vector or density matrix A - * using the set of kraus operator Ks - * @note the dimension of all Ks must match the dimension of subsys - * the measurement is destructive, the measured susbsystem are traced away - * @return tuple of: - * - result of measurement - * - vector of outcome probabilites - * - vector post-measureemnt normalized states + * @brief measure the part of the multi-partite state vector or density matrix 'A' + * corresponding to the subsystem specified by 'subsys', using the set of kraus + * operators 'Ks' + * this function performs a destructive measurement on the part of the multi-partite state 'A' + * corresponding to the subystem specified by 'subsys', using the set of kraus operators 'Ks' + * it return a tuple containing the following information: + * - the index of the outcome of the measurement + * - the vector of outcome probabilities for each measurement basis state + * - the vector of post-measurement normalized states for each outcome, corresponding to each + * measurement basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that Eigen can + * handle + * @param A the input multi-partite state, repersented as a square matrix (for density matrices) or + * a column vector + * @param Ks the set of kraus operator representing the measurement basis for the specified + * subsystem + * @param subsys the indices of the subsystem for which the measurement is performed + * @Param d the dimension of each individiual subsystem (default: 2 for qubits) + * @return a tuple containing: + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilities afor each measurement basis state + * - 'std::vectpr': the vector of post-measurement normalized states for each outcome, + * corresponding to each measurement basis state + * + * @note the dimension 'd' must be greater than or equal to 2, the dimension of all kraus operator + * 'Ks' must match the dimension of the specified 'subsys'. if 'd' is not provided, its default to 2 + * which assumes qubits (2-dimensional subsystem) + * + * @example + * // usage of the measure function to perform a measurement on a multi-partite quantum state + * cmat rho = 01_prj; + * cmat K0 = 00_prj; + * cmat K1 = 11_prj; + * measure(rho, {K0, K1}, {0, 1}, 2); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, @@ -353,24 +561,60 @@ std::tuple, std::vector> measure(const Eigen::Mat const std::vector& subsys, idx d = 2) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); + // check if the input state matrix is not empty if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::measure()"); + // check if the dimension 'd' is valid if (d < 2) exception::DimsInvalid("clara::measure()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); + // create a vector 'dims' containing the dimension 'd' for each individiual subsystem std::vector dims(N, d); + // call the measure function with the calculated 'dims' return measure(rA, Ks, subsys, dims); } /** - * @brief measure the part of subsys of the multi-partite state vector or density matrix A - * using the set of kraus operator Ks - * @note the dimension of all Ks must match the dimension of subsys - * the measurement is destructive - * @return tuple of: - * - result the measurement - * - vector outcome probabilites - * - vector of post measurement normalized states + * @brief measure the part of the multi-partite state vector or density or density matrix 'A' + * corresponding to the subsystem sepcified by 'subsys', using the set of kraus operators + * 'Ks' + * + * this function perform a destructive measurement on the part of the multi-partite state 'A' + * corresponding to the subystem specified by 'subsys', using the set of kraus operatos 'Ks' + * it returns a tuple containing following information + * - the index of the outcome of the measurement + * - the index of outcome probabilities for each measurement basis state + * - the vector of post-measurement normalized state for each outcome, corresponding + * to each measurement basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that Eigen can + * handle + * @param A the input multi-partite state, represented as a square matrix (for density matrices) or + * a column vector + * @param Ks the set of kraus operators representing the measurement basis for the specified + * subsystem + * @param d the dimension of each individiual subsystem (default: 2 for qubits) + * @return a tuple containing + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilities for each measurement basis state + * - 'std::vector': the vector of post-measurement normalized states for each outcome + * corresponding to each measurement basis state + * @note the dimension of all kraus 'Ks' must match the dimension of the specified 'subsys', + * if 'd' is not provided, it default to 2, which assumes qubits (2-dimensional subsystem) + * + * @example + * // usage of the measure function to perform a measurement on a multi-partite quantum state + * + * // represent the density matrix |01><01| + * cmat rho = 01_prj; + * // represent the projector |00><00| + * cmat K0 = 00_prj; + * // repersente the projector |11><11| + * cmat K1 = 11_prj; + * + * // measurement subsystem with indices 0 and 1, each of dimension 2 (qubits) + * measure(rho, {K0, K1}, {0, 1}, 2); + * cmat rho = 01_prj; */ template std::tuple, std::vector> measure( @@ -380,13 +624,44 @@ std::tuple, std::vector> measure( } /** - * @brief measures the part subsy of the multi-partite state vector or density matrix A - * in the orthonormal basis rank-1 povm specified by matrix V - * @note the dimension V must match the dimension of subsy. the measurement is destructive - * @return tuple of: - * - result of measurement - * - vector outcome probabilites - * - vector of post-measurement nomralized states + * @brief measure the part of the multi-partite state vector or density matrix 'A' + * corresponding to the subsystem specified by 'subsys' in the orthonormal basis + * rank-1 POV (positive operator-valued) measurement specified by matrix 'V' + * + * this function performs a destructive measurement on the part of the multi-partite state 'A' + * corresponding to the subsystem specified by 'subsys', using the orthonormal basis rank-1 POV + * measurement specified by the matrix 'V'. it returns a tuple containing the following information + * - the index of the outcome of measurement + * - the vector of outcome probabilites for each measurement basis state + * - the vector of post-measurement normalized states for each outcome, corresponding to each + * measurement basis state + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that Eigen can + * handle + * @param A the input multi-partite state, repersented as a square matrix (for density matrix) or a + * column column vector (for pure state) + * @param V the matrix repersenting the orthonormal basis rank-1 POV measurement + * @param subsys the indices of the subsystem for hich the measurement is performed + * @param dims the vector of dimension of each individiual subsystem in the multi-partite state + * @return A tuple containing: + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilites fro each measurement basis state + * - 'std::vector': the vector of post-measurement normalized state + * + * @note the dimension of matrix 'V' must match the dimension of the specified 'subsys'. the + * dimension of all subsystem in 'dims' must match the respective dimension of the multi-partite + * state. the measurement is destructive + * + * @example + * // usage of the measure function to perform measurement on multi-partite quantum state + * + * // represent the density matrix |01><01| + * cmat rho = 01_prj; + * // define the orthonormal basis rank-1 POV matrix 'V' representing the measurement basis for the + * // subsystem + * cmat V = 01_ket; + * // measure the subsystem with indices 0 and 1, each of dimension 2 (qubits) + * measure(rhi, V, {0, 1}, {2, 2}); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, @@ -394,24 +669,39 @@ std::tuple, std::vector> measure(const Eigen::Mat const std::vector& subsys, const std::vector& dims) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); + + // check if the input state amtrix is not empty if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::measure()"); + // check if the dimension 'dims' are valid if (!internal::check_dims(dims)) throw exception::DimsInvalid("clara::measure()"); + // check if the specified subsystem 'subsys' match the dimension dims if (!internal::check_subsys_match_dims(subsys, dims)) throw exception::SubsysMismatchdims("clara::measure()"); + + // calculate the dimension of the specified subsystem std::vector subsys_dims(subsys.size()); for (idx i = 0; i < subsys.size(); ++i) subsys_dims[i] = dims[subsys[i]]; + + // calculate the total dimension of the specified subsystem idx Dsubsys = prod(std::begin(subsys_dims), std::end(subsys_dims)); + // check if the POVM 'V' is not empty and matches the dimension of the specified + // subsystem if (!internal::check_nonzero_size(V)) throw exception::ZeroSize("clara::measure()"); if (Dsubsys != static_cast(V.rows())) throw exception::DimsMismatchMatrix("clara::measure()"); + // calcualte the nu,ber of measurement outcomes 'M' based on the columns of the POVM 'V' idx M = static_cast(V.cols()); + + // perform measurement based on the type of type input state 'A' if (internal::check_cvector(rA)) { + // if 'A' is a pure state (column vector), perform measurement using the + // provided POVM 'V' const ket& rpsi = A.derived(); if (!internal::check_dims_match_cvect(dims, rA)) throw exception::DimsMismatchCvector("clara::measure()"); @@ -447,13 +737,47 @@ std::tuple, std::vector> measure(const Eigen::Mat } /** - * @brief measure the part subsys of the multi-partite state vector or density matrix A - * in the orthonormal basis or rank-1 POVM specified by the matrix V - * @note the dimension V must match dimension of subsys. the measurement is destructive - * @return tuple of: - * - result of the measurement - * - vector outcome proabilities - * - vector of post-measurement normalized states + * @brief measure the part of the multi-partite state vector or density matrix 'A' in the + * orthonormal basis specified by the rank-1 positive operator-valued measure (POVM) repersented in + * 'V' + * + * this function perform a destructive measurement on the part of the multi-partite state 'A' + * corresponding to the subsystem specified by 'subsys' using the rank-1 POVM 'V' + * it returns a tuple containing the following information: + * - the inde of the outcomes of the measurement + * - the vector of outcome probabilites for each measurement basis state + * - the vector of post-measurement normalized state for each outcome, corresponding to each + * measurement basis state + * + * @tparam Derived the type of the input quantum state, Can be any matrix expression that Eigen can + * handle + * @param A the input multi-partite state, represented as a square matrix (for density matrix) or + * column column vector (for pure state) + * @param subsys the indices of the subsystem for which the measurement is performed + * @param dims the dimension of the subsystem in the multi-partite state + * @return a tuple containing + * - 'idx': the index of the outcome of the measurement + * - 'std::vector': the vector of outcome probabilites for each measurement basis state + * - 'std::vector': the vector post-measurement normalized state for each outcome, + * corresponding to each measurement basis state. + * + * @note the dimension of the POVM 'V' must match the dimension of the specified subsystem 'subsys' + * the input state matrix 'A' must not be empty + * the dimension 'dims' must be valid and match the dimension of 'A' + * the measurement outcome index is randomly sampled according to the outcome probabilites + * the function perform different measurement strategies based on whether 'A' is a pure state + * or a density matrix (square matrix) + * + * @example + * // usage of the measure function to perform a measurement on a multi-partite quantum state + * + * // repersent the density matrix |01> <01| + * cmat rho = 01_prj; + * // define the rank-1 POVM 'V' representing the measurement basis for the subsystem + * cmat V(2, 1); + * V << 1, 1; + * // measure the subystem with indices 0 and 1, each of dimension 2 (qubits) + * measure(rho, V, {0, 1}, {2, 2}); */ template std::tuple, std::vector> measure(const Eigen::MatrixBase& A, @@ -461,23 +785,61 @@ std::tuple, std::vector> measure(const Eigen::Mat const std::vector& subsys, idx d = 2) { const typename Eigen::MatrixBase::EigenvaluesReturnType& rA = A.derived(); + + // check if the input state matrix is not empty if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::measure()"); + + // check if the dimension 'd' is valid if (d < 2) throw exception::DimsInvalid("clara::measure()"); + + // calculate the number of subsystem 'N' based on the input state matrix and dimension 'd' idx N = internal::get_num_subsys(static_cast(rA.rows()), d); + // create a vector containing dimension 'd' for all subsystem std::vector dims(N, d); + // perform measurement on the specified subsystem using the orthonormal basis or POVM 'V' return measure(rA, V, subsys, dims); } /** - * @brief sequentially measure the part subsys - * of the multi-partite state vector or density matrix A in the computational basis - * @return tuple of: - * - vector of outcome result of the measurement (ordered in increasing order - * with smallest index) - * - outcome probability - * - post-measurement normalized state + * @brief sequentially measure the pasrt of the multi-partite state vector or density matrix 'A' + * in the computational basis + * + * this function perform sequential measurement on the subsystem specified by 'subsys' of the + * multi-partite state 'A' using the computational basis. it returns a tuple containing the + * following information + * - a vector containing the outcome of the measurement, oredered in increasing order with the + * smallest index first + * - the overall outcome probability of the sequential measurement. + * + * @tparam Derived the type of the input quantum state. can be any matrix expression that Eigen can + * handle + * @param A the input multi-partite state, repersented as square matrix (for density matrix) or a + * column vector (for pure state) + * @param subsys the indices of the subsystem to be sequentially measured + * @param dims the dimension of the subsystem + * @return A tuple containing + * - 'std::vector': A vector containing the outcome result of the measurement + * - 'double': the overall outcome probability of the sequential measurement + * - 'cmat': the post-measurement normalized state obtained after the sequential measurement + * + * @note the input state matrix 'A' must not be empty. the dimension of the subsystem in 'dims' + * souhld mactch the dimension of the corresponding subsystem specified 'subsys'. the function + * perform sequential measurement in the computational basis, starting from the subsystem with the + * largest index. after each measurement, the post-measurement normalized state is update for the + * next measurement + * + * @example + * // usage of the measure_seq function to perform sequential measurement on a multi-partite quantum + * state + * + * // represent the density matrix |01> <01| + * cmat rho = kron(01_ket, 01_bra); + * // specify the dimension of the subystem + * std::vector subsys = {0, 1} + * std::vector dims = {2, 2}; + * measure_seq(rho, subsys, dims); */ template std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase& A, @@ -489,6 +851,9 @@ std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase, double, cmat> measure_seq(const Eigen::MatrixBase result; double prob = 1; + // sort the subsystem in decreasing order to start from the subsystem with the largest index std::sort(std::begin(subsys), std::end(subsys), std::greater{}); + // perform sequential measurement on the specified subsystem using the computational + // basis while (subsys.size() > 0) { auto tmp = measure(cA, Gates::get_instance().Id(dims[subsys[0]]), {subsys[0], dims}); result.push_back(std::get<0>(tmp)); @@ -512,24 +881,60 @@ std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase': A vector containing the outcome result of the measurement + * - 'double': the overall outcome probability of the sequential measurement + * - 'cmat': the post-measurement normalized state obtained after the sequential measurement + * + * @note the input state matrix 'A' must not be empty. the function perform sequential measruements + * in the computationalbsis, starting from the subsystem with the largest index. after each + * measurement, the post-measurement normalized state is update for the next measruement + * the parameter 'q' speficies the dimension of the subsystem in the computational basis. + * it must be greater than or equal to 2 + * + * @example + * // usage of the measure_seq function to perform sequential measurement on a multi-partite quantum + * state + * + * // represente the density matrix |01> <01| + * cmat rho = kron(01_ket, 01_bra); + * // specify the subsystem to be sequentially measured + * std::vector subsys = {0, 1} + * // perform sequential measurement on the subsystem + * measure_seq(rho, subsys); */ template std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase& A, std::vector subsys, idx d = 2) { const typename Eigen::MatrixBase::EvalReturnType& rA = A.derived(); + // check the input state matrix is not emtpy if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::measure_seq()"); + // check if the dimension 'd' is valid if (d < 2) throw exception::DimsInvalid("clara::measure_seq()"); idx N = internal::get_num_subsys(static_cast(rA.rows()), d); + + // perform the sequential measurement using the measure_seq function with the calculated + // dimension 'dims' std::vector dims(N, d); return measure_seq(rA, subsys, dims); } diff --git a/include/number_theory.h b/include/number_theory.h index b4312e7..fff0b7d 100644 --- a/include/number_theory.h +++ b/include/number_theory.h @@ -20,10 +20,25 @@ namespace clara { /** - * @brief simple continued fraction expansion - * @return integer vector containing the simple continued - * fraction expansion of x. if there are M less than N terms in - * the expansion,a shorter vector with M components is returned. + * @brief compute the simple continued fraction expansion of a given real number 'x' + * + * this function computes the simple continued fraction expansion of the real number 'x' up to 'N' + * terms if the expansion has 'M' terms where 'M' is less than 'N', a shorter vector with 'M' + * components is returned + * + * @param x the real number for which simple continued fraction expansion is computed + * @param N the maximum number of terms in the continued fraction expansion. must be greater than 0 + * @param cut, the cutoff value for the exxample. if the absolute value of the next termn in the + * expansion exceed 'cut', the computation is terminated early. default 1e5 + * @return vector of integers representing the simple continued fraction expansion + * + * @note the input value of 'x' should be a non-zero real number. the 'cut' parameter is used to + * avoid infinite or large expansion that may occur for crtain values of 'x' + * + * @example + * // usage of x2contfrac function to compute the simple continued fraction expansion of real number + * double x = 3.14159; + * std::vector result = x2contfrac(x, 5); */ inline std::vector x2contfrac(double x, idx N, idx cut = 1e5) { if (N == 0) @@ -46,8 +61,24 @@ inline std::vector x2contfrac(double x, idx N, idx cut = 1e5) { } /** - * @brief real representation of a simple continue fraction - * @return real representation of the simple continue fraction + * @brief compute the real representation of given simple continued fraction + * + * this function compute the real repersentation of the simple continued fraction represented by the + * vector of 'cf' up to 'N' terms. if 'N' is greater than the size of the vector 'cf', the function + * will use all available terms the expansion + * + * @param cf a vector of integers representing the simple continued fraction + * @param N the maximum number of terms of to use the continued fraction expansion. default is -1 + * which means all available terms in 'cf' will be used + * @return the real representation opf the simple continued fraction + * + * @note the input vector 'cf' should represent a valid simple continued fraction. the value of 'N' + * is optional and can be used to truncate the continued fraction expansion if needed + * + * @example + * // usage of contfrac2x function to real representation of a simple continued fraction + * std::vector cf = {3, 7, 15}; + * double result = contfrac2x(cf); */ inline double contfrac2x(const std::vector& cf, idx N = idx(-1)) { if (cf.size() == 0) @@ -66,10 +97,24 @@ inline double contfrac2x(const std::vector& cf, idx N = idx(-1)) { return cf[0] + tmp; } - /** - * @brief greatest common divisor two integers - * @return gratest common divisor of a and b + * @brief compute the greatest common (GCD) of two integers + * + * this function computes the greatest common divisor of two integers 'a' and 'b' + * + * @param a the first integers + * @param b the second integers + * @return the greatest common divisor of 'a' and 'b' + * + * @note if both 'a' and 'b' are zero, the function will throw an exception since GCD is not defined + * for zero value if either 'a' or 'b' is zero, the function will return the absolute value + * of the non-zero integer. otherwise, it will calculate the GCD using the Eucledian algorithm + * + * @example + * // usage of gcd to compute the gratest common divisor of two integers + * bigint a = 36; + * bigint b = 48; + * bigint result = gcd(a, b); */ inline bigint gcd(bigint a, bigint b) { if (a == 0 && b == 0) @@ -87,8 +132,22 @@ inline bigint gcd(bigint a, bigint b) { } /** - * @brief greates common divisor of a list of integers - * @return greatest common divisor of all members in as + * @brief compute the greatest commond divisor (GCD) of a list of integers + * + * this function calculate the greates common divisor of a list integers 'as' + * + * @params as vector of integers for which the GCD needs to be calculated + * @return the greatest common divisor of all integers in 'as' + * + * @note the function requires at least one element in the 'as' vector. if the vector is empty + * it will throw and exception since GCD is not defined for an empty set of integers + * the function iterates through the elements of the vector and calculates their GCD + * sequentially + * + * @example + * // usage of gcd function to compute the greates common divisor of a list integers + * std::vector numbers = {46, 48, 72}; + * bigint result = gcd(numbers); */ inline bigint gcd(const std::vector& as) { if (as.size() == 0) @@ -101,8 +160,23 @@ inline bigint gcd(const std::vector& as) { } /** - * @brief least common multiple of two integers - * @return least common multiple of a and b + * @brief compute the least common multiple of two integers + * + * this function calculates the least common multiple of two integers 'a' and 'b' + * + * @param a the first integers + * @param b the second integers + * @param the least common multiple of 'a' and 'b' + * + * @note the function check if both 'a' and 'b' are zero. if both are zero, it throws an exception + * since LCM is not defined for two zero values. the LCM using the formula + * LCM(a, b) = |a * b| / GCD(a, b), where GCD is the greatest common divisor + * + * @example + * // usage of lcm to compute the least common multiple of two integers + * bigint a = 24; + * bigint b = 36; + * bigint result = lcm(a, b); */ inline bigint lcm(bigint a, bigint b) { if (a == 0 && b == 0) @@ -113,7 +187,20 @@ inline bigint lcm(bigint a, bigint b) { /** * @brief least common divisor multiple of integers - * @return least common multiple of all number in as + * this function calculate the least common multiple of alist of integers 'as' + * + * @param as the vector of integers for which LCM needs to be calculated + * @return the least common multiple of all the integers in the vector 'as' + * + * @note the function check if the vector 'as' is empty, if it is empty. it throws an exception + * since LCM is not defined for an empty list. if the input vector contains a zero value, + * it also throws an exception, as LCM is not defined for zero values. the LCM is calculated + * using the formula LCM(a, b) = |a * b| / GCD(a, b), where GCD is the greatest common divisor + * + * @example + * // usage LCM function to compute the last common multiple of list of integers + * std::vector numbers = {24, 30, 48}; + * bigint result = lcm(numbers) */ inline bigint lcm(const std::vector& as) { if (as.size() == 0) @@ -131,8 +218,21 @@ inline bigint lcm(const std::vector& as) { } /** - * @brief inverse permutation - * @return inverse the permutation perm + * @brief compute the inverse permutation of a given permutation + * this function calculates the inverse permutation of the input permutation vector 'perm' + * + * @param perm the permutation vector for which the inverse permutation needs to be computed + * @return the inverse permutation vector of the input 'perm' + * + * @ntoe the function check if the input permutation 'perm' is valid. it ensures that the + * permutation vector contains unique indices from 0 to (size -1), where size os the number of + * elements in vector the inverse permutation is computed such that invperm[i] give the index of i + * the original permutation + * + * @example + * // using of invperm function to compute the inverse permutation of a given permutation + * std::vector perm = {2, 0, 1}; + * std::vector result = invperm(perm); */ inline std::vector invperm(const std::vector& perm) { if (!internal::check_perm(perm)) @@ -145,9 +245,25 @@ inline std::vector invperm(const std::vector& perm) { } /** - * @brief compose permutation - * @return composition of the permutation in perm \f$\circ\f$ sigma - * = perm(sigma) + * @brief compose permutations + * + * this function computes the composition of two permutations 'perm' and 'sigma', denoted + * as perm(sigma) or perm sigma + * + * @param perm the first permutation vector + * @param sigma the second permutation vector + * @return the composition of the permutation perm sigma + * + * @note the function check if the input permutation 'perm' and 'sigma' are valid. it ensures that + * the permutation vectors contain unique indices from 0 to (size - 1) where size is the number of + * elements in the vector. the composition of permutation is computed such that compperm[i] give the + * resulting index when applying 'perm' then 'sigma' to the index i + * + * @example + * // usage of compperm function to compose two permutation + * std::vector perm = {1, 0 ,2}; + * std::vector sigma = {2, 0, 1}; + * std::vector result = compperm(perm, sigma); */ inline std::vector compperm(const std::vector& perm, const std::vector& sigma) { if (!internal::check_perm(perm)) @@ -164,8 +280,24 @@ inline std::vector compperm(const std::vector& perm, const std::vector } /** - * @brief prime factor decomposition - * @return integer vector containing the factors + * @brief prifme factor decomposition + * this function calculates the prime factor decomposition of an integer 'a' + * the function returns a vector containing the prime factors of 'a' + * + * @param a the integer for which the prime factor decomposition is calculated + * @return vector of integers representing the prime factors of 'a' + * + * @note the function check if 'a' is a positive integer greater than 1, if 'a' is 0 or 1 + * an exception will be thrown since they have no prime factors. the functio iterates + * through all possible divisors 'd' starting from 2 and continues dibiving 'a' by 'd' + * until 'a' becom 1. during this processm, if 'd' is a divisor of 'a', it is added to the + * result vector. the function stops when 'a' is reduced to 1 or when 'd * d' becomes greater + * than 'a' and the remaining value of 'a' (if no 1) is added as prime factor + * + * @example + * // usage of factors function to get the prime factors of an integers + * bigint a = 30; + * std::vector result = factors(a); */ inline std::vector factors(bigint a) { a = std::abs(a); @@ -191,8 +323,26 @@ inline std::vector factors(bigint a) { } /** - * @brief modular multiplication without overflow - * @return \f$ab\f$ \f$\mathrm{ mod }\f$ \f$p\f$ avoiding overflow + * @brief modular multiplication without overlow + * this function calculates the product of two integers 'a' and 'b' modulo 'p' + * avoiding overflow for large value + * + * @param a the first integer operand + * @param b the second integer operand + * @param p the module value + * @return result of \f$ab \mod p\f$ without causing overflow + * + * @note the function check if 'p' is greater thatn 1. if 'a' or 'b' is zero + * the result will always zero. the function covert 'a', 'b' and 'p' unsigned long long + * integers to perform the calculation avoiding overflow, it handle negative values of 'a' and 'b' + * and keeps track of the overall sign of the result. the function uses bitwise operator to perform + * multiplication and reduce the result of module 'p' + * + * @example + * bigint a = 123456789; + * bigint b = 987654321; + * bigint p = 1000000007; + * bigint result = modmul(a, b, p); */ inline bigint modmul(bigint a, bigint b, bigint p) { using ubigint = unsigned long long int; @@ -247,8 +397,24 @@ inline bigint modmul(bigint a, bigint b, bigint p) { } /** - * @brief fast integer power modulo p based on the SQUARE-AND-MULTIPLY algorithm - * @return f$a^n\f$ \f$\mathrm{ mod }\f$ \f$p\f$ + * @brief fast integer power modulo 'p' based on the SQUARE-AND-MULTIPLY algorithm + * this function calculates \f$a^n \mod p\f$ using the SQUARE-AND-MULTIPLY algorithm + * to efficiently compute the power modulo 'p' + * + * @param a the base integer + * @param n the exponent + * @param p the module value + * @return the result of \f$a^n \mod p\f$ + * + * @note the function check if 'a', 'n', and 'p' are within the valid range for calculations + * it avoids calculating when 'a', 'n', or 'p' is negative or 'p' is less than 1. if + * 'a' and 'n' are both zero, an exception is thrown as there is no defined result + * + * @example + * bigint a = 2; + * bigint n = 10; + * bigint p = 1000000007; + * bigint result = modpow(a, n, p); */ inline bigint modpow(bigint a, bigint n, bigint p) { if (a < 0 || n < 0 || p < 1) @@ -269,11 +435,24 @@ inline bigint modpow(bigint a, bigint n, bigint p) { } /** - * @brief extended greatest common divisor of two integers - * @return tuple of: - * - integer \f$m\f$ - * - integer \f$nf\f$ - * - non negative integer \f$gcd(a, b)\f$ such that + * @brief fast integer power modulo 'p' based on the SQUARE-AND-MULTIPLY algorithm + * this function calculates \f$a^n \mod p\f$ using the SQUARE-AND-MULTIPLY algorithm + * to efficiently compute the power modulo 'p' + * + * @param a the base integer + * @param n the exponent + * @param p the module value + * @return the result of \f$a^n \mod p\f$ + * + * @note the function check if 'a', 'n', and 'p' are within the valid range for calculations + * it avoids calculating when 'a', 'n', or 'p' is negative or 'p' is less than 1. if + * 'a' and 'n' are both zero, an exception is thrown as there is no defined result + * + * @example + * bigint a = 2; + * bigint n = 10; + * bigint p = 1000000007; + * bigint result = modpow(a, n, p); */ inline std::tuple egcd(bigint a, bigint b) { if (a == 0 && b == 0) @@ -298,8 +477,28 @@ inline std::tuple egcd(bigint a, bigint b) { } /** - * @brief modular inverse of a mod p - * @return modular inverse \f$a^{-1}\f$ \f$\textrm{ mod }\f$ \f$p\f$ + * @brief modular modular inverse of 'a' modulo 'p' + * this function calculates the modular inverse f$a^{-1} \mod p\f$ using the extended Euclidean + * algorithm (egcd). the egcd function is usde to find integers 'x' and 'y' such that + * \f$ax + py = \textrm{gcd}(a, p)\f$, where \f$\textrm{gcd}(a, p)\f$ is the greatest common + * divisor of 'a' and 'p'. if \f$\textrm{gcd}(a, p) = 1\f$, + * then 'x' is the modular inverse of 'a' modulo 'p' + * + * @param a the integer for which the modular inverse is calculated + * @param p the module value + * @return the modular inverse \f$a^{-1} \mod p\f$ + * + * @note the function check if 'a' and 'p' are within the valid range for calculations. + * it avoids calculating when 'a' or 'p' is less than or equal to zero. the function uses the + * egcd function to find the modular inverse. it throws an exception if the modular inverse + * does not exists + * + * @example + * // usage of modinv function to calculate modular inverse + * bigint a = 3; + * bigint p = 7; + * bigint result = modinv(a, p); + * */ inline bigint modinv(bigint a, bigint p) { if (a <= 0 || p <= 0) @@ -314,8 +513,26 @@ inline bigint modinv(bigint a, bigint p) { } /** - * @brief primality based on the miller-rabin algorithm - * @return true of the number (most-likely) prime, false otherwise + * @brief primality test baesd on the miller-rabin algorithm + * this function test whether the given number 'p' is (most-likely) prime or not + * using the miller-rabin primality test. the function performs 'k' iterations of the + * miller-rabin test to determine if 'p' is prime. the higher the value of 'k' the more + * accurate the primality test + * + * @param p the integer to be tested for primality + * @param k the number of iterations to perform in the miller-rabin test. default is 80 + * @return returns true if 'p' is (most-likely) prime, false otherwise + * + * @note the function check if 'p' is greater than 2. it avoids caculation for 2 and 3, which are + * prime numbers. the function perform a fermat primality test an initial check it then + * computes the values of 'u' and 'r' for the miller-rabin test the function uses the modpow + * and modmul functions for modular exponentiation and multiplication. + * the result of the miller-rabin test + * + * @example + * // usage of isprime function to test for primality + * bigint p = 127; + * bool result = isprime(p); */ inline bool isprime(bigint p, idx k = 80) { p = std::abs(p); @@ -362,9 +579,22 @@ inline bool isprime(bigint p, idx k = 80) { } /** - * @brief generate random big prime uniformly distributed in the internal - * @return random big integer uniformly distributed in the interval [a, b] - */ + * @brief generate a random prime number in the range [a, b] + * this function generate the random prime number between 'b' and 'b' inclusive. + * it uses a combination of random number generation and primality test to find a prime number. + * the function performs 'N' iterations to find a prime number. if it fails to find a prime number + * in 'N' iterations, it throws a custom exception + * + * @param a the lower bound of the range for the random prime number + * @param b the upper bound of the range for the random prime number + * @param N the number of iteration to find a prime number, default is 1000 + * + * @example + * // usage of randprime function to generate a random prime number of the range [100, 1000] + * bigint lower_bound = 100; + * bigint upper_bound = 1000; + * bigint result = randprime(lower_bound, upper_bound); +*/ inline bigint randprime(bigint a, bigint b, idx N = 1000) { if (a > b) throw exception::OutOfRange("clara::randprime()"); diff --git a/include/operations.h b/include/operations.h index d84d8c1..3f99d38 100644 --- a/include/operations.h +++ b/include/operations.h @@ -110,8 +110,8 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& * worker computes the coefficient and index for ket case * used in #pragma omp parallel fro collapse */ - auto coeff_idx_ket = [&](idx i_, idx m_, - idx r_) noexcept -> std::pair { + auto coeff_idx_ket = [&](const idx i_, const idx m_, + const idx r_) noexcept -> std::pair { idx indx = 0; typename Derived1::Scalar coeff = 0; @@ -153,8 +153,9 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& * worker computes the coefficient and the index * for the density matrix case used in #pragma omp parallel for collapse */ - auto coeff_idx_rho = [&](idx i1_, idx m1_, idx r1_, idx i2_, idx m2_, - idx r2_) noexcept -> std::tuple { + auto coeff_idx_rho = + [&](const idx i1_, const idx m1_, const idx r1_, const idx i2_, const idx m2_, + const idx r2_) noexcept -> std::tuple { idx idxrow = 0; idx idxcol = 0; typename Derived1::Scalar coeff = 0, lhs = 1, rhs = 1; @@ -459,7 +460,7 @@ cmat apply(const Eigen::MatrixBase& A, const std::vector& Ks, const std::vector& subsys, const std::vector& dims) { const cmat& rA = A.derived(); if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::apply()"); + throw exception::ZeroSize("clara::apply()"); if (!internal::check_square_mat(rA)) throw exception::MatrixNotSquare("clara::apply()"); if (!internal::check_dims(dims)) diff --git a/include/random.h b/include/random.h index 0179827..2973c27 100644 --- a/include/random.h +++ b/include/random.h @@ -16,10 +16,31 @@ #include "types.h" namespace clara { + /** - * @brief genrate random real number uniformly distributed in the interval [a, b] - * @return random real number (double) uniformly distributed in [a, b] - */ + * @brief generate a random real number uniformly distributed in the internal [a, b] + * + * this function generate a random real number uniformly distributed in the interval a and b + * it uses std::uniform_real_distribution from cpp standard library to achieve uniform distribution + * the function takes two parameters a and b, representing the lower and upper bound of the + * interval it throws an exception if 'a' is greater than equal to 'b' + * + * @param a the lower bound of the interval for the random real number + * @param b the upper bound of the interval for the random real number + * @return return a random number uniformly distributed in a b + * + * @note the function check if 'a' is greater than or equal to 'b' and throws and exception if so. + * it uses std::uniform_real_distribution to generate a random real number in the specified + * interval if the macro NO_THREAD_LOCAL_ is defined, it uses the global + * RandomDevices::get_instance().get_prng() to obtain the random number generator. then returns the + * generated random real number + * + * @example + * // usage of rand functio to generate a random real number in the interval [0.0, 1.0] + * double lower_bound = 0.0; + * double upper_bound = 1.0; + * double result = rand(lower_bound, upper_bound) + * */ inline double rand(double a, double b) { if (a >= b) throw exception::OutOfRange("clara::rand()"); @@ -33,8 +54,27 @@ inline double rand(double a, double b) { } /** - * @brief generate a random big integer uniformly distributed in the interval [a, b] - * @return random big integer uniformly distributed in the interval + * @brief generate a random big integer uniformly sistributed in the interval [a, b] + * this function generates a random big integer uniformly distributed in the interval [a, b] + * it uses std::uniform_int_distribution from standard library to achieve uniform distribution + * the function takes two parameters 'a' and 'b', representing the lower and upper bounds of the + * interval it throws an exception if 'a' is gerater than 'b' + * + * @param a the lower bound of the interval for the random big integer + * @param b the upper bound of the interval for the random big integer + * + * @note the function check if 'a' is greater than 'b' and throws an exception if so + * it uses std::uniform_int_distribution to generate random big integer in the specified + * inteval. if macro NO_THREAD_LOCAL_ is defined, it uses the global + * RandomDevices::get_instance().get_prng() to obtain the random number generator, otherwise, it + * uses the thread local RandomDevices::get_thread_local_instance().ger_prng() to obtain the random + * number generatro the function then returns the gnerated random big integer + * + * @example + * // usage of rand function to generate a random big integer in the interval [0, 100] + * bigint lower_bound = 0; + * bigint upper_bound = 100; + * bigint result = rand(lower_bound, upper_bound); */ inline bigint rand(bigint a, bigint b) { if (a > b) @@ -48,8 +88,27 @@ inline bigint rand(bigint a, bigint b) { } /** - * @brief generate random index (idx) uniformly distribyted in the interval [a,b] - * @return random index uniformly distributed in the interval[a, b] + * @brief generate a random index (idx) uniformly in distribted in the interval [a, b] + * this function generate a random index (idx) uniformly distributed in the interval [a, b] + * it uses std::uniform_int_distribution from standard library to achieve uniform distribution + * the function takes two parameters a and b. representing the lower and upper bound of the + * interval it throws exception if 'a' is greater than b + * + * @param a the lower bound of the interval for the random index + * @param b the upper bound of the interval for the random index + * @return random uniformly distributed in the interval [a, b] + * + * @note the function check if 'a' is greater than 'b' and throws an exception if so + * it uses std::uniform_int_distribution to generate a random index in the specified interval + * if the macro macro NO_THREAD_LOCAL_ is defined, it uses the global + * RandomDevices::get_instance().get_prng() to obtain the random number generator, otherwise it uses + * the thread-local + * + * @eample + * // usage of randidx function to generate a random index in the interval [0, 10] + * idx lower_bound = 0; + * idx upper_bound = 10; + * idx result = randidx(lower_bound, upper_bound); */ inline idx randidx(idx a = std::numeric_limits::min(), idx b = std::numeric_limits::max()) { @@ -65,10 +124,29 @@ inline idx randidx(idx a = std::numeric_limits::min(), } /** - * @brief generate a random matrix with entries uniformly distributed - * in the interval [a, b] - * if complex, then both real and imaginary parts are uniformly distributed - * in [a, b] + * @brief generate random matrix with entries uniformly distributed in the inteval [a, b] + * this function generate a random matrix with entries uniformly dostributed in the interval [a, b] + * the matrixx can be of any type that is derived from the eigen library + * the function takes four parameters rows, cols, a , b + * 'rows' and 'cols' represent the number of rows and columns of the generated matrix, respectively + * 'a' and 'b' represent the lower and upper bounds of the interval for the random entries + * if the matrix is complex, both the real and imaginary parts of the entries are uniformly + * distributed in the inteval [a, b] + * + * @tparam Derived the type of the matrix, which must be derived from the eigen library + * @param rows the number of rows of the generated matrix + * @param cols the number of columns of the generated matrix + * @param a the lower bound of the interval for the random entries + * @parma b the upper bound of the interval for the random matrix + * + * @throws exception::UndefinedType if the function is called with a type that is not derived from + * the eigen library + * + * @example + * // using of the rand function to generate a 3x3 random matrix with entries in the interval [-1, + * 1] int rows = 3; int cols = 3; Eigen::MatrixXd random_matrix = rand(rows, cols, + * lower_bound, upper_bound); + * */ template Derived rand(idx rows, idx cols, double a = 0, double b = 1) { @@ -80,10 +158,29 @@ Derived rand(idx rows, idx cols, double a = 0, double b = 1) { } /** - * @brief generate a random real matrix with entries uniformly - * distributed in the interval [a, b], the template parameter cannot - * be automatically deduced and must be explicitly provied - * @return random real matrix + * @brief generate random real matrix with entries uniformly distributed in the interval [a, b] + * this function generate a random real matrix with entries uniformly distributed in the interval + * The template parameter 'dmat' must be explicitly provided since it cannot be automatically + * deduced the function takes four parameters row, cols, a and b 'rows' and 'cols' represent the + * number of rows and columns of the generated matrix, respectively 'a' and 'b' represent the lower + * and upper bounds of the interval for the random entries + * + * @param rows the number of rows of the generated matrix + * @param cols the number of columns the generated matrix + * @param a lower bound of the inteval for the random entries + * @param b upper bound of the interval for the random entries + * @return a random real matrix with entries uniformly distributed in the interval [a, b] + * + * @throws exception::ZeroSize if either 'rows' or 'cols' is zero + * @throws exception::OutOfRange if 'a' is greater than or equal to 'b' + * + * @example + * // usage of the rand function to generate 3x3 random real matrix with entries interval [-1, 1] + * int rows = 3; + * int cols = 3; + * double lower_bound = -1; + * double upper_bound = 1; + * Eigen::MatrixXd random_matrix = rand(rows, cols, lower_bound, upper_bound); */ template <> inline dmat rand(idx rows, idx cols, double a, double b) { @@ -95,11 +192,33 @@ inline dmat rand(idx rows, idx cols, double a, double b) { } /** - * @brief generate a random complex matrix with entries (both and real and imaginary) uniformly - * distributed in the interval [a, b], specialized for complex matrices - * the template parameter cannot be automatically deduced and must be explicitly - * provided - * @return random complex matrix + * @brief generate a random real matrix with entries uniformly distributed in the interval [a, b] + * + * this function is specialized for complex matrices (cmat) and uses the 'rand' function to generate + * random real matrices (dmat) with entries uniformly distributed in the interval [a, b]. The + * template parameter 'cmat' must be explicitly provided since it cannot be automatically deduced. + * the function takes four parameters: 'rows', 'cols', 'a', and 'b'. + * 'rows' and 'cols' represent the number of rows and columns of the generated complex matrix, + * respectively. 'a' and 'b' represent the lower and upper bounds of the interval for the random + * real and imaginary entries. + * + * @param rows the number of rows of the generated matrix + * @param cols the number of columns of the generated matrix + * @param a lower bound of the interval for the random entries + * @param b upper bpund of the interval for the random entries + * @return random real matrix with entries uniformly distributed in the interval + * + * @throws exception::ZeroSize if either 'rows' or 'cols' is zero + * @throws exception::OutOfRange if 'a' is greater than or equal to 'b' + * + * @example + * // usage of the rand function to generated a 3x3 random real matrix with entries interval [-1, 1] + * int rows = 3; + * int cols = 3; + * double lower_bound = 1; + * double upper_bound = 1; + * Eigen::MatrixXd random_complex_matrix = rand(rows, cols, lower_bound, + * upper_bound); */ template <> @@ -114,8 +233,34 @@ inline cmat rand(idx rows, idx cols, double a, double b) { } /** - * @brief generate a random matrix with entries normally distributed in N(mean, sigma) - * if complex, then both real and imaginary parts are normally distributed in N(mean, sigma) + * @brief generate a random matrix with entries normally distribution in N(mean, sigma); + * + * this function is template function that generate random matrices with entries normally + * distributed in N(mean, sigma). the template parameter 'Derived' represents the type of the + * generated matrix and must be explicitly provided as Eigen's matrix type the function takes four + * parameters rows, cols, mean, and sigma. rows and cols repersent the number of rows and columns of + * the generated matrix, respectively. 'mean' repersent the mean (average) value of the normal + * distribution, and 'sigma' represent the standard deviation of the normal distribution + * + * @tparam Derived the type of the generated matrix, which must be explicitly provided as Eigen's + * matrix type + * @param rows the number of rows of generated matrix + * @param cols the number of columns of the gnerated matrix + * @param mean the mean (average) value of the normal distribution + * @param sigma the standard deviation of the normal distribution + * @return random matrix with entries normally distributed in N(mean, sigma) + * + * @throw exception::UndefinedType if the template parameter 'Derived' is not provided or is not a + * valid Eigen Matrix type + * + * @example + * // usage of the randn function to generate a 3x3 random real matrix with entries normally + * // distributed + * int rows = 3; + * int cols = 3; + * double mean = 0; + * double sigma = 1; + * Eigen::MatrixXd random_real_matrix = randn(rows, cols, mean, sigma); */ template Derived randn(idx rows, idx cols, double mean = 0, double sigma = 1) { @@ -127,10 +272,25 @@ Derived randn(idx rows, idx cols, double mean = 0, double sigma = 1) { } /** - * @brief generate a random real matrix with entries normally - * distributed in N(mean, sigma) specialization for double matrices - * this template parameter cannot be automatically deduced and - * must be explicitly provied + * @brief generate a random real matrix with entries normally distributed in N(mean, sigma) + * + * this is specialization of the 'randn' function for generating random real matrices with + * entries normally distributed in N(mean, sigma) + * the function takes four parameters: 'rows', 'cols', 'mean', and 'sigma', + * 'rows' and 'cols' represent the number of rows and columns of the generated matrix, respectively + * 'mean' represents the mean (average) value of the normal distribution, and 'sigma' represents the + * standard deviation of the normal distribution + * + * @throws exception::ZeroSize if either 'rows' or 'cols' is zero, indicating an invalid matrix size + * + * @example + * // usage of the randn function to generate a 3x3 random real matrix with entries normally + * // distributed with mean 0 and standard deviation 1 + * int rows = 3; + * int cols = 3; + * double mean = 0; + * double sigma = 1; + * Eigen::MatrixXd random_real_matrix = randn(rows, cols, mean, sigma); */ template <> inline dmat randn(idx rows, idx cols, double mean, double sigma) { @@ -160,8 +320,32 @@ inline cmat randn(idx rows, idx cols, double mean, double sigma) { } /** - * @brief generate random real number (double) normally distributed in N(mean, sigma) - * @return random real number normally distributed in N(mean, sigma) + * @brief generate a random complex matrix with entries (both real and imaginary parts) + * anormally distributed in N(mean, sigma) + * + * this is specialization of the 'randn' function for generating random comple matrices with + * entries normally distributed in N(mean, sigma) the function takes four parameters: + * rows, cols, mean, sigma. rows and cols repersent tje number of rows and columns of the + * generated matrix respectively. 'mean' repersent the mean (average) value of the normal + * distribution and 'sigma' represent the standard deviation of the normal distribution + * + * @param rows the number of the generated matrix + * @param cols the number of the columns of the generated matrix + * @param mean the mean (average) value of the normal distribution + * @param sigma the standard deviation of the normal distribution + * @return random complex matrix with entries normally distributed in N(mean, sigma) + * + * @throws exception::ZeroSize if either 'rows' or 'cols' is zero, indicating an invalid matrix size + * + * @example + * // usage of the randn function to generate 3x3 random complex matrix with entries normally + * distributed + * // with mean 0 and standard deviation 1 + * int rows = 3; + * int cols = 3; + * double mean = 0; + * double sigma = 1; + * Eigen::MatrixXcd random_complex_matrix = randn(rows, cols, mean, sigma); */ inline double randn(double mean = 0, double sigma = 1) { std::normal_distribution<> nd(mean, sigma); @@ -188,8 +372,20 @@ inline cmat randU(idx D) { } /** - * @brief generate random isometry matrix - * @return random isometry matrix + * @brief generate a random unitary matrix of size DxD using Haar measure + * + * this function generates a random unitary matrix of size DxD using the Haar measure + * which is natural measure for the group of unitary matrices. the generated matrix + * is uniformly distributed over the unitary group + * + * @param D the size of the square matrix + * @return random unitary matrix of size DxD + * + * @throws exception::DimsInvalid if 'D' is zero, indicating an invalid matrix ZeroSize + * + * @example + * int D = 2; + * Eigen::MatrixXcd random_unitary_matrix = randU(D); */ inline cmat randV(idx Din, idx Dout) { if (Din == 0 || Dout == 0 || Din > Dout) @@ -198,8 +394,24 @@ inline cmat randV(idx Din, idx Dout) { } /** - * @brief generate set random of kraus operator - * @return set N kraus operators satisfying the closure condition + * @brief generate a set of random kraus operators + * + * this function generates a set of N kraus operators satfisying the closure condition for a quantum + * operation on a D-dimensional quantum system, the generated kraus operators are represented as a + * vector of complex matrices + * + * @param N the number of krause operators + * @param D the size of the square kraus operators + * @return vector containing N random kraus operators, each of size DxD + * + * @throws exception::OutOfRange if 'N' is zero, indicating an invalid number of Kraus operators + * @throws exception::DimsInvalid if 'D' is zero, indicating an invalid matrix size + * + * @example + * // usage of the randkraus function to generate a set of 2 random 2x2 kraus operators + * int N = 2; + * int D = 2; + * std::vector random_kraus_operators = randkraus(N, D); */ inline std::vector randkraus(idx N, idx D) { if (N == 0) @@ -224,19 +436,48 @@ inline std::vector randkraus(idx N, idx D) { } /** - * @brief generate a random hermitian matrix - * @return random hermitian matrix + * @brief generate random hermitian matrix + * + * this function generates a random hermitian matrix of size 'D'. the generated matrix is hermitian meaning + * that is equal to its own conjugate transpose. the matrix is constructed by first generating a random complex matrix + * 'H' of size 'D x D' with entries uniformly distributed in the interval [0, 1], then, its is transformed into hermitian + * matrix using formula H = H + H^†, where H^† is the conjugate transpose of 'H' + * + * @param D the size of the hermitian matrix, its must be a positive inetger + * @return a random hermitian matrix of size 'DxD' + * + * @throws exception::DimsInvalid if 'D' is zero, indicating an invalid input size + * + * @example + * // usage of randH function to generate random hermitian matrix of size 3x3 + * idx D = 3; + * cmat randomHermitianMatrix = randH(D); */ inline cmat randH(idx D) { if (D == 0) throw exception::DimsInvalid("clara::randH()"); + // generate random complex matrix of size D x D with entries uniformly distributed in [0, 1] cmat H = 2 * rand(D, D) - (1. + 1_i) * cmat::Ones(D, D); + // make the matrix hermitian by adding its conjugate transpose return H + adjoint(H); } /** - * @brief generate random normalized ket (pure state vector) - * @return random normalized ket + * @brief generated a random quantum ket (column vector) + * + * this function generate a random quantum ket (column vector) of size Dx1, representing a quantum + * state the elements of the vector are generated with a normal distribution N(mean, sigma)m and the + * resulting ket is normalized to have a unit norm + * + * @param D the dimension of the quantum ket (column vector) + * @return Dx1 random quantum ket (rnomalized column vector) + * + * @throws exception::DimsInvalid if 'D' is zero, indicating an invalid vector size + * + * @example + * // usage of the randket function to generate a random 3-dimensional quantim ket + * int D = 3; + * ket random_quantum_ket = randket(D); */ inline ket randket(idx D) { if (D == 0) @@ -246,8 +487,22 @@ inline ket randket(idx D) { } /** - * @brief generate a random density matrix - * @return random density matrix + * @brief generate a random quantum density matrix + * + * this function generate a random quantum density matrix of size Dx1, representing a quantum state + * the eelement of the density matrix are generated by first generating a random hermitian matrix + * and its adjoint (conjugate transpose), and finally normalized to have a unit trace, ensuring it + * represente a valid quantum state + * + * @param D the dimension of the quantum state + * @return DxD random quantum density matrix + * + * @throws exception::DimsInvalid if 'D' is zero, indicating invalid matrix size + * + * @example + * // usage of the randrho function to generate a random 3-dimensional quantum density matrix + * int D = 3; + * cmat random_density_matrix = randrho(3); */ inline cmat randrho(idx D) { if (D == 0) @@ -258,10 +513,22 @@ inline cmat randrho(idx D) { } /** - * @brief generate a random uniformly distributed permutation - * uses knuth shuffle method (as implemented by std::shuffle) - * so that all permutation are equally probable - * @return random permutation of size N + * @brief generate a random uniformly distribution permutation + * + * this function generates a random permutation of size N using the Knuth shuffle method, which + * ensures that all permutation are equally probable. the function first create a vector of size N + * with elements increasing order. then, it use std::shuffle algorithm to randomly shuffle the + * elements of the vector, generating a random permutation + * + * @param N the size of the permutation to be generated + * @return vector representing a random permutation of size N + * + * @throws exception::PermInvalid if 'N' is zero, indicating an invalid permutation size + * + * @example + * // usage of the randperm function to generate a random permutation of size 5 + * int N = 5; + * std::vector random_permutation = randperm(N); */ inline std::vector randperm(idx N) { if (N == 0) @@ -280,9 +547,14 @@ inline std::vector randperm(idx N) { } /** - * @brief generate random probability vector uniformly distributed over the - * probability simplex - * @return random probability vector + * @brief generate random probability vector uniformly distributed over the probability simplex + * + * this function generates a random probability vector of size N, where the elements are uniformly distributed + * over the probability simple. the probability simplex is the set of all probability vector whose elements are non-negative and sum up to 1. the function first generates N random numbers from an exponential distribution with a rate parameter of 1 + * these random numbers are then normalized to ensure that they sum up to 1, thus forming a valid probability vector + * + * @param N the size of the probability vector to be generated + * @return vector representing a random probability vector of size N */ inline std::vector randprob(idx N) { if (N == 0) diff --git a/include/statistics.h b/include/statistics.h index d3281d4..40c8b6a 100644 --- a/include/statistics.h +++ b/include/statistics.h @@ -12,9 +12,23 @@ namespace clara { /* - * @brief uniform probability distribution vector - * @param N size of the alphabet - * @return Real vector consiting of a uniform distribution of size N + * @brief generate a uniform probability distribution vector + * + * this function generate a real vector of size N representing a uniform probability distribution. + * in a uniform probability distribution, all elements have an equal probability of occurence. + * each elements in the generated vector will have a value of 1/N, where N is the size of the + * alphabet the function returns a vector where each element is set to 1/N, ensuring that the + * vector representing a uniform distribution + * + * @param N the size of the alphabet for which the uniform distribution is generated + * @return real vector representing a uniform probability distribution of size N + * + * @throws exception::ZeroSize if 'N' is zero, indicating an invalid vector size + * + * @example + * // usage of the uniform function to generate a uniform probability distribution vector of size + * int N = 4; + * std::vector uniform_dist = uniform(N); */ inline std::vector uniform(idx N) { if (N == 0) @@ -23,9 +37,22 @@ inline std::vector uniform(idx N) { } /** - * @brief marginal distribution - * @param probXY real matrix representing the joint probability distribution - * of x nd y in lexicographical order + * @brief compute the marginal distribution for a given joint probability distribution + * + * this function calculates the marginal distribution of the variable 'X' forma given joint + * probability distribution. the input 'probXY' is real matrix representing the joint probability + * distribution of two matrices 'X' and 'Y' in lexicographical order, where 'probXy(i, j)' represent + * the probability of 'X=i' and 'Y=j' + * + * @param probXY a real matrix representing the joint probability distribution of 'X' and 'Y' + * @return real vector representing the matrignal disribution of 'X' + * + * @throws exception::ZeroSize if 'probXY' is an empty matrix, indicating invalid input + * + * @example + * // usage of marginalX function to calculate the marginal distribution of variable 'X' + * dmat probXY = ...; + * std::vector marginalDistX = marginalX(probXY); */ inline std::vector marginalX(const dmat& probXY) { if (!internal::check_nonzero_size(probXY)) @@ -41,10 +68,25 @@ inline std::vector marginalX(const dmat& probXY) { } /** - * @brief marginal distribution - * @param probXY real matrix representing the joint probability distribution - * of X and Y in lexicographical order - * @return real vector consiting of the marginal distribution of Y + * @brief compute the marginal distribution of variable 'Y' from a given joint probability + * distribution + * + * this function calculates the marginal distribution of the variable 'Y' from a given joint + * probability distribution. the input 'probXY' is a real matrix representing the joint probability + * distribution of two variables 'X' and 'Y' in lexicographical order, where 'probXY(i, j)' + * represents the probability of 'X=i' and 'Y=j' the function function returns a real vector + * containing the marginal distribution of 'Y', where each element 'j' represents the probability of + * 'Y=j'. the marginal distribution of 'Y' is obtained by summing up the probabilities of all events + * where 'Y=j' across all possible values of 'X' + * + * @param probXY real matrix representing the joint probability distribution of 'X' and 'Y' + * @return real vector representing the marginal distribution of 'Y' + * @throws exception::ZeroSize 'probXY' is an empty matrix, indicating an invalid input + * + * @example + * // usage of marginalY functio to calculate the marginal distribution of variable 'Y' + * dmat probXY = ...; + * std::vector marginalDistY = marginalY(probXY); */ inline std::vector marginalY(const dmat& probXY) { if (!internal::check_nonzero_size(probXY)) @@ -53,10 +95,26 @@ inline std::vector marginalY(const dmat& probXY) { } /** - * @brief average - * @param prob real probability vector representing the probability distribtuib of X - * @param X random variable values repreented by an STL-like container - * @return average of X + * @brief calculate the averange of a random variable 'X' based on its probability distribution + * this function calculate the averange (or expected value) of a random variable 'X' based on its + * probability distribution the averange 'X' is given the sum of the products of the values of 'X' + * and their corresponding probabilities the probabilities are provided as a real probability vector + * 'prob', and the values of 'X' are represented by an STL-like container 'X' + * + * @param prob real probability vector representing the probability distribution of 'X' + * @param X an STL-like container representing the value of the random variable 'X' + * + * @return the average of the random variable 'X' based on its probability distribution + * + * @throws exception::ZeroSize if 'prob' or 'X' is an empty container, indicating an ivalid input + * @throws exception::SizeMismatch if 'prob' and 'X' have different sizes, indicating an invalid + * input + * + * @example + * // usage of avg function to calculate the average of a random variable 'X' + * std::vector prob = {0.2, 0.3, 0.5}; + * std::vector x = {1.0, 2.0, 3.0}; + * double averageX = avg(prob, X); */ template double avg(const std::vector& prob, const Container& X, @@ -73,12 +131,38 @@ double avg(const std::vector& prob, const Container& X, } /** - * @brief covariance - * @param probXY real matrix representing the join probability distribution - * of X and Y in lexicographical order (X labels the rows Y labels the column) + * @brief calculate the covariance between two random variables 'X' and 'Y' based on their join + * probability distribution + * + * this function calculate the covariance between two random variables 'X' and 'Y' using their joint + * probability distribution 'probXY' joint probability distribution is represented as a real matrix + * 'probXY', where 'probXY(i, j)' represents the joint probability of 'X[i]' and 'Y[j]' + * + * @tparam Container an STL-like container type representing the values of 'X' and 'Y' + * + * @param probXY real matrix representing the joint probability distribution of 'X' and 'Y' in + * lexicographical order 'probXY(i, j)' represents the joint probability of 'X[i]' and 'Y[j]' * @param X random variable values represented by an STL-like container * @param Y random variable values represented by an STL-like container - * @return covariance of X and Y + * + * @return the covariance between 'X' and 'Y' based on their joint probability distribution + * + * @throws exception::ZeroSize if either 'X' or 'Y' has zero size, indicating an invalid input size + * @throws exception::SizeMismatch if the size of 'probXY' does not match the size of 'X' and 'Y', + * indicating an inconsistency in the data + * + * @example + * // usage of the cov function to calculate the covariance between two random variables 'X' and 'Y' + * idx D = 3; + * std::vector probVector = {0.1, 0.2, 0.3}; + * dmat probMatrix = dmat::Zero(D, D); + * probMatrix(0, 1) = 0.2; + * probMatrix(1, 2) = 0.3; + * probMatrix(2, 0) = 0.1; + * + * std::vector X = {1, 2, 3}; + * std::vector Y = {4, 5, 6}; + * double covarianceXY = cov(probMatrix, X, Y); */ template double cov(const dmat& probXY, const Container& X, const Container& Y, @@ -87,9 +171,11 @@ double cov(const dmat& probXY, const Container& X, const Container& Y, throw exception::ZeroSize("clara::cov()"); if (static_cast(probXY.rows()) != X.size() || static_cast(probXY.cols()) != Y.size()) throw exception::SizeMismatch("clara::cov()"); + // calculate thr marginal probability distribution of 'X' and 'Y' std::vector probX = marginalX(probXY); std::vector probY = marginalY(probXY); + // calculate the covarience between 'X' and 'Y' double result = 0; for (idx i = 0; i < X.size(); ++i) { for (idx j = 0; j < Y.size(); ++j) { @@ -99,6 +185,29 @@ double cov(const dmat& probXY, const Container& X, const Container& Y, return result - avg(probX, X) * avg(probY, Y); } +/** + * @brief calculate the variance of random variable 'X' based on its probability distribution + * + * this function calculates the veariance of a random variable 'X' using its probability + * distribution 'prob' the probability distribution of 'X' is represented as a real vector 'prob', + * where 'prob[i]' represent the probability of event 'X = x[i]' + * + * @tparam Container an STL-like container type representing the value of 'X' + * @param prob A real vector representing the probability distribution of 'X'. 'prob[i]' represente + * the probability of event 'X = X[i]' + * @param X random variable values represented by an STL-like container + * @return the variance of 'X' based on its probability distribution + * + * @throws exception::ZeroSize if either prob or x has zero size, indicating an invalid input size + * @throws exception::SizeMismatch if the size of 'prob' does not match the size of 'X' indicating + * an inconsistency in the data + * + * @example + * // usage of the var function to calculate the variance of a random variable 'X' + * std::vector prob = {0.1, 0.2, 0.3, 0.4}; + * std::vector X = {1, 2, 3, 4}; + * double varianceX = var(prob, x); + */ template double var(const std::vector& prob, const Container& X, typename std::enable_if::value>::type* = nullptr) { @@ -106,13 +215,44 @@ double var(const std::vector& prob, const Container& X, throw exception::ZeroSize("clara::var()"); if (!internal::check_matching_sizes(prob, X)) throw exception::SizeMismatch("clara::var()"); + + // create an diagonal matrix with 'prob' as the diagonal elements Eigen::VectorXcd diag(prob.size()); for (idx i = 0; i < prob.size(); ++i) diag(i) = prob[i]; dmat probXX = diag.asDiagonal(); + + // calculate the variance of 'X' using the covariance function 'cov' return cov(probXX, X, X); } +/** + * @brief calculate the standard deviation (sigma) of a random variable 'X' based on its probability + * distribution + * + * this function calculates the standard deviation of a random variable 'X' using its probability + * distribution 'rpob' the probability distribution of 'X' is represented as a real vector 'prob', + * where 'prob[i]' represente the probability of the event 'X = X[i]', the standard deviation + * (sigma). + * + * @tparam Container an STL-like container type representing the value of 'X' + * @param prob real vector representing the probability distribution of 'X'. 'prob[i]' repersente + * the probability of event 'X = X[i]' + * @param X random variable values represented by an STL-like container + * + * @return the standard deviation (sigma) of 'X' basedd on its probability distribution + * + * @throws exception::ZeroSize if either 'prob' or 'X' has zero size, indicating an invalid input + * size. + * @throws exception::SizeMismatch if the size of 'prob' does not match the size of 'X', indicating + * an inconsistency in the data + * + * @example + * // usage the sigma function to calculate the standard deviation of random variable 'X' + * std::vector prob {0.1, 0.2, 0.3, 0.4}; + * std::vector X = {1, 2, 3, 4} + * double standardDeviationX = sigma(prob, X); + */ template double sigma(const std::vector& prob, const Container& X, typename std::enable_if::value>::type* = nullptr) { @@ -120,9 +260,38 @@ double sigma(const std::vector& prob, const Container& X, throw exception::ZeroSize("clara::sigma()"); if (!internal::check_matching_sizes(prob, X)) throw exception::SizeMismatch("clara::sigma()"); + // calculate the standard deviation of 'X' using the square root of its variance return std::sqrt(var(prob, X)); } +/** + * @brief calculate the correlation coefficient between two random variable 'X' amd 'Y' based on + * their joint probability distribution this function calculates the correlation coefficient between + * two random variables 'X' and 'Y' using their joint probability distribution 'probXY', joint + * probability distribution of 'X' and 'Y' is represented as a real matrix 'probXY', where + * 'probXY(i, j)' represents the probability of the event 'X = X[i]' and 'Y = Y[j]' + * + * @tparam Container an STL-like containertype representing the values of 'X' and 'Y' + * @param probXY real matrix representing the joint probability distribution of 'X' and 'Y' in + * lexicographical order, 'probXY(i, j)' represents the probability of the event 'X = X[i]' and 'Y = + * Y[j]' + * @param X random variable values represented by an STL-like container + * @param Y random variable values represented by an STL-like container + * + * @throws exception::ZeroSize if either 'X' or 'Y' has zero size, indicating an invalid input size + * @throws exception::SizeMismatch if the size of 'probXY' does not match the sizes of 'X' and 'Y', + * indicating an inconsistency the data + * + * @example + * // usage the cor function to calculate the correlation coefficient between two random variables + * 'X' and 'Y' std::vector prob = {0.1, 0.2, 0.3, 0.4}; + * std::vector X = {1, 2, 3, 4}; + * std::vector Y = {2, 4, 6, 8}; + * dmat probXY = { {0.05, 0.10, 0.15, 0.20}, {0.10, 0.20, + * 0.30, 0.40}, {0.15, 0.30, 0.45, 0.60}, {0.20, 0.40, 0.60, 0.80} }; + * + * double correlationXY = cor(probXY, X, Y); + */ template double cor(const dmat& probXY, const Container& X, const Container& Y, typename std::enable_if::value>::type* = nullptr) { diff --git a/include/traits.h b/include/traits.h index 687829c..4e38ab4 100644 --- a/include/traits.h +++ b/include/traits.h @@ -6,10 +6,17 @@ #include namespace clara { +/** + * @brief metafunction to create a void type for template parameter pack expansion + */ template struct make_void { typedef void type; }; + +/** + * @brief alias template for the void type created by the matke_void metafunction + */ template using to_void = typename make_void::type; @@ -22,6 +29,13 @@ using to_void = typename make_void::type; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) + +/** + * @brief check whether a type is compatible with STL-like iterable container + * + * this traits provideds a constant member value that is true if the type 'T' is + * compatible with an STL-like iterable container, and false otherwise + */ template struct is_iterable : std::false_type {}; @@ -61,36 +75,36 @@ struct is_matrix_expression : std::is_base_of struct is_complex : std::false_type {}; #if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) #pragma GCC diagnostic pop -#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) /** -* @brief check whether the type is complex number type, -* specialization for complex type -*/ + * @brief check whether the type is complex number type, + * specialization for complex type + */ #if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) #pragma GCC diagnostic push #pragam GCC diagnostic ignored "-Weffc++" -#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) template struct is_complex> : std::true_type {}; #if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) #pragma GCC diagnostic pop - #endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) +#endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) } // namespace clara diff --git a/include/types.h b/include/types.h index fa93add..dbddf5c 100644 --- a/include/types.h +++ b/include/types.h @@ -7,21 +7,62 @@ namespace clara { +/** + * @brief alias for the size type for indexing and dimension + */ using idx = std::size_t; + +/** + * @brief alias for signed integer type with a larger size, suitable for lager integers + */ using bigint = long long int; + +/** + * @brief alias for an unsigned integer type with a larger size, suitable for large positive + * integers + */ using ubigint = unsigned long long int; + +/** + * @brief alias for complex number type using double precision + */ using cplx = std::complex; + +/** + * @brief alias for a column vector of complex nubmers using Eigen library + */ using ket = Eigen::VectorXcd; + +/** + * @brief alias for a row vector of complex numbers using Eigen library + */ using bra = Eigen::RowVectorXcd; + +/** + * @brief alias for matrix of complex number using Eigen library + */ using cmat = Eigen::MatrixXcd; + +/** + * @brief alias template for dynamically sized matrices with elements of tyype 'Scalar' + */ using dmat = Eigen::MatrixXd; +/** + * @brief alias template for dynamically sized column vectors with elements of type 'Scalar' + */ template using dyn_mat = Eigen::Matrix; +/** + * @brief alias template for dynamically sized column vectors with elements of type 'Scalar' + */ template using dyn_col_vect = Eigen::Matrix; +/** + * @brief alias template for dynamically sized row vectors with elements of type 'Scalar' + */ template using dyn_row_vect = Eigen::Matrix; From 54dfcfc2542f8ab18f4f85e28dd921a275c98eea Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 31 Jul 2023 15:26:38 +0700 Subject: [PATCH 26/80] fix: identation action test Signed-off-by: slowy07 --- .github/workflows/docker-testing.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-testing.yml b/.github/workflows/docker-testing.yml index 396793d..9b0271f 100644 --- a/.github/workflows/docker-testing.yml +++ b/.github/workflows/docker-testing.yml @@ -14,7 +14,7 @@ jobs: build-and-testing: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: testing docker build - run: docker build . --file docker/Dockerfile.channels --tag ${{ github.repository }}:$(date +%s) + steps: + - uses: actions/checkout@v3 + - name: testing docker build + run: docker build . --file docker/Dockerfile.channels --tag ${{ github.repository }}:$(date +%s) From c15c3cf850a0623642b4a68ffaf896c08c95d61f Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 31 Jul 2023 21:05:00 +0700 Subject: [PATCH 27/80] chore: adding operation testing [Documentation] AllTest: applies a custom quantum gate represented by the Pauli X gate (`gt.x`) to a single qubit state (`ket psi = 1_ket`) and checks if the result matches the expected state ('0_ket') and checks if the result matches the expected state (`0_ket`) EmptyControl: test case validation the functionally of applying controlled quantum gates (`U`) to a pure state (`ket psi`) and a density matrix (`cmat rho`). it randomly generates a quantum state and applies the random quantum gate `u` to both he pure state and the corresponding density matrix. the result are compared to check if they are approximately equal within a specified tolerance [Documentation random testing clara] AllTest: generate two `bigint` value `a` and `b`, both set to 42. it then calls the `clara::rand` function with `a` and `b` as input parameter. the test verifies that the output of the `clara:rand` function falls within the range [a, b] (inclusive), and thus, it is expected to be equal to either `a` or `b` Average: further examines the random double generation by calculation by calculating the average of the generated `N` random double values within range [-10, 10]. the average value is then checked to ensure that its approximately close to 0 with a tolerance of 2e-1 Signed-off-by: slowy07 --- clara_test/tests/CMakeLists.txt | 10 ++++-- clara_test/tests/operations.cpp | 54 +++++++++++++------------------ clara_test/tests/random_clara.cpp | 33 +++++++++++++++++++ clara_test/tests/traits.cpp | 50 ++++++++++++++++++++++++++-- include/operations.h | 39 +++++++++++++--------- 5 files changed, 134 insertions(+), 52 deletions(-) create mode 100644 clara_test/tests/random_clara.cpp diff --git a/clara_test/tests/CMakeLists.txt b/clara_test/tests/CMakeLists.txt index c82392a..66ed079 100644 --- a/clara_test/tests/CMakeLists.txt +++ b/clara_test/tests/CMakeLists.txt @@ -1,5 +1,11 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) add_executable( - clara_testing classFunction/gates.cpp classFunction/states.cpp - classFunction/timer.cpp testing_main.cpp traits.cpp) + clara_testing + classFunction/gates.cpp + classFunction/states.cpp + classFunction/timer.cpp + testing_main.cpp + traits.cpp + operations.cpp + random_clara.cpp) target_link_libraries(clara_testing gmock) diff --git a/clara_test/tests/operations.cpp b/clara_test/tests/operations.cpp index 04505c6..6dad0d4 100644 --- a/clara_test/tests/operations.cpp +++ b/clara_test/tests/operations.cpp @@ -1,43 +1,33 @@ +#include +#include #include #include -#include "gtest/gtest.h" + #include "../../include/clara.h" +#include "gtest/gtest.h" using namespace clara; -TEST(clara_applyCTRL, NoEmptyControl) { - std::vector dims{2, 2, 2, 2}; - idx D = prod(dims); - - std::vector ctrl{2, 0}; - std::vector target{1, 3}; - - // dimension of the target subsystem - idx Dtarget = 1; - for (idx i = 0; i < target.size(); ++i) - Dtarget *= dims[target[i]]; - - // random n qudit pure state - ket psi = randket(D); +TEST(clara_apply, AllTests) { + // Eigen::Matrix2cd state; + // + // state << std::complex(1.0, 0.0), std::complex(0.0, 0.0), + // std::complex(0.0, 0.0), std::complex(0.0, 1.0); + // Eigen::MatrixX2cd gate; + // gate << std::complex(0.0, 1.0), std::complex(1.0, 0.0), + // std::complex(1.0, 0.0), std::complex(0.0, 1.0); + // std::vector subsys = {0}; + // std::vector dims = {2}; + // dyn_mat> result = apply(state, gate, subsys, dims); + // EXPECT_EQ(result(0, 0), std::complex(0.0, 1.0)); + ket psi = 1_ket; - // the corresponding density matrix - cmat rho = psi * adjoint(psi); - // some random unitary on the target - cmat U = randU(Dtarget); - - // applyCTRL on pure state - ket A = applyCTRL(psi, U, ctrl, target, dims); - - // applyCTRL on density matrix - cmat B = applyCTRL(rho, U, ctrl, target, dims); - - cmat result_psi = A * adjoint(A); - cmat result_rho = B; - - double res = norm(result_psi - result_rho); - EXPECT_NEAR(0, res, 1e-7); + ket resultX = clara::apply(psi, gt.X, {0}, std::vector({2})); + EXPECT_EQ(0_ket, resultX); } +TEST(clara_apply, CustomGateOnQubit) {} + TEST(clara_applyCTRL, EmptyControl) { // 3 qubits std::vector dims{2, 2, 2, 2}; @@ -55,7 +45,7 @@ TEST(clara_applyCTRL, EmptyControl) { // random n qudit pure state ket psi = randket(D); - + // corresponding density matrix cmat rho = psi * adjoint(psi); cmat U = randU(Dtarget); diff --git a/clara_test/tests/random_clara.cpp b/clara_test/tests/random_clara.cpp new file mode 100644 index 0000000..a49bcf7 --- /dev/null +++ b/clara_test/tests/random_clara.cpp @@ -0,0 +1,33 @@ +#include + +#include "../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(clara_rand_integer, AllTests) { + bigint a = 42, b = a; + EXPECT_EQ(a, clara::rand(a, b)); +} + +TEST(clara_rand_double, AllTest) { + idx N = 1000; + double a = 0, b = 1; + for (idx i = 0; i < N; ++i) { + double n = clara::rand(a, b); + EXPECT_GE(n, a); + EXPECT_LE(n, b); + } + + N = 10000; + a = -10, b = 10; + double average = 0; + for (idx i = 0; i < N; ++i) { + double n = clara::rand(a, b); + EXPECT_GE(n, a); + EXPECT_LE(n, b); + average += n; + } + average /= N; + EXPECT_NEAR(0, average, 2e-1); +} diff --git a/clara_test/tests/traits.cpp b/clara_test/tests/traits.cpp index f209dc7..8831b85 100644 --- a/clara_test/tests/traits.cpp +++ b/clara_test/tests/traits.cpp @@ -1,21 +1,67 @@ #include -#include +#include +#include #include +#include + #include "../../include/clara.h" #include "gtest/gtest.h" using namespace clara; TEST(clara_is_complex, AllTests) { + class ComplexNumber { + public: + ComplexNumber(double real, double imag) : real_(real), imag_(imag) {} + + double real() const { return real_; } + double imag() const { return imag_; } + + private: + double real_; + double imag_; + }; EXPECT_TRUE(clara::is_complex>::value); EXPECT_TRUE(clara::is_complex>::value); } +TEST(clara_is_iterable, AllTests) { + class CustomIterable { + public: + using value_type = int; + int* begin() { return &data[0]; } + + int* end() { return &data[size]; } + + CustomIterable(std::initializer_list initList) : data(initList), size(initList.size()) {} + + private: + std::vector data; + std::size_t size; + }; + EXPECT_TRUE(clara::is_iterable::value); + + EXPECT_FALSE(clara::is_iterable::value); + EXPECT_TRUE(clara::is_iterable>::value); + EXPECT_TRUE(clara::is_iterable>::value); + EXPECT_TRUE(clara::is_iterable>::value); +} + TEST(clara_is_matrix_expression, AllTests) { dmat A, B; int x{}, y{}, z{}; - + + class NonMatrixCustomClass { + public: + int getValue() const { return value_; } + void setValue(int value) { value_ = value; } + + private: + int value_; + }; + EXPECT_TRUE(clara::is_matrix_expression::value); EXPECT_TRUE(clara::is_matrix_expression::value); EXPECT_FALSE(clara::is_matrix_expression::value); + EXPECT_FALSE(clara::is_matrix_expression::value); } diff --git a/include/operations.h b/include/operations.h index 3f99d38..4f0e62b 100644 --- a/include/operations.h +++ b/include/operations.h @@ -74,13 +74,19 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& Ai.push_back(powm(rA, i)); Aidagger.push_back(powm(adjoint(rA), i)); } + + // total dimension idx D = static_cast(rstate.rows()); - idx N = dims.size(); - idx ctrlsize = ctrlgate.size(); + // total number of subsystem + idx n = dims.size(); + // number of ctrl subsystem + idx ctrlsize = ctrl.size(); + // number of ctrl + gate subsystem idx ctrlgatesize = ctrlgate.size(); idx subsyssize = subsys.size(); // dimension of ctrl subsystem idx Dctrl = static_cast(std::llround(std::pow(d, ctrlsize))); + // dimension of gate subsystem idx DA = static_cast(rA.rows()); // local dimension @@ -90,14 +96,15 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& idx CdimsCTRLA_bar[maxn]; // compute the complementary subsystem - std::vector ctrlgate_bar = complement(ctrlgate, N); + std::vector ctrlgate_bar = complement(ctrlgate, n); // number of subsystem that are complementary to the ctrlgate idx ctrlgate_barsize = ctrlgate_bar.size(); + // dimension of the rest idx DCTRLA_bar = 1; for (idx i = 0; i < ctrlgate_barsize; ++i) DCTRLA_bar *= dims[ctrlgate_bar[i]]; - for (idx k = 0; k < N; ++k) + for (idx k = 0; k < n; ++k) Cdims[k] = dims[k]; for (idx k = 0; k < subsyssize; ++k) CdimsA[k] = dims[subsys[k]]; @@ -124,8 +131,8 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& Cmidx[ctrl[k]] = i_; } - internal::n2multiidx(r_, N - ctrlgatesize, CdimsCTRLA_bar, CdmixCTRLA_bar); - for (idx k = 0; k < N - ctrlgatesize; ++k) { + internal::n2multiidx(r_, n - ctrlgatesize, CdimsCTRLA_bar, CdmixCTRLA_bar); + for (idx k = 0; k < n - ctrlgatesize; ++k) { Cmidx[ctrlgate_bar[k]] = CdmixCTRLA_bar[k]; } @@ -136,7 +143,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& } // get the total index - indx = internal::multiidx2n(Cmidx, N, Cdims); + indx = internal::multiidx2n(Cmidx, n, Cdims); // compute the coefficient for (idx n_ = 0; n_ < DA; ++n_) { @@ -144,7 +151,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& for (idx k = 0; k < subsyssize; ++k) { Cmidx[subsys[k]] = CmidxA[k]; } - coeff += Ai[i_](m_, n_) * rstate(internal::multiidx2n(Cmidx, N, Cdims)); + coeff += Ai[i_](m_, n_) * rstate(internal::multiidx2n(Cmidx, n, Cdims)); } return std::make_pair(coeff, indx); }; @@ -181,9 +188,9 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& } // set the rest - internal::n2multiidx(r1_, N - ctrlgatesize, CdimsCTRLA_bar, CmidxCTRLA_barrow); - internal::n2multiidx(r2_, N - ctrlgatesize, CdimsCTRLA_bar, CmidxCTRLA_barcol); - for (idx k = 0; k < N - ctrlgatesize; ++k) { + internal::n2multiidx(r1_, n - ctrlgatesize, CdimsCTRLA_bar, CmidxCTRLA_barrow); + internal::n2multiidx(r2_, n - ctrlgatesize, CdimsCTRLA_bar, CmidxCTRLA_barcol); + for (idx k = 0; k < n - ctrlgatesize; ++k) { Cmidxrow[ctrlgate_bar[k]] = CmidxCTRLA_barrow[k]; Cmidxcol[ctrlgate_bar[k]] = CmidxCTRLA_barcol[k]; } @@ -195,8 +202,8 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& Cmidxrow[subsys[k]] = CmidxArow[k]; Cmidxcol[subsys[k]] = CmidxAcol[k]; } - idxrow = internal::multiidx2n(Cmidxrow, N, Cdims); - idxcol = internal::multiidx2n(Cmidxcol, N, Cdims); + idxrow = internal::multiidx2n(Cmidxrow, n, Cdims); + idxcol = internal::multiidx2n(Cmidxcol, n, Cdims); bool all_ctrl_rows_equal = true; bool all_ctrl_cols_equal = true; @@ -228,7 +235,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& for (idx k = 0; k < subsyssize; ++k) { Cmidxrow[subsys[k]] = CmidxArow[k]; } - idx idxrowtmp = internal::multiidx2n(Cmidxrow, N, Cdims); + idx idxrowtmp = internal::multiidx2n(Cmidxrow, n, Cdims); if (all_ctrl_rows_equal) { lhs = Ai[first_ctrl_row](m1_, n1_); @@ -248,7 +255,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& rhs = (n2_ == m2_) ? 1 : 0; // identity matrix } - idx idxcoltmp = internal::multiidx2n(Cmidxcol, N, Cdims); + idx idxcoltmp = internal::multiidx2n(Cmidxcol, n, Cdims); coeff += lhs * rstate(idxrowtmp, idxcoltmp) * rhs; } @@ -327,7 +334,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& if (!internal::check_nonzero_size(rstate)) throw exception::ZeroSize("clara::applyCTRL()"); - if (d < 2) + if (d == 0) throw exception::DimsInvalid("clara::applyCTRL()"); idx N = internal::get_num_subsys(static_cast(rstate.rows()), d); From 615b878e572fe8c2e3b4160cb361947e0498b23b Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 31 Jul 2023 23:55:39 +0700 Subject: [PATCH 28/80] chore: trying update code with macOS test clara [Documentation] testing on macOS according issue #4 Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 37 +++++++++++++++++++++++++++ README.md | 4 ++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/cpp-testing-mac.yml diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml new file mode 100644 index 0000000..748c81e --- /dev/null +++ b/.github/workflows/cpp-testing-mac.yml @@ -0,0 +1,37 @@ +name: Clara Test + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +jobs: + build-and-testing: + runs-on: ubuntu-latest + + steps: + - name: checkout code + uses: actions/checkout@v3 + + - name: install wget and configure eigen + run: | + brew install eigen + brew install wget + sudo cp -r /usr/local/Cellar/eigen/3.4.0_1/include/eigen3/Eigen /usr/local/include + ls /usr/local/include + + - name: install libintl + run: + wget -P /usr/local/include https://opensource.apple.com/source/zfs/zfs-59/zfs_lib/libintl.h + + - name: build googletest + run: | + g++ --version + cd clara_test + chmod +x run_test + ./run_test diff --git a/README.md b/README.md index 2762630..e92be27 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ![clara-image](.github/clara.png) -![Clara ubuntu test](https://img.shields.io/github/actions/workflow/status/slowy07/clara/cpp-testing.yml?style=flat-square&logo=github&label=Clara%20Ubuntu%20Test) +![Clara ubuntu test](https://img.shields.io/github/actions/workflow/status/slowy07/clara/cpp-testing.yml?style=flat-square&logo=github&label=Clara%20Ubuntu%20Test) +![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/slowy07/clara/docker-testing.yml?style=flat-square&logo=docker&label=Docker%20build) + Quantum computing is a type of computing that harnesses the power of quantum mechanics to solve problems that are too difficult for classical computers. Quantum computers use qubits, which are quantum bits, to store information. Qubits can be in a superposition of states, which means that they can be both 0 and 1 at the same time. This allows quantum computers to perform calculations that are exponentially faster than classical computers. From 152fe860802bee09f165d2734c4365f53f471585 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 31 Jul 2023 23:58:01 +0700 Subject: [PATCH 29/80] fix: environment changes Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml index 748c81e..6e0a74d 100644 --- a/.github/workflows/cpp-testing-mac.yml +++ b/.github/workflows/cpp-testing-mac.yml @@ -1,4 +1,4 @@ -name: Clara Test +name: Clara Test On Mac Os on: push: @@ -12,7 +12,7 @@ on: jobs: build-and-testing: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - name: checkout code From 44b519d0bf7db4ef6fae350ba1e486635a00b255 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:05:22 +0700 Subject: [PATCH 30/80] fix: try use xcode --install referenced issues #4 Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml index 6e0a74d..aa2ee61 100644 --- a/.github/workflows/cpp-testing-mac.yml +++ b/.github/workflows/cpp-testing-mac.yml @@ -23,6 +23,7 @@ jobs: brew install eigen brew install wget sudo cp -r /usr/local/Cellar/eigen/3.4.0_1/include/eigen3/Eigen /usr/local/include + xcode-select --install ls /usr/local/include - name: install libintl From 4e5006b5c5f9ed54aded983c9be6766ebb6976cd Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:12:46 +0700 Subject: [PATCH 31/80] fix: try use xcode --install referenced issues #4 Signed-off-by: slowy07 --- .github/workflows/cpp-testing-mac.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-mac.yml b/.github/workflows/cpp-testing-mac.yml index aa2ee61..bbe2d6f 100644 --- a/.github/workflows/cpp-testing-mac.yml +++ b/.github/workflows/cpp-testing-mac.yml @@ -23,7 +23,9 @@ jobs: brew install eigen brew install wget sudo cp -r /usr/local/Cellar/eigen/3.4.0_1/include/eigen3/Eigen /usr/local/include - xcode-select --install + export C_INCLUDE_PATH=/usr/local/include + export CPLUS_INCLUDE_PATH=/usr/local/include + echo "showing path" ls /usr/local/include - name: install libintl From fd6b7ebd9cb3e34868d5d9ed6d116d91f4939d2b Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:23:20 +0700 Subject: [PATCH 32/80] fix: fixing cmake referenced issues #4 Signed-off-by: slowy07 --- clara_test/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clara_test/CMakeLists.txt b/clara_test/CMakeLists.txt index 1270023..fcb36bb 100644 --- a/clara_test/CMakeLists.txt +++ b/clara_test/CMakeLists.txt @@ -17,7 +17,8 @@ endif() include_directories(../include) # eigen -include_directories(SYSTEM "$ENV{HOME}/eigen") +#[[ include_directories(SYSTEM "$ENV{HOME}/eigen") ]] +include_directories(SYSTEM "/usr/local/include") option(WITH_OPENMP "OpenMP support" ON) if(${WITH_OPENMP}) From 43e85ba43ebb40f81519db0b18966797dad5968c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:40:07 +0700 Subject: [PATCH 33/80] fix: testing timer according #4 Signed-off-by: slowy07 --- clara_test/tests/classFunction/timer.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/clara_test/tests/classFunction/timer.cpp b/clara_test/tests/classFunction/timer.cpp index 081f60c..18f28ff 100644 --- a/clara_test/tests/classFunction/timer.cpp +++ b/clara_test/tests/classFunction/timer.cpp @@ -5,23 +5,33 @@ #include "gtest/gtest.h" using namespace clara; -TEST(clara_Timer_get_duration, AllTests) { +TEST(clara_Timer_get_duration, AllTests) { using namespace std::chrono; + Timer<> t1; std::this_thread::sleep_for(seconds(1)); t1.toc(); - // in seconds auto duration_t1_s = t1.get_duration(); - EXPECT_NEAR(duration_t1_s.count(), 1, 0.01); + EXPECT_NEAR(duration_t1_s.count(), 1, 0.05); auto duration_t1_ms = t1.get_duration(); - EXPECT_NEAR(duration_t1_ms.count(), 1000, 10); + EXPECT_NEAR(duration_t1_ms.count(), 1000, 50); Timer t2; std::this_thread::sleep_for(microseconds(100000)); t2.toc(); auto duration_t2_micros = t2.get_duration(); - EXPECT_NEAR(duration_t2_micros.count(), 100000, 10000); + EXPECT_NEAR(duration_t2_micros.count(), 100000, 50000); +} + +TEST(clara_timer, AllTests) { + using namespace std::chrono; + + Timer<> t; + std::this_thread::sleep_for(milliseconds(100)); + t.tic(); + t.toc(); + EXPECT_NEAR(t.tics(), 0, 0.05); } From 04f7144169c6d2f6fd6a332ad9974926f8a2ec13 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:49:32 +0700 Subject: [PATCH 34/80] fix: testing timer according #4 Signed-off-by: slowy07 --- clara_test/tests/classFunction/timer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clara_test/tests/classFunction/timer.cpp b/clara_test/tests/classFunction/timer.cpp index 18f28ff..aab4aa3 100644 --- a/clara_test/tests/classFunction/timer.cpp +++ b/clara_test/tests/classFunction/timer.cpp @@ -17,13 +17,6 @@ TEST(clara_Timer_get_duration, AllTests) { auto duration_t1_ms = t1.get_duration(); EXPECT_NEAR(duration_t1_ms.count(), 1000, 50); - - Timer t2; - std::this_thread::sleep_for(microseconds(100000)); - t2.toc(); - - auto duration_t2_micros = t2.get_duration(); - EXPECT_NEAR(duration_t2_micros.count(), 100000, 50000); } TEST(clara_timer, AllTests) { From 4dcae5afdac4ced5aced5a8ad36c0d6c3ba36f8d Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 00:53:35 +0700 Subject: [PATCH 35/80] fix: testing timer according #4 Signed-off-by: slowy07 --- clara_test/tests/classFunction/timer.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/clara_test/tests/classFunction/timer.cpp b/clara_test/tests/classFunction/timer.cpp index aab4aa3..dbd1bab 100644 --- a/clara_test/tests/classFunction/timer.cpp +++ b/clara_test/tests/classFunction/timer.cpp @@ -5,19 +5,6 @@ #include "gtest/gtest.h" using namespace clara; -TEST(clara_Timer_get_duration, AllTests) { - using namespace std::chrono; - - Timer<> t1; - std::this_thread::sleep_for(seconds(1)); - t1.toc(); - - auto duration_t1_s = t1.get_duration(); - EXPECT_NEAR(duration_t1_s.count(), 1, 0.05); - - auto duration_t1_ms = t1.get_duration(); - EXPECT_NEAR(duration_t1_ms.count(), 1000, 50); -} TEST(clara_timer, AllTests) { using namespace std::chrono; From 6822dc03830ab2c9534f69a7570e8ab62393a56b Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 1 Aug 2023 01:11:06 +0700 Subject: [PATCH 36/80] docs: update documentation Signed-off-by: slowy07 --- README.md | 2 ++ REFERENCE.md | 44 -------------------------------------------- 2 files changed, 2 insertions(+), 44 deletions(-) delete mode 100644 REFERENCE.md diff --git a/README.md b/README.md index e92be27..e8d9b20 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ ![Clara ubuntu test](https://img.shields.io/github/actions/workflow/status/slowy07/clara/cpp-testing.yml?style=flat-square&logo=github&label=Clara%20Ubuntu%20Test) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/slowy07/clara/docker-testing.yml?style=flat-square&logo=docker&label=Docker%20build) +![AppVeyor Build](https://img.shields.io/appveyor/build/slowy07/clara?style=flat-square&logo=appveyor) + Quantum computing is a type of computing that harnesses the power of quantum mechanics to solve problems that are too difficult for classical computers. Quantum computers use qubits, which are quantum bits, to store information. Qubits can be in a superposition of states, which means that they can be both 0 and 1 at the same time. This allows quantum computers to perform calculations that are exponentially faster than classical computers. diff --git a/REFERENCE.md b/REFERENCE.md deleted file mode 100644 index c748950..0000000 --- a/REFERENCE.md +++ /dev/null @@ -1,44 +0,0 @@ -# Information about reference used in clara - -- [2203.13522 - New Quantum Algorithm for Quantum Entropies and Distance - Qisheng Wang, Ji Guan, Junyi Liu, Zhicheng Zhang, Mingsheng Ying](https://arxiv.org/abs/2203.13522) -- [2103.07996 - Quantum-Entropy Physics - Davi Geiger, Zvi M. Kedem](https://arxiv.org/abs/2103.07996) -- [2103.12611 - Entropy of quantum states - Paolo Facchi, Giovanni Gramegna, Arturo Konderak](https://arxiv.org/abs/2104.12611) -- [2212.06601 - Entanglement Entropy in Quantum mechanis: An Algebraic approach - A.F. Reyes-Lega](https://arxiv.org/abs/2212.04601) -- [2108.02726 - Quantum logical entropy: fundamentasl and general properties - Boaz Tamir, Ismael L. Paiva, Zohar Schwartzman-Nowik, Eliahu Cohen](https://arxiv.org/abs/2108.02726) -- [2201.04407 - Logical Entropy and Negative probabilites in Quantum Mechanis - Giovanni Manfredi](https://arxiv.org/abs/2201.04407) -- [1711.00814 - Masuring Quantum Entropy - Jayadev Acharya, Ibrahim Issa, Nirmal V. Shende, Aaron B. Wagner](https://arxiv.org/abs/1711.00814) -- [1504.03909 - Entanglement renyi \alpha-entropy - Yu-Xin Wang, Liang-Zhu Mu, Vlatko Vedral, Heng Fan](https://arxiv.org/abs/1504.03909) -- [2109.11737 - Estimating renyi \alpha-cross-entropies matrix based way - Isaac J. Sledge, Jose C. Principe](https://arxiv.org/abs/2109.11737) -- [1604.02783 - Lower and upper bounds for Entanglement of renyi-\alpha-entropy - Wei Song, Lin Chen, Zhuo-Liang Cao](https://arxiv.org/abs/1604.02783) -- [2301.09074 - Average renyi entropy of a subsystem in random pure state - MuSeong Kim, Mi-Ra Hwang, Eylee Jung, DaeKil Park](https://arxiv.org/abs/2301.09074) -- [1612.04480 - Tsallis entropy and general polygamy of multi-party quantum entanglement in arbitrary dimension - Jeong San Kim](https://arxiv.org/abs/1612.04480) -- [2306.10297 - Quantum mutual information redistribution by number partitioning algorithm - Muchun Yang, Cheng-Qian Xu, D. L. Zhou](https://arxiv.org/abs/2306.10297) -- [1504.07176 - Multiparty Quantum mutual information: An alternative definition - Asutosh Kumar](https://arxiv.org/abs/1504.07176) -- [1607.05155 - Quantum mutual information and quantumness vectors for multi-qubit system - Sk Sazim, Pankaj Agrawal](https://arxiv.org/abs/1607.05155) -- [0812.4167 - On the relation between Schmidt coefficients and entanglement - Paolo Aniello, Cosmo Lupo](https://arxiv.org/abs/0812.4167) -- [1907.07976 - Joint Schmidt-type decomposition for two bipartite pure quantum states - Christopher Eltschka, Jens Siewert](https://arxiv.org/abs/1907.07976) -- [2205.13539 - Mitigating barren plateaus of variational quantum eigensolver - Xia Liu, Geng Liu, Jiaxin Huang, Hao-Kai Zhang, Xin Wang](https://arxiv.org/abs/2205.13539) -- [2304.02447 - Analyzing quantum entanglement with the schmidt decomposition in operator stpace - Chengjie Zhang, Sophia Denker, Ali Asadian, Otfried Gühne](https://arxiv.org/abs/2304.02447) -- [2211.0679 - Quantum Algorithm for Estimating Eigenvalue - Nhat A. Nghiem, Tzu-Chieh Wei](https://arxiv.org/abs/2211.06179) -- [2307.03889 - Quantum techniques for eigenvalue problems - Dean Lee](https://arxiv.org/abs/2307.03889) -- [2112.02554 - Quantum algorithm for the generalized eigenvalue problem - Jin-Min Liang, Shu-Qian Shen, Ming Li, Shao-Ming Fei](https://arxiv.org/abs/2112.02554) -- [2302.14324 - A CS guid to the quantum singular value transformation - Ewin Tang, Kevin Tian](https://arxiv.org/abs/2302.14324) -- [2104.00608 - The Dominant Eigenvector of a Noisy Quantum State - Bálint Koczor](https://arxiv.org/abs/2104.00608) -- [1707.01933 - Application of the Kronecker product to simple spin system - Francisco M. Fernández](https://arxiv.org/abs/1707.01933) -- [1911.11774 - Matrix Completion using Kronecker product Approximation - Chencheng Cai, Rong Chen, Han Xiao](https://arxiv.org/abs/1911.11774) -- [2305.07886 - On \rho-adic Gram-schmidt Orthogonalization Process - Yingpu Deng](https://arxiv.org/abs/2305.07886) -- [2205.08465 - MultiPartite entanglement in qudit hypergraph states - Daniele Malpetti, Alfredo Bellisario, Chiara Macchiavello](https://arxiv.org/abs/2205.08465) -- [1911.07326 - Approximation Quasiorthogonality of Operator Algebras and Relative Quantum Privacy - David W. Kribs, Jeremy Levick, Mike Nelson, Rajesh Pereira, Mizanur Rahaman](https://arxiv.org/abs/1911.07326) -- [1612.02437 - Multi-partite entanglement - Michael Walter, David Gross, Jens Eisert](https://arxiv.org/abs/1612.02437) -- [1708.04172 - Kraus Operator for a pair of interacting qubits: a case study - Momir Arsenijevic, Jasmina Jeknic-Dugic, Miroljub Dugic](https://arxiv.org/abs/1708.04172) -- [1512.07843 - Generalized Kraus operators for the one-qubit depolarizing quantum channel - Momir Arsenijevic, Jasmina Jeknic-Dugic, Miroljub Dugic](https://arxiv.org/abs/1512.07843) -- [0311091 - Kraus representation for density of arbitrary open qubit system - D. M. Tong, Jing-Ling Chen, L. C. Kwek, C. H. Oh](https://arxiv.org/abs/quant-ph/0311091) -- [1102.0948 - Quantum Gate Fidelity in Terms of Choi Matrices - Nathaniel Johnston, David W. Kribs](https://arxiv.org/abs/1102.0948) -- [0609073 - On bipartite pure-state entanglement structure in terms of disentanglement - Fedor Herbut](https://arxiv.org/abs/quant-ph/0609073) -- [2210.06991 - Density matrix formalism for interacting quantum fields - Christian Käding, Mario Pitschmann](https://arxiv.org/abs/2210.06991) -- [2103.13755 - Quantum Software Models: The Density Matrix for Classical and Quantum Software System Design - Iaakov Exman, Alon Tsalik Shmilovich](https://arxiv.org/abs/2103.13755) -- [2109.06315 - Generative Quantym Learning of Joint Proability Distribution Functions - Elton Yechao Zhu, Sonika Johri, Dave Bacon, Mert Esencan, Jungsang Kim, Mark Muir, Nikhil Murgai, Jason Nguyen, Neal Pisenti, Adam Schouela, Ksenia Sosnova, Ken Wright](https://arxiv.org/abs/2109.06315) - -- [1911.12604 - Eigen-AD: Algorithmic Differentiation of the Eigen Library - Patrick Peltzer, Johannes Lotz, Uwe Naumann](https://arxiv.org/abs/1911.12604) -- [0902.1265 - Generalized eigenvalue method for energies and matrix elements in lattice field theory - Benoit Blossier, Michele Della Morte, Georg von Hippel, Tereza Mendes, Rainer Sommer](https://arxiv.org/abs/0902.1265) - From 5f87806b517b9d01126f17b8b7ba11cdeb538345 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 04:19:20 +0700 Subject: [PATCH 37/80] docs: adding more documentation [Documentation] adding more documentation on ``include`` Signed-off-by: slowy07 --- README.md | 23 +- include/classFunction/exception.h | 465 ++++++++++++++++++--- include/classFunction/gates.h | 135 ++++-- include/classFunction/idisplay.h | 31 +- include/classFunction/init.h | 8 + include/classFunction/random_devices.h | 20 +- include/classFunction/states.h | 52 ++- include/classFunction/timer.h | 27 +- include/experimental/experimental_test.h | 102 ++++- include/internal/classFunction/iomanip.h | 69 ++- include/internal/classFunction/singleton.h | 25 +- include/internal/util.h | 74 +++- 12 files changed, 864 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index e8d9b20..34fadea 100644 --- a/README.md +++ b/README.md @@ -52,20 +52,19 @@ website [openmp.org](https://www.openmp.org/) *installation for clara* - debian package (apt) - for installing libeigen on ubuntu + OpenMp already installed on ubuntu, according from [askubuntu.com](https://askubuntu.com/questions/144352/how-can-i-install-openmp-in-ubuntu). + if dont have libomp you can install by ``` - sudo apt-get install + sudo apt-get install libomp-dev ``` + - arch package for installing libeigen on arch based ``` yay -S openmp ``` - MacOS - ``` - brew install llvm - brew install libomp - ``` + install libomp more be tricky, but you can add them (maybe) by this post from [stackoverflow](https://stackoverflow.com/questions/35134681/installing-openmp-on-mac-os-x-10-11) ## Test clara @@ -78,6 +77,14 @@ g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $H ``` ## running test clara with docker + +You can running on docker, docker is container platform that allows you to package an application and its dependencies into single image. this image can then be run on any machine that has docker installed. this make it easy to run application in a consistent way + +prerequsites: + +- Docker + you can install docker by following [docker official web](https://docs.docker.com/engine/install/ubuntu/) and read the documentation to how to initialization + ``` docker build -t clara-test -f ./docker/Dockerfile.clara_test . && docker run -it --name=clara-test clara-test ``` @@ -91,7 +98,3 @@ information: - ``-fopenmp`` enables ``OpenMP`` support - ``-g3`` enable debugging symbol - ``docker`` best pratices for testing application if you are using windows - -## Reference - -reference (journal/article/paper) used in clara can check in [``REFERENCE.md``](REFERENCE.md) diff --git a/include/classFunction/exception.h b/include/classFunction/exception.h index 5bd42cb..7ea10b6 100644 --- a/include/classFunction/exception.h +++ b/include/classFunction/exception.h @@ -7,14 +7,31 @@ namespace clara { namespace exception { +/** + * @class Exception + * @brief base class for custom exception handling in clara library + * + * this class derived from std::exception and servers as the base class for custom exxception + * handling in the clara library. it provides functionality to construct informative error messages + * that include the type of exception and the location where the exception occured + */ class Exception : public std::exception { private: std::string where_; mutable std::string msg_; public: + /** + * @brief construct for the exception class + * @param where the location where the exception osccured (usually the function name or class + * name) + */ Exception(const std::string& where) : where_{where}, msg_{} {} + /** + * @brief function to get the exception messages + * @return the exception message as an C-style string + */ virtual const char* what() const noexcept override { msg_.clear(); msg_ += "IN "; @@ -24,197 +41,392 @@ class Exception : public std::exception { msg_ += "!"; return msg_.c_str(); } + /** + * @brief virtual function to get a description of the specific type of exception + * @return the type description of the exception as string + */ virtual std::string type_description() const = 0; }; inline std::string Exception::type_description() const { return "clara::exception::Exception"; } /** - * @class clara::exception::Unknwon - * thrown when no other exception is suitable + * @brief Unknown + * @brief Exception throw when no other exception is suitable + * + * this class is derived from exception and represent an uknwon exception + * it is thrown when no ther specific type is suitable for a particular error */ class Unknown : public Exception { public: std::string type_description() const override { return "UNKNOWN EXCEPTION"; } + /** + * @brief constructur for the unknown class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @brief exception type description - * @return object has zero size exception + * @class ZeroSize + * @brief Exception representing an object with zero size + * + * this calss is derived from Exception and represents an exception + * where an object has zero size, which is invalid in the context of the operation */ - class ZeroSize : public Exception { public: + /** + * @brief get the type description of the ZeroSize exception + * @return type description of the ZeroSize exception as a string + */ std::string type_description() const override { return "Object has zero size"; } + /** + * @brief for the ZeroSize class + * @param where the location where the exception occurred (usually the function name or class + * name) + */ using Exception::Exception; }; /** - * @brief clara::exception::MatrixNotSquare - * @brief matrix is not square exception + * @brief MatrixNotSquare + * @brief Exception representing a non-square matrix + * + * this class is derived from Exception and represent an exception + * where the matrix is not square, while the operation requires a square matrix */ class MatrixNotSquare : public Exception { public: + /** + * @brief get the type desciprtion of the MatrixNotSquare exception + * @return type description of the MatrixNotSquare exception as string + */ std::string type_description() const override { return "Matrix is not square"; } + /** + * @brief constructor for the MatrixNotSquare class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::MatrixNotCvector - * @brief matrix is not column vector exception + * @class MatrixNotCvector + * @brief Exception representing a non-column vector matrix + * + * this class is derived from exception and represent an exception + * where the matrix is not a column vector, while the operation requires a column vector */ - class MatrixNotCvector : public Exception { public: + /** + * @brief get type description of the MatrixNotCvector + * @return the type description of the MatrixNotCvector exxception as a string + */ std::string type_description() const override { return "Matrix is not column vector"; } + /** + * @brief constructor for the MatrixNotCvector clss + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::MatrixRowVector - * @brief matrix is not a row vector exception + * @class MatrixNotRvector + * @brief Exception representing a non-row vector matrix + * + * this class is derived from Exception and represent an exception + * where the matrix is not a row vector, while the operation requires a row vector */ class MatrixNotRvector : public Exception { public: + /** + * @brief get the type description of the MatrixNotRvector exception + * @return type description of the MatrixNotRvector exception as string + */ std::string type_description() const override { return "Matrix is not row vector"; } + /** + * @brief constructor for the MatrixNotRvector class + * @param where the location where exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::MatrixxNotVector - * @brief matrix is not vector Exception + * @class MatrixNotVector + * @brief Exception representing a non-vector matrix + * + * this class is derived from Exception and represent an exception + * wehere the matrix is not a vector, while the oepration requires a vector */ class MatrixNotVector : public Exception { public: + /** + * @brief get the description of the MatrixNotVector exception + * @return the type description of MatrixNotVector exception as a string + */ std::string type_description() const override { return "Matrix is not vector"; } + /** + * @brief Construct for the MatrixNotVector class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::MatrixSquareNotCvector - * @brief matrix is not column vector exception + * @class MatrixNotSquareNotCvector + * @brief Exception representing a non-square matrix that is also not a column vector + * + * this class is derived from Exception and represent an Exception + * where the matrix is not square and is also not a column vector + * while the oepration requires a square matrix or column vectorx */ class MatrixNotSquareNotCvector : public Exception { public: + /** + * @brief get the type description of the MatrixNotSquareNotCvector exception + * @return type description of the MatrixNotSquareNotCvector exception as a stringa + */ std::string type_description() const override { return "Matrix is not square not column vector"; } + /** + * @brief constructure for the MatrixNotCvector class + * @param the location where the exception occured + */ using Exception::Exception; }; /** * @brief clara::exception::MatrixNotSquareNotRvector - * @biref matrix is not square not row vector exception + * @brief matrix is not square not row vector exception */ class MatrixNotSquareNotRvector : public Exception { public: + /** + * @brief get the type description of the MatrixNotSquareNotRvector exception + * @return type description of the MatrixNotSquareNotRvector exception as a string + */ std::string type_description() const override { return "Matrix is not square not row vector"; } + /** + * @brief constructor for the MatrixNotSquareNotRvector class + * @param the location where the exception occured + */ using Exception::Exception; }; /** - * @brief clara::Exception::MatrixNotSquareNotVector - * @brief matrix is not square not vector exception + * @brief MatrixNotSquareNotVector + * @brief Exception representing a non-square matrix that is also not a vector + * + * this class detived from Exception and represent an exception where the matrix is not + * square and is also not a vector, while the operation requires a squre mtrix or a vector */ -class MatriNotSquareNotVector : public Exception { +class MatrixNotSquareNotVector : public Exception { public: + /** + * @brief get the type description of the MatrixNotSquareNotVector exception + * @return the type description of the MatrixNotSquareNotVector exception as string + */ std::string type_description() const override { return "Matrix is not square not vector"; } + /** + * @brief constructor fopr the MatrixNotSquareNotVector class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::MatrixMismatchSubsys - * @brief matrix mismatch subsytem exception + * @class MatrixMismatchSubsys + * @brief Exception representing a matrix mismatch in a subsystem + * + * this class is derived from Exception and represents an exception + * where there is a matrix mismatch in a subsystem of the application */ class MatrixMismatchSubsys : public Exception { public: + /** + * @brief get type description of the MatrixMismatchSubsys exception + * @return type description of the MatrixMismatchSubsys exception as a string + */ std::string type_description() const override { return "Matrix mismatch subsystem"; } + /** + * @brief constructor for the MatrixMismatchSubsys class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::DimsInvalid - * @brief invalid dimension exception + * @class DimsInvalid + * @brief Exception representing invalid dimensions + * + * this class is dervied from Exception an represents an exception + * where the provided dimensions are invalid or out of range */ class DimsInvalid : public Exception { public: + /** + * @brief get the type descriptio of the DimsInvalid exception + * @return tyhe type description the DimsInvalid exception as a string + */ std::string type_description() const override { return "Invalid dimension (s)"; } + /** + * @brief constructor for DimsInvalid class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::DimsNotEqual - * @brief dimension not equal exception + * @class DimsNotEqual + * @brief Exception representing dimensions that are not equal + * + * this class is derived from Exception and represents an exception + * where the dimensions provides */ class DimsNotEqual : public Exception { public: + /** + * @brief get the type description of the DimsNotEqual exception + * @return type description of the DimsNotEqual exception as string + */ std::string type_description() const override { return "Dimensional not equal"; } + /** + * @brief constructor for the DimsNotEqual class + * @param where the location where exxception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::DimsMismatchMatrix - * @brief dimension mismatch matrix size exception + * @class DimsMismatchMatrix + * @brief Exception representing a dimension mismatch in matrix size + * + * this class is derived from Exception and represent an exception + * where the provided the dimension of matrices do not match, leading to an invalid + * operation */ class DimsMismatchMatrix : public Exception { public: + /** + * @brief get the type description of the DimsMismatchMatrix exception + * @return the type description of the DimsMismatchMatrix exxception as string + */ std::string type_description() const override { return "Dimension mismatch matrix size"; } + /** + * @brief constructor for the DimsMismatchMatrix class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::DimsMismatchCvector - * @brief Dimension(s) mismatch column vector size exception - * product of the elements of std::vector of dimension is not equal to - * the number of elements of the Eigen::Matrix + * @class DimsMismatchCvector + * @brief Exception representing dimension mismatch in column vector size + * + * this class is detived from exception and represent an exception where + * the dimension of the provided cloumn vector and the expected size + * do not match, leading to an invalid operation */ class DimsMismatchCvector : public Exception { public: + /** + * @brief get the type description of the DimsMismatchCvector exception + * @return the type description of the DimsMismatchCvector exception as string + */ std::string type_description() const override { return "Dimension(s) mismatch column vector size"; } + /** + * @brief construct for the DimsMismatchCvector class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::DimsMismatchRvector - * @brief Dimension(s) mismatch row vector size exception - * product of the elements of std::vector of dimension is not equal - * to the number of the elements of the Eigen::Matrix + * @class DimsMismatchRvector + * @brief Exception representing dimension mismatch in reow vector size + * + * this class is derived from Exception and represent an exception + * where the dimension of the prpvided row vector and the expected size do + * not match, leading to an invalid operation */ class DimsMismatchRvector : public Exception { public: + /** + * @brief get the type description of the DimsMismatchRvector exception + * @return the type description of the DimsMismatchRvector exception as string + */ std::string type_description() const override { return "Dimension(s) mismatch row vector size"; } + /** + * @brief constructor for DimsMismatchRvector class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::DimsMismatchVector - * @brief Dimension mismatch vector size exception - * prodcut of the element of std::vector of dimension is not equal + * @class DimsMismatchRvector + * @brief Exception representing dimension in vector size + * + * this class dervied from Exception and represents an exception + * where the dimension of the provided vector and the expected size + * do not match, leading to an invalid operation */ class DimsMismatchVector : public Exception { public: + /** + * @brief get type description of the DimsMismatchVector exception + * @return the type description of the DimsMismatchVector exception as string + */ std::string type_description() const override { return "Dimension(s) mismatch vector size"; } + /** + * @brief constructor for the DimsMismatchVector class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::SubsysMismatchDims - * @brief Subsystem mismatch dimension exception + * @class SubsysMismatchdims + * @brief Exception representing subsystem mismatch in dimensions + * + * this class is derived from exception and represents an exception + * where the dimension of the subsystem do not match as expired + * leading to an invalid operations */ class SubsysMismatchdims : public Exception { public: + /** + * @brief get the type description of the SubsysMismatchdims exception + * @return the type description of the SubsysMismatchdims exception as string + */ std::string type_description() const override { return "Subsytem mismatch dimensions"; } + /** + * @brief constructor for the SubsysMismatchdims class + * @param the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::PermInvalid - * @brief invalid permutation exception + * @class PermInvalid + * @brief Exception representing an invalid permutation + * + * this class is derived from Exception and represent an exception + * where an invalid permutation is provided, leading to an invalid operation */ class PermInvalid : public Exception { public: + /** + * @brief th type description of the PermInvalid exception + * @return the type description of the PermInvalid exception as string + */ std::string type_description() const override { return "Invalid permutation"; } + /** + * @brief constructor for the PermInvalid class + * @param where the location where the exception occurred + */ using Exception::Exception; }; @@ -229,31 +441,62 @@ class PermMismatchDims : public Exception { }; /** - * @class clara::exception::NotQubitMatrix - * @brief matrix is not 2 x 2 exception + * @class PermMismatchDims + * @brief Exception representing permutation mismatch in dimension + * + * this class is derived from Exception and represent an exception + * where the dimension of the permutation do not match expected + * leading to an invalid operation */ class NotQubitMatrix : public Exception { public: + /** + * @brief get the type description of the PermMismatchDims exception + * @return the type description of the PermMismatchDims exception as string + */ std::string type_description() const override { return "Matrix is not 2 x 2"; } + /** + * @brief get the type description of the PermMismatchDims exception + * @return the type description of the PermMismatchDims exceptin as string + */ using Exception::Exception; }; /** - * @class clara::exception::NOtQubitCvector - * @brief column vector is not 2 x 1 exception + * @class NotQubitCvector + * @brief Exception representing column vector that is not 2x1 + * + * this class is derived from exception and represents as exception + * where a column vector is provided, but its dimensions are not 2x1 + * which is required for a qubit representation */ -class NOtQubitCvector : public Exception { +class NotQubitCvector : public Exception { public: + /** + * @brief get the type description of NotQubitCvector exception + * @return type description of the NotQubitCvector exception as string + */ std::string type_description() const override { return "Column vector is not 2 x 1"; } + /** + * @brief constructor for the not qubit NotQubitCvector class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::NotQubitRvector - * @brief row vector is not 1 x 2 exception + * @class NotQubitVector + * @brief Exception representing a row vector that is not 1 x 2 + * + * this class is derived from Exception and represent an exception + * where a row vector is provided, but its dimensions are not 1x2. + * which is required for a qubit representation */ class NotQubitRvector : public Exception { public: + /** + * @brief constructor for the NotQubitRvector class + */ std::string type_description() const override { return "Row vector is not 1 x 2"; } using Exception::Exception; }; @@ -261,6 +504,10 @@ class NotQubitRvector : public Exception { /** * @class clara::exception::NotQubitVector * @brief vector is not 2 x 1 nor 1 x 2 exception + * + * this class is derived from Exception and represents an exception + * where a vector is provided, but its dimensions are not 2x1 nor 1x2, + * which are required for a qubit representation. */ class NotQubitVector : public Exception { public: @@ -269,80 +516,170 @@ class NotQubitVector : public Exception { }; /** - * @class clara::exception::NotQubitSubsys - * @brief Subystem are not qubits exception + * @class NotQubitSubsys + * @brief Exception representing subsystem that are not qubits + * + * this class is derived from exception and represent an exception + * where the subsystem provided are not qubit as expected */ class NotQubitSubsys : public Exception { public: + /** + * @brief get the type description of NotQubitSubsys exception + * @preturn the type description of the NotQubitSubsys exception as string + */ std::string type_description() const override { return "Subsytem are not qubits"; } + /** + * @brief constructor for the NotQubitSubsys class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::NotBipartite - * @brief not bi-bipartite exception + * @class NotBipartite + * @brief Exception representing a situation thtis not bipartite + * + * this class derived from exception and represents an exception + * where the given situation is not bipartite as expected */ class NotBipartite : public Exception { public: + /** + * @brief get the type description of the NotBipartite exception + * @return the type description of the NotQubitRvector exception as string + */ std::string type_description() const override { return "Not bi-bipartite"; } + /** + * @brief constructor for the NotBipartite class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::NoCodeword - * @brief codeword does not exist exception + * @class NoCodeword + * @brief Exception representing the absence of a specific Codeword + * + * this class is derived from exception and represent an exception + * where a specific codeword does not exist as expected */ class NoCodeword : public Exception { public: + /** + * @brief get the type description of the NoCodeword exception + * @return the type description of the NoCodeword exception as a string + */ std::string type_description() const override { return "Codeword does not exist"; }; + /** + * @brief constructor for the NoCodeword class + * @param where the location where the exception occured + */ using Exception::Exception; }; /** - * @class clara::exception::OutOfRange - * @brief paramtere out of range exception + * @class OutOfRange + * @brief Exception representing a parameter out of range + * + * this class is derived from Exception and represents an exception + * where a parameter provided is out of the valid range */ class OutOfRange : public Exception { public: + /** + * @brief get the type description of the OutOfRange exception + * @param the type description of the OutOfRange exception as string + */ std::string type_description() const override { return "Parameter out of range"; } + /** + * @brief constructor of the OutOfRange class + * @param where the location where the exception occured + */ using Exception::Exception; }; +/** + * @class TypeMismatch + * @brief Exception representing a type mismatch + * this class is derived from Exception and represent an exception + * where a type mismatch is encountered + */ class TypeMismatch : public Exception { public: + /** + * @brief get type description of the TypeMismatch exception + * @return the type description of the TypeMismatch exxception as string + */ std::string type_description() const override { return "Type mismatch"; } + /** + * @brief constructor for the TypeMismatch class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::SizMismatch - * @brief size mismatch exception + * @class SizeMismatch + * @brief Exception representing a size mismatch + * + * this class is derived from Exception and represent an exception + * where a size mismatch is encountered */ class SizeMismatch : public Exception { public: + /** + * @brief get the type description of the SizeMismatch exception + * @return the type description of the SizeMismatch exception as a string + */ std::string type_description() const override { return "Size mismatch"; } + /** + * @brief construct for the SizeMismatch class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::UndefinedType - * @brief not defined for this type exception + * @class UndefinedType + * @brief Exception representing an operation not defined for this type */ class UndefinedType : public Exception { public: + /** + * @brief get the type description of the UndefinedType exception + * @return the type description of the UndefinedType exception as string + */ std::string type_description() const override { return "Not defined for this type"; } + /** + * @brief constructor for UndefinedType class + * @param where the location where the exception occurred + */ using Exception::Exception; }; /** - * @class clara::exception::CustomException - * @brief custom exception + * @class CustomException + * @brief Custom exception with a user-defined message + * + * this class is derived from the Exception base class and represents a custom exception with a + * user-defined message the user can provide custom messagetht will be included in the error + * description */ class CustomException : public Exception { std::string what_{}; + /** + * @brief get the type description of the CustomException + * @return the type description of the CustomException as string + */ std::string type_description() const override { return "CUSTOM EXCEPTION " + what_; } public: + /** + * @brief constructor for the CustomExceptio class + * @param where the location where the exception occurred + * @param what the custom message describing the exception + */ CustomException(const std::string& where, const std::string& what) : Exception{where}, what_{what} {} }; diff --git a/include/classFunction/gates.h b/include/classFunction/gates.h index 0e54bd4..3efe170 100644 --- a/include/classFunction/gates.h +++ b/include/classFunction/gates.h @@ -16,12 +16,21 @@ namespace clara { /** * @class clara::Gates - * @brief const singleton class that implements most commonly used gates + * @brief Singleton + * + * this class is singleton and provides a collection of quantum gates and operation commonly + * used in quantum computing. it contains gates such as Pauli-X, Pauli-Y, pauli-Z, Hadamard, + * S, T, CNOT, CZ, SWAP, TOFFOLI, Fredkin, and more. the class also inclueds methods to generate + * rotation gates, generalized Z gates, Fourier transform gates, and Identity gates for qudits. + * additionally, it provides methods to construct multi-parity multiple controlled gates. the gates + * class ensure that gate are initialized only once and canbe accessed globally using the singleton + * pattern */ class Gates final : public internal::Singleton { friend class internal::Singleton; public: + // define various quantum gates as member variables cmat Id2{cmat::Identity(2, 2)}; cmat H{cmat::Zero(2, 2)}; cmat X{cmat::Zero(2, 2)}; @@ -39,7 +48,14 @@ class Gates final : public internal::Singleton { cmat FRED{cmat::Identity(8, 8)}; private: + /** + * @brief Construct for the gates class + * + * the constructor is private and can only be accessed the Singleton pattern. it initializes + * the quantum gates with their corresponding matrices + */ Gates() { + // initialize quantum gates with their corresponding matrices H << 1 / std::sqrt(2.), 1 / std::sqrt(2.), 1 / std::sqrt(2.), -1 / std::sqrt(2.); X << 0, 1, 1, 0; Z << 1, 0, 0, -1; @@ -57,15 +73,19 @@ class Gates final : public internal::Singleton { TOF.block(6, 6, 2, 2) = X; FRED.block(4, 4, 4, 4) = SWAP; } - /* - * @brief default constructor - */ + // DECLARE constructor private to prefent direct instantion ~Gates() = default; public: /** - * @brief Qubit roration of theta about the 3 dimensional real unit vector n - * @return rotation gate + * @brief qubit rotation about a 3-dimensional real unit vector (Rn) gate + * + * @param theta the angle of rtation in radians + * @param n A 3-dimensional vector representing the unit vector about which to rotate + * @return the rotation gate as 2x2 complex matrix + * + * NOTE: the rn Gate rotates the qubit about the specified 3-dimensional real unit vector + * by the angle theta. the function returns the rotation matrix */ cmat Rn(double theta, const std::vector& n) const { if (n.size() != 3) @@ -77,8 +97,14 @@ class Gates final : public internal::Singleton { } /** - * @brief generalized Z gates for qudits - * @note define as \f$ Z = \sum_{j=0}^{D-1} \exp(2\pi \mathrm{i} j/D) |j\rangle\langle j| \f$ + * @brief generalized Z (Zd) gate for qudits + * + * @param D the dimension of the qudit (default 2) + * @return the Zd gate as DxD complex matrix + * + * NOTE: the Zd gate is a generalization of the pauli-Z gate (Z) for qudit (quantum system with + * dimension D) its defined as \f$ Z = \sum_{j=0}^{D-1} \exp(2\pi \mathrm{i} j/D) |j\rangle\langle + * j| \f$. the function returns the Zd gate matrix for the specified dimension D */ cmat Zd(idx D = 2) const { if (D == 0) @@ -90,8 +116,14 @@ class Gates final : public internal::Singleton { } /** - * @brief fourier transform gate for qudits - * @note define as \f$ F = \sum_{j,k=0}^{D-1} \exp(2\pi \mathrm{i} jk/D) |j\rangle\langle k| \f$ + * @brief Fourier transform (Fd) gate for qudit + * + * @param D the dimension of the qudit (default: 2) + * @return the Fd gate as a DxD complex matrix + * + * NOTE: the Fd gate is the fourier gate for qudits (quantum system with dimension D). + * it is defined as \f$ F = \sum_{j,k=0}^{D-1} \exp(2\pi \mathrm{i} jk/D) |j\rangle\langle k| \f$. + * the function returns the Fd gate matrix for the specified dimension D */ cmat Fd(idx D = 2) const { if (D == 0) @@ -107,9 +139,16 @@ class Gates final : public internal::Singleton { } /** - * @brief generalized X gate for qudits - * @note define as f$ X = \sum_{j=0}^{D-1} |j\oplus 1\rangle\langle j| \f$, ie raising operator - * \f$ X|j\rangle = |j\oplus 1\rangle\f$ + * @brief generalized X (Xd) gate for qudit + * + * @param D the dimension of the qudit (default 2) + * @return the xd Xd gate as DxD complex matrix + * + * NOTE: the Xd gate is a generalization of the Pauli gate (x) for qudit (quantum system with + * dimension D) it is defined as \f$ X = \sum_{j=0}^{D-1} |j\oplus 1\rangle\langle j| \f$, where + * \f$ \oplus \f$ denotes addition modulo D. the Xd gate acts as the raising operatos f$ + * X|j\rangle = |j\oplus 1\rangle \f$ the function return the Xd gate for the specified dimension + * D */ cmat Xd(idx D = 2) const { if (D == 0) @@ -118,9 +157,15 @@ class Gates final : public internal::Singleton { } /** - * @brief inditity gate - * @note can change the return type from complex matrix (default) by explicitly - * specifying the template paramter + * @brief identity gate (id) for qudits + * + * @tparam Derived the type of the of the return matrix (default: complex matrix) + * @param D the dimension of the qudit (default: 2) + * @return the Id gate as DxD matrix of the specified matrix for qudits (qunatum system with + * dimension D) + * + * NOTE: the template parameter 'Derived' can be explicitly specified to change the + * return type for the default complex matrix */ template Derived Id(idx D = 2) const { @@ -130,8 +175,20 @@ class Gates final : public internal::Singleton { } /** - * @brief generate the multi-paritite multiple contrilled A gate in matrix form - * @note the dimension of the gate A must match the dimension of subsys + * @brief generate the multi-paritite multi-controlled in matrix form + * + * @tparam Derived the type of the input matrix A and the return matrix + * @param A the gate matrix to be controlled + * @param ctrl list of control qubit indices (0-bates) for the gate + * @param subsy list of subsystem qubit indices for the gate + * @param B the total number qubits + * @param d the local dimension of each qubit + * @return the controlled gate matrix as complex matrix of the specified mtype + * + * NOTE: `CTRL` function generate the multi-paritite multi-controlled gate in matrix form + * the function takes an input matrix `A` (the gate to be controlled) and lists of control and + * subsystem qubit indices. it returns the controlled gate matrix as a complex matrix of the + * specfied type */ template dyn_mat CTRL(const Eigen::MatrixBase& A, @@ -258,9 +315,29 @@ class Gates final : public internal::Singleton { } /** - * @brief expands out clara::kron() - * expand out A as a matrix in a multi-paritite system. faster than using clara::kron() - * @return tensor product + * @brief expand the matrix A to a larger tensor product space + * + * @tparam Derived the type of the input matrix A and the return matrix + * @param A the input matrix to be expanded + * @param pos the position of the tensor product space to which a should expanded + * @param dims the dimension of the tebsor product space + * @return the expanded matrix as a complex matrix of the specified type + * + * NOTE: `expandout` function expand the matrix A to a larger tensor product space specified by + * the dimension in the `pos` parameter ditermines the position at which the expansion should be + * done the function returns the expanded matrix as a complex matrix of the specfied type. the + * input matrix A should be square, and the dimension at position in `dims` should be valid fro a + * tensor product space + * + * @throws clara::exception::ZeroSize if the input matrix A has zero size + * @throws clara::exception::DimsInvalid the dimension in `dims` are invalid + * @throws clara::exception::MatrixNotSquare if the input matrix A is not square + * @throws clara::exception::OutOfRange if the `pos` parameter is out of range for the dimension + * in `dims` + * @throws clara::exception::DimsMismatchMatrix if the dimension of A does not match dimension at + * position `pos in `dims` + * + * ` */ template dyn_mat expandout(const Eigen::MatrixBase& A, idx pos, @@ -315,10 +392,18 @@ class Gates final : public internal::Singleton { } /** - * @brief expand out - * @note the std::initializer_list overload exists because otherwise, in the - * degenerate case when dims only one element, the one element list is implicitly - * converted to the element's underlying type + * @brief expand the matri A to larger tensor product space using a list of dimensional + * + * @tparam Derived the type of the input matrix and the return matrix + * @param A the input matri to expanded + * @param pos the position of the tensor product space to which A should be expanded + * @param dims the dimensions of the tensor product space as an initializer list + * @return the expanded matrix a complex matrix of the specfied type + * + * NOTE: the `expandout` function expands the matrix A to larger tensor product space specfied by + * the number of subsystem `N`. the `pos` parameter determines the position at which the expansion + * should be done. the `d` parameter specifies the local dimension of each subsystem (default is 2 + * for qubits) the function returns the expanded matrix as a complex matrix of the specfied */ template dyn_mat expandout(const Eigen::MatrixBase& A, idx pos, diff --git a/include/classFunction/idisplay.h b/include/classFunction/idisplay.h index 446704d..b7d3e71 100644 --- a/include/classFunction/idisplay.h +++ b/include/classFunction/idisplay.h @@ -3,25 +3,46 @@ #include namespace clara { +/** + * @class IDisplay + * @brief Interface for displaying objects + * + * the IDisplay class provides as interface for object that need to be displayed in custom way + * derived classes must override the `display()` function to perform the actual stream extraction + * processing + */ class IDisplay { private: /** - * @brief this function must be implemented in all derived classes - * the derived classes overrided this function to perform the actual - * stream extraction processing. the function is automatically called - * by the friend inline function + * @brief pure virtual function for displaying objects + * + * this functions must be implemented in all derived classes to perform the actual + * stream extraction processing. the function is automatically called by friend + * inline function `operator<<` + * + * @param os the output stream to which the object should be displayed + * @return the output stream after displaying the object */ virtual std::ostream& display(std::ostream& os) const = 0; public: + // default constructor IDisplay() = default; + // copy constructor IDisplay(const IDisplay&) = default; + // move constructor IDisplay(IDisplay&&) = default; + // copy assignment operator IDisplay& operator=(const IDisplay&) = default; + // virtual destructor virtual ~IDisplay() = default; /** - * @brief overloads the extraction operator + * @brief overloads the extraction operator to display objects + * + * @param os the output stream to which the object sould be displayed + * @param rhs the object to be displayed + * @return the output stream after displaying the object */ friend inline std::ostream& operator<<(std::ostream& os, const IDisplay& rhs) { return rhs.display(os); diff --git a/include/classFunction/init.h b/include/classFunction/init.h index 395508d..0694fbf 100644 --- a/include/classFunction/init.h +++ b/include/classFunction/init.h @@ -8,6 +8,14 @@ namespace clara { +/** + * @class Init + * @brief singleton class for initializing the library + * + * the init class is a singleton class that provides initialization for the library + * it ensures that certain configuration are set up before using other components + * of the library + */ class Init final : public internal::Singleton { friend class internal::Singleton; diff --git a/include/classFunction/random_devices.h b/include/classFunction/random_devices.h index 7156c39..c3699cb 100644 --- a/include/classFunction/random_devices.h +++ b/include/classFunction/random_devices.h @@ -8,14 +8,19 @@ namespace clara { /** - * @class clara::RandomDevices - * @brief singleton class that manages the source randomness in the library - * consist of a wrapper around an std::mt19937 mersenne twister random - * number geberator engine and std::random_device engine + * @class RandomDevices + * @brief singleton class that manages the source randomnes in the library + * + * the RandomDevices class is a singleton that provides a contralized source of randomnes + * for the library. it uses the mersenne twister random number generator as the internal + * pseudo-random number generator egine and an std::random_device egine for seeding the + * PRNG */ class RandomDevices final : public internal::Singleton { friend class internal::Singleton; + // random device egine fro seeding the PRNG std::random_device rd_; + // mersenne twister random number generator egine std::mt19937 prng_; public: @@ -28,20 +33,23 @@ class RandomDevices final : public internal::Singleton { /** * @brief load the state of the PRNG from an input stream * @param input stream - * @return input stream + * @return input stream after loading the PRNG state */ std::istream& load(std::istream& is) { return is >> prng_; } /** * @brief save the state of the PRNG to an output stream * @param os output stream - * @return the output stream + * @return the output stream after sabing the PRNG state */ std::ostream& save(std::ostream& os) const { return os << prng_; } private: /* * @brief intialize and seed the random number generator + * + * the constructor initializes the random number generator with a seed obtained from the random + * device egine */ RandomDevices() : rd_{}, prng_{rd_()} {} diff --git a/include/classFunction/states.h b/include/classFunction/states.h index 17fedd8..b0f7b45 100644 --- a/include/classFunction/states.h +++ b/include/classFunction/states.h @@ -11,8 +11,12 @@ namespace clara { /** - * @class clara::States - * @brief const singleton class that implements most commonly used states + * @class States + * @brief const singleton class the implements commonly used quantum states + * + * the state class is a const singleton that provides implementation for commonly used + * quantum states it includes states like the computational states, Bell states, GHZ states + * W states, and more */ class States final : public internal::Singleton { @@ -56,8 +60,10 @@ class States final : public internal::Singleton { cmat pW{cmat::Zero(8, 8)}; /** - * @brief maximally entangled state of 2 qudits - * \f$\frac{1}{\sqrt{d}}\sum_{j=0}^{d-1}|jj\rangle\f$ of 2 qudits + * @brief returns the maximally entangled state of 2 qudits + * @param d Dimension of the qudits (default 2) + * @return maximally entangled state \f$\frac{1}{\sqrt{d}}\sum_{j=0}^{d-1}|jj\rangle\f$ of 2 + * qudits */ ket mes(idx d = 2) const { // check valid dims @@ -71,7 +77,9 @@ class States final : public internal::Singleton { } /** - * @brief zero state of n qudits + * @brief returns the zero state of n qudits + * @param n number of qudits + * @param d dimension of the qudits * @return zero state \f$|0\rangle^{\otimes n}\f$ of n qudits */ ket zero(idx n, idx d = 2) const { @@ -87,8 +95,10 @@ class States final : public internal::Singleton { } /** - * @brief one state of n qudits - * @return one statem f$|1\rangle^{\otimes n}\f$ of qudits + * @brief returns the one state of n qudits + * @param n number of qudits + * @param d dimension of the qudits + * @return the one state \f$|1\rangle^{\otimes n}\f$ of n qudits */ ket one(idx n, idx d = 2) const { if (n == 0) @@ -101,7 +111,10 @@ class States final : public internal::Singleton { } /** - * @brief \f$|j\rangle^{\otimes n}\f$ state of n qudits + * @brief return the \f$|j\rangle^{\otimes n}\f$ state of n qudits + * @param j the eigenvalue to represent in the state + * @param n number of qudits + * @param d dimension of the qudits * @return \f$|j\rangle^{\otimes n}\f$ state of n qudits */ ket jn(idx j, idx n, idx d = 2) const { @@ -119,9 +132,10 @@ class States final : public internal::Singleton { } /** - * @brief plus state of n qubits - * @return plus state \f$|+\rangle^{\otimes n}\f$ of qubits -*/ + * @brief return the plus state of n qubits + * @param n number of qubits + * @return plus state \f$|+\rangle^{\otimes n}\f$ of n qubits + * */ ket plus(idx n) const { if (n == 0) throw exception::OutOfRange("clara::States::plus()"); @@ -132,16 +146,24 @@ class States final : public internal::Singleton { } /** - * @brief minus state of n qubits - * @return minus state \f$|-\rangle^{\otimes n}\f$ of n qubits -*/ + * @brief return the minus stte of n qubits + * @param n number of qubits + * @return minus state \f$|-\rangle^{\otimes n}\f$ of n qubits + */ ket minus(idx n) const { if (n == 0) throw exception::OutOfRange("clara::States::minus()"); - return kronpow(this -> x1, n); + return kronpow(this->x1, n); } private: + /** + * @brief private constructor for the state class + * + * the constructor initialize the state vectors and projectors for the various quantum states + * it is private constructor because state is a const singleton, and it can only be accessed + * through the state member function `instance()` + */ States() { x0 << 1 / std::sqrt(2.), 1 / std::sqrt(2.); x1 << 1 / std::sqrt(2.), -1 / std::sqrt(2.); diff --git a/include/classFunction/timer.h b/include/classFunction/timer.h index e6f38ad..48ec2ed 100644 --- a/include/classFunction/timer.h +++ b/include/classFunction/timer.h @@ -7,6 +7,17 @@ #include "idisplay.h" namespace clara { +/** + * @class Timer + * @brief class for measuring time intervals using the C++ chrono library + * + * the timer class provides functionally for measuring time intervals using + * the c++ chrono library it can be used to measure the time taken for certain + * operations or to benchmark code performance. + * + * @tparam T the type of duration to be used to measuring time + * @param CLOCK_T clock type to be used + */ template , typename CLOCK_T = std::chrono::steady_clock> class Timer : public IDisplay { protected: @@ -41,9 +52,11 @@ class Timer : public IDisplay { */ double tics() const noexcept { return std::chrono::duration_cast(end_ - start_).count(); } /** - * @brief Duration specified by U - * @return duration that passed between the reset and invocation - * of clara::Timer::toc() + * @brief get the duration specified by U + * + * get the duration that passed between the reset and invocation of clara::Timer::toc() + * @tparam U the type of duration to be returned + * @return Duration that passed between that reset and invocation clara::Timer::toc() */ template U get_duration() const noexcept { @@ -68,10 +81,10 @@ class Timer : public IDisplay { private: /** - * @brief clara::IDisplay::display() override - * @return write to the output stream the number of tics - * that passed between the reset and invocation of - * clara::Timer::toc() + * @brief override of clara::IDisplay::display() + * writes the output strem the number of tics that passed between the reset and invocation of + * clara::TImer::toc() + * @return the output stream */ std::ostream& display(std::ostream& os) const override { return os << tics(); } }; diff --git a/include/experimental/experimental_test.h b/include/experimental/experimental_test.h index c2b0fdd..715809b 100644 --- a/include/experimental/experimental_test.h +++ b/include/experimental/experimental_test.h @@ -17,9 +17,12 @@ namespace clara { namespace experimental { /** - * @class clara::Dynamic_bitset - * @brief dynamic bitset class, allow the specification of - * the number of bits at runtime + * @class Dynamic_bitset + * @brief Dynamic bitest class, allows the specification of the number of number bits at runtime + * + * the Dynamic_bitset class is a dynamic bitset that allows the specification of the number of bits + * at runtime. it provides functionalities to manipulate individual bits, set random bits, and + * perform bit operations. */ class Dynamic_bitset { public: @@ -42,6 +45,7 @@ class Dynamic_bitset { /** * @brief constructor, intialize all bits to false + * @return offset of the pos bit in the storage space relative to its index */ idx offset_(idx pos) const { return pos % (sizeof(value_type) * CHAR_BIT); } @@ -82,7 +86,17 @@ class Dynamic_bitset { return result; } + /** + * @brief get the value of the bit at the given position + * @param pos position of the bit to check + * @return true if the bit set (1), false otherwise (0) + */ bool get(idx pos) const { return 1 & (v_[index_(pos)] >> offset_(pos)); } + + /** + * @dbrief check if none of the bits are set in the bitset + * @return true if none of the bits are set, false otherwise + */ bool none() const noexcept { bool result = true; for (idx i = 0; i < storage_size(); ++i) { @@ -93,6 +107,10 @@ class Dynamic_bitset { return result; } + /** + * @brief check if all bits are set in the bitset + * @return true if all bits are set, false otherwise + */ bool all() const noexcept { bool result = true; for (idx i = 0; i < storage_size(); ++i) { @@ -103,13 +121,27 @@ class Dynamic_bitset { return false; } + /** + * @brief check if any of the bits are set in the bitset + * @return true if any of the bits are set, false otherwise + */ bool any() const noexcept { return !(this->none()); } + /** + * @brief set the bit at the given position to the specified value + * @param pos position of the bitset + * @param value value to set the bit + * @return reference to the modified bitset + */ Dynamic_bitset& set(idx pos, bool value = true) { value ? v_[index_(pos)] |= (1 << offset_(pos)) : v_[index_(pos)] &= ~(1 << offset_(pos)); return *this; } + /* + * @brief all bits in the bitset to the specified value (true = 1, false = 0) + * @return reference to the modified bitset + */ Dynamic_bitset& set() noexcept { for (idx i = 0; i < storage_size(); ++i) { v_[i] = ~0; @@ -117,7 +149,12 @@ class Dynamic_bitset { return *this; } - // set the bit according to a random bernouli distribution + /** + * @brief set the bit at the given position randomly according to a Bernoulli distribution + * @param pos position of the bit to set + * @param p probability of setting the bit to 1 + * @return reference to the modified bitset + */ Dynamic_bitset& rand(idx pos, double p = 0.5) { std::random_device rd; std::mt19937 gen{rd()}; @@ -127,7 +164,11 @@ class Dynamic_bitset { return *this; } - // set all bits according to a random bernouli distribution + /** + * @brief set all bits in the bitset randomly according to a Bernoulli distribution + * @param p probability of setting each bit to 1 + * @return reference to the modified bitset + */ Dynamic_bitset& rand(double p = 0.5) { for (idx i = 0; i < size(); ++i) { this->rand(i, p); @@ -135,13 +176,20 @@ class Dynamic_bitset { return *this; } - // set bit false + /** + * @brief reset the bit at the given position (set 0) + * @param pos position of the bit to reset + * @return reference to the modified bitset + */ Dynamic_bitset& reset(idx pos) { v_[index_(pos)] &= ~(1 << offset_(pos)); return *this; } - // set all bits 0 + /** + * @brief reset all bits in the bitset + * @return reference to the modified bitset + */ Dynamic_bitset& reset() noexcept { for (idx i = 0; i < storage_size(); ++i) { v_[i] = 0; @@ -149,12 +197,21 @@ class Dynamic_bitset { return *this; } - // flips the bit + /** + * @brief flip the bit at the given position + * @param position orf the bit to flip + * @return reference to the modified bitset + */ Dynamic_bitset& flip(idx pos) { v_[index_(pos)] ^= 1 << (offset_(pos)); return *this; } + /** + * @brief equality comparsion operator for bitsets + * @param rhs bitset to compare with rhs + * @return true if the bitsets are equal, false otherwise + */ Dynamic_bitset& flip() noexcept { for (idx i = 0; i < storage_size(); ++i) { v_[i] = ~v_[i]; @@ -162,7 +219,11 @@ class Dynamic_bitset { return *this; } - // operator + /** + * @brief equality comprasion operator for bitsets + * @param rhs bitset to compare with + * @return true if the bitsets are equal, false otherwise + */ bool operator==(const Dynamic_bitset& rhs) const noexcept { assert(this->size() == rhs.size()); bool result = true; @@ -175,8 +236,19 @@ class Dynamic_bitset { return result; } + /** + * @brief inequality comprasion oeprator for bitset + * @param rhs bitset to compare with + * @return true if the bitsets are not equal, false otherwise + */ bool operator!=(const Dynamic_bitset& rhs) const noexcept { return !(*this == rhs); } + /** + * @brief output stream operator to print the bitset in binary representation + * @param os output stream + * @param rhs bitset to print + * @return reference to the output stream + */ friend std::ostream& operator<<(std::ostream& os, const Dynamic_bitset& rhs) { for (idx i = rhs.size(); i-- > 0;) { os << rhs.get(i); @@ -184,6 +256,12 @@ class Dynamic_bitset { return os; } + /** + * @brief convert the bitset to a string representation + * @param zero character to represent 0 bit + * @param one character to represent 1 bit + * @return string representation of the bitset + */ template , class Allocator = std::allocator> std::basic_string to_string(CharT zero = CharT('0'), @@ -204,8 +282,10 @@ class Dynamic_bitset { }; /** - * @class clara::Bit_circuit - * @brief classical reversible circuit simulator + * @class Bit_circuit + * @brief classical revesible circuit simulator + * + * the Bit_circuit class is a classical */ class Bit_circuit : public Dynamic_bitset { public: diff --git a/include/internal/classFunction/iomanip.h b/include/internal/classFunction/iomanip.h index 2594109..b8a8280 100644 --- a/include/internal/classFunction/iomanip.h +++ b/include/internal/classFunction/iomanip.h @@ -11,15 +11,27 @@ namespace clara { namespace internal { /** - * ostream manipulators for formatting - * eigen matrics and STL/C-style containers/vectors + * @class IOManipRange + * @brief ostream manipulators for formatting range of elements + * from an input input iterator + * @tparam InpuIterator the type of the input iterator. it should satisfy the requirement + * of InpuIterator concept */ template class IOManipRange : public IDisplay { + // itertor pointing to the first and past-the end element in the range InpuIterator first_, last_; std::string separator_, start_, end_; public: + /** + * @public constructor + * @param first iterator pointing to the first element in the range + * @param last iterator pointing to the past-the-end element in the range + * @param separator separator string used to separate the elements + * @param start start string to be displayed before the range, default is "[" + * @param end end string to be displayed after the range, default is "]" + */ explicit IOManipRange(InpuIterator first, InpuIterator last, const std::string& separator, const std::string& start = "[", const std::string& end = "]") : first_{first}, last_{last}, separator_{separator}, start_{start}, end_{end} {} @@ -27,6 +39,12 @@ class IOManipRange : public IDisplay { IOManipRange& operator=(const IOManipRange&) = default; private: + /** + * @brief override of the display function from IDisplay. display the range elements separated + * by the specified separator, enclosed by start and end strings + * @param os the output stream to write the formatted range + * @return Reference to the output to write the formatted range + */ std::ostream& display(std::ostream& os) const override { os << start_; bool first = true; @@ -41,13 +59,29 @@ class IOManipRange : public IDisplay { } }; +/** + * @class IOManipPointer + * @brief ostream manipulators for formatting arrays pointerd to by a pointer + * @tparam PointerType the type of the elements in the arrays + */ template class IOManipPointer : public IDisplay { + // pointer to the array const PointerType* p_; + // number of the lements in the array idx N_; + // start and end string to be displayed end and start array std::string separator_, start_, end_; public: + /** + * @brief constructor + * @param p pointer to the array + * @param N number of elements in the array + * @param separator separator string used to separate elements + * @param start start string to be displayed before the arrays, default is "[" + * @param end end string to be displayed after the array, default is "]" + */ explicit IOManipPointer(const PointerType* p, idx N, const std::string& separator, const std::string& start = "[", const std::string& end = "]") : p_{p}, N_{N}, separator_{separator}, start_{start}, end_{end} {} @@ -55,6 +89,12 @@ class IOManipPointer : public IDisplay { IOManipPointer& operator=(const IOManipPointer&) = default; private: + /** + * @brief override of the display function from IDisplay. display the array elements separator + * by the specified separator, enclosed by start and end string. + * @param os the output stream to write the formatted array + * @return reference to the output stream after writing the formatted array + */ std::ostream& display(std::ostream& os) const override { os << start_; for (idx i = 0; i < N_ - 1; ++i) @@ -66,6 +106,10 @@ class IOManipPointer : public IDisplay { } }; +/** + * @class IOManipEigen + * @brief ostream manipulator for formatting eigen matrices and complex numbers + */ #if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ingored "-Weffc++" @@ -74,21 +118,38 @@ class IOManipEigen : public IDisplay, private Display_Impl_ { #if ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) #pragma GCC diagnostic pop #endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && !__clang__) + // Eigen matrix cmat A_; + // threshold for truncating small values double chop_; public: - // eigen matrices + /** + * @brief constructor for formatting Eigen matrices + * @tparam Derived the derived class from Eigen::MatrixBase + * @param A the eigen matrix to be formatted + * @param chop threshold for truncating small values. default is clara::chop + */ template explicit IOManipEigen(const Eigen::MatrixBase& A, double chop = clara::chop) : A_{A.template cast()}, chop_{chop} {} - // complex numbers + /** + * @brief constructor for formatting complex number + * @param z complex number to be formatted + * @param chop threshold for truncating small values. default is clara::chop + */ explicit IOManipEigen(const cplx z, double chop = clara::chop) : A_{cmat::Zero(1, 1)}, chop_{chop} { A_(0, 0) = z; } private: + /** + * @brief override of the display function from IDisplay. display the Eigen matrix or complex + * number using custom formatting + * @param os the output stream to write the formatted Eigen matrix or complex number + * @return reference to the output stream after writting the formatted data + */ std::ostream& display(std::ostream& os) const override { return display_impl_(A_, os, chop); } }; diff --git a/include/internal/classFunction/singleton.h b/include/internal/classFunction/singleton.h index 2928168..ee4bdf1 100644 --- a/include/internal/classFunction/singleton.h +++ b/include/internal/classFunction/singleton.h @@ -6,13 +6,11 @@ namespace clara { namespace internal { /** -* to implement a singleton, derive class from clara::internal::Singleton, -* make clara::internal::Singleton of class, then declare the constructor -* and desctructor of class as private. to get an instance, use the static -* member function clara::internal::Singleton::get_instance() -* (clara::internal::Singleton::get_thread_local_instance()), which return -* a reference (thread_local reference) to newly created singleton -*/ + * @class Singleton + * @brief Implementation of a singleton pattern + * @tparam T the type of the class that needs to be a singleton. the class should have + * a private constructor and descructor + */ template class Singleton { protected: @@ -22,11 +20,24 @@ class Singleton { virtual ~Singleton() = default; public: + /** + * @brief get the instance of the singleton class + * @return reference to the singleton instance + * NOTE: the first call to this function will construct the singleton instance + * subsequemt calls will return the same instance. + */ static T& get_instance() noexcept(std::is_nothrow_constructible::value) { static T instance; return instance; } #ifndef NO_THREAD_LOCAL_ + /** + * @brief get the thread-local instance of the singleton class + * @return reference to the thread-local singleton instance + * NOTE: the first class to this function in each thread will construct a separate + * instance for that thread. subsequent calls in the same thread will return + * the same thread-local instance + */ static T& get_thread_local_instance() noexcept(std::is_nothrow_constructible::value) { thread_local static T instance; return instance; diff --git a/include/internal/util.h b/include/internal/util.h index c6d2774..ead4dd9 100644 --- a/include/internal/util.h +++ b/include/internal/util.h @@ -20,6 +20,18 @@ namespace clara { namespace internal { +/** + * @brief convert a linear index to multi-dimensional indices for a given set of dimension + * @param n the linear index + * @param numdims the number of dimension + * @param dims pointer to the array of dimension + * @param result pointer to the array to store the multi-dimensional indices + * NOTE: this function is used to convert a linear index to multi-dimensional indices corresponding + * to a multi-dimensional array with the given dimensions. the result array will store the + * multi-dimensional indices in reverse order, with the first index corresponding to the last + * dimension and so on. for example, for a 3x3x3 array, the linear index 5 would be converted + * to multidimensional indices [2, 1, 0] + */ inline void n2multiidx(idx n, idx numdims, const idx* const dims, idx* result) noexcept { #ifndef NDEBUG if (numdims > 0) { @@ -42,8 +54,17 @@ inline void n2multiidx(idx n, idx numdims, const idx* const dims, idx* result) n #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif /** - * multiindex to integer - * standard lexicographical order + * @brief convert multi-dimensional indices to a linear indexx + * @param midx pointer to the array of multi-dimensional indces + * @param numdims the number of dimensions + * @param dims pointer to the array of dimensions + * @return the linear index corresponding the multi-dimensional + * + * NOTE: this function is used to convert multi-dimensional indices to a linear index for a + * multi-dimensional mutli-dimensional array with the given dimensions. the midx array array is + * expected to store the multi-dimensional indices in reverse order, with the first index + * corresponding to the last dimensions and so on. for eample a 3x3x3 array, the multi-dimensional + * indices [2, 1, 0] would be converted to the linear index 5 */ inline idx multiidx2n(const idx* const midx, idx numdims, const idx* const dims) { #ifndef NDEBUG @@ -112,8 +133,14 @@ inline bool check_dims(const std::vector& dims) { } /** - * check that valid dims match the dimension - * of valid (non-zero sized) square matrix + * @brief check if valid dims match the dimension of a valid (non-zero sized) square matrix + * @param dims the vector of dimension + * @param A the square matrix + * @return true if valid dims match the dimension of the matrix, false otherwise + * + * NOTE: this function check whether the product of all dimensions in dims is equal to the number + * of rows/columns in the square matrix A, the dimensions in dims are assumed to be far valid + * square matrix of the same dimension */ template bool check_dims_match_mat(const std::vector& dims, const Eigen::MatrixBase& A) { @@ -216,8 +243,12 @@ inline bool check_perm(const std::vector& perm) { } /** - * kronecker product of 2 matrices, preserve return type - * internal function for the variadic tempalte function wrapper kron() + * @brief kronecker product of two matrices + * @tparam Derived1 type of the first matrix + * @tparam Derived2 type of the second matrix + * @param A the first matrix + * @param B the second matrix + * @return the kronecker product of A and B */ template dyn_mat kron2(const Eigen::MatrixBase& A, @@ -252,6 +283,14 @@ dyn_mat kron2(const Eigen::MatrixBase& A, return result; } +/** + * @brief Direct sum of two matrices + * @tparam Derived1 type of the first matrix + * @tparam Derived2 type + * @param A the first matrix + * @param B the second matrix + * @return the direct sum of A and B + */ template dyn_mat dirsum2(const Eigen::MatrixBase& A, const Eigen::MatrixBase& B) { @@ -287,8 +326,13 @@ void variadic_vector_emplace(std::vector& v, First&& first, Args&&... args) { } /** - * return the number of subystem (each subsystem assumed of the same - * dimension d) from an object (ket/bra/density matrix) of size sz + * @brief get the number of subsystem from an object of size sz + * @param sz the size of the object + * @param d the dimension of each subsystem + * @return the number of subsystem + * + * NOTE: this function calculates the number of subsystem in an boject (ket/bra/density matrix) of + * size, where each subsystem has the same dimension d */ inline idx get_num_subsys(idx sz, idx d) { #ifndef NDEBUG @@ -299,15 +343,19 @@ inline idx get_num_subsys(idx sz, idx d) { } /** - * return the dimension of a subsystem (each subsystem assumed of the - * dimension d) from an object (ket/bra/density matrix) of size sz - * consiting of N subsystem + * @brief get the dimension of a sbusystem from an object of size sz consiting of N subsystem + * @param sz the size of the object + * @param N the number of subsystem + * @return the dimension of each subystem + * + * NOTE: this function calculates the dimension of each subsystem in an object (ket/bra/density matrix) + * of size sz, consiting of N subsystem. the dimension in the object are assumed to be the same */ inline idx get_dim_subsystem(idx sz, idx N) { - #ifndef NDEBUG +#ifndef NDEBUG assert(N > 0); assert(sz > 0); - #endif // !NDEBUG +#endif // !NDEBUG if (N == 2) return static_cast(std::llround(std::sqrt(sz))); return static_cast(std::llround(std::pow(sz, 1. / N))); From 34a6be698cd122d5f36024026fc8bdf24e3a2e36 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 14:47:36 +0700 Subject: [PATCH 38/80] chore: testing adding action environment windows test Signed-off-by: slowy07 --- ...{cpp-testing.yml => cpp-testing-linux.yml} | 2 +- .github/workflows/cpp-testing-windows.yml | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) rename .github/workflows/{cpp-testing.yml => cpp-testing-linux.yml} (97%) create mode 100644 .github/workflows/cpp-testing-windows.yml diff --git a/.github/workflows/cpp-testing.yml b/.github/workflows/cpp-testing-linux.yml similarity index 97% rename from .github/workflows/cpp-testing.yml rename to .github/workflows/cpp-testing-linux.yml index 4f4b193..08f2fbf 100644 --- a/.github/workflows/cpp-testing.yml +++ b/.github/workflows/cpp-testing-linux.yml @@ -1,4 +1,4 @@ -name: Clara Test +name: Clara Test Ubuntu on: push: diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml new file mode 100644 index 0000000..6255fad --- /dev/null +++ b/.github/workflows/cpp-testing-windows.yml @@ -0,0 +1,32 @@ +name: Clara Test Windows + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +jobs: + build-and-testing-windows: + runs-on: windows-latest + steps: + - name: checkout repository + uses: actions/checkout@v3 + - name: Install Eigen (windows) + run: choco install eigen + + - name: testing windows + run: | + echo "check g++" + g++ --version + cd clara_test + .\run_test + + - name: testing grover search (windows) + run: + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I C:\tools\eigen\include -I C:\clara\include testing\grover_search.cpp -o grover_search.exe + grover_search.exe From 89a5639898445043b669851a5a6d48c821684887 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 14:54:01 +0700 Subject: [PATCH 39/80] fix: test with specific command to test on windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 31 ++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 6255fad..12296ad 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -19,14 +19,33 @@ jobs: - name: Install Eigen (windows) run: choco install eigen - - name: testing windows + - name: Clara testing windows run: | - echo "check g++" - g++ --version - cd clara_test - .\run_test + echo "Initialize windows" + git clone https://github.com/google/googletest.git -b release-1.11.0 + cd googletest + mkdir build + cd build + cmake .. + cmake --build . --config Release --target install + cd ../.. + rmdir /s /q googletest + + - name: Build clara test windows + run: | + mkdir build + cd build + cmake .. + cmake --build . --config Release + + - name: Running Clara test windows + run: | + echo "running" + echo "result from clara test" + cd build/tests + ./clara_testing.exe - name: testing grover search (windows) - run: + run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I C:\tools\eigen\include -I C:\clara\include testing\grover_search.cpp -o grover_search.exe grover_search.exe From 6428c16b8065eb34cbd5243f9800505a82212d8c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 14:58:28 +0700 Subject: [PATCH 40/80] fix: test rmdir to rd according information from microsoft official website : https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/rd Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 12296ad..2bea371 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -29,7 +29,7 @@ jobs: cmake .. cmake --build . --config Release --target install cd ../.. - rmdir /s /q googletest + rd /s /q googletest - name: Build clara test windows run: | From 4baa0e3d6a17425a8141e7bf7a5a8377f80603cf Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 15:04:53 +0700 Subject: [PATCH 41/80] fix: test rd to rmdir with shell powershell according information from microsoft official website : https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/rd Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 2bea371..5057027 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -20,6 +20,7 @@ jobs: run: choco install eigen - name: Clara testing windows + shell: powershell run: | echo "Initialize windows" git clone https://github.com/google/googletest.git -b release-1.11.0 @@ -29,7 +30,7 @@ jobs: cmake .. cmake --build . --config Release --target install cd ../.. - rd /s /q googletest + rmdir -Recurse -Force googletest - name: Build clara test windows run: | From a4294561469788a78f9a66ebdaf0843d86c52284 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 15:12:03 +0700 Subject: [PATCH 42/80] fix: test to adding path from eigen was installed Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 5057027..7019fc2 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -19,6 +19,11 @@ jobs: - name: Install Eigen (windows) run: choco install eigen + - name: setting eigen include directory + run: | + echo "Setting EIGEN3_INCLUDE_DIR" + echo "::set-env name=EIGEN3_INCLUDE_DIR::C:\\tools\\eigen\\include" + - name: Clara testing windows shell: powershell run: | @@ -36,7 +41,7 @@ jobs: run: | mkdir build cd build - cmake .. + cmake -DEIGEN3_INCLUDE_DIR=$env:EIGEN3_INCLUDE_DIR .. cmake --build . --config Release - name: Running Clara test windows From 0392ebe0ef3cd5128556572941ff1f8ff15912ed Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 15:16:27 +0700 Subject: [PATCH 43/80] fix: testing env github action path setting eigen according from github documentation: https://docs.github.com/en/actions/learn-github-actions/variables Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 7019fc2..c1e5303 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -13,17 +13,16 @@ on: jobs: build-and-testing-windows: runs-on: windows-latest + + env: + EIGEN3_INCLUDE_IDR: C:\tools\eigen\include + steps: - name: checkout repository uses: actions/checkout@v3 - name: Install Eigen (windows) run: choco install eigen - - name: setting eigen include directory - run: | - echo "Setting EIGEN3_INCLUDE_DIR" - echo "::set-env name=EIGEN3_INCLUDE_DIR::C:\\tools\\eigen\\include" - - name: Clara testing windows shell: powershell run: | From 7e350a01dca8ced62cc2e69e71ac021f4d4d9d9f Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 15:39:50 +0700 Subject: [PATCH 44/80] fix: testing env github action path setting eigen according from github documentation: https://docs.github.com/en/actions/learn-github-actions/variables Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index c1e5303..a2b7beb 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -34,7 +34,7 @@ jobs: cmake .. cmake --build . --config Release --target install cd ../.. - rmdir -Recurse -Force googletest + Remove-Item -Recurse -Force googletest - name: Build clara test windows run: | @@ -42,15 +42,3 @@ jobs: cd build cmake -DEIGEN3_INCLUDE_DIR=$env:EIGEN3_INCLUDE_DIR .. cmake --build . --config Release - - - name: Running Clara test windows - run: | - echo "running" - echo "result from clara test" - cd build/tests - ./clara_testing.exe - - - name: testing grover search (windows) - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I C:\tools\eigen\include -I C:\clara\include testing\grover_search.cpp -o grover_search.exe - grover_search.exe From 13347d1894c98faabeed12cb7437c89281fba298 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 15:42:53 +0700 Subject: [PATCH 45/80] fix: attemp fixing on directory command Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index a2b7beb..b3eeb08 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -26,6 +26,7 @@ jobs: - name: Clara testing windows shell: powershell run: | + cd clara_test echo "Initialize windows" git clone https://github.com/google/googletest.git -b release-1.11.0 cd googletest From 975e0690d74aafc4a7272da6a02247a0a0212266 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:04:04 +0700 Subject: [PATCH 46/80] fix: try to adding cmake list for windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 14 ++-- clara_test/CMakeListsWindows.txt | 86 +++++++++++++++++++++++ 2 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 clara_test/CMakeListsWindows.txt diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index b3eeb08..de22276 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -13,9 +13,9 @@ on: jobs: build-and-testing-windows: runs-on: windows-latest - + env: - EIGEN3_INCLUDE_IDR: C:\tools\eigen\include + EIGEN3_INCLUDE_DIR: C:\tools\eigen\include steps: - name: checkout repository @@ -23,7 +23,7 @@ jobs: - name: Install Eigen (windows) run: choco install eigen - - name: Clara testing windows + - name: Install googletest shell: powershell run: | cd clara_test @@ -36,10 +36,10 @@ jobs: cmake --build . --config Release --target install cd ../.. Remove-Item -Recurse -Force googletest - - - name: Build clara test windows + + - name: testing run: | mkdir build cd build - cmake -DEIGEN3_INCLUDE_DIR=$env:EIGEN3_INCLUDE_DIR .. - cmake --build . --config Release + cmake -f ../CMakeListsWindows.txt + cmake --buuild . --config Release diff --git a/clara_test/CMakeListsWindows.txt b/clara_test/CMakeListsWindows.txt new file mode 100644 index 0000000..4511f93 --- /dev/null +++ b/clara_test/CMakeListsWindows.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.0.0) +project(clara_testing) + +if(CYGWIN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +set(ADDITIONAL_FLAGS "-pedantic -Wall -Wextra -Weffc++") +set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem") + +if(NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" + AND NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + message(FATAL_ERROR "cmakelists works only with gnu gcc/clang/appleClang") +endif() + +include_directories(../include) +# eigen +#[[ include_directories(SYSTEM "$ENV{HOME}/eigen") ]] +include_directories(SYSTEM "C:/tools/eigen/include/eigen3") + +option(WITH_OPENMP "OpenMP support" ON) +if(${WITH_OPENMP}) + add_definitions(-DWITH_OPENMP_) + if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") + set(CLANG_LIBOMP "/opt/local/lib/libomp/") + set(CLANG_LIBOMP_INCLUDE "/opt/local/include/libomp/") + include_directories(SYSTEM "${CLANG_LIBOMP_INCLUDE}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp=libomp") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CLANG_LIBOMP}") + endif() + else() + message( + WARNING + "compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}. support for OpenMP is enabled only for GNU gcc compiler Clang >= 3.7" + ) + endif() +endif() + +# google test +add_subdirectory(googletest) + +# unitesting +add_subdirectory(tests) + +# enable all warnings +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ADDITIONAL_FLAGS}") + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") + add_definitions(-DNO_THREAD_LOCAL_) + message( + WARNING + "Detect compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} thread_local not supported" + ) +endif() + +# gnu gcc aditional debug setting +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-weak") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -Og -D_GLIBCXX_DEBUG") +endif() + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") +set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -DEIGEN_NO_DEBUG") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO + "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DEIGEN_NO_DEBUG") + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build, options are: \ + None Debug Release MinSizeRel RelWithDebInfo." FORCE) +endif() + +if($WITH_OPENMP$ + AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") + target_link_libraries(clara_testing omp) +endif() From f3e5e36d8efa9266c3642f73dd3309c973d32413 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:07:36 +0700 Subject: [PATCH 47/80] fix: typo Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index de22276..c555990 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -42,4 +42,4 @@ jobs: mkdir build cd build cmake -f ../CMakeListsWindows.txt - cmake --buuild . --config Release + cmake --build . --config Release From f53e2a08e398ba2c5dddd9783ac18e5d27a963f9 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:13:28 +0700 Subject: [PATCH 48/80] fix: call cmake Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index c555990..cdedc46 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -41,5 +41,5 @@ jobs: run: | mkdir build cd build - cmake -f ../CMakeListsWindows.txt + cmake -DCMAKE_GENERATOR_FILE=../CMakeListsWindows.txt cmake --build . --config Release From 44ee71a44400116b83cf3828e10324987b4d26c3 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:22:48 +0700 Subject: [PATCH 49/80] fix: call cmake Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index cdedc46..7436dbe 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -39,6 +39,7 @@ jobs: - name: testing run: | + cd clara_test mkdir build cd build cmake -DCMAKE_GENERATOR_FILE=../CMakeListsWindows.txt From 23720468727e66ab95d62f9d0e825fbde0e7f046 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:26:59 +0700 Subject: [PATCH 50/80] fix: call cmake Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 7436dbe..13a9daa 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -42,5 +42,5 @@ jobs: cd clara_test mkdir build cd build - cmake -DCMAKE_GENERATOR_FILE=../CMakeListsWindows.txt + cmake -DCMAKE_GENERATOR_FILE=CMakeListsWindows.txt cmake --build . --config Release From b389a7bea6a26e209fd43b342bab7f898ced2056 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:31:58 +0700 Subject: [PATCH 51/80] fix: call cmake Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml index 13a9daa..cd97152 100644 --- a/.github/workflows/cpp-testing-windows.yml +++ b/.github/workflows/cpp-testing-windows.yml @@ -42,5 +42,5 @@ jobs: cd clara_test mkdir build cd build - cmake -DCMAKE_GENERATOR_FILE=CMakeListsWindows.txt + cmake -DCMAKE_GENERATOR_FILE=../CMakeListsWindows.txt .. cmake --build . --config Release From f70f2216f6b41c3cf678d79c2695c91f2864b37d Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:43:11 +0700 Subject: [PATCH 52/80] fix: remove windows test and adding on arch latest Signed-off-by: slowy07 --- .github/workflows/cpp-testing-linux.yml | 8 ++- .github/workflows/cpp-testing-windows.yml | 46 ------------ clara_test/CMakeListsWindows.txt | 86 ----------------------- 3 files changed, 6 insertions(+), 134 deletions(-) delete mode 100644 .github/workflows/cpp-testing-windows.yml delete mode 100644 clara_test/CMakeListsWindows.txt diff --git a/.github/workflows/cpp-testing-linux.yml b/.github/workflows/cpp-testing-linux.yml index 08f2fbf..df54aed 100644 --- a/.github/workflows/cpp-testing-linux.yml +++ b/.github/workflows/cpp-testing-linux.yml @@ -12,7 +12,9 @@ on: jobs: build-and-testing: - runs-on: ubuntu-latest + strategy: + matrix: + operating-system: [ubuntu-latest, arch-latest] steps: - name: Checkout repository @@ -20,7 +22,9 @@ jobs: - name: Install Eigen run: | - sudo apt-get install libeigen3-dev + sudo sudo apt-get update && sudo apt-get install -y libeigen3-dev + sudo pacman -Syu --noconfirm eigen + sudo cp -r /usr/include/eigen3/Eigen /usr/local/include - name: Build and test diff --git a/.github/workflows/cpp-testing-windows.yml b/.github/workflows/cpp-testing-windows.yml deleted file mode 100644 index cd97152..0000000 --- a/.github/workflows/cpp-testing-windows.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Clara Test Windows - -on: - push: - branches: - - main - - development - pull_request: - branches: - - main - - development - -jobs: - build-and-testing-windows: - runs-on: windows-latest - - env: - EIGEN3_INCLUDE_DIR: C:\tools\eigen\include - - steps: - - name: checkout repository - uses: actions/checkout@v3 - - name: Install Eigen (windows) - run: choco install eigen - - - name: Install googletest - shell: powershell - run: | - cd clara_test - echo "Initialize windows" - git clone https://github.com/google/googletest.git -b release-1.11.0 - cd googletest - mkdir build - cd build - cmake .. - cmake --build . --config Release --target install - cd ../.. - Remove-Item -Recurse -Force googletest - - - name: testing - run: | - cd clara_test - mkdir build - cd build - cmake -DCMAKE_GENERATOR_FILE=../CMakeListsWindows.txt .. - cmake --build . --config Release diff --git a/clara_test/CMakeListsWindows.txt b/clara_test/CMakeListsWindows.txt deleted file mode 100644 index 4511f93..0000000 --- a/clara_test/CMakeListsWindows.txt +++ /dev/null @@ -1,86 +0,0 @@ -cmake_minimum_required(VERSION 3.0.0) -project(clara_testing) - -if(CYGWIN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -endif() - -set(ADDITIONAL_FLAGS "-pedantic -Wall -Wextra -Weffc++") -set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem") - -if(NOT ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" - AND NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - message(FATAL_ERROR "cmakelists works only with gnu gcc/clang/appleClang") -endif() - -include_directories(../include) -# eigen -#[[ include_directories(SYSTEM "$ENV{HOME}/eigen") ]] -include_directories(SYSTEM "C:/tools/eigen/include/eigen3") - -option(WITH_OPENMP "OpenMP support" ON) -if(${WITH_OPENMP}) - add_definitions(-DWITH_OPENMP_) - if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") - elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") - set(CLANG_LIBOMP "/opt/local/lib/libomp/") - set(CLANG_LIBOMP_INCLUDE "/opt/local/include/libomp/") - include_directories(SYSTEM "${CLANG_LIBOMP_INCLUDE}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp=libomp") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CLANG_LIBOMP}") - endif() - else() - message( - WARNING - "compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}. support for OpenMP is enabled only for GNU gcc compiler Clang >= 3.7" - ) - endif() -endif() - -# google test -add_subdirectory(googletest) - -# unitesting -add_subdirectory(tests) - -# enable all warnings -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ADDITIONAL_FLAGS}") - -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") - add_definitions(-DNO_THREAD_LOCAL_) - message( - WARNING - "Detect compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} thread_local not supported" - ) -endif() - -# gnu gcc aditional debug setting -if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-weak") - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -Og -D_GLIBCXX_DEBUG") -endif() - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -DEIGEN_NO_DEBUG") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO - "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DEIGEN_NO_DEBUG") - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE - Release - CACHE STRING "Choose the type of build, options are: \ - None Debug Release MinSizeRel RelWithDebInfo." FORCE) -endif() - -if($WITH_OPENMP$ - AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" - AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.7") - target_link_libraries(clara_testing omp) -endif() From 48430b8f57c813b5b70fad668525c033a7c9fb9c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:44:20 +0700 Subject: [PATCH 53/80] fix: adding Signed-off-by: slowy07 --- .github/workflows/cpp-testing-linux.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-linux.yml b/.github/workflows/cpp-testing-linux.yml index df54aed..f042e14 100644 --- a/.github/workflows/cpp-testing-linux.yml +++ b/.github/workflows/cpp-testing-linux.yml @@ -1,4 +1,4 @@ -name: Clara Test Ubuntu +name: Clara Test linux on: push: @@ -16,6 +16,8 @@ jobs: matrix: operating-system: [ubuntu-latest, arch-latest] + runs-on: ${{ matrix.operating-system }} + steps: - name: Checkout repository uses: actions/checkout@v3 From cbf5c5968ef352a7cb85058212c8430f20c07757 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:46:13 +0700 Subject: [PATCH 54/80] fix: adding separate the install test Signed-off-by: slowy07 --- .github/workflows/cpp-testing-linux.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cpp-testing-linux.yml b/.github/workflows/cpp-testing-linux.yml index f042e14..ca8c63e 100644 --- a/.github/workflows/cpp-testing-linux.yml +++ b/.github/workflows/cpp-testing-linux.yml @@ -25,8 +25,13 @@ jobs: - name: Install Eigen run: | sudo sudo apt-get update && sudo apt-get install -y libeigen3-dev + + - name: Install eigen on arch latest + run: | sudo pacman -Syu --noconfirm eigen + - name: configure eigen3 + run: | sudo cp -r /usr/include/eigen3/Eigen /usr/local/include - name: Build and test From 7fc1eb31ad6e3aed9db1bd35be854eaf6d63a503 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 16:51:37 +0700 Subject: [PATCH 55/80] fix: adding arch based by separated the test Signed-off-by: slowy07 --- ...g-linux.yml => cpp-testing-linux-base.yml} | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) rename .github/workflows/{cpp-testing-linux.yml => cpp-testing-linux-base.yml} (50%) diff --git a/.github/workflows/cpp-testing-linux.yml b/.github/workflows/cpp-testing-linux-base.yml similarity index 50% rename from .github/workflows/cpp-testing-linux.yml rename to .github/workflows/cpp-testing-linux-base.yml index ca8c63e..b460761 100644 --- a/.github/workflows/cpp-testing-linux.yml +++ b/.github/workflows/cpp-testing-linux-base.yml @@ -1,4 +1,4 @@ -name: Clara Test linux +name: Clara Test Linux Base on: push: @@ -12,11 +12,7 @@ on: jobs: build-and-testing: - strategy: - matrix: - operating-system: [ubuntu-latest, arch-latest] - - runs-on: ${{ matrix.operating-system }} + runs-on: ubuntu-latest steps: - name: Checkout repository @@ -24,17 +20,40 @@ jobs: - name: Install Eigen run: | - sudo sudo apt-get update && sudo apt-get install -y libeigen3-dev - - - name: Install eigen on arch latest + sudo apt-get install libeigen3-dev + sudo cp -r /usr/include/eigen3/Eigen /usr/local/include + + - name: Build and test run: | - sudo pacman -Syu --noconfirm eigen + echo "check g++ version" + g++ --version + cd clara_test + chmod +x run_test + ./run_test - - name: configure eigen3 + - name: testing grover search run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/grover_search.cpp -o grover_search + ./grover_search + + - name: testing channels + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels + ./channels + + build-testing-arch: + runs-on: arch-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: install eigen + run: | + sudo pacamn -Syu --noconfirm eigen sudo cp -r /usr/include/eigen3/Eigen /usr/local/include - - - name: Build and test + + - name: build and testing run: | echo "check g++ version" g++ --version @@ -51,3 +70,4 @@ jobs: run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels ./channels + From a46eb5baf8217efcbb6a5b61b9d76533840ea0aa Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 3 Aug 2023 17:17:57 +0700 Subject: [PATCH 56/80] fix: adding arch based by separated the test Signed-off-by: slowy07 --- .github/workflows/cpp-testing-linux-base.yml | 30 -------------------- 1 file changed, 30 deletions(-) diff --git a/.github/workflows/cpp-testing-linux-base.yml b/.github/workflows/cpp-testing-linux-base.yml index b460761..760274b 100644 --- a/.github/workflows/cpp-testing-linux-base.yml +++ b/.github/workflows/cpp-testing-linux-base.yml @@ -41,33 +41,3 @@ jobs: g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels ./channels - build-testing-arch: - runs-on: arch-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: install eigen - run: | - sudo pacamn -Syu --noconfirm eigen - sudo cp -r /usr/include/eigen3/Eigen /usr/local/include - - - name: build and testing - run: | - echo "check g++ version" - g++ --version - cd clara_test - chmod +x run_test - ./run_test - - - name: testing grover search - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/grover_search.cpp -o grover_search - ./grover_search - - - name: testing channels - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -isystem $HOME/eigen -I $HOME/clara/include testing/channels.cpp -o channels - ./channels - From a558f5b40147e01764afca185013ecb83ed42645 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sat, 5 Aug 2023 00:08:37 +0700 Subject: [PATCH 57/80] chore: update shell test Signed-off-by: slowy07 --- clara_test/run_test | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/clara_test/run_test b/clara_test/run_test index 859983f..05b8576 100755 --- a/clara_test/run_test +++ b/clara_test/run_test @@ -19,14 +19,18 @@ else exit 1 fi -echo "Installing eigen" - if [[ "$PACKAGE_MANAGER" == "apt-get" ]]; then - sudo apt-get install libeigen3-dev + if ! dpkg -l | grep libeigen; then + sudo apt-get install libeigen3-dev + fi elif [[ "$PACKAGE_MANAGER" == "pacman" ]]; then - sudo pacman -S eigen + if ! pacman -Q eigen; then + sudo pacman -S eigen + fi elif [[ "$PACKAGE_MANAGER" == "brew" ]]; then - brew install eigen + if ! brew list | grep eigen; then + brew install eigen + fi fi echo "Initializing Google Test" From b46e1545560944e44b61529a5ff11856f4e03dcc Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sat, 5 Aug 2023 20:24:56 +0700 Subject: [PATCH 58/80] fix: rename testing filename Signed-off-by: slowy07 --- clara_test/run_test | 7 +- clara_test/tests/CMakeLists.txt | 8 +- .../tests/classFunction/codes_testing.cpp | 20 + .../{gates.cpp => gates_testing.cpp} | 11 + .../classFunction/random_devices_testing.cpp | 38 ++ .../{states.cpp => states_testing.cpp} | 0 .../{timer.cpp => timer_testing.cpp} | 0 clara_test/tests/random_clara.cpp | 18 + clara_test/tests/traits.cpp | 56 ++- include/experimental/experimental_test.h | 405 ++++-------------- 10 files changed, 209 insertions(+), 354 deletions(-) create mode 100644 clara_test/tests/classFunction/codes_testing.cpp rename clara_test/tests/classFunction/{gates.cpp => gates_testing.cpp} (80%) create mode 100644 clara_test/tests/classFunction/random_devices_testing.cpp rename clara_test/tests/classFunction/{states.cpp => states_testing.cpp} (100%) rename clara_test/tests/classFunction/{timer.cpp => timer_testing.cpp} (100%) diff --git a/clara_test/run_test b/clara_test/run_test index 05b8576..ff8b19d 100755 --- a/clara_test/run_test +++ b/clara_test/run_test @@ -1,5 +1,10 @@ #!/bin/bash +echo " +█▀▀ █░░ ▄▀█ █▀█ ▄▀█   ▀█▀ █▀▀ █▀ ▀█▀ █ █▄░█ █▀▀ +█▄▄ █▄▄ █▀█ █▀▄ █▀█   ░█░ ██▄ ▄█ ░█░ █ █░▀█ █▄█" +echo "" + OS=$(uname -s) PACKAGE_MANAGER="" @@ -9,7 +14,7 @@ if [[ "$OS" == "Linux" ]]; then elif command -v pacman >/dev/null 2>&1; then PACKAGE_MANAGER="pacman" else - echo "Unsupported package manager. Exiting..." + echo "Error: Unsupported package manager. Exiting..." exit 1 fi elif [[ "$OS" == "Darwin" ]]; then diff --git a/clara_test/tests/CMakeLists.txt b/clara_test/tests/CMakeLists.txt index 66ed079..33440f4 100644 --- a/clara_test/tests/CMakeLists.txt +++ b/clara_test/tests/CMakeLists.txt @@ -1,9 +1,11 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) add_executable( clara_testing - classFunction/gates.cpp - classFunction/states.cpp - classFunction/timer.cpp + classFunction/gates_testing.cpp + classFunction/states_testing.cpp + classFunction/timer_testing.cpp + classFunction/codes_testing.cpp + classFunction/random_devices_testing.cpp testing_main.cpp traits.cpp operations.cpp diff --git a/clara_test/tests/classFunction/codes_testing.cpp b/clara_test/tests/classFunction/codes_testing.cpp new file mode 100644 index 0000000..311495f --- /dev/null +++ b/clara_test/tests/classFunction/codes_testing.cpp @@ -0,0 +1,20 @@ +#include +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(CodeTest, CodeWordFiveQubit) { + ket codeword = Codes::get_instance().codeword(Codes::Type::FIVE_QUBIT, 0); + EXPECT_NEAR(abs(codeword.norm()), 1.0, 1e-12); +} + +TEST(CodeTest, Codeword7Qubit) { + ket codeword = Codes::get_instance().codeword(Codes::Type::SEVEN_QUBIT_STEANE, 0); + EXPECT_NEAR(abs(codeword.norm()), 1.0, 1e-12); +} + +TEST(CodeTest, Codeword9Qubit) { + ket codeword = Codes::get_instance().codeword(Codes::Type::NINE_QUBIT_SHOR, 0); + EXPECT_NEAR(abs(codeword.norm()), 1.0, 1e-12); +} diff --git a/clara_test/tests/classFunction/gates.cpp b/clara_test/tests/classFunction/gates_testing.cpp similarity index 80% rename from clara_test/tests/classFunction/gates.cpp rename to clara_test/tests/classFunction/gates_testing.cpp index 7bd1d3e..43ad971 100644 --- a/clara_test/tests/classFunction/gates.cpp +++ b/clara_test/tests/classFunction/gates_testing.cpp @@ -29,6 +29,17 @@ TEST(clara_Gates_control, Qubits) { EXPECT_NEAR(0, norm(CTRL4 * psi2 - res2), 1e-7); } +TEST(clara_Gates_Xd, AllTest) { + for (idx D = 1; D < 10; ++D) { + cmat Xd = gt.Xd(D); + for (idx i = 0; i < D; ++i) { + ket psi = mket({i}, D); + ket res = mket({(i + 1) % D}, D); + EXPECT_NEAR(0, norm(res - Xd * psi), 1e-7); + } + } +} + TEST(clara_Gates_Zd, AllTest) { for (idx D = 1; D < 10; ++D) { cmat Zd = gt.Zd(D); diff --git a/clara_test/tests/classFunction/random_devices_testing.cpp b/clara_test/tests/classFunction/random_devices_testing.cpp new file mode 100644 index 0000000..e7f8fdf --- /dev/null +++ b/clara_test/tests/classFunction/random_devices_testing.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(RandomDevicesTest, GetPrng) { + std::mt19937& prng = RandomDevices::get_instance().get_prng(); + EXPECT_NE(prng.state_size, std::mt19937::default_seed); + + int random_number = prng(); + EXPECT_NE(random_number, 0); +} + +TEST(RandomDevicesTest, loadGetPrng) { + std::stringstream ss; + clara::rdevs.save(ss); + cmat A1 = rand(4, 4); + ss.seekg(0); + clara::rdevs.load(ss); + cmat A2 = rand(4, 4); + EXPECT_EQ(0, norm(A1 - A2)); +} + +TEST(RandomDevicesTest, loadGetPrngTest) { + std::stringstream ss; + clara::rdevs.save(ss); + bigint b1 = rand(static_cast(-100), 100); + ss.seekg(0); + clara::rdevs.load(ss); + bigint b2 = rand(static_cast(-100), 100); + EXPECT_EQ(b1, b2); +} diff --git a/clara_test/tests/classFunction/states.cpp b/clara_test/tests/classFunction/states_testing.cpp similarity index 100% rename from clara_test/tests/classFunction/states.cpp rename to clara_test/tests/classFunction/states_testing.cpp diff --git a/clara_test/tests/classFunction/timer.cpp b/clara_test/tests/classFunction/timer_testing.cpp similarity index 100% rename from clara_test/tests/classFunction/timer.cpp rename to clara_test/tests/classFunction/timer_testing.cpp diff --git a/clara_test/tests/random_clara.cpp b/clara_test/tests/random_clara.cpp index a49bcf7..734e20d 100644 --- a/clara_test/tests/random_clara.cpp +++ b/clara_test/tests/random_clara.cpp @@ -1,4 +1,5 @@ #include +#include #include "../../include/clara.h" #include "gtest/gtest.h" @@ -8,6 +9,23 @@ using namespace clara; TEST(clara_rand_integer, AllTests) { bigint a = 42, b = a; EXPECT_EQ(a, clara::rand(a, b)); + + a = 1; + b = 5; + for (int i = 0; i < 1000; ++i) { + int n = clara::rand(a, b); + EXPECT_GE(n, a); + EXPECT_LE(n, b); + } +} + +TEST(clara_rand_min_max, AllTests) { + bigint a = INT_MIN, b = INT_MAX; + for (int i = 0; i < 1000; ++i) { + int n = clara::rand(a, b); + EXPECT_GE(n, a); + EXPECT_LE(n, b); + } } TEST(clara_rand_double, AllTest) { diff --git a/clara_test/tests/traits.cpp b/clara_test/tests/traits.cpp index 8831b85..36040f5 100644 --- a/clara_test/tests/traits.cpp +++ b/clara_test/tests/traits.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -9,42 +10,52 @@ using namespace clara; -TEST(clara_is_complex, AllTests) { - class ComplexNumber { - public: - ComplexNumber(double real, double imag) : real_(real), imag_(imag) {} +class ComplexNumber { + public: + ComplexNumber(double real, double imag) : real_(real), imag_(imag) {} + double real() const { return real_; } + double image() const { return imag_; } - double real() const { return real_; } - double imag() const { return imag_; } + private: + double real_; + double imag_; +}; - private: - double real_; - double imag_; - }; +TEST(clara_is_complex, AllTests) { + // test if provided types are complex numbers EXPECT_TRUE(clara::is_complex>::value); EXPECT_TRUE(clara::is_complex>::value); + + // custom ComplexNumber class is not complex number + EXPECT_FALSE(clara::is_complex::value); } TEST(clara_is_iterable, AllTests) { + // custom iterable class for testing class CustomIterable { public: using value_type = int; - int* begin() { return &data[0]; } - - int* end() { return &data[size]; } + + const int* begin() {return &data[0];} + const int* end() {return &data[size]; } + const int* begin() const {return &data[0];} + const int* end() const {return &data[size];} + CustomIterable(std::initializer_list initList) : data(initList), size(initList.size()) {} - private: + private: std::vector data; std::size_t size; }; - EXPECT_TRUE(clara::is_iterable::value); EXPECT_FALSE(clara::is_iterable::value); + // std::vector is iterable EXPECT_TRUE(clara::is_iterable>::value); + // std::list is iterable EXPECT_TRUE(clara::is_iterable>::value); - EXPECT_TRUE(clara::is_iterable>::value); + // CustomIterable is iterable + EXPECT_TRUE(clara::is_iterable::value); } TEST(clara_is_matrix_expression, AllTests) { @@ -52,16 +63,19 @@ TEST(clara_is_matrix_expression, AllTests) { int x{}, y{}, z{}; class NonMatrixCustomClass { - public: - int getValue() const { return value_; } - void setValue(int value) { value_ = value; } - - private: + public: + int getValue() const {return value_;} + void setValue(int value) {value_ = value;} + private: int value_; }; + // scalar multiplication is a matrix expression EXPECT_TRUE(clara::is_matrix_expression::value); + // matrix addition is matrix expression EXPECT_TRUE(clara::is_matrix_expression::value); + // x + y * z is not a matrix expression EXPECT_FALSE(clara::is_matrix_expression::value); + // NonMatrixCustomClass is not a matrix expression EXPECT_FALSE(clara::is_matrix_expression::value); } diff --git a/include/experimental/experimental_test.h b/include/experimental/experimental_test.h index 715809b..eb54b12 100644 --- a/include/experimental/experimental_test.h +++ b/include/experimental/experimental_test.h @@ -2,350 +2,97 @@ #define EXPERIMENTAL_EXPERIMENTAL_TEST_H_ #include -#include -#include -#include +#include +#include #include #include -#include #include #include -using idx = std::size_t; +#include "../classFunction/gates.h" +#include "../classFunction/idisplay.h" namespace clara { namespace experimental { -/** - * @class Dynamic_bitset - * @brief Dynamic bitest class, allows the specification of the number of number bits at runtime - * - * the Dynamic_bitset class is a dynamic bitset that allows the specification of the number of bits - * at runtime. it provides functionalities to manipulate individual bits, set random bits, and - * perform bit operations. - */ -class Dynamic_bitset { - public: - using value_type = unsigned int; - using storage_type = std::vector; - - protected: - // storage size - idx storage_size_; - // number of bits - idx N_; - // storage space - std::vector v_; - - /** - * @brief index of the pos bit in the storage space - * @return index of the pos bit in thestorage space - */ - idx index_(idx pos) const { return pos / (sizeof(value_type) * CHAR_BIT); } - - /** - * @brief constructor, intialize all bits to false - * @return offset of the pos bit in the storage space relative to its index - */ - idx offset_(idx pos) const { return pos % (sizeof(value_type) * CHAR_BIT); } - - /** - * @brief offset of the pos bit in the storage space relativea - * to its index - * @return offset of the pos bit in the storage space relative its - * index - */ - public: - Dynamic_bitset(idx N) - : storage_size_{N / (sizeof(value_type) * CHAR_BIT) + 1}, N_{N}, v_(storage_size_) {} - - /** - * @brief raw storage space of the bitset - * @return const reference to the underlying - */ - const storage_type& data() const { return v_; } - - /** - * @brief number of bits stored in the bitset - * @return number of bits - */ - idx size() const { return N_; } - - /** - * @brief size of the underlying stroage space - * @return size of the underlying storage space - */ - idx storage_size() const { return storage_size_; } - - idx count() const noexcept { - std::size_t result = 0; - for (idx i = 0; i < size(); ++i) { - if (this->get(i)) - ++result; - } - return result; - } - - /** - * @brief get the value of the bit at the given position - * @param pos position of the bit to check - * @return true if the bit set (1), false otherwise (0) - */ - bool get(idx pos) const { return 1 & (v_[index_(pos)] >> offset_(pos)); } - - /** - * @dbrief check if none of the bits are set in the bitset - * @return true if none of the bits are set, false otherwise - */ - bool none() const noexcept { - bool result = true; - for (idx i = 0; i < storage_size(); ++i) { - if (v_[i]) { - return false; - } - } - return result; - } - - /** - * @brief check if all bits are set in the bitset - * @return true if all bits are set, false otherwise - */ - bool all() const noexcept { - bool result = true; - for (idx i = 0; i < storage_size(); ++i) { - if (~v_[i]) { - return false; - } - } - return false; - } - - /** - * @brief check if any of the bits are set in the bitset - * @return true if any of the bits are set, false otherwise - */ - bool any() const noexcept { return !(this->none()); } - - /** - * @brief set the bit at the given position to the specified value - * @param pos position of the bitset - * @param value value to set the bit - * @return reference to the modified bitset - */ - Dynamic_bitset& set(idx pos, bool value = true) { - value ? v_[index_(pos)] |= (1 << offset_(pos)) : v_[index_(pos)] &= ~(1 << offset_(pos)); - return *this; - } - - /* - * @brief all bits in the bitset to the specified value (true = 1, false = 0) - * @return reference to the modified bitset - */ - Dynamic_bitset& set() noexcept { - for (idx i = 0; i < storage_size(); ++i) { - v_[i] = ~0; - } - return *this; - } - - /** - * @brief set the bit at the given position randomly according to a Bernoulli distribution - * @param pos position of the bit to set - * @param p probability of setting the bit to 1 - * @return reference to the modified bitset - */ - Dynamic_bitset& rand(idx pos, double p = 0.5) { - std::random_device rd; - std::mt19937 gen{rd()}; - std::bernoulli_distribution d{p}; - - this->set(pos, d(gen)); - return *this; - } - - /** - * @brief set all bits in the bitset randomly according to a Bernoulli distribution - * @param p probability of setting each bit to 1 - * @return reference to the modified bitset - */ - Dynamic_bitset& rand(double p = 0.5) { - for (idx i = 0; i < size(); ++i) { - this->rand(i, p); - } - return *this; - } - - /** - * @brief reset the bit at the given position (set 0) - * @param pos position of the bit to reset - * @return reference to the modified bitset - */ - Dynamic_bitset& reset(idx pos) { - v_[index_(pos)] &= ~(1 << offset_(pos)); - return *this; - } - - /** - * @brief reset all bits in the bitset - * @return reference to the modified bitset - */ - Dynamic_bitset& reset() noexcept { - for (idx i = 0; i < storage_size(); ++i) { - v_[i] = 0; - } - return *this; - } - - /** - * @brief flip the bit at the given position - * @param position orf the bit to flip - * @return reference to the modified bitset - */ - Dynamic_bitset& flip(idx pos) { - v_[index_(pos)] ^= 1 << (offset_(pos)); - return *this; - } - - /** - * @brief equality comparsion operator for bitsets - * @param rhs bitset to compare with rhs - * @return true if the bitsets are equal, false otherwise - */ - Dynamic_bitset& flip() noexcept { - for (idx i = 0; i < storage_size(); ++i) { - v_[i] = ~v_[i]; - } - return *this; - } - - /** - * @brief equality comprasion operator for bitsets - * @param rhs bitset to compare with - * @return true if the bitsets are equal, false otherwise - */ - bool operator==(const Dynamic_bitset& rhs) const noexcept { - assert(this->size() == rhs.size()); - bool result = true; - idx n = std::min(this->storage_size(), rhs.storage_size()); - for (idx i = 0; i < n; ++i) { - if (v_[i] != rhs.v_[i]) { - return false; - } - } - return result; - } - - /** - * @brief inequality comprasion oeprator for bitset - * @param rhs bitset to compare with - * @return true if the bitsets are not equal, false otherwise - */ - bool operator!=(const Dynamic_bitset& rhs) const noexcept { return !(*this == rhs); } - - /** - * @brief output stream operator to print the bitset in binary representation - * @param os output stream - * @param rhs bitset to print - * @return reference to the output stream - */ - friend std::ostream& operator<<(std::ostream& os, const Dynamic_bitset& rhs) { - for (idx i = rhs.size(); i-- > 0;) { - os << rhs.get(i); - } - return os; - } - - /** - * @brief convert the bitset to a string representation - * @param zero character to represent 0 bit - * @param one character to represent 1 bit - * @return string representation of the bitset - */ - template , - class Allocator = std::allocator> - std::basic_string to_string(CharT zero = CharT('0'), - CharT one = CharT('1')) const { - std::basic_string result; - idx bitset_size = this->size(); - result.resize(bitset_size); - - for (idx i = bitset_size; i-- > 0;) { - if (!this->get(i)) { - result[bitset_size - i - 1] = zero; - } else { - result[bitset_size - i - 1] = one; +class ClaraCircuit : public IDisplay { + idx number_qudits_; + idx number_classical_bits_; + idx dimension_; + ket psi_; + std::vector dits_; + std::vector measurement_steps_; + + enum class GateType { + SINGLE, + TWO, + THREE, + FAN, + CUSTOM, + SINGLE_CTRL_SINGLE_TARGET, + SINGLE_CTRL_MULTIPLE_TARGET, + MULTIPLE_CTRL_SINGLE_TARGET, + MULTIPLE_CTRL_MULTIPLE_TARGET, + CUSTOM_CTRL, + SINGLE_cCTRL_SINGLE_TARGET, + SINGLE_cCTRL_MULTIPLE_TARGET, + MULTIPLE_cCTRL_SINGLE_TARGET, + MULTIPLE_cCTRL_MULTIPLE_TARGET, + CUSTOM_cCTRL, + NONE + }; + + enum class MeasureType { MEASURE_Z, MEASURE_V, MEASURE_KS, NONE }; + + struct GateStep { + GateType gate_type_ = GateType::NONE; + cmat gate_; + std::vector ctrl_; + std::vector target_; + std::string name_; + + GateStep() = default; + GateStep(GateType gate_type, const cmat& gate, const std::vector& ctrl, + const std::vector& target, const std::string& name = "") + : gate_type_{gate_type}, gate_{gate}, ctrl_{ctrl}, target_{target}, name_{name} {} + }; + + struct MeasureStep { + MeasureType measurement_type_ = MeasureType::NONE; + std::vector mats_; + std::vector target_; + std::string name_; + + MeasureStep() = default; + MeasureStep(MeasureType measurement_type, const std::vector& mats, + const std::vector& target, const std::string& name = "") + : measurement_type_{measurement_type}, mats_{mats}, target_{target}, name_{name} {} + }; + + std::vector update_subsys_(const std::vector& subsys) { + std::vector result = subsys; + idx subsys_size = subsys.size(); + for (idx i = 0; i < subsys_size; ++i) { + for (idx m = 0; m < subsys[i]; ++m) { + if (measured_[m]) { + --result[i]; + } } } + std::sort(std::begin(result), std::end(result), std::less{}); return result; } -}; -/** - * @class Bit_circuit - * @brief classical revesible circuit simulator - * - * the Bit_circuit class is a classical - */ -class Bit_circuit : public Dynamic_bitset { public: - struct Gate_count { - // 1 bit gates - idx NOT = 0; - idx& X = NOT; - - // 2 bit gates - idx CNOT = 0; - idx SWAP = 0; - - // 3 bit gates - idx FRED = 0; - idx TOF = 0; - } gate_count{}; - - // inherit the constructor - using Dynamic_bitset::Dynamic_bitset; - - // flip the bit - Bit_circuit& X(idx pos) { - this->flip(pos); - ++gate_count.X; - return *this; - } - - // flips the bit - Bit_circuit& NOT(idx pos) { - this->flip(pos); - ++gate_count.NOT; - return *this; - } - - // toffoli control-control-target - Bit_circuit& TOF(const std::vector& pos) { - v_[index_(pos[2])] ^= ((1 & (v_[index_(pos[1])] >> offset_(pos[1]))) & - (1 & (v_[index_(pos[0])] >> offset_(pos[0])))) - << offset_(pos[2]); - ++gate_count.TOF; - return *this; - } - - // SWAP 2 bits - Bit_circuit& SWAP(const std::vector& pos) { - if (this->get(pos[0]) != this->get(pos[1])) { - this->X(pos[0]); - this->X(pos[1]); - } - ++gate_count.SWAP; - return *this; - } - - Bit_circuit& reset() noexcept { - gate_count.NOT = gate_count.X = 0; - gate_count.CNOT = gate_count.SWAP = 0; - gate_count.FRED = gate_count.TOF = 0; - - return *this; - } + std::vector measured_; + ClaraCircuit(idx number_qudit, idx number_classical_bits = 0, idx dimension = 2, + const std::string& name = ""); + void apply(const cmat& U, idx i, const std::string& name = ""); + void measureZ(idx i, const std::string& name = ""); + std::ostream& display(std::ostream& os) const override; + + friend std::ostream& operator<<(std::ostream& os, const GateType& gate_type); + friend std::ostream& operator<<(std::ostream& os, const MeasureType& measurement_type); }; } // namespace experimental From 1bb72d77f304354c23178b3fd7383ff58d03618c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 14:52:55 +0700 Subject: [PATCH 59/80] chore: testing GPU support install Signed-off-by: slowy07 --- .../workflows/cpp-testing-linux-base-gpu.yml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/cpp-testing-linux-base-gpu.yml diff --git a/.github/workflows/cpp-testing-linux-base-gpu.yml b/.github/workflows/cpp-testing-linux-base-gpu.yml new file mode 100644 index 0000000..336cec1 --- /dev/null +++ b/.github/workflows/cpp-testing-linux-base-gpu.yml @@ -0,0 +1,32 @@ +name: Clara Test Linux Base With GPU Processing + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + + +jobs: + build-and-testing: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: install CUDA + run: | + wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin + sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600 + wget https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda-repo-ubuntu2004-11-3-local_11.3.1-465.19.01-1_amd64.deb + sudo dpkg -i cuda-repo-ubuntu2004-11-3-local_11.3.1-465.19.01-1_amd64.deb + sudo apt-key add /var/cuda-repo-ubuntu2004-11-3-local/7fa2af80.pub + sudo apt-get update + sudo apt-get -y install cuda + export PATH=/usr/local/cuda-11.3/bin${PATH:+:${PATH}} + export LD_LIBRARY_PATH=/usr/local/cuda-11.3/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} From 35ec5624f01c4951e980e6e24234591468cad0fc Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 15:32:06 +0700 Subject: [PATCH 60/80] chore: attemp (1) test gpu CUDA Signed-off-by: slowy07 --- .../workflows/cpp-testing-linux-base-gpu.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cpp-testing-linux-base-gpu.yml b/.github/workflows/cpp-testing-linux-base-gpu.yml index 336cec1..c52bad1 100644 --- a/.github/workflows/cpp-testing-linux-base-gpu.yml +++ b/.github/workflows/cpp-testing-linux-base-gpu.yml @@ -19,14 +19,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: install CUDA + - name: testing using Jimver cuda toolkit + uses: cuda-toolkit + with: + cuda: '12.1.0' + + - name: test cuda-toolkit run: | - wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin - sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600 - wget https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda-repo-ubuntu2004-11-3-local_11.3.1-465.19.01-1_amd64.deb - sudo dpkg -i cuda-repo-ubuntu2004-11-3-local_11.3.1-465.19.01-1_amd64.deb - sudo apt-key add /var/cuda-repo-ubuntu2004-11-3-local/7fa2af80.pub - sudo apt-get update - sudo apt-get -y install cuda - export PATH=/usr/local/cuda-11.3/bin${PATH:+:${PATH}} - export LD_LIBRARY_PATH=/usr/local/cuda-11.3/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} + echo "Installed cuda version is: ${{steps.cuda-toolkit.outputs.cuda}}" + echo "Cuda install location: ${{steps.cuda-toolkit.outputs.CUDA_PATH}}" + nvcc -V From ea40b0bd50b3af9d9fdf52abff33983304043e53 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 15:33:40 +0700 Subject: [PATCH 61/80] chore: attemp (2) test gpu CUDA Signed-off-by: slowy07 --- .github/workflows/cpp-testing-linux-base-gpu.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp-testing-linux-base-gpu.yml b/.github/workflows/cpp-testing-linux-base-gpu.yml index c52bad1..16b320d 100644 --- a/.github/workflows/cpp-testing-linux-base-gpu.yml +++ b/.github/workflows/cpp-testing-linux-base-gpu.yml @@ -20,7 +20,8 @@ jobs: uses: actions/checkout@v3 - name: testing using Jimver cuda toolkit - uses: cuda-toolkit + uses: Jimver/cuda-toolkit@v0.2.11 + id: cuda-toolkit with: cuda: '12.1.0' From 189bd376b1e4cd714706fd18062acea972f78f52 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 15:46:08 +0700 Subject: [PATCH 62/80] chore: attemp (3) test base on windows Signed-off-by: slowy07 --- .../workflows/cpp-testing-linux-base-gpu.yml | 32 --------- .../workflows/cpp-testing-windows-base.yml | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+), 32 deletions(-) delete mode 100644 .github/workflows/cpp-testing-linux-base-gpu.yml create mode 100644 .github/workflows/cpp-testing-windows-base.yml diff --git a/.github/workflows/cpp-testing-linux-base-gpu.yml b/.github/workflows/cpp-testing-linux-base-gpu.yml deleted file mode 100644 index 16b320d..0000000 --- a/.github/workflows/cpp-testing-linux-base-gpu.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Clara Test Linux Base With GPU Processing - -on: - push: - branches: - - main - - development - pull_request: - branches: - - main - - development - - -jobs: - build-and-testing: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: testing using Jimver cuda toolkit - uses: Jimver/cuda-toolkit@v0.2.11 - id: cuda-toolkit - with: - cuda: '12.1.0' - - - name: test cuda-toolkit - run: | - echo "Installed cuda version is: ${{steps.cuda-toolkit.outputs.cuda}}" - echo "Cuda install location: ${{steps.cuda-toolkit.outputs.CUDA_PATH}}" - nvcc -V diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml new file mode 100644 index 0000000..1620466 --- /dev/null +++ b/.github/workflows/cpp-testing-windows-base.yml @@ -0,0 +1,65 @@ +name: Clara Test Windows Base + +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +jobs: + build-and-testing: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Cygwin + run: | + choco install -y cygwin + + - name: Install Eigen + run: | + cygwin_setup -q -P wget,tar,xz -P gcc-g++ make cmake libeigen-devel + + - name: Install Google Test + run: | + echo "Initializing Google Test" + git clone https://github.com/google/googletest.git -b release-1.11.0 + cd googletest + mkdir build + cd build + cmake .. + make install + + - name: Build and test + run: | + echo "Testing Clara" + cd .. + cd .. + mkdir build + cd build + cmake .. + make + + - name: Run Clara Tests + run: | + echo "" + echo "Result from Clara test" + cd build + cd tests + ./clara_testing + + - name: testing grover search + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/grover_search.cpp -o grover_search + ./grover_search + + - name: testing channels + run: | + g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/channels.cpp -o channels + ./channels From f4c47015eddaef7605fdd4dabdd70cd69fe6fa65 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 15:51:13 +0700 Subject: [PATCH 63/80] chore: attemp (4) test base on windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows-base.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml index 1620466..12241dd 100644 --- a/.github/workflows/cpp-testing-windows-base.yml +++ b/.github/workflows/cpp-testing-windows-base.yml @@ -16,15 +16,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Cygwin run: | choco install -y cygwin - - - name: Install Eigen - run: | - cygwin_setup -q -P wget,tar,xz -P gcc-g++ make cmake libeigen-devel + choco install -y eigen - name: Install Google Test run: | From 01b8ca0002a3d1883427e111f11aeb78f1c16e18 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 15:56:53 +0700 Subject: [PATCH 64/80] chore: attemp (5) test base on windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows-base.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml index 12241dd..f35f408 100644 --- a/.github/workflows/cpp-testing-windows-base.yml +++ b/.github/workflows/cpp-testing-windows-base.yml @@ -30,8 +30,8 @@ jobs: cd googletest mkdir build cd build - cmake .. - make install + cmake -G "MinGW Makefiles" .. + mingw32-make - name: Build and test run: | @@ -43,14 +43,6 @@ jobs: cmake .. make - - name: Run Clara Tests - run: | - echo "" - echo "Result from Clara test" - cd build - cd tests - ./clara_testing - - name: testing grover search run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/grover_search.cpp -o grover_search From c6ecdbc788587be936b6dd0b6e80f10ad54af9c5 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 16:36:26 +0700 Subject: [PATCH 65/80] chore: attemp (6) test base on windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows-base.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml index f35f408..2a75a6c 100644 --- a/.github/workflows/cpp-testing-windows-base.yml +++ b/.github/workflows/cpp-testing-windows-base.yml @@ -36,12 +36,10 @@ jobs: - name: Build and test run: | echo "Testing Clara" - cd .. - cd .. mkdir build cd build cmake .. - make + mingw32-make - name: testing grover search run: | From 705b87294141683ab3fdaca151890f7c6e31b234 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 16:41:30 +0700 Subject: [PATCH 66/80] chore: attemp (7) test base on windows Signed-off-by: slowy07 --- .github/workflows/cpp-testing-windows-base.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml index 2a75a6c..efc0fa3 100644 --- a/.github/workflows/cpp-testing-windows-base.yml +++ b/.github/workflows/cpp-testing-windows-base.yml @@ -23,24 +23,6 @@ jobs: choco install -y cygwin choco install -y eigen - - name: Install Google Test - run: | - echo "Initializing Google Test" - git clone https://github.com/google/googletest.git -b release-1.11.0 - cd googletest - mkdir build - cd build - cmake -G "MinGW Makefiles" .. - mingw32-make - - - name: Build and test - run: | - echo "Testing Clara" - mkdir build - cd build - cmake .. - mingw32-make - - name: testing grover search run: | g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/grover_search.cpp -o grover_search From 0e5a02b7023ea45bc47816d47311e8f1fcec0076 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 7 Aug 2023 21:01:34 +0700 Subject: [PATCH 67/80] fix: fixing CWE-120, CWE-20 [Documentation] information CWE-120: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow. fix: typo on input_output.h fix: delete windows test chore: adding ``input_output_testing`` [Documentation] adding unitesting using google test framwork to test the functionallity of the `save` and `load` function from `clara` library `input_output` Signed-off-by: slowy07 --- .../workflows/cpp-testing-windows-base.yml | 34 ------------------- clara_test/tests/input_output_testing.cpp | 32 +++++++++++++++++ include/input_output.h | 15 ++++++-- 3 files changed, 44 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/cpp-testing-windows-base.yml create mode 100644 clara_test/tests/input_output_testing.cpp diff --git a/.github/workflows/cpp-testing-windows-base.yml b/.github/workflows/cpp-testing-windows-base.yml deleted file mode 100644 index efc0fa3..0000000 --- a/.github/workflows/cpp-testing-windows-base.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Clara Test Windows Base - -on: - push: - branches: - - main - - development - pull_request: - branches: - - main - - development - -jobs: - build-and-testing: - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Cygwin - run: | - choco install -y cygwin - choco install -y eigen - - - name: testing grover search - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/grover_search.cpp -o grover_search - ./grover_search - - - name: testing channels - run: | - g++ -pedantic -std=c++11 -Wall -Wextra -Weffc++ -fopenmp -g3 -DDEBUG -I /usr/include/eigen3 -I $HOME/clara/include testing/channels.cpp -o channels - ./channels diff --git a/clara_test/tests/input_output_testing.cpp b/clara_test/tests/input_output_testing.cpp new file mode 100644 index 0000000..c2fe6f8 --- /dev/null +++ b/clara_test/tests/input_output_testing.cpp @@ -0,0 +1,32 @@ +#include + +#include "../../include/clara.h" +#include "../../include/input_output.h" +#include "gtest/gtest.h" + +using namespace clara; + +class InputOutputTest : public ::testing::Test { + protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(InputOutputTest, SaveAndLoadEigenMatrix) { + Eigen::MatrixXd mat(3, 3); + mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; + + const std::string filename = "test_matrix.bin"; + clara::save(mat, filename); + + auto loadedMatrix = clara::load(filename); + + ASSERT_EQ(mat.rows(), loadedMatrix.rows()); + ASSERT_EQ(mat.cols(), loadedMatrix.cols()); + + for (int i = 0; i < mat.rows(); ++i) { + for (int j = 0; j < mat.cols(); ++j) { + ASSERT_DOUBLE_EQ(mat(i, j), loadedMatrix(i, j)); + } + } +} diff --git a/include/input_output.h b/include/input_output.h index 19d2d17..a3413a5 100644 --- a/include/input_output.h +++ b/include/input_output.h @@ -203,16 +203,25 @@ dyn_mat load(const std::string& fname) { // read the header from file fin.read(fheader_.get(), header_.length()); + // null-terminate the string + fheader_[header_.length()] = '\0'; if (std::string(fheader_.get(), header_.length()) != header_) { + // compare with the entire string throw std::runtime_error("clara::load() corrupted file \"" + std::string(fname) + "\"!"); } - idx rows, cols; + typename Derived::Index rows, cols; fin.read(reinterpret_cast(&rows), sizeof(rows)); - fin.read(reinterpret_cast(&rows), sizeof(cols)); + fin.read(reinterpret_cast(&cols), sizeof(cols)); + + if (rows < 0 || cols < 0) { + throw exception::CustomException( + "clara::load()", "invalid matrix dimension in file \"" + std::string(fname) + "\""); + } + dyn_mat A(rows, cols); - fin.read(reinterpret_cast(A.rows()), sizeof(typename Derived::Scalar) * rows * cols); + fin.read(reinterpret_cast(A.data()), sizeof(typename Derived::Scalar) * rows * cols); fin.close(); return A; } From bda75ac2bb1e9e135bd1b91d33eeb7a2d4a9d533 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 8 Aug 2023 13:18:55 +0700 Subject: [PATCH 68/80] docs: adding documentation code chore: update ``experimental_test`` [Documentation] The `experimental_test.h` header file defines classes and functions for simulating quantum circuits. The classes provided are: - `clara::experimental::ClaraCircuit`: Represents a quantum circuit for simulation. - `clara::experimental::LogicalCircuit`: Represents a logical circuit with gates and their details. - `clara::experimental::Test`: Represents a test class for quantum circuit operations. ClaraCircuit Class This class represents a quantum circuit for simulation. It provides functionalities for creating, manipulating, and simulating quantum circuits. Class: `clara::experimental::ClaraCircuit` - Constructor: Initializes a quantum circuit. Parameters: - `number_qubit`: Number of qubits in the circuit. - `number_classical`: Number of classical bits in the circuit. - `dimension`: Dimension of the quantum state space (default: 2). - Methods: - `measure(std::vector subsys)`: Measures specified subsystem qubits. - `measure_all()`: Measures all unmeasured qubits in the circuit. - `apply(const cmat& gate, const std::vector& subsys)`: Applies a quantum gate to specified subsystem qubits. - `apply_all(const cmat& gate)`: Applies a quantum gate to all unmeasured qubits. - `reset()`: Resets the quantum circuit to its initial state. - Accessor methods for retrieving circuit information: - `dimension()`: Returns the dimension of the quantum state space. - `get_number_qubit()`: Returns the number of qubits in the circuit. - `get_number_classical()`: Returns the number of classical bits in the circuit. - `get_size()`: Returns the total size of the circuit (qubits + classical bits). - `get_num_measured_qubits()`: Returns the number of measured qubits. - `get_num_active_qubits()`: Returns the number of active (unmeasured) qubits. - `get_psi()`: Returns the current quantum state of the circuit. - `get_results()`: Returns the measured results as a vector of indices. - `get_results_as_N()`: Returns the measured results as an integer in base-N representation. - `bits()`: Returns a reference to the classical bits associated with the circuit. LogicalCircuit Class This class represents a logical circuit with gates and their details. Class: `clara::experimental::LogicalCircuit` - Methods: - `add(const cmat& gate, const std::string& gate_name, const idx_vec& ctrl, const idx_vec& target)`: Adds a gate to the logical circuit. - `gate_gate_count()`: Returns the total count of gates added to the logical circuit. - `display(std::ostream& os) const`: Displays the logical circuit's gates and their details. chore: add more testing for make sure code works very well chore: adding reversible [Documentation] provided functionalities to work with dynamic bitset, perform bitwise operations, and represent bit circuits with various gate operations Signed-off-by: slowy07 --- clara_test/tests/CMakeLists.txt | 9 +- .../tests/classFunction/gates_testing.cpp | 29 ++ .../tests/classFunction/idisplay_testing.cpp | 38 ++ .../classFunction/random_devices_testing.cpp | 7 +- .../classFunction/reversible_testing.cpp | 25 + .../tests/classFunction/states_testing.cpp | 26 +- .../tests/classFunction/timer_testing.cpp | 16 + .../classFunction/iomanip_testing.cpp | 47 ++ .../classFunction/singleton_testing.cpp | 33 ++ clara_test/tests/testing_main.cpp | 2 +- include/classFunction/codes.h | 17 + include/classFunction/gates.h | 125 ++++- include/classFunction/idisplay.h | 1 + include/classFunction/reversible.h | 444 ++++++++++++++++++ include/classFunction/states.h | 4 +- include/classFunction/timer.h | 4 + include/experimental/experimental_test.h | 371 ++++++++++++--- 17 files changed, 1131 insertions(+), 67 deletions(-) create mode 100644 clara_test/tests/classFunction/idisplay_testing.cpp create mode 100644 clara_test/tests/classFunction/reversible_testing.cpp create mode 100644 clara_test/tests/internal/classFunction/iomanip_testing.cpp create mode 100644 clara_test/tests/internal/classFunction/singleton_testing.cpp create mode 100644 include/classFunction/reversible.h diff --git a/clara_test/tests/CMakeLists.txt b/clara_test/tests/CMakeLists.txt index 33440f4..51d9d99 100644 --- a/clara_test/tests/CMakeLists.txt +++ b/clara_test/tests/CMakeLists.txt @@ -1,13 +1,18 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) add_executable( clara_testing + classFunction/codes_testing.cpp classFunction/gates_testing.cpp + classFunction/idisplay_testing.cpp + classFunction/random_devices_testing.cpp + classFunction/reversible_testing.cpp classFunction/states_testing.cpp classFunction/timer_testing.cpp - classFunction/codes_testing.cpp - classFunction/random_devices_testing.cpp + internal/classFunction/iomanip_testing.cpp + internal/classFunction/singleton_testing.cpp testing_main.cpp traits.cpp + input_output_testing.cpp operations.cpp random_clara.cpp) target_link_libraries(clara_testing gmock) diff --git a/clara_test/tests/classFunction/gates_testing.cpp b/clara_test/tests/classFunction/gates_testing.cpp index 43ad971..75fb86b 100644 --- a/clara_test/tests/classFunction/gates_testing.cpp +++ b/clara_test/tests/classFunction/gates_testing.cpp @@ -1,11 +1,40 @@ #include #include +#include #include "../../../include/clara.h" #include "gtest/gtest.h" using namespace clara; +const double tolerance = 1e-9; + +TEST(clara_gates_test, Initialization) { + const Gates& gates = Gates::get_instance(); + EXPECT_EQ(gates.Id2, cmat::Identity(2, 2)); +} + +TEST(clara_gates_test, RotationGates) { + const Gates& gates = Gates::get_instance(); + cmat rx_gate = gates.RX(0.5); + cmat expected_rx_gate(2, 2); + expected_rx_gate << std::cos(0.25), -1.0_i * std::sin(0.25), -1.0_i * std::sin(0.25), + std::cos(0.25); + EXPECT_TRUE(rx_gate.isApprox(expected_rx_gate, tolerance)); +} + +bool complexMatricesEqual(const cmat& A, const cmat& B, double tolerance) { + return (A - B).norm() <= tolerance; +} + +TEST(clara_gates_test, ZdGate) { + const Gates& gates = Gates::get_instance(); + cmat expectedZ2 = cmat::Zero(2, 2); + expectedZ2(0, 0) = 1.0; + expectedZ2(1, 1) = -1.0; + EXPECT_TRUE(complexMatricesEqual(gates.Zd(2), expectedZ2, tolerance)); +} + TEST(clara_Gates_control, Qubits) { cmat CTRL1 = gt.CTRL(gt.X, {0}, {1}, 2); EXPECT_EQ(CTRL1, gt.CNOT); diff --git a/clara_test/tests/classFunction/idisplay_testing.cpp b/clara_test/tests/classFunction/idisplay_testing.cpp new file mode 100644 index 0000000..ef06cda --- /dev/null +++ b/clara_test/tests/classFunction/idisplay_testing.cpp @@ -0,0 +1,38 @@ +#include +#include + +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +class MockDisplay : public IDisplay { + public: + virtual std::ostream& display(std::ostream& os) const override { + os << "MockTestDisplay"; + return os; + } +}; + +TEST(clara_display_test, DisplayMockObject) { + MockDisplay mock; + std::ostringstream oss; + oss << mock; + EXPECT_EQ(oss.str(), "MockTestDisplay"); +} + +class OtherTestMockDisplay : public IDisplay { + public: + virtual std::ostream& display(std::ostream& os) const override { + os << "OtherTestMockDisplay"; + return os; + } +}; + +TEST(clara_display_test, OtherDisplayMockObject) { + OtherTestMockDisplay anotherMock; + std::ostringstream oss; + oss << anotherMock; + + EXPECT_EQ(oss.str(), "OtherTestMockDisplay"); +} diff --git a/clara_test/tests/classFunction/random_devices_testing.cpp b/clara_test/tests/classFunction/random_devices_testing.cpp index e7f8fdf..712ef6c 100644 --- a/clara_test/tests/classFunction/random_devices_testing.cpp +++ b/clara_test/tests/classFunction/random_devices_testing.cpp @@ -5,11 +5,12 @@ #include #include "../../../include/clara.h" +#include "../../../include/classFunction/random_devices.h" #include "gtest/gtest.h" using namespace clara; -TEST(RandomDevicesTest, GetPrng) { +TEST(clara_random_device_test, GetPrng) { std::mt19937& prng = RandomDevices::get_instance().get_prng(); EXPECT_NE(prng.state_size, std::mt19937::default_seed); @@ -17,7 +18,7 @@ TEST(RandomDevicesTest, GetPrng) { EXPECT_NE(random_number, 0); } -TEST(RandomDevicesTest, loadGetPrng) { +TEST(clara_random_device_test, loadGetPrng) { std::stringstream ss; clara::rdevs.save(ss); cmat A1 = rand(4, 4); @@ -27,7 +28,7 @@ TEST(RandomDevicesTest, loadGetPrng) { EXPECT_EQ(0, norm(A1 - A2)); } -TEST(RandomDevicesTest, loadGetPrngTest) { +TEST(clara_random_device_test, loadGetPrngTest) { std::stringstream ss; clara::rdevs.save(ss); bigint b1 = rand(static_cast(-100), 100); diff --git a/clara_test/tests/classFunction/reversible_testing.cpp b/clara_test/tests/classFunction/reversible_testing.cpp new file mode 100644 index 0000000..f7109f2 --- /dev/null +++ b/clara_test/tests/classFunction/reversible_testing.cpp @@ -0,0 +1,25 @@ +#include "../../../include/classFunction/reversible.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(clara_reversibe_test, X) { + Bit_circuit circuit(4); + circuit.X(0); + EXPECT_EQ(circuit.get(0), true); +} + +TEST(clara_reversibe_test, CNOT) { + Bit_circuit circuit(4); + circuit.X(0); + circuit.CNOT({0, 1}); + EXPECT_EQ(circuit.get(1), true); +} + +TEST(clara_reversibe_test, TOF) { + Bit_circuit circuit(4); + circuit.X(0); + circuit.X(1); + circuit.TOF({0, 1, 2}); + EXPECT_EQ(circuit.get(2), true); +} diff --git a/clara_test/tests/classFunction/states_testing.cpp b/clara_test/tests/classFunction/states_testing.cpp index a0a6282..0899656 100644 --- a/clara_test/tests/classFunction/states_testing.cpp +++ b/clara_test/tests/classFunction/states_testing.cpp @@ -6,10 +6,32 @@ using namespace clara; -TEST(clara_States_zero, AllTests) { +const double TOLERANCE = 1e-9; + +bool complexVectorsAlmostEqual(const clara::ket& v1, const clara::ket& v2, double tol) { + if (v1.size() != v2.size()) { + return false; + } + + for (clara::idx i = 0; i < v1.size(); ++i) { + if (std::abs(v1[i].real() - v2[i].real()) > tol || + std::abs(v1[i].imag() - v2[i].imag()) > tol) { + return false; + } + } + return true; +} + +TEST(clara_states_testing, SingletonInstance) { + const States& states = States::get_instance(); + const States& states2 = States::get_instance(); + EXPECT_EQ(&states, &states2); +} + +TEST(clara_states_testing, OtherTest) { idx n = 1; EXPECT_NEAR(0, norm(clara::st.z0 - clara::st.zero(n)), 1e-7); - + n = 2; EXPECT_NEAR(0, norm(kron(st.z0, st.z0) - clara::st.zero(n)), 1e-7); } diff --git a/clara_test/tests/classFunction/timer_testing.cpp b/clara_test/tests/classFunction/timer_testing.cpp index dbd1bab..47cbe34 100644 --- a/clara_test/tests/classFunction/timer_testing.cpp +++ b/clara_test/tests/classFunction/timer_testing.cpp @@ -6,6 +6,22 @@ using namespace clara; +const double TOLERANCE = 1e-6; + +class TimerTest : public ::testing::Test { +protected: + void sleep_for(int milliseconds) { +std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); + } +}; + +TEST_F(TimerTest, BasicFunctionallyTest) { + Timer timer; + sleep_for(100); + timer.toc(); + EXPECT_NEAR(timer.tics(), 100.0, TOLERANCE); +} + TEST(clara_timer, AllTests) { using namespace std::chrono; diff --git a/clara_test/tests/internal/classFunction/iomanip_testing.cpp b/clara_test/tests/internal/classFunction/iomanip_testing.cpp new file mode 100644 index 0000000..e578b9d --- /dev/null +++ b/clara_test/tests/internal/classFunction/iomanip_testing.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include "../../../../include/clara.h" +#include "gtest/gtest.h" + +class ClaraIOManipTesting : public ::testing::Test { + protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(ClaraIOManipTesting, DisplayIOManipRangeClara) { + int arr[] = {1, 2, 3, 4, 5}; + std::ostringstream oss; + clara::internal::IOManipRange rangeDisp(arr, arr + 5, ", ", "<", ">"); + oss << rangeDisp; + ASSERT_EQ(oss.str(), "<1, 2, 3, 4, 5>"); +} + +TEST_F(ClaraIOManipTesting, DisplayIOManipPointer) { + int arr[] = {1, 2, 3, 4, 5}; + std::ostringstream oss; + clara::internal::IOManipPointer pointerDisp(arr, 5, ", ", "[", "]"); + oss << pointerDisp; + ASSERT_EQ(oss.str(), "[1, 2, 3, 4, 5]"); +} + +TEST_F(ClaraIOManipTesting, SaveAndLoadEigenMatrix) { + Eigen::MatrixXd originalMat(2, 2); + originalMat << 1.0, 2.0, 3.0, 4.0; + std::string filename = "test_matrix.bin"; + clara::save(originalMat, filename); + Eigen::MatrixXd loadedMat = clara::load(filename); + ASSERT_EQ(originalMat, loadedMat); +} + +TEST_F(ClaraIOManipTesting, DisplayIOManipComplex) { + std::complex complexNum(3.14, 2.17); + std::ostringstream oss; + clara::internal::IOManipEigen complexDisp(complexNum); + oss << complexDisp; + + std::string actualOutput = oss.str(); + std::string expectedOutput = "[3.14, 2.17]"; + ASSERT_TRUE(actualOutput.find(expectedOutput) == std::string::npos); +} diff --git a/clara_test/tests/internal/classFunction/singleton_testing.cpp b/clara_test/tests/internal/classFunction/singleton_testing.cpp new file mode 100644 index 0000000..eacb45a --- /dev/null +++ b/clara_test/tests/internal/classFunction/singleton_testing.cpp @@ -0,0 +1,33 @@ +#include "../../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +class ClaraSingletonTesting : public ::testing::Test { + protected: + class TestingSingleton : public clara::internal::Singleton { + public: + int getValue() const { return value_; } + + void setValue(int value) { value_ = value; } + + private: + int value_ = 0; + }; +}; + +TEST_F(ClaraSingletonTesting, SingletonInstance) { + TestingSingleton& instance1 = TestingSingleton::get_instance(); + instance1.setValue(42); + TestingSingleton& instance2 = TestingSingleton::get_instance(); + EXPECT_EQ(instance1.getValue(), instance2.getValue()); +} + +#ifndef NO_THREAD_LOCAL +TEST_F(ClaraSingletonTesting, ThreadLocalSingletonInstance) { + TestingSingleton& instance1 = TestingSingleton::get_thread_local_instance(); + instance1.setValue(50); + TestingSingleton& instance2 = TestingSingleton::get_thread_local_instance(); + EXPECT_EQ(instance1.getValue(), instance2.getValue()); +} +#endif // !NO_THREAD_LOCAL diff --git a/clara_test/tests/testing_main.cpp b/clara_test/tests/testing_main.cpp index 51bff64..443e2db 100644 --- a/clara_test/tests/testing_main.cpp +++ b/clara_test/tests/testing_main.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" -int main (int argc, char** argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/include/classFunction/codes.h b/include/classFunction/codes.h index ee4c3d9..9aed9fa 100644 --- a/include/classFunction/codes.h +++ b/include/classFunction/codes.h @@ -7,10 +7,13 @@ #include "exception.h" namespace clara { + +// codes class for handling different types of quantum error correction code class Codes final : public internal::Singleton { friend class internal::Singleton; public: + // enum to define different type of quantum error correction code enum class Type { FIVE_QUBIT = 1, SEVEN_QUBIT_STEANE, NINE_QUBIT_SHOR }; private: @@ -22,12 +25,22 @@ class Codes final : public internal::Singleton { ~Codes() = default; public: + /** + * @brief retrieve the codeword for the specified quantum error correction code + * type and index. + * @param type the type of quantum error correction code + * @param i the inde of the codeword to retrieve + * @return the codeword as a ket (quantum state) for the specified type and index + * + * @throws NoCodeword exception if the specified type or index is invalid + */ ket codeword(Type type, idx i) const { ket result; switch (type) { case Type::FIVE_QUBIT: switch (i) { case 0: + // define the codeword for the specified type and index result = (mket({0, 0, 0, 0, 0}) + mket({1, 0, 0, 1, 0}) + mket({0, 1, 0, 0, 1}) + mket({1, 0, 1, 0, 0}) + mket({0, 1, 0, 1, 0}) - mket({1, 1, 0, 1, 1}) - mket({0, 0, 1, 1, 0}) - mket({1, 1, 0, 0, 0}) - mket({1, 1, 1, 0, 1}) - @@ -37,6 +50,7 @@ class Codes final : public internal::Singleton { 4.; break; case 1: + // defined anoterh codeword for the specified type and index result = (mket({1, 1, 1, 1, 1}) + mket({0, 1, 1, 0, 1}) + mket({1, 0, 1, 1, 0}) + mket({0, 1, 0, 1, 1}) + mket({1, 0, 1, 0, 1}) - mket({0, 0, 1, 0, 0}) - mket({1, 1, 0, 0, 1}) - mket({0, 0, 1, 1, 1}) - mket({0, 0, 0, 1, 0}) - @@ -71,6 +85,7 @@ class Codes final : public internal::Singleton { break; case Type::NINE_QUBIT_SHOR: ket shora, shorb; + // define quantum state for the shora and shorb kets shora = mket({0, 0, 0}) + mket({ 1, 1, @@ -83,9 +98,11 @@ class Codes final : public internal::Singleton { }); switch (i) { case 0: + // define qthe codeword using kronecker prodduct of shora ket result = kron(shora, kron(shora, shora)) / std::sqrt(8.); break; case 1: + // defined the codeword using the kronecker product of shorb ket result = kron(shorb, kron(shorb, shorb)) / std::sqrt(8.); break; default: diff --git a/include/classFunction/gates.h b/include/classFunction/gates.h index 3efe170..f673ed9 100644 --- a/include/classFunction/gates.h +++ b/include/classFunction/gates.h @@ -2,6 +2,7 @@ #define CLASSFUNCTION_GATES_H_ #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "../functions.h" #include "../internal/classFunction/singleton.h" #include "../internal/util.h" +#include "../number_theory.h" #include "exception.h" namespace clara { @@ -32,19 +34,33 @@ class Gates final : public internal::Singleton { public: // define various quantum gates as member variables cmat Id2{cmat::Identity(2, 2)}; + // hadamard gate cmat H{cmat::Zero(2, 2)}; + // pauli sigma X gate cmat X{cmat::Zero(2, 2)}; + // pauli sigma Y gate cmat Y{cmat::Zero(2, 2)}; + // pauli sigma Z gate cmat Z{cmat::Zero(2, 2)}; + /// S gate cmat S{cmat::Zero(2, 2)}; + // T gate cmat T{cmat::Zero(2, 2)}; + // two qubit gates + + // controlled-not controll target gate cmat CNOT{cmat::Identity(4, 4)}; + // controlled phase gate cmat CZ{cmat::Identity(4, 4)}; + // controlled not gate control gate cmat CNOTba{cmat::Zero(4, 4)}; + // swap gate cmat SWAP{cmat::Identity(4, 4)}; + // toffoli gate cmat TOF{cmat::Identity(8, 8)}; + // fredkin gate cmat FRED{cmat::Identity(8, 8)}; private: @@ -96,6 +112,39 @@ class Gates final : public internal::Singleton { return result; } + /** + * @brief the 2x2 rotation matrix around the X-axis in quantum circuit + * @param theta the angle of rotation in radians + * @return the 2x2 rotation amtrix corresponding to the RX gate + */ + cmat RX(double theta) const { + // call the Rn function to generate the rotation matrix for the RX gate + // the axis of rotation is {1, 0, 0} which corresponds to the X-axis + return Rn(theta, {1, 0, 0}); + } + + /** + * @brief calculates the 2x2 rotation matrix around the Y axis in quamtum circuit + * @param theta the angle of rotation in radians + * @return the 2x2 rotation matrix corresponding to the RY gate + */ + cmat RY(double theta) const { + // call the Rn function to generate the rotation matrix for the RY gate + // the axis of rotation is {0, 1, 0}, which corresponds to the Y-axis + return Rn(theta, {0, 1, 0}); + } + + /** + * @brief calculate the 2x2 rotation amtrix around the Z-axis in a quantum circuit + * @param theta the angle of rotation in radians + * @return the 2x2 rotation matrix corresponding to the RZ gate + */ + cmat RZ(double theta) const { + // call the Rn function to generate the rotation matrix for the RZ gate + // the axis of rotation is {0, 0, 1} which corresponds to the Z-axis + return Rn(theta, {0, 0, 1}); + } + /** * @brief generalized Z (Zd) gate for qudits * @@ -115,6 +164,28 @@ class Gates final : public internal::Singleton { return result; } + /** + * @brief generate the SWAP gate for a given dimension + * the SWAP gate exchange the position of two qubits + * @param D dimension of the qubit + * @return the SWAP gate matrix of size DxDxDxD + * + * @throw DimsInvalid exception if D is invalid + */ + cmat SWAPd(idx D = 2) const { + if (D == 0) + throw exception::DimsInvalid("clara::Gates::SWAPd()"); + cmat result = cmat::Zero(D * D, D * D); + +#ifdef WITH_OPENMP_ +#pragma omp parallel for collapse(2) +#endif + for (idx j = 0; j < D; ++j) + for (idx i = 0; i < D; ++i) + result(D * i + j, i + D * j) = 1; + return result; + } + /** * @brief Fourier transform (Fd) gate for qudit * @@ -138,6 +209,49 @@ class Gates final : public internal::Singleton { return result; } + /** + * @brief genrate MODMUL gate for a given value a, N, and n + * the MODMUL gate implements modular multiplication + * @param a the integer value a + * @param N the modulis value N + * @param n the number of qubits (size of the gate matrix) + * @return the modmul gate matrix of size 2^n x 2^n + * + * @throws OutOfRange exception if a, N or n is out of valid range + */ + cmat MODMUL(idx a, idx N, idx n) const { +#ifndef DEBUG + assert(gcd(a, N) == 1); +#endif // !DEBUG + if (N < 3 || a >= N) { + throw exception::OutOfRange("clara::Gates::MODMUL()"); + } + if (n < static_cast(std::ceil(std::log2(N)))) { + throw exception::OutOfRange("clara::Gtes::MODMUL()"); + } + + // calculate the dimension of the gate matrix + idx D = static_cast(std::llround(std::pow(2, n))); + cmat result = cmat::Zero(D, D); + +#ifdef WITH_OPENMP_ +#pragma omp parallel for collapse(2) +#endif // WITH_OPENMP_ + // poplulate the MODMUL gate matrix using a loop + for (idx j = 0; j < N; ++j) + for (idx i = 0; i < N; ++i) + if (static_cast(modmul(j, a, N)) == i) + result(i, j) = 1; + +#ifdef WITH_OPENMP_ +#pragma omp parallel for +#endif // WITH_OPENMP_ + // set diagonal elements of the gate matrix for remaining indices + for (idx i = N; i < D; ++i) + result(i, i) = 1; + return result; + } + /** * @brief generalized X (Xd) gate for qudit * @@ -412,8 +526,15 @@ class Gates final : public internal::Singleton { } /** - * @brief expands out - * @note expands out A as a matrix in a multi-paritite system + * @brief expand a given matrix into a larger multi-parite system + * @param A the input matrix to be expanded + * @param pos the position (index) where the matrix A will be inserted + * @param N the total number of parties + * @param d the dimension of each subsystem (default is 2) + * @return expanded matrix in the multi-paritite system + * + * @throws ZeroSize exception if the input matrix matrix has zero size + * @throws DimsInvalid exception if d is invalid */ template dyn_mat expandout(const Eigen::MatrixBase& A, idx pos, idx N, diff --git a/include/classFunction/idisplay.h b/include/classFunction/idisplay.h index b7d3e71..60e28ca 100644 --- a/include/classFunction/idisplay.h +++ b/include/classFunction/idisplay.h @@ -34,6 +34,7 @@ class IDisplay { IDisplay(IDisplay&&) = default; // copy assignment operator IDisplay& operator=(const IDisplay&) = default; + IDisplay& operator=(IDisplay&&) = default; // virtual destructor virtual ~IDisplay() = default; diff --git a/include/classFunction/reversible.h b/include/classFunction/reversible.h new file mode 100644 index 0000000..c291dda --- /dev/null +++ b/include/classFunction/reversible.h @@ -0,0 +1,444 @@ +#ifndef CLASSFUNCTION_REVESIBLE_H_ +#define CLASSFUNCTION_REVESIBLE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "idisplay.h" + +namespace clara { + +/** + * @brief a dynamic bitset calss that implements a flexible-size arrays of bits + * @details this class represent a dynamic bitset, which is an array of bits + * that can dynamically grow as needed. it is used for efficient storage and manipulation + * of binary data + */ +class Dynamic_bitset : public IDisplay { + public: + // type used to store individual bits + using value_type = unsigned int; + // type for the storage container + using storage_type = std::vector; + + protected: + // number of storage elements needed + idx storage_size_; + // total number of bits + idx N_; + // storage vector for the bitset + std::vector v_; + + /** + * @brief calculate the index of the storage element containing a bit at the given position + * @param pos the position of the bit + * @return the index of the storage element + */ + idx index_(idx pos) const { return pos / (sizeof(value_type) * CHAR_BIT); } + /** + * @brief calculate the offset within the storage element fora bit at the given position + * @param pos the position of the bit + * @return the offset within the storage element + */ + idx offset_(idx pos) const { return pos % (sizeof(value_type) * CHAR_BIT); } + + public: + /** + * @brief constructor to create a dynamic bitset of a given size + * @param N the total number of bits in the bitset + */ + Dynamic_bitset(idx N) + : storage_size_{N / (sizeof(value_type) * CHAR_BIT) + 1}, N_{N}, v_(storage_size_) {} + + /** + * @brief get a reference to the internal stroage internal container + * @return a const reference to the internal storage vector + */ + const storage_type& data() const { return v_; } + /** + * @brief get a reference to the internal storage container + * @return const reference to the internal storage vector + */ + idx size() const noexcept { return N_; } + /** + * @brief get the number of storage elements used in the bitset + * @return the number of storage elements + */ + idx storage_size() const noexcept { return storage_size_; } + + /** + * @brief count the number of set bits (ones) in the bitset + * @return the count of set bits in the bitset + */ + idx count() const noexcept { + idx result = 0; + idx bitset_size = this->size(); + for (idx i = 0; i < bitset_size; ++i) { + if (this->get(i)) + ++result; + } + return result; + } + + /** + * @brief get the value of the bit at the specified position + * @param pos position of the bit to retrieve + * @return the value of the bit at the given position + */ + bool get(idx pos) const noexcept { return 1 & (v_[index_(pos)] >> offset_(pos)); } + + /** + * @brief check if not bits are set in the bitset + * @return true if not bits are set, false otherwise + */ + bool none() const noexcept { + bool result = true; + idx bitset_storage_size = this->storage_size(); + for (idx i = 0; i < bitset_storage_size; ++i) { + if (v_[i]) { + return false; + } + } + return result; + } + + /** + * @brief check if all bits are set in the bitset + * @return true if all bits are set, false otherwise + */ + bool all() const noexcept { + bool result = true; + idx bitset_storage_size = this->storage_size(); + for (idx i = 0; i < bitset_storage_size; ++i) { + if (~v_[i]) { + return false; + } + } + return result; + } + + /** + * @brief check if any bits are set in the bitset + * @return true if any bit is set, false if no bits are set + */ + bool any() const noexcept { return !(this->none()); } + + /** + * @brief set the value of the bit at the specified position + * @param pos the position of the bit to set + * @param value the value to set the bit + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& set(idx pos, bool value = true) { + value ? v_[index_(pos)] |= (1 << offset_(pos)) : v_[index_(pos)] &= ~(1 << offset_(pos)); + return *this; + } + + /** + * @brief set all bits in the bitset to the specified value + * @param value the value to set all bits (default is true) + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& set() noexcept { + idx bitset_storage_size = this->storage_size(); + for (idx i = 0; i < bitset_storage_size; ++i) { + v_[i] = ~0; + } + return *this; + } + + /** + * @brief randomly set the value of the bit at the specified position + * @param pos the position of the bit to set randomly + * @param p the probability of setting the bit of true (default is 0.5) + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& rand(idx pos, double p = 0.5) { + std::random_device rd; + std::mt19937 gen{rd()}; + std::bernoulli_distribution d{p}; + + this->set(pos, d(gen)); + return *this; + } + + /** + * @brief randomly set the value of all bits in the bitset + * @param p the probability of setting each bit to true (default is 0.5) + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& rand(double p = 0.5) { + idx bitset_size = this->size(); + for (idx i = 0; i < bitset_size; ++i) { + this->rand(i, p); + } + return *this; + } + + /** + * @brief reset the value of the bit at the specified position to false + * @param pos the position of the bit to reset + * @return A reference to the modified Dynamic_bitset + */ + Dynamic_bitset& reset(idx pos) { + v_[index_(pos)] &= ~(1 << offset_(pos)); + return *this; + } + + /** + * @brief reset the value of all bits in the bitset to false + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& reset() noexcept { + idx bitset_storage_size = this->storage_size(); + for (idx i = 0; i < bitset_storage_size; ++i) { + v_[i] = 0; + } + return *this; + } + + /** + * @brief flip the value of the bit at the specified position + * @param pos the position of the bit to flip + * @return reference to the modified Dynamic_bitset + */ + Dynamic_bitset& flip(idx pos) { + v_[index_(pos)] ^= 1 << (offset_(pos)); + return *this; + } + + /** + * @brief flip the value of all bits in the bitset + * @return a reference to the modified Dynamic_bitset + */ + Dynamic_bitset& flip() noexcept { + idx bitset_storage_size = this->storage_size(); + for (idx i = 0; i < bitset_storage_size; ++i) { + v_[i] = ~v_[i]; + } + return *this; + } + + /** + * @brief compare two Dynamic_bitset object for equality + * @param rhs the right-hand side Dynamic_bitset to compare with. + * @return true if the bitset are equal, false otherwise + */ + bool operator==(const Dynamic_bitset& rhs) const noexcept { + assert(this->size() == rhs.size()); + bool result = true; + idx n = std::min(this->storage_size(), rhs.storage_size()); + for (idx i = 0; i < n; ++i) { + if (v_[i] != rhs.v_[i]) { + return false; + } + } + return result; + } + + /** + * @brief compare two Dynamic_bitset object for inequality + * @param ths the right-hand side Dynamic_bitset to compare with + * @return true if the bitset are not equal, false if the are equal. + */ + bool operator!=(const Dynamic_bitset& rhs) const noexcept { return !(*this == rhs); } + + /** + * @brief calculate the hamming distance between two Dynamic_bitset objects + * @param rhs right-hand side Dynamic_bitset to calculate the distance to + * @return the hamming distance between the bitset + */ + idx operator-(const Dynamic_bitset& rhs) const noexcept { + idx result = 0; + idx bitset_size = this->size(); + for (idx i = 0; i < bitset_size; ++i) { + if (this->get(i) != rhs.get(i)) + ++result; + } + return result; + } + + /** + * @brief convert the Dynamic_bitset to a string representation + * @tparam CharT are character type for the string (default is 'char') + * @tparam Traits the character traits type (default is 'std::char_traits') + * @tparam Allocator the allocator type for memory management (default is 'std::allocator') + * @param zero the character representing a bit value of 0 + * @param one the character representing a bit value of 1 + * @return A string representation of the dynamic bitset + * + * NOTE: this function converts the Dynamic_bitset to a string representation using the specified + * characters for bit values 0 and 1. the resulting string is constructed by iterating through the + * bits in the Dynamic_bitset and appending the correspoinding character to the string + */ + template , + class Allocator = std::allocator> + std::basic_string to_string(CharT zero = CharT('0'), + CharT one = CharT('1')) const { + std::basic_string result; + idx bitset_size = this->size(); + result.resize(bitset_size); + + // iterate through the bits in reverse order and append characters to the string + for (idx i = bitset_size; i-- > 0;) { + if (!this->get(i)) { + result[bitset_size - i - 1] = zero; + } else { + result[bitset_size - i - 1] = one; + } + } + return result; + } + + private: + /** + * @brief display the binary representation of Dynamic_bitset to an output stream + * @param os the output stream to write the binary repersentation to + * @return the update output stream after writing the binary representation + * + * @override IDisplay::display + * + * NOTE: this private member function is used to display the binary repersentation of the + * Dynamic_bitset to the specified output stream. it iterates through the bits in reverse + * order and writes each bit's value (0 or 1) to the output stream. this function is intended + * to be used as an override for the IDisplay::display function + */ + std::ostream& display(std::ostream& os) const override { + idx bitset_size = this->size(); + for (idx i = bitset_size; i-- > 0;) { + os << this->get(i); + } + return os; + } +}; + +/** + * @brief A class repersenting a circuit of bit operations, derived from Dynamic_bitset + * + * NOTE: this class extend the functionally of Dynamic_bitset to repersent a circuit of bit. + * it includes a struct Gate_count to keep track of different gate counts within the circuit + */ +class Bit_circuit : public Dynamic_bitset { + public: + /** + * @brief a structure to keep track of gate counts within the circuit + */ + struct Gate_count { + // count of NOT Gates + idx NOT = 0; + // alias for the count of X gates (same as NOT gates) + idx& X = NOT; + + // count CNOT gates + idx CNOT = 0; + // count SWAP gates + idx SWAP = 0; + // count FREDKIN gates + idx FRED = 0; + // count of toffoli gates + idx TOF = 0; + } gate_count{}; + + /** + * @brief constructor that forwards arguments to the base Dynamic_bitset constructor + * @tparam Args parameter for the Dynamic_bitset constructor + * @param args erguments to forward to the Dynamic_bitset constructor + */ + using Dynamic_bitset::Dynamic_bitset; + Bit_circuit(const Dynamic_bitset& dynamic_bitset) : Dynamic_bitset{dynamic_bitset} {} + Bit_circuit& X(idx pos) { + this->flip(pos); + ++gate_count.X; + return *this; + } + Bit_circuit& NOT(idx pos) { + this->flip(pos); + ++gate_count.NOT; + return *this; + } + + /** + * @brief apply CNOT gate to the circuit + * @param pos A vector two position: control bit and target bit + * @return a reference to the modified Bit_circuit + */ + Bit_circuit& CNOT(const std::vector& pos) { + v_[index_(pos[1])] ^= (1 & (v_[index_(pos[0])] >> offset_(pos[0]))) << offset_(pos[1]); + ++gate_count.CNOT; + return *this; + } + + /** + * @brief apply toffoli gate to the circuit + * @param pos A vector of three positions: control bit 1, control bit 2, and target bit + * @return reference to the modified Bit_circuit + */ + Bit_circuit& TOF(const std::vector& pos) { + v_[index_(pos[2])] ^= ((1 & (v_[index_(pos[1])] >> offset_(pos[1]))) & + (1 & (v_[index_(pos[0])] >> offset_(pos[0])))) + << offset_(pos[2]); + ++gate_count.TOF; + return *this; + } + + /** + * @brief apply a SWAP gate to the circuit + * @param pos A vector of two position to SWAP + * @return A reference to the modified Bit_circuit + * + * NOTE: function swaps the value of the two specified position in the circuit + */ + Bit_circuit& SWAP(const std::vector& pos) { + if (this->get(pos[0]) != this->get(pos[1])) { + this->X(pos[0]); + this->X(pos[0]); + this->X(pos[1]); + } + ++gate_count.SWAP; + return *this; + } + + /** + * @brief apply FREDKIN gate to the circuit + * @param pos A vector of three position: control bit, target bit 1, target bit 2 + * @return reference to the modified Bit_circuit + * + * NOTE: if the control bit is set, this function swaps the values of target bits 1 and 2 in the + * circuit + */ + Bit_circuit& FRED(const std::vector& pos) { + if (this->get(pos[0])) { + this->SWAP({pos[1], pos[2]}); + } + ++gate_count.FRED; + return *this; + } + + /** + * @brief reset the circuit and gat counts to their initial state + * @return reference to the modified Bit_circuit + * + * NOTE: this function reset all bits in the circuit to zero and reset the gate count + * to zero + */ + Bit_circuit& reset() noexcept { + gate_count.NOT = gate_count.X = 0; + gate_count.CNOT = gate_count.SWAP = 0; + gate_count.FRED = gate_count.TOF = 0; + Dynamic_bitset::reset(); + + return *this; + } +}; +} // namespace clara + +#endif // !CLASSFUNCTION_REVESIBLE_H_ diff --git a/include/classFunction/states.h b/include/classFunction/states.h index b0f7b45..de0954e 100644 --- a/include/classFunction/states.h +++ b/include/classFunction/states.h @@ -87,7 +87,7 @@ class States final : public internal::Singleton { throw exception::OutOfRange("clara::States::zero()"); if (d == 0) throw exception::DimsInvalid("clara::States::zero()"); - idx D = static_cast(std::pow(d, n)); + idx D = static_cast(std::llround(std::pow(d, n))); ket result = ket::Zero(D); result(0) = 1; @@ -123,7 +123,7 @@ class States final : public internal::Singleton { if (j >= d) throw exception::SubsysMismatchdims("clara::States::jn()"); - if (j >= d) + if (d == 0) throw exception::DimsInvalid("clara::States::jn()"); ket result = ket::Zero(static_cast(std::pow(d, n))); diff --git a/include/classFunction/timer.h b/include/classFunction/timer.h index 48ec2ed..e3f1e14 100644 --- a/include/classFunction/timer.h +++ b/include/classFunction/timer.h @@ -66,6 +66,10 @@ class Timer : public IDisplay { * @brief default copy constructor */ Timer(const Timer&) = default; + /** + * @brief default move constructor + */ + Timer(Timer&&) = default; /** * @brief default copy assignment operator */ diff --git a/include/experimental/experimental_test.h b/include/experimental/experimental_test.h index eb54b12..9b66854 100644 --- a/include/experimental/experimental_test.h +++ b/include/experimental/experimental_test.h @@ -7,73 +7,307 @@ #include #include #include +#include #include -#include "../classFunction/gates.h" -#include "../classFunction/idisplay.h" +#include "../clara.h" namespace clara { namespace experimental { -class ClaraCircuit : public IDisplay { - idx number_qudits_; - idx number_classical_bits_; +/** + * @brief a class representing a quantum circuit for simulation + * + * this class provides functionalities to create and manipulate quantum + * circuits for simulation, including measurement, gate application, and state + * retrieval. it supports quantum and classical bits and allows for state + * manipulations + */ +template +class ClaraCircuit { + // dimension of the quantum state space idx dimension_; + // number of qubits and classical bit + idx number_qubit_, number_classical_; + // quantum state vector ket psi_; - std::vector dits_; - std::vector measurement_steps_; - - enum class GateType { - SINGLE, - TWO, - THREE, - FAN, - CUSTOM, - SINGLE_CTRL_SINGLE_TARGET, - SINGLE_CTRL_MULTIPLE_TARGET, - MULTIPLE_CTRL_SINGLE_TARGET, - MULTIPLE_CTRL_MULTIPLE_TARGET, - CUSTOM_CTRL, - SINGLE_cCTRL_SINGLE_TARGET, - SINGLE_cCTRL_MULTIPLE_TARGET, - MULTIPLE_cCTRL_SINGLE_TARGET, - MULTIPLE_cCTRL_MULTIPLE_TARGET, - CUSTOM_cCTRL, - NONE - }; + // vector indicating measured qubits + std::vector measured_; + // measured result + std::vector results_; + // classical bits + std::vector bits_; - enum class MeasureType { MEASURE_Z, MEASURE_V, MEASURE_KS, NONE }; + protected: + /** + * @brief update subsystem indices after measurement + * @param subsys indices of the subsystem to update + * @return update the subsystem indices after measurement + * + * NOTE: function update the subsystem indices after measurement by reducing the indices + * for measured qubits + */ + std::vector update_subsys_(const std::vector& subsys) { + std::vector result = subsys; + idx subsys_size = subsys.size(); + for (idx i = 0; i < subsys_size; ++i) { + for (idx m = 0; m < subsys[i]; ++m) { + if (measured_[m]) { + --result[i]; + } + } + } + std::sort(std::begin(result), std::begin(result), std::less{}); + return result; + } - struct GateStep { - GateType gate_type_ = GateType::NONE; - cmat gate_; - std::vector ctrl_; - std::vector target_; - std::string name_; + public: + /** + * @brief constructor to initialize a quantum circuit + * @param number_qubit number of qubits in the circuit + * @param number_classical number of classical bits in the circuit + * @param dimension dimension of the quantum state space + */ + ClaraCircuit(idx number_qubit, idx number_classical, idx dimension = 2) + : dimension_{dimension}, + number_qubit_{number_qubit}, + number_classical_{number_classical}, + psi_{clara::st.zero(number_qubit_, dimension_)}, + measured_(number_qubit_, false), + results_(number_qubit_, -1), + bits_(number_classical_, 0) {} - GateStep() = default; - GateStep(GateType gate_type, const cmat& gate, const std::vector& ctrl, - const std::vector& target, const std::string& name = "") - : gate_type_{gate_type}, gate_{gate}, ctrl_{ctrl}, target_{target}, name_{name} {} - }; + /** + * @brief measure specified subsystem qubits + * @param subsys inidices of the subystem qubits to measure + * + * @throws clara::exception::CustomException if subsystem was measured before + * + * NOTE: function measure specifeid subsystem qubits and updates the circuit state + */ + void measure(std::vector subsys) { + idx subsys_size = subsys.size(); + for (idx i = 0; i < subsys_size; ++i) { + if (measured_[subsys[i]]) + throw exception::CustomException("clara::ClaraCirquit::measure()", + "subsystem was measured before"); + } + std::vector subsys_update = update_subsys_(subsys); + for (idx i = 0; i < subsys_size; ++i) { + measured_[subsys[i]] = true; + } + auto m = measure_seq(psi_, subsys_update, dimension_); + auto result = std::get<0>(m); + for (idx i = 0; i < subsys_size; ++i) + results_[subsys[i]] = result[i]; + psi_ = std::get<2>(m); + } + + /** + * @brief measure all unmeasured qubiits in the circuit + * this function measures all unmeasured qubits in the circuit and updates the circuit state + * accordingly. if all qubits have been measured before, a custom exception will be thrown + */ + void measure_all() { + std::vector subsys; + for (idx i = 0; i < number_qubit_; ++i) { + if (!measured_[i]) + subsys.push_back(i); + } + std::vector subsys_update = update_subsys_(subsys); + if (subsys.size() != 0) + this->measure(subsys); + else + throw exception::CustomException("clara::ClaraCirquit::measure_all()", + "All qubit were measured before"); + } - struct MeasureStep { - MeasureType measurement_type_ = MeasureType::NONE; + /** + * @brief appl a quantum gate to specified subsystem qubit + * @param gate quantum gate to be applied + * @param subsys indices of the subsystem qubits to apply the gate + * + * NOTE: function applies a quantum gate to the specified subsystem qubits and + * updates the circuit using clara::applyCTRL function + */ + void apply(const cmat& gate, const std::vector& subsys) { + psi_ = clara::applyCTRL(psi_, gate, update_subsys_(subsys), dimension_); + } + + /** + * @brief apply a quantum gate to all unmeasured qubit circuit + * @param gate quantum gate to be applied + * + * NOTE: this function applies a quantum gate to all unmeasured in the circuit + * and update the circuit state using clara::apply function + */ + void apply_all(const cmat& gate) { + for (idx i = 0; i < number_qubit_; ++i) { + if (!measured_[i]) { + psi_ = clara::apply(psi_, gate, update_subsys_({i}), dimension_); + } + } + } + + /** + * @brief reset the quantum circuit to its initial state + * this function resets the quantum circuit by setting the quantum state to the zero state + * unmeasuring all qubits, resetting the measured results, and resetting the classical bits + */ + void reset() { + psi_ = clara::st.zero(number_qubit_); + measured_ = std::vector(number_qubit_, false); + results_ = std::vector(number_qubit_, -1); + bits_ = std::vector(number_classical_, 0); + } + + /** + * @brief get the dimension of the quantum state space + * @return dimension of the quatum state space + */ + idx dimension() const noexcept { return dimension_; } + /** + * @brief get the number of qubits in the circuit + * @return number of qubits in the circuit + */ + idx get_number_qubit() const noexcept { return number_qubit_; } + /** + * @brief get the number of classical bits in the circuit + * @return number of classical bits in the circuit + */ + idx get_number_classical() const noexcept { return number_classical_; } + /** + * @brief get the total size of the circuit (qubits + classical bits) + * @return total size of the circuit + */ + idx get_size() const noexcept { return number_qubit_ + number_classical_; } + /** + * @brief get the number of qubits that have been measured + * @return number of measured qubits + */ + idx get_num_measured_qubits() const noexcept { + return std::count(std::begin(measured_), std::end(measured_), true); + } + /** + * @brief get the number of active (unmeasured) qubits in the circuit + * @return number of active qubits + */ + idx get_num_active_qubits() const noexcept { + return this->get_number_qubit() - this->get_num_measured_qubits(); + } + + /** + * @brief get the current quantum state of the circuit + * @return quantum state vector (ket) of the circuit + */ + ket get_psi() const { return psi_; } + /** + * @brief get the measured results as a vector indices + * @return vector of measured qubit results + */ + std::vector get_results() const { return results_; } + /** + * @brief get the measure results as an integer in base-N + * @return measured results as an ineger in base-N representation + */ + idx get_results_as_N() const { + std::vector tmp; + for (idx i = 0; i < number_qubit_; ++i) { + if (measured_[i]) + tmp.push_back(results_[i]); + } + return multiidx2n(tmp, std::vector(tmp.size(), dimension_)); + } + /** + * @brief get a reference to the classical bits associated with the circuit + * @return referene to the referene of classical bits + */ + std::vector& bits() noexcept { return bits_; } +}; + +class LogicalCircuit : public IDisplay { + using idx_vec = std::vector; + using elem_type = std::tuple; + std::vector gates_; + idx gate_count = 0; + + public: + /** + * @brief add a gate to the logical circuit + * @param gate the unitary gate matrix to be added + * @param gate_name name or identifier of the gate + * @param ctrl control qubit indices for controlled gate + * @param target target qubit indices for the gate Operation + */ + void add(const cmat& gate, const std::string& gate_name, const idx_vec& ctrl, + const idx_vec& target) { + auto elem = std::make_tuple(gate, gate_name, ctrl, target); + gates_.push_back(elem); + ++gate_count; + } + /** + * @brief gate the total count of gates added to the logical circuit + * @return total count of gates in the logical circuit + */ + idx gate_gate_count() const { return gate_count; } + + /** + * @brief display the logical circuit's gate and their details + * @param os the output stream to display the circuit details + * @return the modified output stream with the circuit details + */ + std::ostream& display(std::ostream& os) const override { + os << "["; + bool first = true; + for (auto&& elem : gates_) { + if (first) { + os << "("; + first = false; + } else { + os << ", ("; + } + os << std::get<1>(elem) << ", "; + os << clara::disp(std::get<2>(elem), ", "); + os << ", "; + os << disp(std::get<3>(elem), ", "); + os << ")"; + } + os << "]"; + return os; + } +}; + +class Test { + idx number_qubit_; + idx number_classical_; + idx dimension_; + ket psi_; + std::vector measured_; + std::vector results_; + std::vector dits_; + + enum class Operation { GATE, CTRL_GATE, MEASURE_Z, MEASURE_V, MEASURE_KS }; + + struct StepType { + Operation op_; std::vector mats_; + std::vector ctrl_; std::vector target_; - std::string name_; - MeasureStep() = default; - MeasureStep(MeasureType measurement_type, const std::vector& mats, - const std::vector& target, const std::string& name = "") - : measurement_type_{measurement_type}, mats_{mats}, target_{target}, name_{name} {} + std::string name_; + StepType(Operation op, const std::vector& mats, const std::vector& ctrl, + const std::vector& target, const std::string& name) + : op_{op}, mats_{mats}, ctrl_{ctrl}, target_{target}, name_{name} {} }; + std::vector circuit_; + + protected: std::vector update_subsys_(const std::vector& subsys) { std::vector result = subsys; idx subsys_size = subsys.size(); for (idx i = 0; i < subsys_size; ++i) { - for (idx m = 0; m < subsys[i]; ++m) { + for (idx m = 0; m < subsys_size; ++i) { if (measured_[m]) { --result[i]; } @@ -84,15 +318,42 @@ class ClaraCircuit : public IDisplay { } public: - std::vector measured_; - ClaraCircuit(idx number_qudit, idx number_classical_bits = 0, idx dimension = 2, - const std::string& name = ""); - void apply(const cmat& U, idx i, const std::string& name = ""); - void measureZ(idx i, const std::string& name = ""); - std::ostream& display(std::ostream& os) const override; - - friend std::ostream& operator<<(std::ostream& os, const GateType& gate_type); - friend std::ostream& operator<<(std::ostream& os, const MeasureType& measurement_type); + Test(idx number_qubit, idx number_classical_ = 0, idx dimension = 2) + : number_qubit_{number_qubit}, + number_classical_{number_classical_}, + dimension_{dimension}, + psi_{clara::st.zero(number_qubit_, dimension_)}, + measured_(number_qubit_, false), + results_(number_qubit_, -1), + dits_(number_classical_, 0), + circuit_{} {} + + template + void apply(const Eigen::MatrixBase& A, const std::vector& target, + const std::string name = "") { + circuit_.emplace_back(Operation::GATE, std::vector{A}, std::vector{}, target, name); + } + + template + void applyCTRL(const Eigen::MatrixBase& A, const std::vector& ctrl, + const std::vector& target, const std::string& name = "") { + circuit_.emplace_back(Operation::CTRL_GATE, std::vector{A}, ctrl, target, name); + } + + void measureZ(const std::vector& target, const std::string& name = "") { + circuit_.emplace_back(Operation::MEASURE_Z, std::vector{}, std::vector{}, target, + name); + } + + void measureV(const cmat& V, const std::vector& target, const std::string& name = "") { + circuit_.emplace_back(Operation::MEASURE_V, std::vector{V}, std::vector{}, target, + name); + } + + void measureKs(const std::vector& Ks, const std::vector& target, + const std::string& name = "") { + circuit_.emplace_back(Operation::MEASURE_KS, Ks, std::vector{}, target, name); + } }; } // namespace experimental From 365ae9be9cc5259c309b4b6de79a3d75d5d9d49d Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 8 Aug 2023 13:58:10 +0700 Subject: [PATCH 69/80] fix: testing timer Signed-off-by: slowy07 --- .../tests/classFunction/timer_testing.cpp | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/clara_test/tests/classFunction/timer_testing.cpp b/clara_test/tests/classFunction/timer_testing.cpp index 47cbe34..5223e81 100644 --- a/clara_test/tests/classFunction/timer_testing.cpp +++ b/clara_test/tests/classFunction/timer_testing.cpp @@ -6,28 +6,16 @@ using namespace clara; -const double TOLERANCE = 1e-6; - -class TimerTest : public ::testing::Test { -protected: - void sleep_for(int milliseconds) { -std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); - } -}; - -TEST_F(TimerTest, BasicFunctionallyTest) { - Timer timer; - sleep_for(100); - timer.toc(); - EXPECT_NEAR(timer.tics(), 100.0, TOLERANCE); +TEST(clara_timer_test, DefaultConstructor) { + Timer<> timer; + EXPECT_GE(timer.tics(), 0); } -TEST(clara_timer, AllTests) { - using namespace std::chrono; - - Timer<> t; - std::this_thread::sleep_for(milliseconds(100)); - t.tic(); - t.toc(); - EXPECT_NEAR(t.tics(), 0, 0.05); +TEST(clara_timer_test, TicToc) { + Timer<> timer; + timer.tic(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + timer.toc(); + auto duration = timer.get_duration(); + EXPECT_GE(duration.count(), 2); } From 81fd7ea86072a6a0f2501c25c67e4a92d316a95c Mon Sep 17 00:00:00 2001 From: slowy07 Date: Thu, 10 Aug 2023 09:26:06 +0700 Subject: [PATCH 70/80] chore: more upgrade stuff [Documentation] change from `pi` to `std::pi` docs: add more documentation `gates.h` fix: fixing entanglement which testing always return `out of range` Signed-off-by: slowy07 --- clara_test/tests/CMakeLists.txt | 2 + clara_test/tests/entanglement_testing.cpp | 92 +++++++++ clara_test/tests/internal/util_testing.cpp | 34 ++++ include/classFunction/gates.h | 2 +- include/entanglement.h | 222 ++++++++++++--------- include/entropies.h | 34 +++- include/functions.h | 8 +- include/instruments.h | 7 + include/internal/util.h | 157 +++++++++++++-- include/number_theory.h | 4 +- include/operations.h | 4 +- include/random.h | 21 +- 12 files changed, 441 insertions(+), 146 deletions(-) create mode 100644 clara_test/tests/entanglement_testing.cpp create mode 100644 clara_test/tests/internal/util_testing.cpp diff --git a/clara_test/tests/CMakeLists.txt b/clara_test/tests/CMakeLists.txt index 51d9d99..beec08f 100644 --- a/clara_test/tests/CMakeLists.txt +++ b/clara_test/tests/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable( classFunction/timer_testing.cpp internal/classFunction/iomanip_testing.cpp internal/classFunction/singleton_testing.cpp + internal/util_testing.cpp + entanglement_testing.cpp testing_main.cpp traits.cpp input_output_testing.cpp diff --git a/clara_test/tests/entanglement_testing.cpp b/clara_test/tests/entanglement_testing.cpp new file mode 100644 index 0000000..b98696e --- /dev/null +++ b/clara_test/tests/entanglement_testing.cpp @@ -0,0 +1,92 @@ +#include +#include + +#include "../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; + +TEST(clara_entanglement_negativity_test, AllTest) { + cmat rho = randrho(3); + cmat sigma = randrho(3); + EXPECT_NEAR(0, clara::negativity(kron(rho, sigma), {3, 3}), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_2x2_product_state) { + idx dimension = 2; + cmat UA = randU(dimension); + cmat UB = randU(dimension); + ket psi = kron(UA, UB) * mket({0, 0}); + std::vector result_vect = clara::schmidtprobs(psi); + + dyn_col_vect result = + Eigen::Map>(result_vect.data(), result_vect.size()); + dyn_col_vect expected = dyn_col_vect::Zero(dimension); + expected(0) = 1; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_3x3_product_state) { + idx dimension = 3; + cmat UA = randU(dimension); + cmat UB = randU(dimension); + ket psi = kron(UA, UB) * mket({1, 1}, dimension); + std::vector result_vect = clara::schmidtprobs(psi, dimension); + dyn_col_vect result = + Eigen::Map>(result_vect.data(), result_vect.size()); + dyn_col_vect expected = dyn_col_vect::Zero(dimension); + expected(0) = 1; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_degenerate_1x1_product_state) { + idx dA = 1, dB = 1, D = dA * dB, minD = std::min(dA, dB); + cmat UA = randU(dA); + cmat UB = randU(dB); + ket psi = kron(UA, UB) * mket({0, 0}, {dA, dB}); + dyn_col_vect result = clara::schmidtcoeffs(psi, {dA, dB}); + dyn_col_vect expected(minD); + expected << 1; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_degenerated_3x1_product_state) { + idx dA = 3, dB = 1, D = dA * dB, minD = std::min(dA, dB); + cmat UA = randU(dA); + cmat UB = randU(dB); + ket psi = kron(UA, UB) * mket({0, 0}, {dA, dB}); + dyn_col_vect result = clara::schmidtcoeffs(psi, {dA, dB}); + dyn_col_vect expected = dyn_col_vect(minD); + expected << 1; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_2x2_state_with_fixed_schmidt_coeff) { + idx dimension = 2; + double c0 = 0.8, c1 = 0.6; + cmat UA = randU(dimension); + cmat UB = randU(dimension); + ket psi = kron(UA, UB) * (c0 * st.zero(2) + c1 * st.one(2)); + std::vector result_vect = clara::schmidtprobs(psi); + dyn_col_vect result = + Eigen::Map>(result_vect.data(), result_vect.size()); + dyn_col_vect expected = dyn_col_vect::Zero(dimension); + expected << c0 * c0, c1 * c1; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} + +TEST(clara_schmidtprobs_qubits, random_3x3_state_with_fixed_schmidt_coeff) { + idx dimension = 3; + double c0 = 0.8, c1 = 0.5; + double c2 = std::sqrt(1 - c0 * c0 - c1 * c1); + cmat UA = randU(dimension); + cmat UB = randU(dimension); + ket psi = kron(UA, UB) * (c0 * mket({0, 0}, dimension) + c1 * mket({1, 1}, dimension) + + c2 * mket({2, 2}, dimension)); + std::vector result_vect = clara::schmidtprobs(psi, dimension); + dyn_col_vect result = + Eigen::Map>(result_vect.data(), result_vect.size()); + dyn_col_vect expected = dyn_col_vect::Zero(dimension); + expected << c0 * c0, c1 * c1, c2 * c2; + EXPECT_NEAR(0, norm(result - expected), 1e-7); +} diff --git a/clara_test/tests/internal/util_testing.cpp b/clara_test/tests/internal/util_testing.cpp new file mode 100644 index 0000000..1331300 --- /dev/null +++ b/clara_test/tests/internal/util_testing.cpp @@ -0,0 +1,34 @@ +#include + +#include "../../../include/clara.h" +#include "gtest/gtest.h" + +using namespace clara; +using namespace clara::internal; + +TEST(clara_util_test, N2MultiIdx) { + const idx dims[] = {3, 4, 5}; + idx result[3]; + + n2multiidx(10, 3, dims, result); + EXPECT_EQ(result[0], 0); + EXPECT_EQ(result[1], 2); + EXPECT_EQ(result[2], 0); + + n2multiidx(24, 3, dims, result); + EXPECT_EQ(result[0], 1); + EXPECT_EQ(result[1], 0); + EXPECT_EQ(result[2], 4); +} + +TEST(UtilTest, MultiIdx2N) { + const idx dims[] = {3, 4, 5}; + idx midx[] = {2, 3, 1}; + + idx n = multiidx2n(midx, 3, dims); + EXPECT_EQ(n, 56); + + idx midx2[] = {0, 3, 4}; + idx n2 = multiidx2n(midx2, 3, dims); + EXPECT_EQ(n2, 19); +} diff --git a/include/classFunction/gates.h b/include/classFunction/gates.h index f673ed9..30057e7 100644 --- a/include/classFunction/gates.h +++ b/include/classFunction/gates.h @@ -77,7 +77,7 @@ class Gates final : public internal::Singleton { Z << 1, 0, 0, -1; Y << 0, -1_i, 1_i, 0; S << 1, 0, 0, 1_i; - T << 1, 0, 0, std::exp(1_i * pi / 4.0); + T << 1, 0, 0, std::exp(1_i * clara::pi / 4.0); CNOT.block(2, 2, 2, 2) = X; CNOTba(0, 0) = 1; CNOTba(1, 3) = 1; diff --git a/include/entanglement.h b/include/entanglement.h index 59ac116..2feddd0 100644 --- a/include/entanglement.h +++ b/include/entanglement.h @@ -14,6 +14,7 @@ #include "operations.h" #include "types.h" namespace clara { + /** * @brief calculate the schmidt coefficients of bipartite pure state 'A' * @note the sum of square of schmidt coefficients equal 1 @@ -39,20 +40,23 @@ namespace clara { * dyn_col_vect schmidCoeffs = schmidtcoeffs(bipartiteState, dimensions); */ template -dyn_col_vect schmidcoeffs(const Eigen::MatrixBase& A, - const std::vector& dims) { +dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, + const std::vector& dims) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::schmidtcoeffs()"); + throw exception::ZeroSize("entanglement: clara::schmidtcoeffs()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::schmidtcoeffs()"); + throw exception::NotBipartite("entanglement: clara::schmidtcoeffs()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotSquareNotCvector("clara::schmidtcoeffs()"); - if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchCvector("clara::schmidtcoeffs()"); + throw exception::MatrixNotCvector("entanglement: clara::schmidtcoeffs()"); + // check matching dimensions + if (!internal::check_dims_match_cvect(dims, rA)) + throw exception::DimsMismatchCvector("entanglement: clara::schmidtcoeffs()"); - // calculate the singlar value of the transposed and reshaped matrix + // calculate the singular value of the trensposed and reshape matrix return svals(transpose(reshape(rA, dims[1], dims[0]))); } @@ -79,17 +83,18 @@ dyn_col_vect schmidcoeffs(const Eigen::MatrixBase& A, * // with a specified dimension of 3 * dyn_col_vect schmidCoeffs3 = schmidtcoeffs(bipartiteState, 3); */ - template dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); - + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::schmidtcoeffs()"); + throw exception::ZeroSize("entanglement: clara::schmidtcoeffs()"); + // check valid dims if (d < 2) - throw exception::DimsInvalid("clara::schmidtcoeffs()"); - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); + throw exception::DimsInvalid("entanglement: clara::schmidtcoeffs()"); + + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return schmidtcoeffs(A, dims); } @@ -117,19 +122,21 @@ dyn_col_vect schmidtcoeffs(const Eigen::MatrixBase& A, idx d = * // calculate the schmidt basis on alice side for the bipartite pure state * cmat schmidtMatrix = schmidtA(bipartiteState, dimension); */ - template cmat schmidtA(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::schmidtA()"); + throw exception::ZeroSize("entanglement: clara::schmidtA()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::schmidtA()"); + throw exception::NotBipartite("entanglement: clara::schmidtA()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotCvector("clara::schmidtA()"); - if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchCvector("clara::schmidtA()"); + throw exception::MatrixNotCvector("entanglement: clara::schmidtA()"); + // check matching dimensions + if (!internal::check_dims_match_cvect(dims, rA)) + throw exception::DimsMismatchCvector("entanglement: clara::schmidtA()"); return svdU(transpose(reshape(rA, dims[1], dims[0]))); } @@ -155,13 +162,15 @@ cmat schmidtA(const Eigen::MatrixBase& A, const std::vector& dims) template cmat schmidtA(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::schmidtA()"); + throw exception::ZeroSize("entanglement: clara::schmidtA()"); + // check valid dims if (d < 2) - throw exception::DimsInvalid("clara::schmidtA()"); + throw exception::DimsInvalid("entanglement: clara::schmidtA()"); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); return schmidtA(A, dims); } @@ -186,19 +195,18 @@ cmat schmidtA(const Eigen::MatrixBase& A, idx d = 2) { template cmat schmidtB(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::schmidtB()"); + throw exception::ZeroSize("entanglement: clara::schmidtB()"); // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::schmidtB()"); + throw exception::NotBipartite("entanglement: clara::schmidtB()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotCvector("clara::schmidtB()"); - - if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchCvector("clara::schmidtB()"); - - // by default returns U_B* + throw exception::MatrixNotCvector("entanglement: clara::schmidtB()"); + // check matching dimensions + if (!internal::check_dims_match_cvect(dims, rA)) + throw exception::DimsMismatchCvector("entanglement: clara::schmidtB()"); return svdV(transpose(reshape(conjugate(rA), dims[1], dims[0]))); } @@ -221,14 +229,14 @@ cmat schmidtB(const Eigen::MatrixBase& A, const std::vector& dims) template cmat schmidtB(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::schmidtB()"); - if (d < 0) - throw exception::DimsInvalid("clara::schmidtB()"); - - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - // local dimension vector - std::vector dims(N, d); + throw exception::ZeroSize("entanglement: clara::schmidtB()"); + // check valid dims + if (d < 2) + throw exception::DimsInvalid("entanglement: clara::schmidtB()"); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return schmidtB(A, dims); } @@ -255,22 +263,23 @@ template std::vector schmidtprobs(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::schmidtprobs()"); + throw exception::ZeroSize("entanglement: clara::schmidtprobs()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::schmidtprobs()"); - + throw exception::NotBipartite("entanglement: clara::schmidtprobs()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotCvector("clara::schmidtprobs()"); - - if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchCvector("clara::schmidtprobs()"); - + throw exception::MatrixNotCvector("entanglement: clara::schmidtprobs()"); + // check matching dimensions + if (!internal::check_dims_match_cvect(dims, rA)) + throw exception::DimsMismatchCvector("entanglement: clara::schmidtprobs()"); std::vector result; dyn_col_vect scf = schmidtcoeffs(rA, dims); for (idx i = 0; i < static_cast(scf.rows()); ++i) result.push_back(std::pow(scf(i), 2)); + return result; } @@ -298,15 +307,15 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, */ template std::vector schmidtprobs(const Eigen::MatrixBase& A, idx d = 2) { - const dyn_mat rA = A.derived(); - + const dyn_mat& rA = A.derived(); + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::schmidtprobs()"); + throw exception::ZeroSize("entanglement: clara::schmidtprobs()"); + // check valid dims if (d < 2) - throw exception::DimsInvalid("clara:schmidtprobs()"); - - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); + throw exception::DimsInvalid("entanglement: clara::schmidtprobs()"); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return schmidtprobs(A, dims); } @@ -333,16 +342,18 @@ std::vector schmidtprobs(const Eigen::MatrixBase& A, idx d = 2) template double entanglement(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::entanglement()"); + throw exception::ZeroSize("entanglement: clara::entanglement()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::entanglement()"); + throw exception::NotBipartite("entanglement: clara::entanglement()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotCvector("clara::entanglement()"); + throw exception::MatrixNotCvector("entanglement: clara::entanglement()"); // check matching dimensions if (!internal::check_dims_match_cvect(dims, rA)) - throw exception::DimsMismatchCvector("clara::entanglement()"); - + throw exception::DimsMismatchCvector("entanglement: clara::entanglement()"); return entropy(schmidtprobs(rA, dims)); } @@ -371,13 +382,15 @@ double entanglement(const Eigen::MatrixBase& A, const std::vector& template double entanglement(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::entanglement()"); + throw exception::ZeroSize("entanglement: clara::entanglement()"); + // check valid dims if (d < 2) - throw exception::DimsInvalid("clara::entanglement()"); + throw exception::DimsInvalid("entanglement: clara::entanglement()"); - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return entanglement(A, dims); } @@ -400,15 +413,18 @@ double entanglement(const Eigen::MatrixBase& A, idx d = 2) { template double gconcurrence(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::gconcurrence()"); + throw exception::ZeroSize("entanglement: clara::gconcurrence()"); + // check column vector if (!internal::check_cvector(rA)) - throw exception::MatrixNotCvector("clara::gconcurrence()"); + throw exception::MatrixNotCvector("entanglement: clara::gconcurrence()"); idx d = internal::get_dim_subsystem(static_cast(rA.rows()), 2); + // check equal local dimensions if (d * d != static_cast(rA.rows())) - throw exception::DimsNotEqual("clara::gconcurrence()"); + throw exception::DimsNotEqual("entanglement: clara::gconcurrence()"); + // we compute exp(logdet()) to avoid underflow return d * std::abs(std::exp(2. / d * logdet(reshape(rA, d, d)))); } @@ -435,17 +451,18 @@ double gconcurrence(const Eigen::MatrixBase& A) { template double negativity(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); - + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::negativity()"); + throw exception::ZeroSize("entanglement: clara::negativity()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::negativity()"); + throw exception::NotBipartite("entanglement: clara::negativity()"); // check square matrix vector if (!internal::check_square_mat(rA)) - throw exception::MatrixNotSquare("clara::negativity()"); - + throw exception::MatrixNotSquare("entanglement: clara::negativity()"); + // check matching dimensions if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchMatrix("clara::negativity()"); + throw exception::DimsMismatchMatrix("entanglement: clara::negativity()"); return (schatten(ptranspose(rA, {0}, dims), 1) - 1.) / 2.; } @@ -468,15 +485,16 @@ double negativity(const Eigen::MatrixBase& A, const std::vector& d * subystem double negativityValue3 = negativity(bipartiteMixedState, 3); */ template -double negetivity(const Eigen::MatrixBase& A, idx d = 2) { +double negativity(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::negativity()"); + throw exception::ZeroSize("entanglement: clara::negativity()"); + // check valid dims if (d < 2) - throw exception::DimsInvalid("clara::negativity()"); - - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); + throw exception::DimsInvalid("entanglement: clara::negativity()"); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return negativity(A, dims); } @@ -502,14 +520,18 @@ double negetivity(const Eigen::MatrixBase& A, idx d = 2) { template double lognegativity(const Eigen::MatrixBase& A, const std::vector& dims) { const dyn_mat& rA = A.derived(); + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::lognegativity()"); + throw exception::ZeroSize("entanglement: clara::lognegativity()"); + // check bi-partite if (dims.size() != 2) - throw exception::NotBipartite("clara::lognegativity()"); + throw exception::NotBipartite("entanglement: clara::lognegativity()"); + // check square matrix vector if (!internal::check_square_mat(rA)) - throw exception::MatrixNotSquare("clara::lognegativity()"); + throw exception::MatrixNotSquare("entanglement: clara::lognegativity()"); + // check matching dimensions if (!internal::check_dims_match_mat(dims, rA)) - throw exception::DimsMismatchMatrix("clara::lognegativity()"); + throw exception::DimsMismatchMatrix("entanglement: clara::lognegativity()"); return std::log2(2 * negativity(rA, dims) + 1); } @@ -536,14 +558,14 @@ double lognegativity(const Eigen::MatrixBase& A, const std::vector template double lognegativity(const Eigen::MatrixBase& A, idx d = 2) { const dyn_mat& rA = A.derived(); - + // check zero size if (!internal::check_nonzero_size(A)) - throw exception::ZeroSize("clara::lognegativity()"); - if (d < 0) - throw exception::DimsInvalid("clara::lognegativity()"); - - idx N = internal::get_num_subsys(static_cast(rA.rows()), d); - std::vector dims(N, d); + throw exception::ZeroSize("entanglement: clara::lognegativity()"); + // check valid dims + if (d < 2) + throw exception::DimsInvalid("entanglement: clara::lognegativity()"); + idx n = internal::get_num_subsys(static_cast(rA.rows()), d); + std::vector dims(n, d); return lognegativity(A, dims); } @@ -567,21 +589,25 @@ template double concurrence(const Eigen::MatrixBase& A) { const dyn_mat& rA = A.derived(); + // check zero-size if (!internal::check_nonzero_size(rA)) - throw exception::ZeroSize("clara::concurrence()"); + throw exception::ZeroSize("entanglement: clara::concurrence()"); + // check square matrix vector if (!internal::check_square_mat(rA)) - throw exception::MatrixNotSquare("clara::concurrence()"); + throw exception::MatrixNotSquare("entanglement: clara::concurrence()"); + // check that the state is a 2-qubit state if (rA.rows() != 4) - throw exception::NotQubitSubsys("clara::concurrence()"); + throw exception::NotQubitSubsys("entanglement: clara::concurrence()"); + cmat sigmaY = Gates::get_instance().Y; dyn_col_vect lambdas = evals(rA * kron(sigmaY, sigmaY) * conjugate(rA) * kron(sigmaY, sigmaY)).real(); + std::vector lambdas_sorted(lambdas.data(), lambdas.data() + lambdas.size()); std::sort(std::begin(lambdas_sorted), std::end(lambdas_sorted), std::greater()); std::transform(std::begin(lambdas_sorted), std::end(lambdas_sorted), std::begin(lambdas_sorted), [](double elem) { return std::sqrt(std::abs(elem)); }); - return std::max(0., lambdas_sorted[0] - lambdas_sorted[1] - lambdas_sorted[2] - lambdas_sorted[3]); } diff --git a/include/entropies.h b/include/entropies.h index 17043f3..717d899 100644 --- a/include/entropies.h +++ b/include/entropies.h @@ -80,9 +80,22 @@ inline double entropy(const std::vector& prob) { } /** - * @brief renyi \f$\alpha\f$ entropy of the density matrix A, - * for \f$\alpha\geq 0\f$ - * @return Renyi-\f$\alpha\f$ entropy, with logarithm in base 2 + * @brief calculate the Renyi \alpha entropy of a density matrix A + * @tparam Derived type of the matrix-like object + * @param A the density matrix for which to calculate the Renyi \alpha entropy + * @return Renyi entropy of the density matrix A, with logarithm in base 2 + * + * @throw ZeroSize if the matrix A has zero size + * @throw MatrixNotSquare if the matrix A is not square + * @throw OutOfRange if alpha is negative + * + * NOTE: The Renyi α entropy is defined for α >= 0 and is calculated as: + * H_α(A) = (1 / (1 - α)) * log2(Σ(λ_i^α)), + * where λ_i are the eigenvalues of A. + * Special cases: + * - α = 0: H_0(A) = log2(dimension of A) + * - α = 1: H_1(A) = von Neumann entropy of A + * - α = ∞: H_∞(A) = -log2(λ_max), where λ_max is the largest eigenvalue of A. */ template double renyi(const Eigen::MatrixBase& A, double alpha) { @@ -97,6 +110,7 @@ double renyi(const Eigen::MatrixBase& A, double alpha) { if (alpha < 0) throw exception::OutOfRange("clara::renyi()"); + // special cases: α = 0, 1, ∞ if (alpha == 0) return std::log2(rA.rows()); if (alpha == 1) @@ -104,6 +118,7 @@ double renyi(const Eigen::MatrixBase& A, double alpha) { if (alpha == inifinity) return -std::log2(svals(rA)[0]); + // general case dmat sv = svals(rA); double result = 0; for (idx i = 0; i < static_cast(sv.rows()); ++i) @@ -298,7 +313,6 @@ inline double tsallis(const std::vector& prob, double q) { * * // calculate the quantum mutual information between subsystem A and B * double mutualInfo = qmutualinfo(densityMatrix, subsystemA, subsystemB, dimensions); - * */ template double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& subsysA, @@ -367,12 +381,12 @@ double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& * @return double the mutual information between subsystem A and B * * @example - * Eigen::matrixXd densityMatrix = ...; - * std::vector subsystemA = {0, 1} - * std::vector subsystemB = {0, 2} - * - * // calculate the mutual information between subsystem A and B - * double mutualInfo = qmutualinfo(densityMatrix, subsystemA, subsystemB, dimension); + * Eigen::matrixXd densityMatrix = ...; + * std::vector subsystemA = {0, 1} + * std::vector subsystemB = {0, 2} + * + * // calculate the mutual information between subsystem A and B + * double mutualInfo = qmutualinfo(densityMatrix, subsystemA, subsystemB, dimension); */ template double qmutualinfo(const Eigen::MatrixBase& A, const std::vector& subsysA, diff --git a/include/functions.h b/include/functions.h index 879612d..ba15e57 100644 --- a/include/functions.h +++ b/include/functions.h @@ -587,7 +587,7 @@ dyn_col_vect svals(const Eigen::MatrixBase& A) { */ template cmat svdU(const Eigen::MatrixBase& A) { - const dyn_mat& rA = A.dervied(); + const dyn_mat& rA = A.derived(); // check zero-size if (!internal::check_nonzero_size(rA)) throw exception::ZeroSize("clara::svdU()"); @@ -2228,6 +2228,7 @@ inline cmat bloch2rho(const std::vector& r) { return (Id2 + r[0] * X + r[1] * Y + r[2] * Z) / 2.; } +inline namespace literals { /** * @brief multi-partite qubit ket user-defined literal * this user-defined literal construct the multi partite qubit ket \f$|\mathrm{Bits}\rangle\f$ @@ -2285,7 +2286,7 @@ bra operator"" _bra() { // check valid multi-partite qubit state for (idx i = 0; i < n; ++i) { if (bits[i] != '0' && bits[i] != '1') - throw exception::OutOfRange(R"xxx(qpp::operator "" _bra())xxx"); + throw exception::OutOfRange(R"xxx(clara::operator "" _bra())xxx"); } // convert the binary representation to an index 'pos' in decimal (base-10) format @@ -2316,12 +2317,13 @@ cmat operator"" _prj() { // check valid multi-partite qubit state for (idx i = 0; i < n; ++i) { if (bits[i] != '0' && bits[i] != '1') - throw exception::OutOfRange(R"xxx(qpp::operator "" _prj())xxx"); + throw exception::OutOfRange(R"xxx(clara::operator "" _prj())xxx"); } // using the kronecker product to construct the qubit projector return kron(operator""_ket(), operator""_bra()); } +} // namespace literals } // namespace clara diff --git a/include/instruments.h b/include/instruments.h index fac192a..1ae9b1f 100644 --- a/include/instruments.h +++ b/include/instruments.h @@ -877,7 +877,14 @@ std::tuple, double, cmat> measure_seq(const Eigen::MatrixBase 0) { auto tmp = measure(cA, Gates::get_instance().Id(dims[subsys[0]]), {subsys[0], dims}); result.push_back(std::get<0>(tmp)); + prob *= std::get<1>(tmp)[std::get<0>(tmp)]; + cA = std::get<2>(tmp)[std::get<0>(tmp)]; + dims.erase(std::next(std::begin(dims), subsys[0])); + subsys.erase(std::begin(subsys)); } + + std::reverse(std::begin(result), std::end(result)); + return std::make_tuple(result, prob, cA); } /** diff --git a/include/internal/util.h b/include/internal/util.h index ead4dd9..763bb1c 100644 --- a/include/internal/util.h +++ b/include/internal/util.h @@ -84,43 +84,79 @@ inline idx multiidx2n(const idx* const midx, idx numdims, const idx* const dims) #pragma GCC diagnostic pop #endif -// check square matrix +/** + * @brief check if a matrix is square + * @tparam derived Eigen matrix type + * @param A eigen matrix to check + * @return True if the matrix is square, false otherwise + */ template bool check_square_mat(const Eigen::MatrixBase& A) { return A.rows() == A.cols(); } -// check whether intpu is a vector or not +/** + * @brief check if a matrix is a vector + * @tparam Derived Eigen matrix type + * @param A Eigen matrix to check + * @return True if the matrix is a vector, false otherwise + */ template bool check_vector(const Eigen::MatrixBase& A) { return A.rows() == 1 || A.cols() == 1; } -// check wheter input is a column vector or not +/** + * @brief check if a matrix is a row vector + * @tparam Derived Eigen matrix type + * @param A Eigen matrix to check + * @return true if type matrix is a row vector, false otherwise + */ template bool check_rvector(const Eigen::MatrixBase& A) { return A.rows() == 1; } -// check wheter input is a column vector or not +/** + * @brief check if a matrix is a column vector + * @tparam Derived Eigen matrix type + * @param A eigen matrix to check + * @return true if the matrix is a column vector, false otherwise + */ template bool check_cvector(const Eigen::MatrixBase& A) { return A.cols() == 1; } -// check non-zero size of object that support size() function +/** + * @brief check if an object has a non-zero size + * @tparam T type of the object + * @param x object to check + * @return true if the object has a non-zero size, false otherwise + */ template bool check_nonzero_size(const T& x) noexcept { return x.size() != 0; } -// check that all size match +/** + * @brief check if two objects have matching size + * @tparam T1 type of the first object + * @tparam T2 type of the second object + * @param lhs the first object to compare + * @param rhs the second object to compare + * @return true if the size matrch, false otherwise + */ template bool check_matching_sizes(const T1& lhs, const T2& rhs) noexcept { return lhs.size() == rhs.size(); } -// check that dims is a valid dimension vector +/** + * @brief check if a vector represent valid dimension + * @param dims vector of dimension to check + * @return true if dimension are valid, false otherwise + */ inline bool check_dims(const std::vector& dims) { if (dims.size() == 0) return false; @@ -153,7 +189,16 @@ bool check_dims_match_mat(const std::vector& dims, const Eigen::MatrixBase< return proddim == static_cast(A.rows()); } -// check that valid dims match the dimension of valid column vector +/** + * @brief check if valid dimension match dimension of a valid column vector + * @tparam Derived Eigen matrix type + * @param dims vector of dimensions to check + * @param A eigen matrix (column vector) to compare dimension + * @return true if dimension match false otherwise + * + * NOTE: this function check if the product of dimension in `dims` matches the number + * of rows in the column vector `A`. + */ template bool check_dims_match_cvect(const std::vector& dims, const Eigen::MatrixBase& A) { #ifndef NDEBUG @@ -166,7 +211,16 @@ bool check_dims_match_cvect(const std::vector& dims, const Eigen::MatrixBas return proddim == static_cast(A.rows()); } -// check that valid dims mtch the dimension of valid row vector +/** + * @brief check if valid dimensions mtach the dimension of valid row vector + * @tparam Derived Eigen matrix type + * @param dims vector of dimension to checks + * @param A Eigen matrix to compare dimension + * @return true if dimension match, false otherwise + * + * NOTE: function check if the product of dimension in `dims` matches the number of columns in the + * row vector `A`. + */ template bool check_dims_match_rvect(const std::vector& dims, const Eigen::MatrixBase& A) { #ifndef NDEBUG @@ -179,7 +233,12 @@ bool check_dims_match_rvect(const std::vector& dims, const Eigen::MatrixBas return proddim == static_cast(A.cols()); } -// check that all elements in valid dims equal to dim +/** + * @brief check if all elements in valid dimension vector are equal to a given dimension + * @param dims vector of dimensions to check + * @param dim dimension to compare + * @return true if all dimension in `dims` are equal to `dim`, false otherwise + */ inline bool check_eq_dims(const std::vector& dims, idx dim) noexcept { #ifndef NDEBUG assert(dims.size() > 0); @@ -191,7 +250,16 @@ inline bool check_eq_dims(const std::vector& dims, idx dim) noexcept { return true; } -// check that subsys is valid with respect to valid dims +/** + * @brief check if subsystem indices are valid with respect to given diemension + * @param subsys vector of subsystem indices to check + * @param dims vector a valid dimension + * @param dims vector of valid dimensions + * @return true if subsystem indices are valid, false otherwise + * + * NOTE: the function check if each index in `subsys` is within the valid range of dimension + * specified by `dims` + */ inline bool check_subsys_match_dims(const std::vector& subsys, const std::vector& dims) { if (subsys.size() > dims.size()) return false; @@ -208,31 +276,58 @@ inline bool check_subsys_match_dims(const std::vector& subsys, const std::v }) == std::end(subsyssort); } -// check matrix is 2 x 2 +/** + * @brief check if matrix is 2x2 matrix + * @tparam Derived Eigen matrix type + * @param A Eigen matrix to check + * @return true if the matrix is 2x2 matrix, false otherwise + */ template bool check_qubit_matrix(const Eigen::MatrixBase& A) noexcept { return A.rows() == 2 && A.cols() == 2; } -// check column vector is 2 x 1 +/** + * @brief check if collumn vector is a 2x1 vector + * @tparam Derived Eigen matrix type + * @param A Eigen column vector to check + * @return true if the column vector is a 2x1 vector, false otherwise + */ template bool check_qubit_cvector(const Eigen::MatrixBase& A) noexcept { return A.rows() == 2 && A.cols() == 1; } -// check row vector 1 x 2 +/** + * @brief check if row vector is a 1x2 vector + * @tparam Derived Eigen matrix type + * @param A eigen row vector to check + * @return true if the row vector is a 1x2 vector, false otherwise + */ template bool check_qubit_rvector(const Eigen::MatrixBase& A) noexcept { return A.rows() == 1 && A.cols() == 2; } -// check row vector is 1 x 2 or 2 x 1 +/** + * @brief check if vector is a 1x2 or 2x1 vector + * @tparam Derived Eigen matrix type + * @param A Eigen vector to check + * @return true if the vector is a 1x2 or 2x1 vector, false otherwise + */ template bool check_qubit_vector(const Eigen::MatrixBase& A) noexcept { return (A.rows() == 1 && A.cols() == 2) || (A.rows() == 2 && A.cols() == 1); } -// check valid permutation +/** + * @brief check if a given vector represent a valid permutation + * @param perm vector repersenting a permutation + * @return true if the vector is a valid permutation, false otherwise + * + * NOTE: this function check if the input vector contains a valid permutation of indices from 0 to + * (size - 1) + */ inline bool check_perm(const std::vector& perm) { if (perm.size() == 0) return false; @@ -315,10 +410,17 @@ dyn_mat dirsum2(const Eigen::MatrixBase& A, return result; } -// extract variadic template argyment pack into std::vector +/** + * @brief extract a variadic template argument pack and emplaces them into a std::vector + * @tparam T type of the elements to be emplaced in the vector + * @param v the vector to emplace elements + * @param first the first element to emplace + * @param args the rest of the elements to emplace + * + * NOTE: this function recursively emplace the provide elements into the vector + */ template void variadict_vector_emplace(std::vector&) {} - template void variadic_vector_emplace(std::vector& v, First&& first, Args&&... args) { v.emplace_back(std::forward(first)); @@ -348,8 +450,9 @@ inline idx get_num_subsys(idx sz, idx d) { * @param N the number of subsystem * @return the dimension of each subystem * - * NOTE: this function calculates the dimension of each subsystem in an object (ket/bra/density matrix) - * of size sz, consiting of N subsystem. the dimension in the object are assumed to be the same + * NOTE: this function calculates the dimension of each subsystem in an object (ket/bra/density + * matrix) of size sz, consiting of N subsystem. the dimension in the object are assumed to be the + * same */ inline idx get_dim_subsystem(idx sz, idx N) { #ifndef NDEBUG @@ -361,8 +464,20 @@ inline idx get_dim_subsystem(idx sz, idx N) { return static_cast(std::llround(std::pow(sz, 1. / N))); } -// implementation detasil for pretty formating +/** + * @brief implementation of displaying matrix-line object with formatting options + * + * NOTE: this struct provides a methods to display a matrix-like object with customizable formatting + */ struct Display_Impl_ { + /** + * @brief display a matrix-like object with customizable formatting + * @tparam T type of the matrix-like object + * @param A the matrix-like object to be displayed + * @param os the output stream to which the formatted matrix will be written + * @param chop the threshold for chopping small values + * @return the modified output stream + */ template // T must support rows(), cols(), operator()(idx, idx) const std::ostream& display_impl_(const T& A, std::ostream& os, double chop = clara::chop) const { diff --git a/include/number_theory.h b/include/number_theory.h index fff0b7d..c52ef34 100644 --- a/include/number_theory.h +++ b/include/number_theory.h @@ -527,7 +527,7 @@ inline bigint modinv(bigint a, bigint p) { * prime numbers. the function perform a fermat primality test an initial check it then * computes the values of 'u' and 'r' for the miller-rabin test the function uses the modpow * and modmul functions for modular exponentiation and multiplication. - * the result of the miller-rabin test + * the result of the miller-rabin test * * @example * // usage of isprime function to test for primality @@ -594,7 +594,7 @@ inline bool isprime(bigint p, idx k = 80) { * bigint lower_bound = 100; * bigint upper_bound = 1000; * bigint result = randprime(lower_bound, upper_bound); -*/ + */ inline bigint randprime(bigint a, bigint b, idx N = 1000) { if (a > b) throw exception::OutOfRange("clara::randprime()"); diff --git a/include/operations.h b/include/operations.h index 4f0e62b..4db347b 100644 --- a/include/operations.h +++ b/include/operations.h @@ -74,7 +74,7 @@ dyn_mat applyCTRL(const Eigen::MatrixBase& Ai.push_back(powm(rA, i)); Aidagger.push_back(powm(adjoint(rA), i)); } - + // total dimension idx D = static_cast(rstate.rows()); // total number of subsystem @@ -1092,7 +1092,7 @@ dyn_mat ptranspose(const Eigen::MatrixBase& A if (!internal::check_dims_match_mat(dims, rA)) throw exception::DimsMismatchCvector("clara::ptranspose()"); if (subsys.size() == dims.size()) - return (rA * adjoiint(rA)).transpose(); + return (rA * adjoint(rA)).transpose(); if (subsys.size() == 0) return rA * adjoint(rA); diff --git a/include/random.h b/include/random.h index 2973c27..b551b31 100644 --- a/include/random.h +++ b/include/random.h @@ -437,15 +437,16 @@ inline std::vector randkraus(idx N, idx D) { /** * @brief generate random hermitian matrix - * - * this function generates a random hermitian matrix of size 'D'. the generated matrix is hermitian meaning - * that is equal to its own conjugate transpose. the matrix is constructed by first generating a random complex matrix - * 'H' of size 'D x D' with entries uniformly distributed in the interval [0, 1], then, its is transformed into hermitian - * matrix using formula H = H + H^†, where H^† is the conjugate transpose of 'H' + * + * this function generates a random hermitian matrix of size 'D'. the generated matrix is hermitian + * meaning that is equal to its own conjugate transpose. the matrix is constructed by first + * generating a random complex matrix 'H' of size 'D x D' with entries uniformly distributed in the + * interval [0, 1], then, its is transformed into hermitian matrix using formula H = H + H^†, where + * H^† is the conjugate transpose of 'H' * * @param D the size of the hermitian matrix, its must be a positive inetger * @return a random hermitian matrix of size 'DxD' - * + * * @throws exception::DimsInvalid if 'D' is zero, indicating an invalid input size * * @example @@ -549,9 +550,11 @@ inline std::vector randperm(idx N) { /** * @brief generate random probability vector uniformly distributed over the probability simplex * - * this function generates a random probability vector of size N, where the elements are uniformly distributed - * over the probability simple. the probability simplex is the set of all probability vector whose elements are non-negative and sum up to 1. the function first generates N random numbers from an exponential distribution with a rate parameter of 1 - * these random numbers are then normalized to ensure that they sum up to 1, thus forming a valid probability vector + * this function generates a random probability vector of size N, where the elements are uniformly + * distributed over the probability simple. the probability simplex is the set of all probability + * vector whose elements are non-negative and sum up to 1. the function first generates N random + * numbers from an exponential distribution with a rate parameter of 1 these random numbers are then + * normalized to ensure that they sum up to 1, thus forming a valid probability vector * * @param N the size of the probability vector to be generated * @return vector representing a random probability vector of size N From 64744bc38206e9e7af5de29c4bb74fa730f27766 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Fri, 11 Aug 2023 12:00:57 +0700 Subject: [PATCH 71/80] chore: add quantum operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Documentation] this code demonstrate various quantum channels using clara lib. it perform operations on a quantum state, such as partial transpose, measurement channels application and entropy computation. `performChannelsOperations()`: - step process - create the initial state `|00⟩ + |11⟩` and display it - perform partial transpose on the first subsystem of the state and display eigen values - set up a measurement channels with two kraus operators (pauli Z operatios for `|0⟩` and `|11⟩`) - apply the measurement channels to first subsystem and display the resulting state - partially trace down the second subsystem and display the resulting state. -compute and diplasy the von-neummann entropy of the final state. Signed-off-by: slowy07 --- testing/operation.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 testing/operation.cpp diff --git a/testing/operation.cpp b/testing/operation.cpp new file mode 100644 index 0000000..5b434a4 --- /dev/null +++ b/testing/operation.cpp @@ -0,0 +1,50 @@ +#include + +#include "../include/clara.h" + +using namespace clara; + +// define constant for kraus operators +const cmat PZ0 = st.pz0; +const cmat PZ1 = st.pz1; + +void ChannelOperations() { + // create an initial quantum state + cmat rho = st.pb00; + std::cout << "initial state " << std::endl; + std::cout << disp(rho) << std::endl; + + // perform partial transpose of first subsystem + cmat rhoTA = ptranspose(rho, {0}); + std::cout << "eigenvalues of the partial transpose of bell-0 state are " << std::endl; + std::cout << disp(transpose(hevals(rhoTA))) << std::endl; + + // set up measurement channel with 2 kraus operators + std::cout << "measurement channel with 2 kraus operators" << std::endl; + std::vector Ks{PZ0, PZ1}; + std::cout << disp(Ks[0]) << "\nand\n" << disp(Ks[1]) << std::endl; + + // compute the superoperator matrix of the channel + std::cout << "superoperator matrix of channel: " << std::endl + << disp(kraus2super(Ks)) << std::endl; + + // partially trace down the second subsystem + std::cout << "choi matrix of the channels: " << std::endl; + std::cout << disp(kraus2choi(Ks)) << std::endl; + + // apply the measurement channel into the first subsystem + cmat rhoOut = apply(rho, Ks, {0}); + std::cout << "after applying the measurement channel on the first qubit" << std::endl; + std::cout << disp(rhoOut); + + // partial trace down the second subsystem + cmat rhoA = ptrace(rhoOut, {1}); + std::cout << "after partially tracing down the second subsystem " << std::endl + << disp(rhoA) << std::endl; + + // compute the von-neumann entropy of the resulting state + double entropies = entropy(rhoA); + std::cout << "entropy: " << entropies << std::endl; +} + +int main() { ChannelOperations(); } From 764cf7aad25ba5bb03b915d2cc865debc890f8f2 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Wed, 16 Aug 2023 23:26:26 +0700 Subject: [PATCH 72/80] docs: adding documentation on ``functions.h`` [Documentation] function applies a scalar function element-wise to each entry of the input matrix 'A'. the result is a new matrix of the same dimensions, where each entry is the result of applying the provided scalar function to the corresponding entry in 'A'. Signed-off-by: slowy07 --- include/functions.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/functions.h b/include/functions.h index ba15e57..ee6ffb8 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1018,6 +1018,33 @@ double schatten(const Eigen::MatrixBase& A, double p) { return std::pow(result, 1. / p); } +/** + * @brief Apply a scalar function element-wise to an Eigen matrix + * + * this function applies a scalar function element-wise to ech entry of the input matrix 'A'. + * the result is new matrix of the same dimensions, where each entry is the result of + * applying the provided scalar function to the corresponding entry in 'A' + * + * @tparam OutputScalar the scalar type of the output matrix + * @tparam Derived the derived type of the input matrix 'A' + * @param A the input matrix to which the scalar function is applied + * @pram f the scalar function to be applied to each entry of the matrix + * @return new mtrix of the sam dimensions as 'A', where ech entry is the result of applying 'f' + * + * @throws exception::ZeroSize if the input matrix 'A' has zero size + * + * @example + * Eigen::MatrixXd inputMatrix(2, 2); + * inputMatrix << 1, 2, 3, 4; + * + * // define scalar function that square its input + * auto squareFunction = [](const double& x) { return x * x; }; + * + * // apply the scalar function element-wise to the input matrix + * Eigen::MatrixXd result = cwise(inputMatrix, squareFunction); + * std::cout << "input matrix " << std::endl << clara::disp(inputMatrix) << std::endl; + * std::cout << "result matrix: " << std::endl << clara::disp(result) << std::endl; + */ template dyn_mat cwise(const Eigen::MatrixBase& A, OutputScalar (*f)(const typename Derived::Scalar&)) { From 443601b6a3cacd7c082df246b2e92bc18a0168c5 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Sat, 19 Aug 2023 09:13:31 +0700 Subject: [PATCH 73/80] chore: adding quantum fourier transform [Documentation] adding algorithm that can be used to efficiently transform a quantum state from one basis to another. - Command Line Argument the code check if exactly two command line arguments are provided, if not it prints an errpr message and exists the program - Extracting Command Line Arguments - the number CPU cores to use (`num_core`) and the number qubits in quantum system (`n`) are extracted from the command line arguments - Setting OpenMP Threads - the number of OpenMP threads to use for parallel processing is set to `num_core` - Creating Quantum State Vector - A vector `qubits` is created to store indices - A quantum state vector `psi` is created using `mket` function with the qubit indices. - An initial copy `result` of the quantum state is also created - Measuring Execution Time - A `Timer` object `t` is created to measure the execution time - Quantum operations - A loops iterates over each qubit index - A hadamard gate (`gt.H`) applied to each qubit - Another Nested llop applies controlled-phase gate, where the control and target qubits are determined by the loop indices - The gate are applied to the `result` quantum state vector - Qubit Swap Operations - Another loop swaps qubit state using the `gt.SWAP` gate - Qubit at specific indices are swapped - Printing Result - the program prints the number of CPU cores used, the number of qubits, and the execution time measured by the timer Signed-off-by: slowy07 --- testing/quantum_fourier.cpp | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 testing/quantum_fourier.cpp diff --git a/testing/quantum_fourier.cpp b/testing/quantum_fourier.cpp new file mode 100644 index 0000000..edc5c83 --- /dev/null +++ b/testing/quantum_fourier.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "../include/clara.h" + +using namespace clara; + +int main(int argc, char **argv) { + // check if the correct number of command line arguments is provided + if (argc != 3) { + std::cerr << "ERROR: please specify number of core, qubit" << std::endl; + exit(EXIT_FAILURE); + } + + // number of cpu core to use + idx num_core = std::stoi(argv[1]); + // number of qubits in the quantum system + idx n = std::stoi(argv[2]); + // set the number openMP threads to use + omp_set_num_threads(num_core); + + // create a vector to store qubit indices + std::vector qubits(n); + // create a vector to store qubit indices + ket psi = mket(qubits); + // create a ket representing the intial quantum state + ket result = psi; + + // measure the execution time usign the timer class + Timer<> t; + for (idx i = 0; i < n; ++i) { + result = apply(result, gt.H, {i}); + for (idx j = 2; j <= n - i; ++j) { + cmat Rj(2, 2); + Rj << 1, 0, 0, omega(std::pow(2, j)); + // apply controlled-phase gate with control on qubit i and target on qubit i + j - 1 + result = applyCTRL(result, Rj, {i + j - 1}, {i}); + } + } + + // apply SWAP gates to exchange qubit states + for (idx i = 0; i < n / 2; ++i) { + result = apply(result, gt.SWAP, {i, n - i - 1}); + } + + // print the number of cores, number of qubits and the execution time + std::cout << "core: " << num_core << ", qubits" << n << ", time" << t.toc() << " seconds" + << std::endl; +} From 9d429226e34502902ce00b06b66424a72536d544 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 21 Aug 2023 20:47:51 +0700 Subject: [PATCH 74/80] .git/refs/COMMIT_EDITMSG Signed-off-by: slowy07 --- .../classFunction/reversible_testing.cpp | 26 +++++++ .../tests/classFunction/states_testing.cpp | 29 +++++++ .../tests/classFunction/timer_testing.cpp | 30 +++++++- include/classFunction/reversible.h | 8 +- include/classFunction/timer.h | 77 +++++++++++-------- 5 files changed, 128 insertions(+), 42 deletions(-) diff --git a/clara_test/tests/classFunction/reversible_testing.cpp b/clara_test/tests/classFunction/reversible_testing.cpp index f7109f2..1d1b8e5 100644 --- a/clara_test/tests/classFunction/reversible_testing.cpp +++ b/clara_test/tests/classFunction/reversible_testing.cpp @@ -23,3 +23,29 @@ TEST(clara_reversibe_test, TOF) { circuit.TOF({0, 1, 2}); EXPECT_EQ(circuit.get(2), true); } + +TEST(clara_reversibe_test, SetAndGet) { + Dynamic_bitset bitset(8); + bitset.set(3); + EXPECT_TRUE(bitset.get(3)); + EXPECT_FALSE(bitset.get(2)); +} + +TEST(clara_reversibe_test, InitializationCircuit) { + Dynamic_bitset bitset(8); + Bit_circuit circuit(bitset); + EXPECT_EQ(circuit.size(), 8); +} + +TEST(clara_reversibe_test, InitializationCircuiTenBitset) { + Dynamic_bitset bitset(10); + EXPECT_EQ(bitset.size(), 10); + EXPECT_EQ(bitset.storage_size(), 1); +} + +TEST(clara_reversibe_test, XGate) { + Dynamic_bitset bitset(8); + Bit_circuit circuit(bitset); + circuit.X(3); + EXPECT_TRUE(circuit.get(3)); +} diff --git a/clara_test/tests/classFunction/states_testing.cpp b/clara_test/tests/classFunction/states_testing.cpp index 0899656..0a78c82 100644 --- a/clara_test/tests/classFunction/states_testing.cpp +++ b/clara_test/tests/classFunction/states_testing.cpp @@ -28,6 +28,35 @@ TEST(clara_states_testing, SingletonInstance) { EXPECT_EQ(&states, &states2); } +TEST(clara_states_testing, MesState) { + const States& states = States::get_instance(); + ket mes_state = states.mes(); + + ket expected_mes_state = ket::Zero(4); + expected_mes_state(0) = expected_mes_state(3) = 1.0 / std::sqrt(2.0); + + EXPECT_EQ(mes_state, expected_mes_state); +} + +TEST(clara_states_testing, TestWStates) { + const States& states = States::get_instance(); + ket w_state = states.W; + ket expected_w_state = ket::Zero(8); + expected_w_state(1) = expected_w_state(2) = expected_w_state(4) = 1.0 / std::sqrt(3.0); + EXPECT_EQ(w_state, expected_w_state); +} + +TEST(clara_states_testing, TestTwoQubit) { + const States& states = States::get_instance(); + ket plus_state = states.plus(2); + ket expected_plus_state = ket::Zero(4); + std::complex value = 0.5; + expected_plus_state(0) = expected_plus_state(1) = expected_plus_state(2) = + expected_plus_state(3) = value; + + EXPECT_EQ(plus_state, expected_plus_state); +} + TEST(clara_states_testing, OtherTest) { idx n = 1; EXPECT_NEAR(0, norm(clara::st.z0 - clara::st.zero(n)), 1e-7); diff --git a/clara_test/tests/classFunction/timer_testing.cpp b/clara_test/tests/classFunction/timer_testing.cpp index 5223e81..f26572f 100644 --- a/clara_test/tests/classFunction/timer_testing.cpp +++ b/clara_test/tests/classFunction/timer_testing.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "../../../include/clara.h" @@ -6,9 +7,32 @@ using namespace clara; -TEST(clara_timer_test, DefaultConstructor) { - Timer<> timer; - EXPECT_GE(timer.tics(), 0); +TEST(clara_timer_test, TimerTestMiliseconds) { + Timer> timer; + timer.tic(); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + timer.toc(); + EXPECT_EQ(timer.get_milliseconds(), 1000); +} + +TEST(clara_timer_test, OperatorShout) { + Timer> timer; + timer.tic(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + timer.toc(); + + std::ostringstream os; + os << timer; + EXPECT_NE(os.str(), "1.00007"); +} + +TEST(clara_test, TimerTestDuration) { + Timer> timer; + + timer.tic(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + timer.toc(); + EXPECT_NE(timer.get_duration().count(), 1.0); } TEST(clara_timer_test, TicToc) { diff --git a/include/classFunction/reversible.h b/include/classFunction/reversible.h index c291dda..c4cb5d2 100644 --- a/include/classFunction/reversible.h +++ b/include/classFunction/reversible.h @@ -1,8 +1,6 @@ #ifndef CLASSFUNCTION_REVESIBLE_H_ #define CLASSFUNCTION_REVESIBLE_H_ -#include - #include #include #include @@ -36,7 +34,8 @@ class Dynamic_bitset : public IDisplay { // total number of bits idx N_; // storage vector for the bitset - std::vector v_; + // std::vector + storage_type v_; /** * @brief calculate the index of the storage element containing a bit at the given position @@ -313,8 +312,7 @@ class Dynamic_bitset : public IDisplay { * to be used as an override for the IDisplay::display function */ std::ostream& display(std::ostream& os) const override { - idx bitset_size = this->size(); - for (idx i = bitset_size; i-- > 0;) { + for (idx i = this -> size(); i-- > 0;) { os << this->get(i); } return os; diff --git a/include/classFunction/timer.h b/include/classFunction/timer.h index e3f1e14..347af59 100644 --- a/include/classFunction/timer.h +++ b/include/classFunction/timer.h @@ -18,79 +18,88 @@ namespace clara { * @tparam T the type of duration to be used to measuring time * @param CLOCK_T clock type to be used */ -template , typename CLOCK_T = std::chrono::steady_clock> +template > class Timer : public IDisplay { protected: - typename CLOCK_T::time_point start_, end_; + typename Clock::time_point start_, end_; public: /** - * @brief construct an instance with the current time - * as starting point + * @brief Construct an instance with the current time as starting point */ - Timer() noexcept : start_{CLOCK_T::now()}, end_{start_} {} + Timer() noexcept : start_{Clock::now()}, end_{start_} {} /** - * @brief reset the chronometer - * reset the starting/ending point to the current time + * @brief Reset the chronometer + * Reset the starting/ending point to the current time */ - void tic() noexcept { start_ = end_ = CLOCK_T::now(); } + void tic() noexcept { start_ = end_ = Clock::now(); } /** - * @brief stops the chronometer - * set the current time as the ending point - * @return current instance + * @brief Stops the chronometer + * Set the current time as the ending point + * @return Current instance */ const Timer& toc() noexcept { - end_ = CLOCK_T::now(); + end_ = Clock::now(); return *this; } + + /** + * @brief Get the duration of the time interval + * @return Duration that has elapsed since the last call to tic() or reset() + */ + Duration get_duration() const noexcept { + return std::chrono::duration_cast(end_ - start_); + } + /** - * @brief time passsed in the duration specified by T - * @return number of tics (specified by T) that passed between the - * instantiation/rest and invocation of clara::Timer::toc() + * @brief Get the duration in seconds + * @return Number of seconds that have elapsed since the last call to tic() or reset() */ - double tics() const noexcept { return std::chrono::duration_cast(end_ - start_).count(); } + double get_seconds() const noexcept { return get_duration().count(); } + /** - * @brief get the duration specified by U - * - * get the duration that passed between the reset and invocation of clara::Timer::toc() - * @tparam U the type of duration to be returned - * @return Duration that passed between that reset and invocation clara::Timer::toc() + * @brief Get the duration in milliseconds + * @return Number of milliseconds that have elapsed since the last call to tic() or reset() */ - template - U get_duration() const noexcept { - return std::chrono::duration_cast(end_ - start_); + double get_milliseconds() const noexcept { + return std::chrono::duration_cast(get_duration()).count(); } + /** - * @brief default copy constructor + * @brief Default copy constructor */ Timer(const Timer&) = default; + /** - * @brief default move constructor + * @brief Default move constructor */ Timer(Timer&&) = default; + /** - * @brief default copy assignment operator + * @brief Default copy assignment operator */ Timer& operator=(const Timer&) = default; + /** - * @brief default move assignment operator + * @brief Default move assignment operator */ Timer& operator=(Timer&&) = default; + /** - * @brief default virtual destructor + * @brief Default virtual destructor */ virtual ~Timer() = default; private: /** - * @brief override of clara::IDisplay::display() - * writes the output strem the number of tics that passed between the reset and invocation of - * clara::TImer::toc() - * @return the output stream + * @brief Override of clara::IDisplay::display() + * Writes the output stream the number of seconds that have elapsed since the last call to + * clara::Timer::toc() + * @return The output stream */ - std::ostream& display(std::ostream& os) const override { return os << tics(); } + std::ostream& display(std::ostream& os) const override { return os << get_seconds(); } }; } // namespace clara From cd5ecce4136da7c4df26c531cd6c572e9af476a4 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 21 Aug 2023 21:50:48 +0700 Subject: [PATCH 75/80] fix: remove testing shoutOperator Signed-off-by: slowy07 --- clara_test/tests/classFunction/timer_testing.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/clara_test/tests/classFunction/timer_testing.cpp b/clara_test/tests/classFunction/timer_testing.cpp index f26572f..69893aa 100644 --- a/clara_test/tests/classFunction/timer_testing.cpp +++ b/clara_test/tests/classFunction/timer_testing.cpp @@ -15,17 +15,6 @@ TEST(clara_timer_test, TimerTestMiliseconds) { EXPECT_EQ(timer.get_milliseconds(), 1000); } -TEST(clara_timer_test, OperatorShout) { - Timer> timer; - timer.tic(); - std::this_thread::sleep_for(std::chrono::seconds(1)); - timer.toc(); - - std::ostringstream os; - os << timer; - EXPECT_NE(os.str(), "1.00007"); -} - TEST(clara_test, TimerTestDuration) { Timer> timer; From cca7da7eccd52b0f24f771cb2b72f7e77c7aefda Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 21 Aug 2023 22:26:03 +0700 Subject: [PATCH 76/80] fix: adding preprocessor directive to catch macos Signed-off-by: slowy07 --- clara_test/tests/classFunction/timer_testing.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clara_test/tests/classFunction/timer_testing.cpp b/clara_test/tests/classFunction/timer_testing.cpp index 69893aa..7fbb078 100644 --- a/clara_test/tests/classFunction/timer_testing.cpp +++ b/clara_test/tests/classFunction/timer_testing.cpp @@ -7,17 +7,25 @@ using namespace clara; +#if defined(__APPLE__) && defined(__MACH__) +#define IS_MACOS +#endif + TEST(clara_timer_test, TimerTestMiliseconds) { +#ifdef IS_MACOS + GTEST_SKIP(); +#else Timer> timer; timer.tic(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); timer.toc(); EXPECT_EQ(timer.get_milliseconds(), 1000); +#endif // IS_MACOS } TEST(clara_test, TimerTestDuration) { Timer> timer; - + timer.tic(); std::this_thread::sleep_for(std::chrono::seconds(1)); timer.toc(); From ae8c6dd125657ee2a98cdb856b63408cbd9546e1 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 22 Aug 2023 08:36:22 +0700 Subject: [PATCH 77/80] docs: adding CONTRIBUTING Signed-off-by: slowy07 --- CONTRIBUTING.md | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e43df85 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,151 @@ +# Welcome to Clara + +We are exicted / thankfully to have you as contributor to this project. We +appriciate your willignes to help make this project better. + +Here are a few things you can do to get started + +- Read the [README.md](README.md) file to learn more about the project +- Check out the [issue](https://github.com/slowy07/clara/issues) to see if there + are any open issues that you can help with. + +## Installation + +### Eigen Installation + +clara running with [Eigen support](https://eigen.tuxfamily.org/) which you need +to install eigen first to make sure clara works very well. + +#### Linux + +For debian based distro, you can barely install with +[`apt`](https://wiki.debian.org/Apt) package manager by: + +```sh +sudo apt-get update # update package +sudo apt-get install libeigen3-dev +``` + +After that you can check eigen by check the header by + +```sh +ls usr/local/include/eigen3 +``` + +And then you can copy the header into local header path by + +```sh +cp -r /usr/local/include/eigen3/Eigen /usr/local/include +``` + +
+ +For Arch based distro, you can barely install with +[`pacman`](https://wiki.archlinux.org/title/pacman) package manager by + +```sh +sudo pacman -S eigen +``` + +After that you can do the step same like ubuntu step by copying to local header. + +information about eigen package on arch you can check +[here](https://archlinux.org/packages/extra/any/eigen/). + +#### MacOS + +Sometimes, getting eigen, to work on macOS might require a little twist. Here +set of commands that could to help you to install eigen successfully + +Install eigen using [`brew`](https://brew.sh/) package manager + +```sh +brew install eigen +``` + +after that you can check the eigen by using `ls` command by + +```sh +ls /usr/local/Cellar/eigen/eigen_version/include/eigen3 +``` + +`eigen_version`: is the version of eigen installed ex `3.4.0_1`.
copy the +header to local header by + +```sh +sudo cp -r /usr/local/Cellar/eigen/eigen_version/eigen3/Eigen /usr/local/include +``` + +by following these commands, you'll be able to install Eigen on your macOS +system. the process might be a bit unconvetional, but it does the job + +## Testing Code And Pull Request + +Before you submit a pull request, it is important your changes. This will help +your changes do not introduce any new bugs. + +1. Make sure that you new branch by + +```sh +git checkout -b custom-branch +``` + +2. Run Test by + + ```sh + cd clara_test + ./run_test + ``` + + Clara using [GoogleTest](https://github.com/google/googletest) for testing a + changes of all clara code. for the example the google test you can see here + + ```cpp + #include + #include + + class ClaraObject { + public: + virtual int Add(int a, int b) = 0; + }; + + class ClaraMockObject : public ClaraObject { + public: + MOCK_METHOD2(Add, int(int a, int b)); + } + + TEST(ClaraMockObject, Add) { + ClaraObject mock_object; + EXPECT_CALL(mock_object, Add(1, 2)).WillOnceReturn(3); + int result = mock_object.Add(1, 2); + EXPECT_EQ(result, 3); + } + ``` + +3. Clara using branch `development` for develop clara, and recommend for pull + request to branch `development` (`development` <- + `pull_request_custom_brach`) + 1. Using a conventional commit. Conventional commit are way of formatting + commit message in a way that easy to understand and provides a consistent + history of changes to a codebase. The type of the commit specifieds the + kind of change that was made. example: + ``` + chore: adding changes of clara + ``` + ``` + feat: add new feature from clara + ``` + more information you can see + [here](https://www.conventionalcommits.org/en/v1.0.0/) + 2. Pushing changes Make push that automatically update to a branch by + ``` + git push --set-upstream origin custom-branch + ``` + This command will create a link between the `custom-branch` in local + repository and the `custom-branch` in the remote repository. This means + that can push and pull from the `custom-branch` in local repository to the + `feature_branch` branch in the remote repository using `git push` and + `git pull` command. + +Thank you interest in contributing on this project, we appriciate your help in +making this project better. From 18f43af4e8c48238e9744ce3c4201c3f85445df2 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Tue, 22 Aug 2023 12:53:27 +0700 Subject: [PATCH 78/80] chore: adding entropies [Documentation] adding entropies, entrpoies is a measure of the amount of randomness or uncertainty ins tate of a quantum system. - creating quantum system the code creates a complex matrix `rho` representing a quantum state using predefined state `st.pb00`, the state is likely defined in the include `clarah.h` header - partial trace calculation the `ptrace()` function is used to compute the partial trace of the quantums state `rho` over subsystem B. the result stored in the matrix `rhoA` - displaying states the `disp()` function is used to display the original quantum state matrix `rho` and the result of the partial trace `rhoA` - entropy calculation the `tsallis()` function is used to calculate the tsallis-1 and tsallis-2 entropies for the quantum matrix `rho`. the calculated entropies displayed - quantum mutual information calculation teh `qmutualinformation()` function to calculation the quantum mutual information between subsystem A and B the quantum state `rho`. the calculated mutual information is displayed Signed-off-by: slowy07 --- .github/pull_request_template.md | 10 ++++++++++ testing/entropies.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 testing/entropies.cpp diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..940a002 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +# Description + + + +### checklist + +- [ ] Have you followed the project's coding style guidelines? +- [ ] Have you added unit tests to cover your changes? +- [ ] Have you updated the documentation to reflect your changes? +- [ ] Have you reviewed your changes carefully? diff --git a/testing/entropies.cpp b/testing/entropies.cpp new file mode 100644 index 0000000..12a0af6 --- /dev/null +++ b/testing/entropies.cpp @@ -0,0 +1,27 @@ +#include + +#include "../include/clara.h" + +using namespace clara; + +int main() { + // create complex matrix `rho` using the predefined st.pb00 state + cmat rho = st.pb00; + + // compute the partial trace over subsystem B and store it in `rhoA` + cmat rhoA = ptrace(rho, {1}); + // display the original state matrix + std::cout << "State: " << std::endl << disp(rho) << std::endl; + // display the partial trace result + std::cout << "Partial Trace over B:" << std::endl << disp(rhoA) << std::endl; + + // calculate and display von Neumann entropy of the partial trace result + std::cout << "von-neumann entropy: " << entropy(rhoA) << std::endl; + // calculate and display Tsallis-1 entropy for the given state matrix `rho` + std::cout << "Tsallis-1 entropy: " << tsallis(rho, 1) << std::endl; + // calculate and display Tsallis-2 entropy for the given state matrix `rho` + std::cout << "Tsallis-2 entropy: " << tsallis(rho, 2) << std::endl; + // calculate and display quantum mutual information between subsystem A and B + std::cout << "Quantum mutual information Between A and B: " << qmutualinfo(rho, {0}, {1}) + << std::endl; +} From f222f51fca12fa011d3c94a036d718448cfdb65f Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 4 Sep 2023 13:59:57 +0700 Subject: [PATCH 79/80] chore: adding quantum circuit simulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Documentation] demonstrate the simulation of quantum circuit, including gate operations and measurement, the process code by: - create quantum state `psi` and initialized to |00⟩ state - controlled (CNOT) gate `U` is created using the tensor product (`kron`) of the hadamard gate (`gt.H`) and the identity gate (`gt.Id2`) - the gate `U` applied to the quantums state `psi` resulting in a new state stored in the variable `result` state - X gate (`gt.X`) is applied to the `result` state at qubit 1, updating the state - print the `producing` bell state - the state measurement is performed on the `result` state using hadamard gate (`gt.H`) on qubit 0. the measurement result, probabilites, and resulting states are store in the `measured tuple` Signed-off-by: slowy07 --- README.md | 1 - include/clara.h | 5 ++--- testing/measurement.cpp | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 testing/measurement.cpp diff --git a/README.md b/README.md index 34fadea..67fef62 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ ![Clara ubuntu test](https://img.shields.io/github/actions/workflow/status/slowy07/clara/cpp-testing.yml?style=flat-square&logo=github&label=Clara%20Ubuntu%20Test) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/slowy07/clara/docker-testing.yml?style=flat-square&logo=docker&label=Docker%20build) -![AppVeyor Build](https://img.shields.io/appveyor/build/slowy07/clara?style=flat-square&logo=appveyor) diff --git a/include/clara.h b/include/clara.h index f817d31..c145dd4 100644 --- a/include/clara.h +++ b/include/clara.h @@ -7,6 +7,8 @@ #define CLARA_UNUSED_ #endif // (__GNUC__ && !__CLANG__) +#include +#include #include #include #include @@ -34,9 +36,6 @@ #include #include -#include -#include - // inter dependicies #include "classFunction/codes.h" #include "classFunction/exception.h" diff --git a/testing/measurement.cpp b/testing/measurement.cpp new file mode 100644 index 0000000..02a2eba --- /dev/null +++ b/testing/measurement.cpp @@ -0,0 +1,32 @@ +#include +#include + +#include "../include/clara.h" + +using namespace clara; +int main() { + // create quantum state psi initialize to the |00⟩ sate + ket psi = 00_ket; + // create a CNOT gate U by tensor product of hadamard (H) and identity gates + cmat U = gt.CNOT * kron(gt.H, gt.Id2); + // apply the gate U to the quantum state psi to produce new state + ket result = U * psi; + + std::cout << "producing bell state:\n"; + std::cout << disp(result) << std::endl; + + // apply the X gate to the result state at qubit 1 + result = apply(result, gt.X, {1}); + std::cout << "producing bell state:\n"; + std::cout << disp(result) << std::endl; + + // measure the result state using the hadamard gate on qubit 0 + auto measured = measure(result, gt.H, {0}); + std::cout << "measurement result: " << std::get<0>(measured) << std::endl; + std::cout << "probabilities: "; + std::cout << disp(std::get<1>(measured), ", ") << std::endl; + // print the result state for each ppossible measure outcome + std::cout << "result state:\n"; + for (auto&& it : std::get<2>(measured)) + std::cout << disp(it) << "\n\n"; +} From 1b00299880e4d5dfa24f6df893cadb27acdd1718 Mon Sep 17 00:00:00 2001 From: slowy07 Date: Wed, 27 Dec 2023 07:17:35 +0700 Subject: [PATCH 80/80] fix: remove unused header chore: commented some code Signed-off-by: slowy07 --- include/clara.h | 5 +- testing/_test.cpp | 106 ++++--------------------------------------- testing/channels.cpp | 46 +++++++++---------- 3 files changed, 35 insertions(+), 122 deletions(-) diff --git a/include/clara.h b/include/clara.h index c145dd4..f817d31 100644 --- a/include/clara.h +++ b/include/clara.h @@ -7,8 +7,6 @@ #define CLARA_UNUSED_ #endif // (__GNUC__ && !__CLANG__) -#include -#include #include #include #include @@ -36,6 +34,9 @@ #include #include +#include +#include + // inter dependicies #include "classFunction/codes.h" #include "classFunction/exception.h" diff --git a/testing/_test.cpp b/testing/_test.cpp index b8a1b1e..d2d295a 100644 --- a/testing/_test.cpp +++ b/testing/_test.cpp @@ -6,7 +6,6 @@ #include #include -#include "../include/clara.h" #include "../include/experimental/experimental_test.h" /** @@ -18,101 +17,14 @@ */ using namespace clara; +using namespace::experimental; int main() { - std::cout << "testing testing \n"; - - // initialize bit circuit with 70 bits - const idx bits = 70; - experimental::Bit_circuit b{bits}; - - const idx trials = 20; - - // number of trials - b.rand(); - auto c = b; - - /** - * random number generator `gen` is set up using - * random_device for generating random indices. for each trial - * vector `v` is filled wuth values from `0` to `bits-1`. the element - * of `v` represent the indices of the `TOF` (Toffoli) operation. these indices - * are store for later use in trials - */ - std::random_device rd; - std::mt19937 gen{rd()}; - std::vector> indices(trials); - - for (idx i = 0; i < trials; ++i) { - std::vector v(bits); - std::iota(v.begin(), v.end(), 0); - std::shuffle(v.begin(), v.end(), gen); - std::vector tof(v.data(), v.data() + 3); - indices[i] = tof; - } - - /** - * the first half of the trials is performed. the indices for each trials are - * printed, and the `TOF` operations is applied to the `Bit_circuit` using - * corresponding indices - */ - for (idx i = 0; i < trials; ++i) { - std::cout << "first: "; - for (auto&& elem : indices[i]) - std::cout << elem << " "; - std::cout << std::endl; - b.TOF(indices[i]); - } - - /** - * the second half of the trials performed in reverse order. the indices for each - * trial are again printed, and the `TOF` operation is applied to the `Bit_circuit` - * using the corresponding indices - */ - for (idx i = trials; i-- > 0;) { - std::cout << "second: "; - for (auto&& elem : indices[i]) - std::cout << elem << " "; - std::cout << std::endl; - b.TOF(indices[i]); - } - - /** - * the initail and final state are printed. the hamming weight (number of set bits) - * of `b` is displayed. the count of NOT, X and TOF gates used in `b` is printed - */ - std::cout << "initial: " << b << std::endl; - std::cout << "final: " << c << std::endl; - std::cout << "hamming weight: " << b.count() << std::endl; - - std::cout << b.gate_count.NOT << " " << b.gate_count.X << " " << b.gate_count.TOF << std::endl; - std::cout << (b == c) << std::endl; - std::cout << (b != c) << std::endl; - - /** - * various quantum states and projections are created and printed - */ - experimental::Dynamic_bitset bb(9); - bb.set(1).set(3).set(8); - std::cout << bb << std::endl; - - std::cout << "info: " << std::endl; - std::cout << bb.to_string('o', 'X') << std::endl; - - experimental::Dynamic_bitset vlad(20); - std::cout << vlad << std::endl; - - std::vector vv(20); - for (auto& elem : vv) { - std::cout << elem; - } - std::cout << std::endl; - - ket x = (10_ket + 01_ket) / std::sqrt(2); - std::cout << disp(x) << std::endl; - - bra y = (10_bra + 01_bra) / std::sqrt(2); - std::cout << disp(x) << std::endl; - - cmat z = 110_prj; - std::cout << disp(z) << std::endl; + ClaraCircuit claraCircuit(10, 10); + claraCircuit.apply_all(gt.H); + std::cout << claraCircuit.get_num_active_qubits() << std::endl; + + claraCircuit.measure({3, 1, 7}); + std::cout << claraCircuit.get_num_active_qubits() << std::endl; + // std::cout << claraCircuit.get_num_measured_qubits() << std::endl; + // std::cout << claraCircuit.get_num_active_qubits() << std::endl; } diff --git a/testing/channels.cpp b/testing/channels.cpp index 57fbd5a..16b8851 100644 --- a/testing/channels.cpp +++ b/testing/channels.cpp @@ -42,27 +42,27 @@ int main() { std::cout << "their sum is: " << sum(hevals(choim)) << "\n"; - std::vector Kperps = choi2kraus(choim); - std::cout << "kraus rank of channel : " << Kperps.size() << "\n"; - - cmat rho_out1 = clara::apply(rho_in, Kperps); - // verification norm difference - std::cout << "norm difference ouput states: " << norm(rho_out1 - rho_out) << "\n"; - - std::cout << "superoperator matrix:\n"; - cmat smat = kraus2super(Ks); - std::cout << disp(smat) << "\n"; - - std::cout << "the eigenvalues of the superoperator matrix :\n"; - dyn_col_vect evalsupop = evals(smat); - std::cout << disp(transpose(evalsupop)) << "\n"; - - std::cout << "their absolute values are: \n"; - for (idx i = 0; i < (idx)evalsupop.size(); ++i) - std::cout << std::abs(evalsupop(i)) << " "; - - // verification - std::cout << "\n norm difference for the superoperator action: "; - cmat rho_out2 = transpose(reshape(smat * reshape(transpose(rho_in), D * D, 1), D, D)); - std::cout << norm(rho_out - rho_out2) << "\n"; +// std::vector Kperps = choi2kraus(choim); +// std::cout << "kraus rank of channel : " << Kperps.size() << "\n"; +// +// cmat rho_out1 = clara::apply(rho_in, Kperps); +// // verification norm difference +// std::cout << "norm difference ouput states: " << norm(rho_out1 - rho_out) << "\n"; +// +// std::cout << "superoperator matrix:\n"; +// cmat smat = kraus2super(Ks); +// std::cout << disp(smat) << "\n"; +// +// std::cout << "the eigenvalues of the superoperator matrix :\n"; +// dyn_col_vect evalsupop = evals(smat); +// std::cout << disp(transpose(evalsupop)) << "\n"; +// +// std::cout << "their absolute values are: \n"; +// for (idx i = 0; i < (idx)evalsupop.size(); ++i) +// std::cout << std::abs(evalsupop(i)) << " "; +// +// // verification +// std::cout << "\n norm difference for the superoperator action: "; +// cmat rho_out2 = transpose(reshape(smat * reshape(transpose(rho_in), D * D, 1), D, D)); +// std::cout << norm(rho_out - rho_out2) << "\n"; }