Coder Social home page Coder Social logo

pasqal-io / pulser Goto Github PK

View Code? Open in Web Editor NEW
164.0 10.0 58.0 43.51 MB

Library for pulse-level/analog control of neutral atom devices. Emulator with QuTiP.

License: Apache License 2.0

Python 99.97% Shell 0.01% Makefile 0.03%
neutral-atom-devices quantum-computing quantum-simulation unitaryhack

pulser's Introduction

Pulser

Pulser is a framework for composing, simulating and executing pulse sequences for neutral-atom quantum devices.

Documentation for the latest release of pulser is available at https://pulser.readthedocs.io (for the docs tracking the develop branch of this repository, visit https://pulser.readthedocs.io/en/latest instead).

The source code can be found at https://github.com/pasqal-io/Pulser.

Overview of Pulser

Pulser is designed to let users create experiments that are tailored to specific neutral-atom devices. This reduces the level of abstraction and gives you maximal flexibility and control over the behaviour of the relevant physical parameters, within the bounds set by the chosen device.

Consequently, Pulser breaks free from the paradigm of digital quantum computing and also allows the creation of analog quantum simulations, outside of the scope of traditional quantum circuit approaches. Whatever the type of experiment or paradigm, if it can be done on the device, it can be done with Pulser.

Additionally, the pulser_simulation extension provides tools for classical simulation (using QuTiP libraries) to aid in the development and testing of new pulse sequences.

For a comprehensive overview of Pulser, check out Pulser's white paper.

Installation

Note: Pulser v0.6 introduced a split of the pulser package that prevents it from being correctly upgraded. If you have an older version of pulser installed and wish to upgrade, make sure to uninstall it first by running pip uninstall pulser.

To install the latest release of pulser, have Python 3.8 or higher installed, then use pip:

pip install pulser

The standard pulser distribution will install the core pulser package and the pulser_simulation extension package, which is required if you want to access the emulation features.

If you wish to install only the core pulser features, you can instead run:

pip install pulser-core

If you wish to install the development version of Pulser from source instead, do the following from within this repository after cloning it:

git checkout develop
make dev-install

Bear in mind that this installation will track the contents of your local Pulser repository folder, so if you checkout a different branch (e.g. master), your installation will change accordingly.

Development Requirements (Optional)

To run the tutorials or the test suite locally, after installation first run the following to install the development requirements:

pip install -r dev_requirements.txt

Then, you can do the following to run the test suite and report test coverage:

pytest --cov

Contributing

Want to contribute to Pulser? Great! See How to Contribute for information on how you can do so.

Citing Pulser

Citation references are generated through Zenodo. Click the badge below to get the citation to the latest Pulser release.

DOI

pulser's People

Contributors

a-corni avatar anneclairelh avatar awennersteen avatar cdalyac avatar cdeterra avatar codoscope avatar dakk avatar darcangelomauro avatar dehond avatar hgsilveri avatar julien-bremont avatar karalekas avatar kvaithin avatar laurentajdnik avatar lhenriet avatar louis-paulhenry avatar louisjustintallot avatar lucasgitq avatar lvignoli avatar matthieumoreau0 avatar nathanshammah avatar paniash avatar rbstsai avatar sebgrijalva avatar slimane33 avatar soufianekaghad98 avatar triplerd avatar varda-star avatar wingcode avatar yash-10 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pulser's Issues

Effective Pulse Sequence in Simulations

When a sampling_rate is specified in a Simulation, a fraction of the samples is chosen to create the QobjEvo of the Hamiltonian. If the sampling rate value is very small, this may result in an oversimplified sequence. On the other hand, if one is looking for a good compromise of a low sampling_rate that runs faster and still faithful to the ideal sequence, having a visual aid is valuable.

It would be useful to have a Simulation.draw() method (or maybe a better suited name) such that the user can see what the resulting pulse sequence looks like.

All the information is stored in Simulation.samples so one solution would be to plot:

def _draw_effective_seq(my_sim):
    samples_a = my_sim.samples['Global']['ground-rydberg']['amp']
    samples_d = my_sim.samples['Global']['ground-rydberg']['det']
    times = (1000*my_sim._times).astype(int)
    plt.figure(figsize=(14,6))
    plt.plot(times, samples_a[times], '.', ls='-')
    plt.plot(times, samples_d[times], '.', ls='-')

