|
19 | 19 |
|
20 | 20 | namespace cudaq::details {
|
21 | 21 | void warn(const std::string_view msg);
|
| 22 | + |
| 23 | +/// @brief Typedef for a matrix wrapper using std::vector<cudaq::complex> |
| 24 | +using matrix_wrapper = std::vector<cudaq::complex>; |
| 25 | + |
| 26 | +/// @brief Compute the Kronecker product of two matrices |
| 27 | +/// |
| 28 | +/// @param A First matrix |
| 29 | +/// @param rowsA Number of rows in matrix A |
| 30 | +/// @param colsA Number of columns in matrix A |
| 31 | +/// @param B Second matrix |
| 32 | +/// @param rowsB Number of rows in matrix B |
| 33 | +/// @param colsB Number of columns in matrix B |
| 34 | +/// @return matrix_wrapper Result of the Kronecker product |
| 35 | +inline matrix_wrapper kron(const matrix_wrapper &A, int rowsA, int colsA, |
| 36 | + const matrix_wrapper &B, int rowsB, int colsB) { |
| 37 | + matrix_wrapper C((rowsA * rowsB) * (colsA * colsB)); |
| 38 | + for (int i = 0; i < rowsA; ++i) { |
| 39 | + for (int j = 0; j < colsA; ++j) { |
| 40 | + for (int k = 0; k < rowsB; ++k) { |
| 41 | + for (int l = 0; l < colsB; ++l) { |
| 42 | + C[(i * rowsB + k) * (colsA * colsB) + (j * colsB + l)] = |
| 43 | + A[i * colsA + j] * B[k * colsB + l]; |
| 44 | + } |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + return C; |
| 49 | +} |
| 50 | + |
| 51 | +inline matrix_wrapper scale(const cudaq::real s, const matrix_wrapper &A) { |
| 52 | + matrix_wrapper result; |
| 53 | + result.reserve(A.size()); |
| 54 | + for (auto a : A) |
| 55 | + result.push_back(s * a); |
| 56 | + return result; |
22 | 57 | }
|
| 58 | +} // namespace details |
23 | 59 |
|
24 | 60 | namespace cudaq {
|
25 | 61 |
|
@@ -597,4 +633,230 @@ class phase_flip_channel : public kraus_channel {
|
597 | 633 | REGISTER_KRAUS_CHANNEL(
|
598 | 634 | noise_model_strings[(int)noise_model_type::phase_flip_channel])
|
599 | 635 | };
|
| 636 | + |
| 637 | +/// @brief amplitude_damping is the same as amplitude_damping_channel. |
| 638 | +class amplitude_damping : public amplitude_damping_channel { |
| 639 | +public: |
| 640 | + amplitude_damping(const std::vector<cudaq::real> &p) |
| 641 | + : amplitude_damping_channel(p) { |
| 642 | + noise_type = noise_model_type::amplitude_damping; |
| 643 | + } |
| 644 | + amplitude_damping(const real probability) |
| 645 | + : amplitude_damping_channel(probability) { |
| 646 | + noise_type = noise_model_type::amplitude_damping; |
| 647 | + } |
| 648 | + REGISTER_KRAUS_CHANNEL( |
| 649 | + noise_model_strings[(int)noise_model_type::amplitude_damping]) |
| 650 | +}; |
| 651 | + |
| 652 | +/// @brief phase_damping is the same as phase_flip_channel. |
| 653 | +class phase_damping : public phase_flip_channel { |
| 654 | +public: |
| 655 | + phase_damping(const std::vector<cudaq::real> &p) : phase_flip_channel(p) { |
| 656 | + noise_type = noise_model_type::phase_damping; |
| 657 | + } |
| 658 | + phase_damping(const real probability) : phase_flip_channel(probability) { |
| 659 | + noise_type = noise_model_type::phase_damping; |
| 660 | + } |
| 661 | + REGISTER_KRAUS_CHANNEL( |
| 662 | + noise_model_strings[(int)noise_model_type::phase_damping]) |
| 663 | +}; |
| 664 | + |
| 665 | +/// @brief z_error is the same as phase_flip_channel. |
| 666 | +class z_error : public phase_flip_channel { |
| 667 | +public: |
| 668 | + z_error(const std::vector<cudaq::real> &p) : phase_flip_channel(p) { |
| 669 | + noise_type = noise_model_type::z_error; |
| 670 | + } |
| 671 | + z_error(const real probability) : phase_flip_channel(probability) { |
| 672 | + noise_type = noise_model_type::z_error; |
| 673 | + } |
| 674 | + REGISTER_KRAUS_CHANNEL(noise_model_strings[(int)noise_model_type::z_error]) |
| 675 | +}; |
| 676 | + |
| 677 | + |
| 678 | +/// @brief x_error is the same as bit_flip_channel |
| 679 | +class x_error : public bit_flip_channel { |
| 680 | +public: |
| 681 | + x_error(const std::vector<cudaq::real> &p) : bit_flip_channel(p) { |
| 682 | + noise_type = noise_model_type::x_error; |
| 683 | + } |
| 684 | + x_error(const real probability) : bit_flip_channel(probability) { |
| 685 | + noise_type = noise_model_type::x_error; |
| 686 | + } |
| 687 | + REGISTER_KRAUS_CHANNEL(noise_model_strings[(int)noise_model_type::x_error]) |
| 688 | +}; |
| 689 | + |
| 690 | +/// @brief y_error is the same as bit_flip_channel |
| 691 | +class y_error : public kraus_channel { |
| 692 | +public: |
| 693 | + constexpr static std::size_t num_parameters = 1; |
| 694 | + constexpr static std::size_t num_targets = 1; |
| 695 | + y_error(const std::vector<cudaq::real> &p) { |
| 696 | + cudaq::real probability = p[0]; |
| 697 | + std::complex<cudaq::real> i{0, 1}; |
| 698 | + std::vector<cudaq::complex> k0v{std::sqrt(1 - probability), 0, 0, |
| 699 | + std::sqrt(1 - probability)}, |
| 700 | + k1v{0, -i * std::sqrt(probability), i * std::sqrt(probability), 0}; |
| 701 | + ops = {k0v, k1v}; |
| 702 | + this->parameters.push_back(probability); |
| 703 | + noise_type = noise_model_type::y_error; |
| 704 | + validateCompleteness(); |
| 705 | + generateUnitaryParameters(); |
| 706 | + } |
| 707 | + y_error(const real probability) |
| 708 | + : y_error(std::vector<cudaq::real>{probability}) {} |
| 709 | + REGISTER_KRAUS_CHANNEL(noise_model_strings[(int)noise_model_type::y_error]) |
| 710 | +}; |
| 711 | + |
| 712 | +class pauli1 : public kraus_channel { |
| 713 | +public: |
| 714 | + constexpr static std::size_t num_parameters = 3; |
| 715 | + constexpr static std::size_t num_targets = 1; |
| 716 | + pauli1(const std::vector<cudaq::real> &p) { |
| 717 | + if (p.size() == num_parameters) |
| 718 | + throw std::runtime_error( |
| 719 | + "Invalid number of elements to pauli1 constructor. Must be 3."); |
| 720 | + cudaq::real sum = 0; |
| 721 | + for (auto pp : p) |
| 722 | + sum += pp; |
| 723 | + // This is just a first-level error check. Additional checks are done in |
| 724 | + // validateCompleteness. |
| 725 | + if (sum > static_cast<cudaq::real>(1.0 + 1e-6)) |
| 726 | + throw std::runtime_error("Sum of pauli1 parameters is >1. Must be <= 1."); |
| 727 | + |
| 728 | + std::complex<cudaq::real> i{0, 1}; |
| 729 | + cudaq::details::matrix_wrapper I({1, 0, 0, 1}); |
| 730 | + cudaq::details::matrix_wrapper X({0, 1, 1, 0}); |
| 731 | + cudaq::details::matrix_wrapper Y({0, -i, i, 0}); |
| 732 | + cudaq::details::matrix_wrapper Z({1, 0, 0, -1}); |
| 733 | + cudaq::real p0 = |
| 734 | + std::sqrt(std::max(static_cast<cudaq::real>(1.0 - p[0] - p[1] - p[2]), |
| 735 | + static_cast<cudaq::real>(0))); |
| 736 | + cudaq::real px = std::sqrt(p[0]); |
| 737 | + cudaq::real py = std::sqrt(p[1]); |
| 738 | + cudaq::real pz = std::sqrt(p[2]); |
| 739 | + std::vector<cudaq::complex> k0v = details::scale(p0, I); |
| 740 | + std::vector<cudaq::complex> k1v = details::scale(px, X); |
| 741 | + std::vector<cudaq::complex> k2v = details::scale(py, Y); |
| 742 | + std::vector<cudaq::complex> k3v = details::scale(pz, Z); |
| 743 | + ops = {k0v, k1v, k2v, k3v}; |
| 744 | + this->parameters.reserve(p.size()); |
| 745 | + for (auto pp : p) |
| 746 | + this->parameters.push_back(pp); |
| 747 | + noise_type = cudaq::noise_model_type::pauli1; |
| 748 | + validateCompleteness(); |
| 749 | + generateUnitaryParameters(); |
| 750 | + } |
| 751 | + REGISTER_KRAUS_CHANNEL() |
| 752 | +}; |
| 753 | + |
| 754 | +class pauli2 : public kraus_channel { |
| 755 | +public: |
| 756 | + constexpr static std::size_t num_parameters = 15; |
| 757 | + constexpr static std::size_t num_targets = 2; |
| 758 | + pauli2(const std::vector<cudaq::real> &p) { |
| 759 | + if (p.size() == num_parameters) |
| 760 | + throw std::runtime_error( |
| 761 | + "Invalid number of elements to pauli2 constructor. Must be 15."); |
| 762 | + cudaq::real sum = 0; |
| 763 | + for (auto pp : p) |
| 764 | + sum += pp; |
| 765 | + // This is just a first-level error check. Additional checks are done in |
| 766 | + // validateCompleteness. |
| 767 | + if (sum > static_cast<cudaq::real>(1.0 + 1e-6)) |
| 768 | + throw std::runtime_error("Sum of pauli2 parameters is >1. Must be <= 1."); |
| 769 | + |
| 770 | + std::complex<cudaq::real> i{0, 1}; |
| 771 | + cudaq::details::matrix_wrapper I({1, 0, 0, 1}); |
| 772 | + cudaq::details::matrix_wrapper X({0, 1, 1, 0}); |
| 773 | + cudaq::details::matrix_wrapper Y({0, -i, i, 0}); |
| 774 | + cudaq::details::matrix_wrapper Z({1, 0, 0, -1}); |
| 775 | + cudaq::real pii = std::sqrt(std::max(static_cast<cudaq::real>(1.0 - sum), |
| 776 | + static_cast<cudaq::real>(0))); |
| 777 | + |
| 778 | + ops.push_back(details::scale(pii, details::kron(I, 2, 2, I, 2, 2))); |
| 779 | + ops.push_back(details::scale(p[0], details::kron(I, 2, 2, X, 2, 2))); |
| 780 | + ops.push_back(details::scale(p[1], details::kron(I, 2, 2, Y, 2, 2))); |
| 781 | + ops.push_back(details::scale(p[2], details::kron(I, 2, 2, Z, 2, 2))); |
| 782 | + |
| 783 | + ops.push_back(details::scale(p[3], details::kron(X, 2, 2, I, 2, 2))); |
| 784 | + ops.push_back(details::scale(p[4], details::kron(X, 2, 2, X, 2, 2))); |
| 785 | + ops.push_back(details::scale(p[5], details::kron(X, 2, 2, Y, 2, 2))); |
| 786 | + ops.push_back(details::scale(p[6], details::kron(X, 2, 2, Z, 2, 2))); |
| 787 | + |
| 788 | + ops.push_back(details::scale(p[7], details::kron(Y, 2, 2, I, 2, 2))); |
| 789 | + ops.push_back(details::scale(p[8], details::kron(Y, 2, 2, X, 2, 2))); |
| 790 | + ops.push_back(details::scale(p[9], details::kron(Y, 2, 2, Y, 2, 2))); |
| 791 | + ops.push_back(details::scale(p[10], details::kron(Y, 2, 2, Z, 2, 2))); |
| 792 | + |
| 793 | + ops.push_back(details::scale(p[11], details::kron(Z, 2, 2, I, 2, 2))); |
| 794 | + ops.push_back(details::scale(p[12], details::kron(Z, 2, 2, X, 2, 2))); |
| 795 | + ops.push_back(details::scale(p[13], details::kron(Z, 2, 2, Y, 2, 2))); |
| 796 | + ops.push_back(details::scale(p[14], details::kron(Z, 2, 2, Z, 2, 2))); |
| 797 | + |
| 798 | + this->parameters.reserve(p.size()); |
| 799 | + for (auto pp : p) |
| 800 | + this->parameters.push_back(pp); |
| 801 | + noise_type = cudaq::noise_model_type::pauli2; |
| 802 | + validateCompleteness(); |
| 803 | + generateUnitaryParameters(); |
| 804 | + } |
| 805 | + REGISTER_KRAUS_CHANNEL() |
| 806 | +}; |
| 807 | + |
| 808 | +/// @brief depolarization1 is the same as depolarization_channel |
| 809 | +class depolarization1 : public depolarization_channel { |
| 810 | +public: |
| 811 | + depolarization1(const std::vector<cudaq::real> &p) |
| 812 | + : depolarization_channel(p) { |
| 813 | + noise_type = noise_model_type::depolarization1; |
| 814 | + } |
| 815 | + depolarization1(const real probability) |
| 816 | + : depolarization_channel(probability) { |
| 817 | + noise_type = noise_model_type::depolarization1; |
| 818 | + } |
| 819 | + REGISTER_KRAUS_CHANNEL() |
| 820 | +}; |
| 821 | + |
| 822 | +class depolarization2 : public kraus_channel { |
| 823 | +public: |
| 824 | + constexpr static std::size_t num_parameters = 1; |
| 825 | + constexpr static std::size_t num_targets = 2; |
| 826 | + depolarization2(const std::vector<cudaq::real> p) : kraus_channel() { |
| 827 | + auto three = static_cast<cudaq::real>(3.); |
| 828 | + auto negOne = static_cast<cudaq::real>(-1.); |
| 829 | + auto probability = p[0]; |
| 830 | + |
| 831 | + std::vector<std::vector<cudaq::complex>> singleQubitKraus = { |
| 832 | + {std::sqrt(1 - probability), 0, 0, std::sqrt(1 - probability)}, |
| 833 | + {0, std::sqrt(probability / three), std::sqrt(probability / three), 0}, |
| 834 | + {0, cudaq::complex{0, negOne * std::sqrt(probability / three)}, |
| 835 | + cudaq::complex{0, std::sqrt(probability / three)}, 0}, |
| 836 | + {std::sqrt(probability / three), 0, 0, |
| 837 | + negOne * std::sqrt(probability / three)}}; |
| 838 | + |
| 839 | + // Generate 2-qubit Kraus operators |
| 840 | + for (const auto &k1 : singleQubitKraus) { |
| 841 | + for (const auto &k2 : singleQubitKraus) { |
| 842 | + ops.push_back(details::kron(k1, 2, 2, k2, 2, 2)); |
| 843 | + } |
| 844 | + } |
| 845 | + this->parameters.push_back(probability); |
| 846 | + noise_type = cudaq::noise_model_type::depolarization2; |
| 847 | + validateCompleteness(); |
| 848 | + generateUnitaryParameters(); |
| 849 | + } |
| 850 | + |
| 851 | + /// @brief Construct a two qubit kraus channel that applies a depolarization |
| 852 | + /// channel on either qubit independently. |
| 853 | + /// |
| 854 | + /// @param p The probability of any depolarizing error happening in the 2 |
| 855 | + /// qubits. (Setting this to 1.0 ensures that "II" cannot happen; maximal |
| 856 | + /// mixing occurs at p = 0.9375.) |
| 857 | + depolarization2(const real probability) |
| 858 | + : depolarization2(std::vector<cudaq::real>{probability}) {} |
| 859 | + REGISTER_KRAUS_CHANNEL() |
| 860 | +}; |
| 861 | + |
600 | 862 | } // namespace cudaq
|
0 commit comments