Skip to content

Commit e7beff9

Browse files
committed
Implement new noise models
Signed-off-by: Ben Howe <[email protected]>
1 parent c47d92c commit e7beff9

File tree

6 files changed

+379
-7
lines changed

6 files changed

+379
-7
lines changed

runtime/common/NoiseModel.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,19 @@ noise_model::get_channels(const std::string &quantumOp,
379379
}
380380

381381
noise_model::noise_model() {
382-
register_channel<bit_flip_channel>();
383-
register_channel<phase_flip_channel>();
384382
register_channel<depolarization_channel>();
385383
register_channel<amplitude_damping_channel>();
384+
register_channel<bit_flip_channel>();
385+
register_channel<phase_flip_channel>();
386+
register_channel<x_error>();
387+
register_channel<y_error>();
388+
register_channel<z_error>();
389+
register_channel<amplitude_damping>();
390+
register_channel<phase_damping>();
391+
register_channel<pauli1>();
392+
register_channel<pauli2>();
393+
register_channel<depolarization1>();
394+
register_channel<depolarization2>();
386395
}
387396

388397
std::string get_noise_model_type_name(noise_model_type type) {

runtime/common/NoiseModel.h

+262
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,43 @@
1919

2020
namespace cudaq::details {
2121
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;
2257
}
58+
} // namespace details
2359

2460
namespace cudaq {
2561

@@ -597,4 +633,230 @@ class phase_flip_channel : public kraus_channel {
597633
REGISTER_KRAUS_CHANNEL(
598634
noise_model_strings[(int)noise_model_type::phase_flip_channel])
599635
};
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+
600862
} // namespace cudaq

runtime/nvqir/cutensornet/simulator_cutensornet.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,52 @@ void SimulatorTensorNetBase::applyKrausChannel(
170170
}
171171
}
172172

173+
bool SimulatorTensorNetBase::isValidNoiseChannel(
174+
const cudaq::noise_model_type &type) const {
175+
switch (type) {
176+
case cudaq::noise_model_type::depolarization_channel:
177+
case cudaq::noise_model_type::bit_flip_channel:
178+
case cudaq::noise_model_type::phase_flip_channel:
179+
case cudaq::noise_model_type::x_error:
180+
case cudaq::noise_model_type::y_error:
181+
case cudaq::noise_model_type::z_error:
182+
case cudaq::noise_model_type::phase_damping:
183+
case cudaq::noise_model_type::pauli1:
184+
case cudaq::noise_model_type::pauli2:
185+
case cudaq::noise_model_type::depolarization1:
186+
case cudaq::noise_model_type::depolarization2:
187+
case cudaq::noise_model_type::unknown: // may be unitary, so return true
188+
return true;
189+
// These are explicitly non-unitary and unsupported
190+
case cudaq::noise_model_type::amplitude_damping_channel:
191+
case cudaq::noise_model_type::amplitude_damping:
192+
default:
193+
return false;
194+
}
195+
}
196+
197+
void SimulatorTensorNetBase::applyNoise(
198+
const cudaq::kraus_channel &channel,
199+
const std::vector<std::size_t> &targets) {
200+
LOG_API_TIME();
201+
// Do nothing if no execution context
202+
if (!executionContext)
203+
return;
204+
205+
// Do nothing if no noise model
206+
if (!executionContext->noiseModel)
207+
return;
208+
209+
// Apply all prior gates before applying noise.
210+
std::vector<int32_t> qubits{targets.begin(), targets.end()};
211+
cudaq::info(
212+
"[SimulatorTensorNetBase] Applying kraus channel {} on qubits: {}",
213+
cudaq::get_noise_model_type_name(channel.noise_type), qubits);
214+
215+
flushGateQueue();
216+
applyKrausChannel(qubits, channel);
217+
}
218+
173219
void SimulatorTensorNetBase::applyNoiseChannel(
174220
const std::string_view gateName, const std::vector<std::size_t> &controls,
175221
const std::vector<std::size_t> &targets,

runtime/nvqir/cutensornet/simulator_cutensornet.h

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class SimulatorTensorNetBase : public nvqir::CircuitSimulatorBase<double> {
3636
const std::vector<std::size_t> &targets,
3737
const std::vector<double> &params) override;
3838

39+
bool isValidNoiseChannel(const cudaq::noise_model_type &type) const override;
40+
41+
/// @brief Apply the given kraus_channel on the provided targets.
42+
void applyNoise(const cudaq::kraus_channel &channel,
43+
const std::vector<std::size_t> &targets) override;
44+
3945
// Override base calculateStateDim (we don't instantiate full state vector in
4046
// the tensornet backend). When the user want to retrieve the state vector, we
4147
// check if it is feasible to do so.

runtime/nvqir/stim/StimCircuitSimulator.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,30 @@ class StimCircuitSimulator : public nvqir::CircuitSimulatorBase<double> {
4444
isValidStimNoiseChannel(const kraus_channel &channel) const {
4545

4646
// Check the old way first
47-
if (channel.noise_type == cudaq::noise_model_type::bit_flip_channel)
47+
switch (channel.noise_type) {
48+
case cudaq::noise_model_type::bit_flip_channel:
49+
case cudaq::noise_model_type::x_error:
4850
return "X_ERROR";
49-
50-
if (channel.noise_type == cudaq::noise_model_type::phase_flip_channel)
51+
case cudaq::noise_model_type::y_error:
52+
return "Y_ERROR";
53+
case cudaq::noise_model_type::phase_flip_channel:
54+
case cudaq::noise_model_type::phase_damping:
55+
case cudaq::noise_model_type::z_error:
5156
return "Z_ERROR";
52-
53-
if (channel.noise_type == cudaq::noise_model_type::depolarization_channel)
57+
case cudaq::noise_model_type::depolarization_channel:
58+
case cudaq::noise_model_type::depolarization1:
5459
return "DEPOLARIZE1";
60+
case cudaq::noise_model_type::depolarization2:
61+
return "DEPOLARIZE2";
62+
case cudaq::noise_model_type::pauli1:
63+
return "PAULI_CHANNEL_1";
64+
case cudaq::noise_model_type::pauli2:
65+
return "PAULI_CHANNEL_2";
66+
case cudaq::noise_model_type::amplitude_damping_channel:
67+
case cudaq::noise_model_type::amplitude_damping:
68+
case cudaq::noise_model_type::unknown:
69+
return std::nullopt;
70+
}
5571

5672
return std::nullopt;
5773
}

0 commit comments

Comments
 (0)