diff --git a/pulser-core/pulser/sequence/sequence.py b/pulser-core/pulser/sequence/sequence.py index 7c0ef98f..3a67cc1d 100644 --- a/pulser-core/pulser/sequence/sequence.py +++ b/pulser-core/pulser/sequence/sequence.py @@ -824,9 +824,6 @@ def check_channels_match( "fixed_retarget_t", "clock_period", ] - if isinstance(old_ch_obj, DMM): - params_to_check.append("bottom_detuning") - params_to_check.append("total_bottom_detuning") if check_retarget(old_ch_obj) or check_retarget(new_ch_obj): params_to_check.append("min_retarget_interval") for param_ in params_to_check: @@ -934,8 +931,10 @@ def build_sequence_from_matching( call.name == "declare_channel" or call.name == "config_detuning_map" or call.name == "config_slm_mask" + or call.name == "add_dmm_detuning" ): pass + # if calling declare_channel elif "name" in sw_channel_kw_args: # pragma: no cover sw_channel_kw_args["channel_id"] = channel_match[ sw_channel_kw_args["name"] @@ -944,21 +943,42 @@ def build_sequence_from_matching( sw_channel_kw_args["channel_id"] = channel_match[ sw_channel_args[0] ] - elif "dmm_id" in sw_channel_kw_args: # pragma: no cover - sw_channel_kw_args["dmm_id"] = channel_match[ - _get_dmm_name(sw_channel_kw_args["dmm_id"], dmm_calls) - ] - dmm_calls.append(sw_channel_kw_args["dmm_id"]) elif call.name == "declare_channel": sw_channel_args[1] = channel_match[sw_channel_args[0]] - else: - sw_channel_args[1] = channel_match[ - _get_dmm_name(sw_channel_args[1], dmm_calls) + # if adding a detuning waveform to the dmm + elif "dmm_name" in sw_channel_kw_args: # program: no cover + sw_channel_kw_args["dmm_name"] = channel_match[ + sw_channel_kw_args["dmm_name"] ] - dmm_calls.append(sw_channel_args[1]) + elif call.name == "add_dmm_detuning": + sw_channel_args[1] = channel_match[sw_channel_args[1]] + # if configuring a detuning map or an SLM mask + else: + assert ( + call.name == "config_detuning_map" + or call.name == "config_slm_mask" + ) + if "dmm_id" in sw_channel_kw_args: # pragma: no cover + dmm_called = _get_dmm_name( + sw_channel_kw_args["dmm_id"], dmm_calls + ) + sw_channel_kw_args["dmm_id"] = channel_match[ + dmm_called + ] + else: + dmm_called = _get_dmm_name( + sw_channel_args[1], dmm_calls + ) + sw_channel_args[1] = channel_match[dmm_called] + dmm_calls.append(dmm_called) + channel_match[dmm_called] = _get_dmm_name( + channel_match[dmm_called], + list(new_seq.declared_channels.keys()), + ) getattr(new_seq, call.name)( *sw_channel_args, **sw_channel_kw_args ) + if strict: for eom_channel in active_eom_channels: if ( @@ -1012,9 +1032,9 @@ def build_sequence_from_matching( err_channel_match[tuple(channel_match.items())] = e.args continue raise ValueError( - "No matching found between declared channels and channels in the" - "new device that does not modify the samples of the Sequence." - "Here is a list of matching tested and their associated errors:" + "No matching found between declared channels and channels in the " + "new device that does not modify the samples of the Sequence. " + "Here is a list of matching tested and their associated errors: " f"{err_channel_match}" ) diff --git a/tests/test_sequence.py b/tests/test_sequence.py index 876cd56e..03d7a022 100644 --- a/tests/test_sequence.py +++ b/tests/test_sequence.py @@ -17,6 +17,7 @@ import dataclasses import itertools import json +import re from typing import Any from unittest.mock import patch @@ -357,8 +358,9 @@ def devices(): clock_period=4, min_duration=16, max_duration=2**26, - bottom_detuning=-2 * np.pi * 20, - total_bottom_detuning=-2 * np.pi * 2000, + # Better than DMM of DigitalAnalogDevice + bottom_detuning=-2 * np.pi * 40, + total_bottom_detuning=-2 * np.pi * 4000, ), ), ) @@ -742,40 +744,81 @@ def test_switch_device_down( ): # Can't find a match for the 2nd dmm_0 seq.switch_device(phys_Chadoq2) - # Strict switch imposes to have same bottom detuning for DMMs - with pytest.raises( - ValueError, - match="No match for channel dmm_0_1 with the same bottom_detuning.", - ): - # Can't find a match for the 1st dmm_0 + # There is no need to have same bottom detuning to have a strict switch + dmm_down = dataclasses.replace( + phys_Chadoq2.dmm_channels["dmm_0"], bottom_detuning=-10 + ) + new_seq = seq.switch_device( + dataclasses.replace(phys_Chadoq2, dmm_objects=(dmm_down, dmm_down)), + strict=True, + ) + assert list(new_seq.declared_channels.keys()) == [ + "global", + "dmm_0", + "dmm_1", + ] + seq.add_dmm_detuning(ConstantWaveform(100, -20), "dmm_0_1") + # Still works with reusable channels + new_seq = seq.switch_device( + dataclasses.replace( + phys_Chadoq2.to_virtual(), + reusable_channels=True, + dmm_objects=(dataclasses.replace(dmm_down, bottom_detuning=-20),), + ), + strict=True, + ) + assert list(new_seq.declared_channels.keys()) == [ + "global", + "dmm_0", + "dmm_0_1", + ] + # Still one compatible configuration + new_seq = seq.switch_device( + dataclasses.replace( + phys_Chadoq2, + dmm_objects=(phys_Chadoq2.dmm_channels["dmm_0"], dmm_down), + ), + strict=True, + ) + assert list(new_seq.declared_channels.keys()) == [ + "global", + "dmm_1", + "dmm_0", + ] + # No compatible configuration + error_msg = ( + "No matching found between declared channels and channels in the " + "new device that does not modify the samples of the Sequence. " + "Here is a list of matching tested and their associated errors: " + "{(('global', 'rydberg_global'), ('dmm_0', 'dmm_0'), ('dmm_0_1', " + "'dmm_1')): ('The detunings on some atoms go below the local bottom " + "detuning of the DMM (-10 rad/µs).',), (('global', 'rydberg_global'), " + "('dmm_0', 'dmm_1'), ('dmm_0_1', 'dmm_0')): ('The detunings on some " + "atoms go below the local bottom detuning of the DMM (-10 rad/µs).',)}" + ) + with pytest.raises(ValueError, match=re.escape(error_msg)): seq.switch_device( dataclasses.replace( - phys_Chadoq2, - dmm_objects=( - phys_Chadoq2.dmm_channels["dmm_0"], - dataclasses.replace( - phys_Chadoq2.dmm_channels["dmm_0"], bottom_detuning=-10 - ), - ), + phys_Chadoq2, dmm_objects=(dmm_down, dmm_down) ), strict=True, ) - with pytest.raises( - ValueError, - match="No match for channel dmm_0_1 with the same " - "total_bottom_detuning.", - ): - # Can't find a match for the 1st dmm_0 + dmm_down = dataclasses.replace( + phys_Chadoq2.dmm_channels["dmm_0"], + bottom_detuning=-10, + total_bottom_detuning=-10, + ) + seq.switch_device( + dataclasses.replace( + phys_Chadoq2, + dmm_objects=(phys_Chadoq2.dmm_channels["dmm_0"], dmm_down), + ), + strict=True, + ) + with pytest.raises(ValueError, match=re.escape(error_msg)): seq.switch_device( dataclasses.replace( - phys_Chadoq2, - dmm_objects=( - phys_Chadoq2.dmm_channels["dmm_0"], - dataclasses.replace( - phys_Chadoq2.dmm_channels["dmm_0"], - total_bottom_detuning=-500, - ), - ), + phys_Chadoq2, dmm_objects=(dmm_down, dmm_down) ), strict=True, )