Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete summing constraint in DetuningMap #610

Merged
merged 21 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions pulser-core/pulser/register/_reg_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,7 @@ def _draw_2D(
):
raise ValueError("masked qubits and dmm qubits must be the same.")
elif masked_qubits:
dmm_qubits = {
masked_qubit: 1.0 / len(masked_qubits)
for masked_qubit in masked_qubits
}
dmm_qubits = {masked_qubit: 1.0 for masked_qubit in masked_qubits}

if dmm_qubits:
dmm_pos = []
Expand Down
3 changes: 1 addition & 2 deletions pulser-core/pulser/register/base_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ def define_detuning_map(

Args:
detuning_weights: A mapping between the IDs of the targeted qubits
and detuning weights (between 0 and 1, their sum must be equal
to 1).
and detuning weights (between 0 and 1).
slug: An optional identifier for the detuning map.

Returns:
Expand Down
3 changes: 1 addition & 2 deletions pulser-core/pulser/register/mappable_reg.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ def define_detuning_map(

Args:
detuning_weights: A mapping between the IDs of the targeted traps
and detuning weights (between 0 and 1, their sum must be equal
to 1).
and detuning weights (between 0 and 1).
slug: An optional identifier for the detuning map.

Returns:
Expand Down
3 changes: 1 addition & 2 deletions pulser-core/pulser/register/register_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ def define_detuning_map(

Args:
detuning_weights: A mapping between the IDs of the targeted traps
and detuning weights (between 0 and 1, their sum must be equal
to 1).
and detuning weights (between 0 and 1).
slug: An optional identifier for the detuning map.

Returns:
Expand Down
19 changes: 9 additions & 10 deletions pulser-core/pulser/register/weight_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,9 @@
class WeightMap(Traps, RegDrawer):
"""Defines a generic map of weights on traps.

The sum of the provided weights must be equal to 1.

Args:
trap_coordinates: An array containing the coordinates of the traps.
weights: A list weights to associate to the traps.
weights: A list of weights (between 0 and 1) to associate to the traps.
"""

weights: tuple[float, ...]
Expand All @@ -56,10 +54,10 @@ def __init__(
super().__init__(trap_coordinates, slug)
if len(cast(list, trap_coordinates)) != len(weights):
raise ValueError("Number of traps and weights don't match.")
if not np.all(np.array(weights) >= 0):
raise ValueError("All weights must be non-negative.")
if not np.isclose(sum(weights), 1.0, atol=1e-16):
raise ValueError("The sum of the weights should be 1.")
if not (
np.all(np.array(weights) >= 0) and np.all(np.array(weights) <= 1)
):
raise ValueError("All weights must be between 0 and 1.")
object.__setattr__(self, "weights", tuple(weights))

@property
Expand Down Expand Up @@ -173,10 +171,11 @@ def _to_abstract_repr(self) -> dict[str, Any]:
class DetuningMap(WeightMap):
"""Defines a DetuningMap.

A DetuningMap associates a detuning weight to the coordinates of a trap.
The sum of the provided weights must be equal to 1.
A DetuningMap associates a detuning weight (a value between 0 and 1)
to the coordinates of a trap.

Args:
trap_coordinates: An array containing the coordinates of the traps.
weights: A list of detuning weights to associate to the traps.
weights: A list of detuning weights (between 0 and 1) to associate
to the traps.
"""
9 changes: 4 additions & 5 deletions pulser-core/pulser/sequence/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,10 +496,9 @@ def set_magnetic_field(
self._calls.append(_Call("set_magnetic_field", mag_vector, {}))

def _set_slm_mask_dmm(self, dmm_id: str, targets: set[QubitId]) -> None:
ntargets = len(targets)
detuning_map = self.register.define_detuning_map(
{
qubit: (1 / ntargets if qubit in targets else 0)
qubit: (1.0 if qubit in targets else 0)
for qubit in self.register.qubit_ids
}
)
Expand Down Expand Up @@ -538,7 +537,7 @@ def config_slm_mask(
channel starting the earliest in the schedule.

If the sequence is in Ising, the SLM Mask is a DetuningMap where
the detuning of each masked qubit is the same. DMM "dmm_id" is
the detuning of each masked qubit is 1.0. DMM "dmm_id" is
configured using this Detuning Map, and modulated by a pulse having
a large negative detuning and either a duration defined from pulses
already present in the sequence (same as in XY mode) or by the first
Expand Down Expand Up @@ -598,8 +597,8 @@ def config_detuning_map(
``MockDevice`` DMM can be repeatedly declared if needed.

Args:
detuning_map: A DetuningMap defining atoms to act on and bottom
detuning to modulate.
detuning_map: A DetuningMap defining the amont of detuning each
HGSilveri marked this conversation as resolved.
Show resolved Hide resolved
atom receives.
dmm_id: How the channel is identified in the device.
See in ``Sequence.available_channels`` which DMM IDs are still
available (start by "dmm" ) and the associated description.
Expand Down
10 changes: 5 additions & 5 deletions tests/test_abstract_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,13 +859,13 @@ def test_parametrized_fails_validation(self):
@pytest.mark.parametrize("is_empty", [True, False])
def test_dmm_slm_mask(self, triangular_lattice, is_empty):
mask = {"q0", "q2", "q4", "q5"}
dmm = {"q0": 0.2, "q1": 0.3, "q2": 0.4, "q3": 0.1}
det_map = {"q0": 1.0, "q1": 0.5, "q2": 0.5, "q3": 0.0}
reg = triangular_lattice.rectangular_register(3, 4)
seq = Sequence(reg, MockDevice)
seq.config_slm_mask(mask, "dmm_0")
if not is_empty:
seq.config_detuning_map(
reg.define_detuning_map(dmm, "det_map"), "dmm_0"
reg.define_detuning_map(det_map, "det_map"), "dmm_0"
)
seq.add_dmm_detuning(ConstantWaveform(100, -10), "dmm_0_1")
seq.declare_channel("rydberg_global", "rydberg_global")
Expand Down Expand Up @@ -900,7 +900,7 @@ def test_dmm_slm_mask(self, triangular_lattice, is_empty):
"x": reg._coords[i][0],
"y": reg._coords[i][1],
}
for i, weight in enumerate(list(dmm.values()))
for i, weight in enumerate(list(det_map.values()))
]
assert (
abstract["operations"][1]["detuning_map"]["slug"] == "det_map"
Expand Down Expand Up @@ -1121,8 +1121,8 @@ def test_deserialize_seq_with_slm_mask_xy(self):

def test_deserialize_seq_with_slm_dmm(self):
traps = [
{"weight": 0.5, "x": -2.0, "y": 9.0},
{"weight": 0.5, "x": 0.0, "y": 2.0},
{"weight": 1.0, "x": -2.0, "y": 9.0},
{"weight": 1.0, "x": 0.0, "y": 2.0},
{"weight": 0, "x": 12.0, "y": 0.0},
]
op = [
Expand Down
13 changes: 4 additions & 9 deletions tests/test_dmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def map_reg(self, layout: RegisterLayout) -> MappableRegister:

@pytest.fixture
def det_dict(self) -> dict[int, float]:
return {0: 0.7, 1: 0.3, 2: 0}
return {0: 1.0, 1: 0.3, 2: 0}

@pytest.fixture
def det_map(
Expand All @@ -53,7 +53,7 @@ def det_map(

@pytest.fixture
def slm_dict(self) -> dict[int, float]:
return {0: 1 / 3, 1: 1 / 3, 2: 1 / 3}
return {0: 1.0, 1: 1.0, 2: 1.0}

@pytest.fixture
def slm_map(
Expand Down Expand Up @@ -89,7 +89,7 @@ def test_define_detuning_map(

def test_qubit_weight_map(self, register):
# Purposefully unsorted
qid_weight_map = {1: 0.5, 0: 0.1, 3: 0.4}
qid_weight_map = {1: 1.0, 0: 0.1, 3: 0.4}
sorted_qids = sorted(qid_weight_map)
det_map = register.define_detuning_map(qid_weight_map)
qubits = register.qubits
Expand Down Expand Up @@ -158,16 +158,11 @@ def test_detuning_map_bad_init(
DetuningMap([(0, 0), (1, 0)], [0])

bad_weights = {0: -1.0, 1: 1.0, 2: 1.0}
bad_sum = {0: 0.1, 2: 0.9, 3: 0.1}
for reg in (layout, map_reg, register):
with pytest.raises(
ValueError, match="All weights must be non-negative."
ValueError, match="All weights must be between 0 and 1."
):
reg.define_detuning_map(bad_weights) # type: ignore
with pytest.raises(
ValueError, match="The sum of the weights should be 1."
):
reg.define_detuning_map(bad_sum) # type: ignore

def test_init(
self,
Expand Down
12 changes: 6 additions & 6 deletions tests/test_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def reg():
@pytest.fixture
def det_map(reg: Register):
return reg.define_detuning_map(
{"q" + str(i): (1 / 4 if i in [0, 1, 3, 4] else 0) for i in range(10)}
{"q" + str(i): (1.0 if i in [0, 1, 3, 4] else 0) for i in range(10)}
)


Expand Down Expand Up @@ -496,7 +496,7 @@ def init_seq(
if config_det_map:
det_map = reg.define_detuning_map(
{
"q" + str(i): (1 / 4 if i in [0, 1, 3, 4] else 0)
"q" + str(i): (1.0 if i in [0, 1, 3, 4] else 0)
for i in range(10)
}
)
Expand Down Expand Up @@ -865,7 +865,7 @@ def test_switch_device_up(
mod_trap_ids = [20, 32, 54, 66]
assert np.all(
nested_s_loc[:100]
== (-2.5 if trap_id in mod_trap_ids else 0)
== (-10.0 if trap_id in mod_trap_ids else 0)
)
else:
# first pulse is covered by SLM Mask
Expand Down Expand Up @@ -1339,8 +1339,8 @@ def test_config_slm_mask(qubit_ids, device, det_map):
seq.config_detuning_map(det_map, "dmm_0")
seq.declare_channel("rydberg_global", "rydberg_global")
assert set(seq._schedule.keys()) == {"dmm_0", "rydberg_global"}
assert seq._schedule["dmm_0"].detuning_map.weights[0] == 0.5
assert seq._schedule["dmm_0"].detuning_map.weights[2] == 0.5
assert seq._schedule["dmm_0"].detuning_map.weights[0] == 1.0
assert seq._schedule["dmm_0"].detuning_map.weights[2] == 1.0

with pytest.raises(ValueError, match="configured only once"):
seq.config_slm_mask(targets)
Expand Down Expand Up @@ -1572,7 +1572,7 @@ def test_draw_register_det_maps(reg, ch_name, patch_plt_show):
[(0, 0), (10, 10), (-10, -10), (20, 20), (30, 30), (40, 40)]
)
det_map = reg_layout.define_detuning_map(
{0: 0, 1: 0, 2: 0, 3: 0.5, 4: 0.5}
{0: 0, 1: 0, 2: 0, 3: 1.0, 4: 1.0}
)
reg = reg_layout.define_register(0, 1, 2, qubit_ids=["q0", "q1", "q2"])
targets = ["q0", "q2"]
Expand Down
4 changes: 1 addition & 3 deletions tests/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,9 +1122,7 @@ def test_mask_local_channel():
if q in masked_qubits:
assert np.array_equal(
sim.samples["Local"]["ground-rydberg"][q]["det"],
np.concatenate(
(-10 / len(masked_qubits) * pulse.amplitude.samples, [0])
),
np.concatenate((-10 * pulse.amplitude.samples, [0])),
)
else:
assert np.all(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"source": [
"Even when working with **global** addressing channels, the **detuning** of individual qubits can be addressed **locally** by using a specific channel named the **Detuning Map Modulator** or `DMM`.\n",
"\n",
"This `Channel` applies a `Global` pulse of **zero amplitude** and **negative detuning** on a `DetuningMap`. The `DetuningMap` consists of a set of weights on specific sites that dictate how the detuning applied by the `DMM` is distributed.\n",
"This `Channel` applies a `Global` pulse of **zero amplitude** and **negative detuning** on a `DetuningMap`. The `DetuningMap` consists of a set of weights on specific sites that dictate the amount of detuning applied by the `DMM` each site receives.\n",
"\n",
"This modulation of the `DetuningMap` by the `DMM` Channel is equivalent to adding a term $-\\frac{\\hbar}{2}\\sum_{i}\\epsilon_{i}\\Delta(t)\\sigma^{z}_{i}$ to the Ising Hamiltonian. Here, $\\Delta(t)$ is the detuning applied on the `DMM`, and $(\\epsilon_i)_{i}$ are the weights defined in the `DetuningMap` for each atom."
]
Expand All @@ -56,7 +56,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"A `DetuningMap` associates a set of locations with a set of weights. The weights $(\\epsilon_i)_i$ have to be positive, between 0 and 1, and their sum has to be equal to 1. The locations are the trap coordinates to address."
"A `DetuningMap` associates a set of locations with a set of weights. The locations are the trap coordinates to address and the weights $(\\epsilon_i)_i$ have to be between 0 and 1."
]
},
{
Expand All @@ -66,8 +66,8 @@
"outputs": [],
"source": [
"trap_coordinates = [(0.0, 0.0), (0.0, 5.0), (5.0, 0.0), (5.0, 5.0)]\n",
"weights_1 = [0.5, 0.25, 0.25, 0] # between 0 and 1, sum equal to 1\n",
"weights_2 = [1 / 3, 1 / 3, 1 / 3, 0] # between 0 and 1, sum equal to 1"
"weights_1 = [1.0, 0.5, 0.5, 0] # between 0 and 1\n",
"weights_2 = [1.0, 1.0, 1.0, 0] # between 0 and 1"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@
"id": "09b56b15",
"metadata": {},
"source": [
"In Ising mode, configuring an SLM Mask with a `DMM` labeled `dmm_id` in the device internally configures a detuning map using `config_detuning_map` (see notebook [\"Local Addressability with DMM\"](dmm.nblink) for an introduction) with `dmm_id` and a `DetuningMap` **distributing the applied detuning equally over all the masked qubits**.\n",
"In Ising mode, configuring an SLM Mask with a `DMM` labeled `dmm_id` in the device internally configures a detuning map using `config_detuning_map` (see notebook [\"Local Addressability with DMM\"](dmm.nblink) for an introduction) with `dmm_id` and a `DetuningMap` **distributing all the applied detuning to the masked qubits**.\n",
"\n",
"For instance in the last example qubits \"q1\" and \"q2\" are masked, so we expect a `DetuningMap` associating to the trap location of \"q0\" the weight 0, and to the trap locations of \"q1\" and \"q2\" the weight $1/2 = 0.5$:"
"For instance in the last example qubits \"q1\" and \"q2\" are masked, so we expect a `DetuningMap` associating to the trap location of \"q0\" the weight $0$, and to the trap locations of \"q1\" and \"q2\" the weight $1$:"
]
},
{
Expand Down