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

Update displayed device specs #763

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from

Conversation

HaroldErbin
Copy link
Contributor

Add more information to _specs() and move the method to BaseDevice.

A public property specs has been added.

See issue #756.

@HaroldErbin
Copy link
Contributor Author

I don't understand how to make the typing test pass. If I use a try: ... except AttributeError to see if ch has an attribute bottom_detuning, then I get an error saying that ch has no attribute bottom_detuning (which is what I am testing. If I am using a different test with hasattr, then I get an error saying that there is a redundant cast to float`. Any pointer to solve this would be appreciated.

@HaroldErbin
Copy link
Contributor Author

mypy continues to fail, but in a file I did not modify. Should I try to fix it nonetheless?

@HGSilveri
Copy link
Collaborator

mypy continues to fail, but in a file I did not modify. Should I try to fix it nonetheless?

Yeah, I saw this pop-up too in another PR. Please just copy this line and it goes away:

assert isinstance(batch.sequence_builder, str)

@HaroldErbin
Copy link
Contributor Author

Concerning the test coverage, which is now needed for specs since it's not internal, I was wondering what's the best approach. The simplest way is to assert that the specs string of different devices matches what is expected. However, this means having many lines of text (since we have to split the lines to respect the code style). Do you see a simpler way?

@HGSilveri
Copy link
Collaborator

Concerning the test coverage, which is now needed for specs since it's not internal, I was wondering what's the best approach. The simplest way is to assert that the specs string of different devices matches what is expected. However, this means having many lines of text (since we have to split the lines to respect the code style). Do you see a simpler way?

I'm afraid I don't see a way around it, no. I would maybe use a string with some format fields so that you can change some parameters (using @pytest.mark.parametrize) and check that the string always matches what you expect.

@HaroldErbin
Copy link
Contributor Author

I have added a test for three devices. At the end, I did not use pytest.mark.parametrize but a function, let me know if this works for you.

@HaroldErbin
Copy link
Contributor Author

HaroldErbin commented Nov 20, 2024

To give an example of what has been implemented in the PR, here is the output of AnalogDevice._specs() before:

Register parameters:
 - Dimensions: 2D
 - Rydberg level: 60
 - Maximum number of atoms: 25
 - Maximum distance from origin: 35 μm
 - Minimum distance between neighbouring atoms: 5 μm
 - Maximum layout filling fraction: 0.5
 - SLM Mask: No
 - Maximum sequence duration: 4000 ns

Channels:
 - 'rydberg_global': Rydberg.Global(Max Absolute Detuning: 125.66370614359172 rad/µs, Max Amplitude: 12.566370614359172 rad/µs, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 100000000 ns, Modulation Bandwidth: 8 MHz, Basis: 'ground-rydberg')

and after:

Register parameters:
 - Dimensions: 2D
 - Rydberg level: 60
 - Maximum number of atoms: 25
 - Maximum distance from origin: 35 μm
 - Minimum distance between neighbouring atoms: 5 μm
 - Maximum layout filling fraction: 0.5
 - SLM Mask: No
 - Maximum sequence duration: 4000 ns

Device parameters:
 - Maximum number of runs: 2000
 - Channels can be reused: No
 - Supported bases: ground-rydberg
 - Supported states: r, g
 - Ising interaction coefficient: 865723.02

Layout parameters:
 - Requires layout: Yes
 - Accepts new layout: No
 - Minimal number of traps: 1
 - Maximal number of traps: None

Channels:
 - 'rydberg_global': Rydberg.Global(Max Absolute Detuning: 125.66370614359172 rad/µs, Max Amplitude: 12.566370614359172 rad/µs, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 100000000 ns, Modulation Bandwidth: 8 MHz, Basis: 'ground-rydberg')

Also, we can invoke this method for MockDevice now:

Register parameters:
 - Dimensions: 3D
 - Rydberg level: 70
 - Maximum number of atoms: None
 - Maximum distance from origin: None μm
 - Minimum distance between neighbouring atoms: 0.0 μm
 - Maximum layout filling fraction: 0.5
 - SLM Mask: Yes

Device parameters:
 - Channels can be reused: Yes
 - Supported bases: XY, ground-rydberg, digital
 - Supported states: u, d, r, g, h
 - Ising interaction coefficient: 5420158.53
 - XY interaction coefficient: 3700.0

