Skip to content

Commit

Permalink
Delete identity condition, 2nd order approximation
Browse files Browse the repository at this point in the history
  • Loading branch information
a-corni committed Nov 23, 2023
1 parent 822a2f8 commit 9a88e2e
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 15,556 deletions.
23 changes: 2 additions & 21 deletions pulser-core/pulser/backend/noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ class NoiseModel:
depolarizing_prob: The probability of a depolarizing error occuring.
eff_noise_probs: The probability associated to each effective noise
operator.
eff_noise_opers: The operators for the effective noise model. The
first operator must be the identity.
eff_noise_opers: The operators for the effective noise model.
"""

noise_types: tuple[NOISE_TYPES, ...] = ()
Expand Down Expand Up @@ -170,9 +169,8 @@ def _check_eff_noise(self) -> None:
prob_distr = np.array(self.eff_noise_probs)
lower_bound = np.any(prob_distr < 0.0)
upper_bound = np.any(prob_distr > 1.0)
sum_p = not np.isclose(sum(prob_distr), 1.0)

if sum_p or lower_bound or upper_bound:
if lower_bound or upper_bound:
raise ValueError(
"The distribution given is not a probability distribution."
)
Expand All @@ -186,20 +184,3 @@ def _check_eff_noise(self) -> None:
raise NotImplementedError(
"Operator's shape must be (2,2) " f"not {operator.shape}."
)
# Identity position
identity = np.eye(2)
if np.any(self.eff_noise_opers[0] != identity):
raise NotImplementedError(
"You must put the identity matrix at the "
"beginning of the operator list."
)
# Completeness relation checking
sum_op = np.zeros((2, 2), dtype=complex)
for prob, op in zip(self.eff_noise_probs, self.eff_noise_opers):
sum_op += prob * op @ op.conj().transpose()

if not np.all(np.isclose(sum_op, identity)):
raise ValueError(
"The completeness relation is not verified."
f" Ended up with {sum_op} instead of {identity}."
)
55 changes: 9 additions & 46 deletions pulser-simulation/pulser_simulation/hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from __future__ import annotations

import itertools
import warnings
from collections import defaultdict
from collections.abc import Mapping
from dataclasses import asdict
Expand Down Expand Up @@ -120,59 +119,23 @@ def basis_check(noise_type: str) -> None:
if "dephasing" in config.noise_types:
basis_check("dephasing")
# Probability of phase (Z) flip:
# First order in prob
prob = config.dephasing_prob / 2
if prob > 0.1 and self._size > 1:
warnings.warn(
"The dephasing model is a first-order approximation in the"
f" dephasing probability. p = {2*prob} is too large for "
"realistic results.",
stacklevel=2,
)
local_collapse_ops.append(
np.sqrt(prob * (1 - prob) ** (self._size - 1)) * qutip.sigmaz()
)
coeff = np.sqrt(config.dephasing_prob / 2)
local_collapse_ops.append(coeff * qutip.sigmaz())

if "depolarizing" in config.noise_types:
basis_check("dephasing")
# Probability of error occurrence
prob = config.depolarizing_prob / 4
if prob > 0.1 and self._size > 1:
warnings.warn(
"The depolarizing model is a first-order approximation"
f" in the depolarizing probability. p = {4*prob}"
" is too large for realistic results.",
stacklevel=2,
)

k = np.sqrt(prob * (1 - 3 * prob) ** (self._size - 1))
local_collapse_ops.append(k * qutip.sigmax())
local_collapse_ops.append(k * qutip.sigmay())
local_collapse_ops.append(k * qutip.sigmaz())
coeff = np.sqrt(config.depolarizing_prob / 4)
local_collapse_ops.append(coeff * qutip.sigmax())
local_collapse_ops.append(coeff * qutip.sigmay())
local_collapse_ops.append(coeff * qutip.sigmaz())

if "eff_noise" in config.noise_types:
basis_check("general")
# Probability distribution of error occurences
m = len(config.eff_noise_opers)
if self._size > 1:
for i in range(1, m):
prob_i = config.eff_noise_probs[i]
if prob_i > 0.1:
warnings.warn(
"The effective noise model is a first-order"
" approximation in the noise probability."
f"p={prob_i} is large for realistic results.",
stacklevel=2,
)
break
# Deriving Kraus operators
prob_id = config.eff_noise_probs[0]
for i in range(1, m):
k = np.sqrt(
config.eff_noise_probs[i] * prob_id ** (self._size - 1)
for id, prob in enumerate(config.eff_noise_probs):
local_collapse_ops.append(
np.sqrt(prob) * config.eff_noise_opers[id]
)
k_op = k * config.eff_noise_opers[i]
local_collapse_ops.append(k_op)

# Building collapse operators
self._collapse_ops = []
Expand Down
34 changes: 0 additions & 34 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,24 +151,6 @@ def matrices(self):
matrices["I3"] = np.eye(3)
return matrices

@pytest.mark.parametrize(
"prob_distr",
[
[-1.0, 0.5],
[0.5, 2.0],
[0.3, 0.2],
],
)
def test_eff_noise_probs(self, prob_distr, matrices):
with pytest.raises(
ValueError, match="is not a probability distribution."
):
NoiseModel(
noise_types=("eff_noise",),
eff_noise_opers=[matrices["I"], matrices["X"]],
eff_noise_probs=prob_distr,
)

def test_eff_noise_opers(self, matrices):
with pytest.raises(ValueError, match="The operators list length"):
NoiseModel(noise_types=("eff_noise",), eff_noise_probs=[1.0])
Expand Down Expand Up @@ -197,22 +179,6 @@ def test_eff_noise_opers(self, matrices):
eff_noise_opers=[matrices["I3"]],
eff_noise_probs=[1.0],
)
with pytest.raises(
NotImplementedError, match="You must put the identity matrix"
):
NoiseModel(
noise_types=("eff_noise",),
eff_noise_opers=[matrices["X"], matrices["I"]],
eff_noise_probs=[0.5, 0.5],
)
with pytest.raises(
ValueError, match="The completeness relation is not"
):
NoiseModel(
noise_types=("eff_noise",),
eff_noise_opers=[matrices["I"], matrices["Zh"]],
eff_noise_probs=[0.5, 0.5],
)


class _MockConnection(RemoteConnection):
Expand Down
19 changes: 5 additions & 14 deletions tests/test_simconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,11 @@ def test_eff_noise_opers(matrices):
eff_noise_opers=[matrices["I3"]],
eff_noise_probs=[1.0],
)
with pytest.raises(
NotImplementedError, match="You must put the identity matrix"
):
SimConfig(
noise=("eff_noise"),
eff_noise_opers=[matrices["X"], matrices["I"]],
eff_noise_probs=[0.5, 0.5],
)
with pytest.raises(ValueError, match="The completeness relation is not"):
SimConfig(
noise=("eff_noise"),
eff_noise_opers=[matrices["I"], matrices["Zh"]],
eff_noise_probs=[0.5, 0.5],
)
SimConfig(
noise=("eff_noise"),
eff_noise_opers=[matrices["X"], matrices["I"]],
eff_noise_probs=[0.5, 0.5],
)


def test_from_noise_model():
Expand Down
40 changes: 3 additions & 37 deletions tests/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,16 +746,6 @@ def test_dephasing():
)
assert sim.run().sample_final_state() == Counter({"0": 595, "1": 405})
assert len(sim._hamiltonian._collapse_ops) != 0
with pytest.warns(UserWarning, match="first-order"):
reg = Register.from_coordinates([(0, 0), (0, 10)], prefix="q")
seq2 = Sequence(reg, Chadoq2)
seq2.declare_channel("ch0", "rydberg_global")
seq2.add(pulse, "ch0")
sim = QutipEmulator.from_sequence(
seq2,
sampling_rate=0.01,
config=SimConfig(noise="dephasing", dephasing_prob=0.5),
)


def test_depolarizing():
Expand All @@ -769,20 +759,10 @@ def test_depolarizing():
sim = QutipEmulator.from_sequence(
seq, sampling_rate=0.01, config=SimConfig(noise="depolarizing")
)
assert sim.run().sample_final_state() == Counter({"0": 587, "1": 413})
assert sim.run().sample_final_state() == Counter({"1": 523, "0": 477})
trace_2 = sim.run().states[-1] ** 2
assert np.trace(trace_2) < 1 and not np.isclose(np.trace(trace_2), 1)
assert len(sim._hamiltonian._collapse_ops) != 0
with pytest.warns(UserWarning, match="first-order"):
reg = Register.from_coordinates([(0, 0), (0, 10)], prefix="q")
seq2 = Sequence(reg, Chadoq2)
seq2.declare_channel("ch0", "rydberg_global")
seq2.add(pulse, "ch0")
sim = QutipEmulator.from_sequence(
seq2,
sampling_rate=0.01,
config=SimConfig(noise="depolarizing", depolarizing_prob=0.5),
)


def test_eff_noise(matrices):
Expand All @@ -798,8 +778,8 @@ def test_eff_noise(matrices):
sampling_rate=0.01,
config=SimConfig(
noise="eff_noise",
eff_noise_opers=[matrices["I"], matrices["Z"]],
eff_noise_probs=[0.975, 0.025],
eff_noise_opers=[matrices["Z"]],
eff_noise_probs=[0.025],
),
)
sim_dph = QutipEmulator.from_sequence(
Expand All @@ -810,20 +790,6 @@ def test_eff_noise(matrices):
and sim.run().states[-1] == sim_dph.run().states[-1]
)
assert len(sim._hamiltonian._collapse_ops) != 0
with pytest.warns(UserWarning, match="first-order"):
reg = Register.from_coordinates([(0, 0), (0, 10)], prefix="q")
seq2 = Sequence(reg, Chadoq2)
seq2.declare_channel("ch0", "rydberg_global")
seq2.add(pulse, "ch0")
sim = QutipEmulator.from_sequence(
seq2,
sampling_rate=0.01,
config=SimConfig(
noise="eff_noise",
eff_noise_opers=[matrices["I"], matrices["Z"]],
eff_noise_probs=[0.5, 0.5],
),
)


def test_add_config(matrices):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"\n",
"### 3. Effective noise channels:\n",
"\n",
"More generally, we can include effective noise channels in our simulation. These can represent the aggregate of different noise sources, as well as an interaction with an environment. To include these in our simulations, we use the Lindblad master equation, an evolution equation for the _density matrix_ $\\rho$ of the system. Some introductory lecture notes on the topic can be found [here](http://theory.caltech.edu/~preskill/ph219/chap3_15.pdf). `pulser-simulation` includes the following noise channels:\n",
"More generally, we can include effective noise channels in our simulation. These can represent the aggregate of different noise sources, as well as an interaction with an environment. To include these in our simulations, we use the Lindblad master equation, an evolution equation for the _density matrix_ $\\rho$ of the system. Some introductory lecture notes on the topic can be found [here (see chapters 8.2 to 8.4)](https://profmcruz.files.wordpress.com/2017/08/quantum-computation-and-quantum-information-nielsen-chuang.pdf). `pulser-simulation` includes the following noise channels:\n",
"\n",
"- **Dephasing channel**: Implements a decay in the _coherence_ or _interference_ terms (the off-diagonal terms in $\\rho$). It applies random $z$-rotations on each qubit according to a given probability $p$, the dephasing probability.\n",
"- **Depolarizing channel**: A more general type of noise in which $\\rho$ loses information due to interactions with the environment. This is represented as an evolution towards the completely mixed state $\\rho \\rightarrow \\frac{\\mathbb{I}}{2}$, effectively erasing the coherence in $\\rho$ with a probability $p$.\n",
Expand Down
15,486 changes: 83 additions & 15,403 deletions tutorials/classical_simulation/Simulating with effective noise channels.ipynb

Large diffs are not rendered by default.

0 comments on commit 9a88e2e

Please sign in to comment.