This is an example with only Global pulses in a 'ground-rydberg' basis, so a more robust method should look in all the provided samples dictionary. I also haven't included any phases that the pulses may have.

Simulation is using MHz and ns

Having the waveform amplitudes in MHz and the time steps in ns is incompatible with an adimensional simulation. Adjustments should be made to one or the other inside the simulation module (I would suggest dividing all samples by 1000, ie going to GHz).

Consider registering package name on PyPI

You could register the name of the package on PyPI. pulser is currently available (https://pypi.org/search/?q=pulser), also checkable with pip search pulser.
Here are more details on packaging, I can provide support about. I'd register a pretty empty version (e.g., v. 0.1a1, according to semantic versioning) on PyPI, with a minimal setup.py file and a basic module, e.g., utils.py. It would be nice to mirror this also on github with a mirror release.

You could also register on TestPyPI. It requires setting up two different accounts.

Removing _chirps from Pulse?

I realise that the Pulser._chirps() method is a bit out of place, considering we don't use it anywhere. I need it for the QM translation, but I can also just have it there instead.
The argument I have in favour of leaving it in Pulser is that it contains the information on how the detuning waveform will actually be done, which we might want to use for a more realistic display of the Sequence or even its simulation, but I don't know if this is something we want anyway.

What's your take on this @lhenriet @sebgrijalva ? Do we keep it or not?

How to get the parameters of the phase diagram?

Hello, this package is helpful in my research. I really appreciate for your work.
However, I am a little confused while reading the tutorial Building 1D Rydberg Crystals.ipynb. In the function phase_diagram() , the lobes of the phase diagram are draw by the fill_between() function. It seems that the parameters are already determined (x = 2*(0.6+8*(y-1.2)**2)). I do not know how to obtain these parameters.
I think it is better to get these parameters by using the eigensolver of Qutip or DMRG.

Remove try/except import block from notebooks

Now that we have a setup.py, we shouldn't need to do this import hack in the notebooks:

try:
    import pulser
except ModuleNotFoundError:
    import sys
    sys.path.insert(1, '../')

I have verified locally that I am able to remove this block and everything works. If it's still not working for you @HGSilveri, then try running git clean -fdx in the top-level directory of Pulser to get rid of the cache files.

I can make this fix after #17 is done, so there are no conflicts.

Make Device a static dataclass and add Register to Sequence directly

This change simplifies the creation of a Sequence by asking the Register to be provided directly to it, side-by-side with the Device, instead of feeding the Register through the device. It also allows each Device to be an instance of a PasqalDevice dataclass that, after initialization with the characteristics of a specific device, can be static and immutable (i.e. does not receive the Register).

Get interatomic distance from register in regular arrays.

It can be useful, when calculating statistical properties from the qubits (e.g. Correlation functions), to store the interatomic distance(s) that defined the register, specially if it is some regular array. This could be something like Register.atom_spacing, or some better name.

Special operators for waveforms

I think it would be a nice improvement to have some special operators for waveforms. I came up with these ones:

  • Negation: Doing -wf would return a Waveform with all it's samples' sign flipped
  • Multiplication: Doing wf * 2 would multiply all it's values by 2. It doesn't make sense to multiply two waveforms.

Not so sure these make sense:

  • Addition: Doing wf + 2 would add 2 to all values. wf1 + wf2 would concatenate two waveforms.
  • Absolute value
  • Power
  • Modulo

Feel free to discuss the pertinence of each or add more suggestions.

Change the Branch Protection Rules

Two things to do here:

  • Require at least one approved review to merge a PR
  • Require that all CI tests pass to merge a PR

As far as I can tell, these options can only be set with a Free account once the repository is public. I leave the issue here so that we don't forget to do this once that happens, or in case we decide to upgrade one of the accounts.

Measurement in three-level systems.

We discussed in #61 that sampling from a simulation with the three levels involves finding out how to handle any residual populations in the extra level.

SciPy error when running "Usecase 3" notebook

When running cell 11, I get the following:

/Users/peter/.pyenv/versions/3.8.5/envs/pulser/lib/python3.8/site-packages/scipy/integrate/_ode.py:1011: UserWarning: zvode: Excess work done on this call. (Perhaps wrong MF.)
  warnings.warn('{:s}: {:s}'.format(self.__class__.__name__,
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-11-85845f327bc1> in <module>
     10 
     11 total_duration = max([seq._last(ch).tf for ch in seq._schedule])
---> 12 output = qutip.sesolve(H, psi0, range(total_duration), [observable])

~/.pyenv/versions/3.8.5/envs/pulser/lib/python3.8/site-packages/qutip/sesolve.py in sesolve(H, psi0, tlist, e_ops, args, options, progress_bar, _safe_mode)
    167         func(0., v, *ode_args) + v
    168 
--> 169     res = _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, options,
    170                              progress_bar, dims=psi0.dims)
    171     if e_ops_dict:

~/.pyenv/versions/3.8.5/envs/pulser/lib/python3.8/site-packages/qutip/sesolve.py in _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, opt, progress_bar, dims)
    346         progress_bar.update(t_idx)
    347         if not r.successful():
--> 348             raise Exception("ODE integration error: Try to increase "
    349                             "the allowed number of substeps by increasing "
    350                             "the nsteps parameter in the Options class.")

Exception: ODE integration error: Try to increase the allowed number of substeps by increasing the nsteps parameter in the Options class.

`Sequence.draw()` method can't be plotted when the sequence contains many qubits

When a (local) channel acts on several qubits, there's a maximum number of pulses until the draw() method gives an error.

Here's an example of running 17 pulses on different qubits with duration 500ns:

image

If I now do the same thing with 18 qubits, I get the following error:

image

I don't know if this is necessarily an issue (it might not be a problem in actual applications of PulseR), but I think we should have in mind how far can the draw()method be pushed.

Follow PEP8 style guidelines, use flake8 to enforce in CI

Currently, running flake8 doesn't give many errors:

./pulser/waveforms.py:330:80: E501 line too long (80 > 79 characters)
./pulser/sequence.py:100:80: E501 line too long (80 > 79 characters)
./pulser/sequence.py:178:80: E501 line too long (80 > 79 characters)
./pulser/sequence.py:205:80: E501 line too long (80 > 79 characters)
./pulser/sequence.py:318:80: E501 line too long (80 > 79 characters)
./pulser/__init__.py:3:1: F401 'pulser.pulse.Pulse' imported but unused
./pulser/__init__.py:5:1: F401 'pulser.register.Register' imported but unused
./pulser/__init__.py:7:1: F401 'pulser.sequence.Sequence' imported but unused
./pulser/seq_drawer.py:203:80: E501 line too long (80 > 79 characters)
./pulser/tests/test_sequence.py:62:80: E501 line too long (80 > 79 characters)
./pulser/tests/test_sequence.py:141:80: E501 line too long (80 > 79 characters)

See https://www.python.org/dev/peps/pep-0008/

Add a LICENSE

Before open-sourcing, you'll need to pick what type of license you want to use (e.g. Apache-2.0). After that, do the following:

  1. Put a copy of it in the repository.
  2. Update the setup function in setup.py to tell it what license you chose.
  3. Add include LICENSE to MANIFEST.in.
  4. Take the shortened license and apply it to all the source code files in the repository (see top of this file).

Turn Channel into a Dataclass

Following the adoption of the Dataclass format for Device, it makes sense to do the same for Channel.

I've already started this on branch channel-dataclass, so this issue serves as a reminder for myself or any other to finish the job and write a PR when the time is right.

Add setup.py and requirements.txt

These are essential for any Python project. They describe how to package and install the project, and how to set up a development environment for it in a repeatable way. As such, they are pre-requisites for implementing continuous integration.

Blackman Waveform from maximum reachable value

The duration of a Blackman waveform is often irrelevant and what matters in fact is the maximum value it reaches, for it can influence the Rydberg radius on amplitude waveforms for Rydberg channels.

The proposal here is a class method for the BlackmanWaveform where the defining arguments are the area and the maximum value reached, so the duration of the pulse adapts accordingly.

Serializing a Sequence

In order to send a Sequence over to the QPU, there will need to be some sort of serialization. Although JSON is the obvious choice (mainly because it's the only one I know...), I'm sure there are other options, which might be better suited for the job. What's your take on this, @bejito ?

Another thing to decide here is whether or not we want to deserialize back to a Pulser Sequence. Currently, the translation from Pulser to the QM program is built to take in the Sequence object, so that would be a point in favour of having a mechanism for deserialization. Apart from the work involved (which I would risk saying is not tremendous), do you find any points against building this?

Slow Optimization for Variational processes

The classical loop in optimization process for QAOA takes a long time. Possibly because the whole sequence has to be re-created for each new value of variational parameters.

Chadoq2 -> Fresnel

  • Change the name of "Chadoq2" to "Fresnel"
  • Update the device and its channels' specs

Define quantities and units consistently

Here are some issues with the units and quantities of things:

  1. The usage of MHz and ns is awkward because it requires an adjustment factor of 1e3 every time something like an area is computed. We've seen this error occur in the development of the Simulation module (#26) and it seems prone to confuse the users too.

  2. There is currently some ambiguity in the definition of the Amplitude quantity. Is it the Rabi frequency directly? If so, perhaps we should call it that instead.

  3. It is common to see Rabi frequency (and, to a lesser extent, detuning) values defined up to a factor of 2ฯ€. We should clearly establish if we're doing this or not.

Having access to the blockade radius from the device

My suggestion :

  1. Add the attribute c_6 to PasqalDevice
  2. Add a public method to PasqalDevice taking as an argument an (allowed for that device) value of \Omega in Mhz, and returning the corresponding Rydberg blockade radius in \mu m.

Thoughts ?

Assess the effect of pulse duration restrictions on the tutorials

The restriction of a pulse duration to multiples of 4 ns might have unwanted effects on the results of two tutorials in particular: "Preparing an antiferromagnetic state in the Ising model" and "Tutorial MIS & QAOA".

I kindly ask the main authors, @sebgrijalva and @cdalyac, to verify whether the automatic rounding that is now occurring affects the expected outcomes and, if so, to adapt the notebooks accordingly.

Removing "rydberg_local2" from Chadoq2

With the new mechanism for retargeting local channels, the "rydberg_local2" channel is no longer necessary for CZ gate implementations and I would guess it won't be added to Chadoq2, unless I'm missing something. As such, I was thinking I should remove it. Any objections @lhenriet @sebgrijalva ?

Random initial state

In order to check the stability of the emulator, it could be useful to be able to input a random initial state with a simple caracterisation (say a product state were each atom is in , with r_0 an input from the user)

Complete source code docstrings

Although the majority of the code in master is already documented, the task is not complete. This issue serve as a reminder of such, and can be closed once that objective is met.

Adding the MW channel and specific functionality

Adding the MW channel type for implementation of the XY Hamiltonian. Note that, unlike with the Rydberg and Raman channels, the MW channel cannot coexist with other channel types on the same Sequence, as it requires a specific initialisation procedure and works on a basis that is incompatible with the other channels. This will demand changes to the Sequence and Simulation classes.

Dataclasses vs. ABC + property

Reading through the source, I've noticed some places that I think would be better served by using dataclasses, which are part of the Python standard library as of py37. For example, see the following definition of a PasqalDevice:

class PasqalDevice(ABC):
    def __init__(self, qubits):
        ...

    @property
    @abstractmethod
    def name(self):
        """The device name."""
        pass

    @property
    @abstractmethod
    def max_dimensionality(self):
        """Whether it works at most with a 2D or 3D array (returns 2 or 3)."""
        pass

    @property
    @abstractmethod
    def max_atom_num(self):
        """Maximum number of atoms that can be simultaneously trapped."""
        pass

    @property
    @abstractmethod
    def max_radial_distance(self):
        """Maximum allowed distance from the center of the array."""
        pass

    @property
    @abstractmethod
    def min_atom_distance(self):
        """Minimal allowed distance of atoms in the trap (in um)."""
        pass

    @property
    @abstractmethod
    def channels(self):
        """Channels available on the device."""
        pass

The properties can be much more succinctly defined using a dataclass, as follows:

from dataclasses import dataclass
from typing import Dict

@dataclass
class PQDevice:
    name: str
    max_dimensionality: int
    max_atom_num: int
    max_radial_distance: int
    min_atom_distance: int
    channels: Dict[str, Channel]

There are a couple things that are missing from my dataclass implementation, namely the input parsing and validation that goes into __init__.py, and the definitions of the supported_bases and qubits properties, but these can all be managed by implementing a simple __post_init__ magic method.

As for the ABC part, now instead of defining each device as a subclass of PQDevice, each device would simply be an instantiation (object) of class PQDevice:

Chadoq2 = PQDevice(
    name = "Chadoq2",
    max_dimensionality = 2,
    max_atom_num = 100,
    max_radial_distance = 50,
    min_atom_distance = 4,
    channels = {
            "rydberg_global": Rydberg.Global(50, 2.5),
            "rydberg_local": Rydberg.Local(50, 10, 100),
            "rydberg_local2": Rydberg.Local(50, 10, 100),
            "raman_local": Raman.Local(50, 10, 100),
        },
)

This style removes a lot of boilerplate, and I personally prefer it over abstract classes & properties. Let me know what you think!

Custom bounds on the blockade radius

From my point of view, the user's access to the Rydberg blockade radius is still a little rough. It's strange to start with the register and then not really knowing whether your chosen pulse amplitudes actually give you the connectivity you're hoping for. I though it could be useful to have the option of setting the upper and/or lower bound of the blockade radius.

This would amount to translating the given blockade radius bounds to maximum Rabi frequency values and then filtering the pulses added to Rydberg channels to ensure this requirement is met. I have a feeling this might be a bit too restrictive and that perhaps one might not want all pulses in the Rydberg channels to obey these bounds, in which case the user would have to relax or give up on setting them altogether. For now, this is just an idea, the precise implementation might change.

@lhenriet @sebgrijalva I'm curious to hear your thoughts on this.

Use absolute imports instead of relative imports

The syntax from pulser.devices import PasqalDevice is preferred to from .devices import PasqalDevice. In addition, it is best practice to segment imports into 3 sections: standard library, third-party, and local. It looks like the code currently groups standard library & third-party, and has a second section for local. See this article for more details.

Set up continuous integration using Github Actions

Once some tests have been written (#7) and the project has the necessary setup.py and requirements.txt files (#10), we can set up a continuous integration (CI) pipeline for automatically running the tests upon opening a PR or making a commit to master. There are many tools for CI, but I'd recommend using Github Actions, mostly for convenience. For an example CI workflow, check out the one we wrote for mitiq here.

Cohesive and extensive documentation and tutorials

A barebones setup for the docs has been started in the docs branch but has since gone out-of-sync.

  • Update the docs once the source code reaches (or is close to) its v0 form
  • Structure and write docs
  • Incorporate tutorials

Variable retarget time

To more closely mimic the actual implementation, the retarget times should vary according to the last time a "target" command was added to a channel. The only criteria is that two "target" commands have to be spaced 220 ns. A retarget time is only added to enforce this rule. This will mean, in almost every case, that the target times will go down to 0.

It will have an effect on some tutorials, other than that it should be fine.

@sebgrijalva @lhenriet The only question is: do we want it in Pulser v0 or can we leave it for the next release?

Create a MockDevice

Add a device class that is not tied down to an existing or near-future device, thus giving the user the option to create and test currently unfeasible sequences freely.

This device's specs should have some connection to reality but be very permissive, to the point of being unrealistic for a near-term device but potentially achievable in the somewhat distant future.

Add type hints, and use mypy to enforce in CI

Python is a dynamically-typed language (see: duck typing), but Python 3.5 and pep-484 introduced type hints as a way to annotate Python code with types. These are not enforced by the language at runtime, but they can be checked by third-party libraries such as mypy. The impetus for adding type hints and using mypy is that they generally encourage developers to write saner, simpler code (the flexibility of Python is a double-edged sword). This issue is mostly about good code style and therefore is optional -- it doesn't block CI setup (but once it is complete, the CI can be used to do this type checking automatically).

Add tests

Adding unit tests also at this early development stage will prove useful as it reduces tech debt. Tests could be grouped into a tests folder, divided into modules that mirror the main modules, such as test_pulses.py. Tests can be checked with pytest, which I advise using instead of other packages. Tests should be as simple as possible.

[This first step then can be the starting point for continuous development / continuous integration cycle, to follow. Even without setting up packaging for the installation of Pulser, CI tests with Github Actions or Travis CI can then call pytest from the root folder and check on different operating systems that things work. This would be a different issue though.]

Happy to help, yet they may best be made by whoever wrote the functions or classes in the first place.

Drawing the Rydberg radius

Just an idea that might be useful: we could add the option to draw the Rydberg blockade radius around the atoms in the Register, in typical unit-sphere fashion.

Further on, we could even combine this with a routine to extract the maximum Rabi frequency from a sequence, to automatically determine the Rydberg blockade radius.

Another idea would be to take in the Rydberg radius and spit out the corresponding graph, like we do in MIS problems. Something like a Register.to_graph(blockade_r) method.

What do you think @lhenriet , @sebgrijalva , @cdalyac ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.