79 const Eigen::Index dimension = krausOperators_.front().rows();
80 const Eigen::Index choiDimension = dimension * dimension;
81 Matrix choi = Matrix::Zero(choiDimension, choiDimension);
82 for (
const Matrix& krausOperator : krausOperators_) {
83 const Eigen::Map<const Eigen::VectorXcd> vectorized(
84 krausOperator.data(), choiDimension);
85 choi.noalias() += vectorized * vectorized.adjoint();
131 if (probabilities.empty())
132 throw std::invalid_argument(
"A Pauli channel must contain probabilities");
134 size_t count = probabilities.size();
135 size_t numQubits = 0;
136 while (count > 1 && count % 4 == 0) {
140 if (count != 1 || numQubits == 0)
141 throw std::invalid_argument(
142 "A Pauli channel needs exactly 4^n probabilities for n >= 1");
145 for (
const double probability : probabilities) {
146 CheckedProbability(probability);
147 total += probability;
149 if (!std::isfinite(total) || std::abs(total - 1.0) > kProbabilityTolerance)
150 throw std::invalid_argument(
151 "Pauli channel probabilities must sum to one");
154 kraus.reserve(
static_cast<size_t>(
155 std::count_if(probabilities.begin(), probabilities.end(),
156 [](
double probability) { return probability > 0.0; })));
157 for (
size_t index = 0; index < probabilities.size(); ++index) {
158 if (probabilities[index] == 0.0)
continue;
163 for (
size_t q = 0; q < numQubits; ++q) divisor *= 4;
164 for (
size_t q = numQubits; q-- > 0;) {
166 const size_t pauliId = (index / divisor) % 4;
167 pauli = Kronecker(pauli, PauliMatrix(pauliId));
169 kraus.emplace_back(std::sqrt(probabilities[index]) * pauli);
223 double gamma,
double excitedStatePopulation) {
224 CheckedProbability(gamma);
225 CheckedProbability(excitedStatePopulation);
226 const double groundStatePopulation = 1.0 - excitedStatePopulation;
228 Matrix k0 = Matrix::Zero(2, 2);
229 k0(0, 0) = std::sqrt(groundStatePopulation);
230 k0(1, 1) = std::sqrt(groundStatePopulation * (1.0 - gamma));
231 Matrix k1 = Matrix::Zero(2, 2);
232 k1(0, 1) = std::sqrt(groundStatePopulation * gamma);
233 Matrix k2 = Matrix::Zero(2, 2);
234 k2(0, 0) = std::sqrt(excitedStatePopulation * (1.0 - gamma));
235 k2(1, 1) = std::sqrt(excitedStatePopulation);
236 Matrix k3 = Matrix::Zero(2, 2);
237 k3(1, 0) = std::sqrt(excitedStatePopulation * gamma);
239 {std::move(k0), std::move(k1), std::move(k2), std::move(k3)});
251 double excitedStatePopulation = 0.0) {
252 if (!std::isfinite(duration) || duration < 0.0)
253 throw std::invalid_argument(
254 "Thermal-relaxation duration must be finite and nonnegative");
255 ValidatePositiveTime(t1,
"T1");
256 ValidatePositiveTime(t2,
"T2");
257 CheckedProbability(excitedStatePopulation);
259 if (std::isinf(t2) && !std::isinf(t1))
260 throw std::invalid_argument(
"Thermal relaxation requires T2 <= 2*T1");
261 if (std::isfinite(t1) && std::isfinite(t2) &&
262 t2 > 2.0 * t1 * (1.0 + kProbabilityTolerance))
263 throw std::invalid_argument(
"Thermal relaxation requires T2 <= 2*T1");
265 const double coherenceMultiplier =
266 std::isinf(t2) ? 1.0 : std::exp(-duration / t2);
267 const double gamma = std::isinf(t1) ? 0.0 : -std::expm1(-duration / t1);
268 const double up = gamma * excitedStatePopulation;
269 const double down = gamma * (1.0 - excitedStatePopulation);
274 const double a = 1.0 - up;
275 const double c = 1.0 - down;
276 if (coherenceMultiplier * coherenceMultiplier >
277 a * c + kProbabilityTolerance)
278 throw std::invalid_argument(
279 "The supplied T1 and T2 do not define a completely-positive map");
281 const double discriminant = std::hypot(a - c, 2.0 * coherenceMultiplier);
282 const double lambdaPlus = 0.5 * (a + c + discriminant);
283 const double lambdaMinus = std::max(0.0, 0.5 * (a + c - discriminant));
284 const double angle = 0.5 * std::atan2(2.0 * coherenceMultiplier, a - c);
285 const double cosine = std::cos(angle);
286 const double sine = std::sin(angle);
289 if (lambdaPlus > kZeroTolerance) {
290 Matrix diagonal = Matrix::Zero(2, 2);
291 diagonal(0, 0) = std::sqrt(lambdaPlus) * cosine;
292 diagonal(1, 1) = std::sqrt(lambdaPlus) * sine;
293 kraus.emplace_back(std::move(diagonal));
295 if (lambdaMinus > kZeroTolerance) {
296 Matrix diagonal = Matrix::Zero(2, 2);
297 diagonal(0, 0) = -std::sqrt(lambdaMinus) * sine;
298 diagonal(1, 1) = std::sqrt(lambdaMinus) * cosine;
299 kraus.emplace_back(std::move(diagonal));
301 if (up > kZeroTolerance) {
302 Matrix excitation = Matrix::Zero(2, 2);
303 excitation(1, 0) = std::sqrt(up);
304 kraus.emplace_back(std::move(excitation));
306 if (down > kZeroTolerance) {
307 Matrix decay = Matrix::Zero(2, 2);
308 decay(0, 1) = std::sqrt(down);
309 kraus.emplace_back(std::move(decay));
325 double correlation) {
326 CheckedProbability(probability);
327 CheckedProbability(correlation);
328 std::vector<double> probabilities(16, 0.0);
329 const double independent = 1.0 - correlation;
331 independent * (1.0 - probability) * (1.0 - probability) +
332 correlation * (1.0 - probability);
333 probabilities[3] = independent * probability * (1.0 - probability);
334 probabilities[12] = probabilities[3];
335 probabilities[15] = independent * probability * probability +
336 correlation * probability;
337 return Pauli(probabilities);
403 result.block(row * right.rows(), column * right.cols(), right.rows(),