Layout parameters:
 - Requires layout: No
 - Minimal number of traps: 1
 - Maximal number of traps: None

Channels:
 - 'rydberg_global': Rydberg.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'ground-rydberg')
 - 'rydberg_local': Rydberg.Local(Max Absolute Detuning: None, Max Amplitude: None, Minimum retarget time: 0 ns, Fixed retarget time: 0 ns, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'ground-rydberg')
 - 'raman_global': Raman.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'digital')
 - 'raman_local': Raman.Local(Max Absolute Detuning: None, Max Amplitude: None, Minimum retarget time: 0 ns, Fixed retarget time: 0 ns, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'digital')
 - 'mw_global': Microwave.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'XY')
 - 'dmm_0': DMM.Global(Max Absolute Detuning: None, Max Amplitude: 0, Clock period: 1 ns, Minimum pulse duration: 1 ns, Maximum pulse duration: 100000000 ns, Basis: 'ground-rydberg')

Copy link
Collaborator

@HGSilveri HGSilveri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this @HaroldErbin , very nice work! I have some questions and suggestions, I'd be happy to hear your thoughts.

pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
tests/test_devices.py Outdated Show resolved Hide resolved
pulser-core/pulser/devices/_device_datacls.py Show resolved Hide resolved
One change is to use a string instead of joining elements of a list to
get the final string. The reason is that lists were cumbersome to use
when there were conditional statements.
@HaroldErbin
Copy link
Contributor Author

I have updated the codes with your suggestions, leaving only two conversations open for further discussions.

Copy link
Collaborator

@HGSilveri HGSilveri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! Can you please share a print of how this is looking right now?

pulser-core/pulser/devices/_device_datacls.py Outdated Show resolved Hide resolved
@HGSilveri HGSilveri changed the title Specs Update displayed device specs Nov 21, 2024
@HGSilveri HGSilveri linked an issue Nov 21, 2024 that may be closed by this pull request
Create one _specs method for each sections (register, layout, device,
channels). The layout section is defined only in Device, such that it is
not displayed for VirtualDevice.

This commit also goes back to using lists for storing the lines.
@HaroldErbin
Copy link
Contributor Author

I have implemented the changes suggested by @HGSilveri:

  1. Individual private methods have been created for each section (register, layout, device, channels).
  2. The layout section is defined only for Device.
  3. I have reverted back to using lists instead of strings.
  4. I have defined two new private methods which are used to improve handling of None and boolean parameters.

Here are the results:

DigitalAnalogDevice

Register parameters:
 - Dimensions: 2D
 - Rydberg level: 70
 - Maximum number of atoms: 100
 - Maximum distance from origin: 50 µm
 - Minimum distance between neighbouring atoms: 4 μm
 - SLM Mask: Yes

Layout parameters:
 - Requires layout: Yes
 - Accepts new layout: Yes
 - Minimal number of traps: 1
 - Maximum layout filling fraction: 0.5

Device parameters:
 - Channels can be reused: No
 - Supported bases: digital, ground-rydberg
 - Supported states: r, g, h
 - Ising interaction coefficient: 5420158.53

Channels:
 - 'rydberg_global': Rydberg.Global(Max Absolute Detuning: 125.66370614359172 rad/µs, Max Amplitude: 15.707963267948966 rad/µs, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 67108864 ns, Basis: 'ground-rydberg')
 - 'rydberg_local': Rydberg.Local(Max Absolute Detuning: 125.66370614359172 rad/µs, Max Amplitude: 62.83185307179586 rad/µs, Minimum retarget time: 220 ns, Fixed retarget time: 0 ns, Max targets: 1, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 67108864 ns, Basis: 'ground-rydberg')
 - 'raman_local': Raman.Local(Max Absolute Detuning: 125.66370614359172 rad/µs, Max Amplitude: 62.83185307179586 rad/µs, Minimum retarget time: 220 ns, Fixed retarget time: 0 ns, Max targets: 1, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 67108864 ns, Basis: 'digital')
 - 'dmm_0': DMM.Global(Max Absolute Detuning: None, Max Amplitude: 0, Clock period: 4 ns, Minimum pulse duration: 16 ns, Maximum pulse duration: 67108864 ns, Basis: 'ground-rydberg')

MockDevice

Register parameters:
 - Dimensions: 3D
 - Rydberg level: 70
 - Minimum distance between neighbouring atoms: 0.0 μm
 - SLM Mask: Yes

Device parameters:
 - Channels can be reused: Yes
 - Supported bases: digital, ground-rydberg, XY
 - Supported states: u, d, r, g, h
 - Ising interaction coefficient: 5420158.53
 - XY interaction coefficient: 3700.0

Channels:
 - 'rydberg_global': Rydberg.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'ground-rydberg')
 - 'rydberg_local': Rydberg.Local(Max Absolute Detuning: None, Max Amplitude: None, Minimum retarget time: 0 ns, Fixed retarget time: 0 ns, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'ground-rydberg')
 - 'raman_global': Raman.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'digital')
 - 'raman_local': Raman.Local(Max Absolute Detuning: None, Max Amplitude: None, Minimum retarget time: 0 ns, Fixed retarget time: 0 ns, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'digital')
 - 'mw_global': Microwave.Global(Max Absolute Detuning: None, Max Amplitude: None, Clock period: 1 ns, Minimum pulse duration: 1 ns, Basis: 'XY')
 - 'dmm_0': DMM.Global(Max Absolute Detuning: None, Max Amplitude: 0, Clock period: 1 ns, Minimum pulse duration: 1 ns, Maximum pulse duration: 100000000 ns, Basis: 'ground-rydberg')

Copy link
Collaborator

@HGSilveri HGSilveri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nice work @HaroldErbin , thanks a lot! I have a final proposition for the return types of these new methods that I think can make them slightly more flexible. Consider all the comments below together, they are related

" - Maximum sequence duration: "
f"{self.max_sequence_duration} ns"
)
return "\n".join(line for line in register_lines if line != "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we return list[str] instead? This (and the other similar methods) could change to

Suggested change
return "\n".join(line for line in register_lines if line != "")
return [line for line in register_lines if line != ""]

Comment on lines +709 to +715
return "\n".join(
[
self._register_lines(),
self._device_lines(),
self._channel_lines(for_docs=for_docs),
]
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would then become

Suggested change
return "\n".join(
[
self._register_lines(),
self._device_lines(),
self._channel_lines(for_docs=for_docs),
]
)
return "\n".join(
self._register_lines()
+ self._device_lines()
+ self._channel_lines(for_docs=for_docs)
)

Comment on lines +893 to +907
def _layout_lines(self) -> str:

layout_lines = [
"\nLayout parameters:",
f" - Requires layout: {self._param_yes_no(self.requires_layout)}",
" - Accepts new layout: "
+ self._param_yes_no(self.accepts_new_layouts),
f" - Minimal number of traps: {self.min_layout_traps}",
self._param_check_none(self.max_layout_traps)(
" - Maximal number of traps: {}"
),
f" - Maximum layout filling fraction: {self.max_layout_filling}",
]

return "\n".join(line for line in layout_lines if line != "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had something slightly different in mind for the layout parameters. I think the ones that are in VirtualDevice should also appear in its specs, so this method could be moved to BaseDevice expect for the accepts_new_layout line. That one, we could add it like this:

Suggested change
def _layout_lines(self) -> str:
layout_lines = [
"\nLayout parameters:",
f" - Requires layout: {self._param_yes_no(self.requires_layout)}",
" - Accepts new layout: "
+ self._param_yes_no(self.accepts_new_layouts),
f" - Minimal number of traps: {self.min_layout_traps}",
self._param_check_none(self.max_layout_traps)(
" - Maximal number of traps: {}"
),
f" - Maximum layout filling fraction: {self.max_layout_filling}",
]
return "\n".join(line for line in layout_lines if line != "")
def _layout_lines(self) -> list[str]:
layout_lines = super()._layout_lines()
layout_lines.insert(2, f" - Accepts new layout: {self._param_yes_no(self.accepts_new_layouts)}"
return layout_lines

Comment on lines +909 to +918
def _specs(self, for_docs: bool = False) -> str:

return "\n".join(
[
self._register_lines(),
self._layout_lines(),
self._device_lines(),
self._channel_lines(for_docs=for_docs),
]
)
Copy link
Collaborator

@HGSilveri HGSilveri Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (or rather its new version) would then be in BaseDevice and we don't need to redefine it here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Show all the specs of the Device
2 participants