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

Pulse simulator doc strings and changing naming of qubit_list #562

Merged
merged 15 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions qiskit/providers/aer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
QasmSimulator
StatevectorSimulator
UnitarySimulator
PulseSimulator

Job Class
=========
Expand All @@ -42,6 +43,15 @@

AerJob

OpenPulse
=========

.. autosummary::
:toctree: ../stubs/

PulseSystemModel
duffing_system_model

Exceptions
==========
.. autosummary::
Expand All @@ -64,6 +74,7 @@
from .aerjob import AerJob
from .aererror import AerError
from .backends import *
from .openpulse import *
from . import noise
from . import utils
from .version import __version__
Expand Down
1 change: 1 addition & 0 deletions qiskit/providers/aer/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
from .qasm_simulator import QasmSimulator
from .statevector_simulator import StatevectorSimulator
from .unitary_simulator import UnitarySimulator
from .pulse_simulator import PulseSimulator
95 changes: 44 additions & 51 deletions qiskit/providers/aer/backends/pulse_simulator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
# (C) Copyright IBM 2018, 2019, 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -32,74 +32,57 @@


class PulseSimulator(AerBackend):
"""
Aer OpenPulse simulator

The `PulseSimulator` simulates pulse `Schedules` on a model of a quantum system, where a model
is specified by a `PulseSystemModel` object, which stores Hamiltonian and control channel
information. Simulation is performed in the rotating frame of the drift Hamiltonian in the
`PulseSystemModel`.

`PulseSystemModel` objects can be constructed from backends with a hamiltonian description, or
from the `transmon_system_model` function.
r"""Aer OpenPulse simulator.

Results are returned in the same format as when jobs are submitted to actual devices.
The ``PulseSimulator`` simulates continuous time Hamiltonian dynamics of a quantum system,
with controls specified by pulse :class:`Schedule` objects, and the model of the physical
system specified by :class:`PulseSystemModel` objects. Results are returned in the same format
as when jobs are submitted to actual devices.

**Example of usage**
**Example**

To use the simulator, `assemble` a `PulseQobj` object from a list of pulse `Schedules`, using
`backend=PulseSimulator()`.

In the following, `schedules` is a list of pulse `Schedule` objects, and `system_model` is a
`PulseSystemModel` object.
To use the simulator, first :meth:`assemble` a :class:`PulseQobj` object
from a list of pulse :class:`Schedule` objects, using ``backend=PulseSimulator()``.
Call the simulator with the :class:`PulseQobj` and a :class:`PulseSystemModel`
object representing the physical system.

.. code-block:: python

backend_sim = qiskit.Aer.get_backend('pulse_simulator')
backend_sim = qiskit.providers.aer.PulseSimulator()

# assemble pulse_qobj with backend=backend_sim
# Assemble schedules using PulseSimulator as the backend
pulse_qobj = assemble(schedules, backend=backend_sim)

# Run simulation
# Run simulation on a PulseSystemModel object
results = backend_sim.run(pulse_qobj, system_model)

**Important parameters**

* `qubit_lo_freq`: The local oscillator frequency for each `DriveChannel`. This can be drawn
from several places, listed in order of importance:
* passed as an argument to `assemble`
* determined from PulseSystemModel attribute `_qubit_freq_est`, if not passed to assemble
* computed from the dressed energy gaps of the drift Hamiltonian

**Measurement and output**
**Supported PulseQobj parameters**

The measurement results are from projections of the state vector in dressed energy basis of
the drift Hamiltonian.
* ``qubit_lo_freq``: Local oscillator frequencies for each :class:`DriveChannel`.
Defaults to either the value given in the :class:`PulseSystemModel`, or
is calculated directly from the Hamiltonian.
* ``meas_level``: Type of desired measurement output, in ``[1, 2]``.
``1`` gives complex numbers (IQ values), and ``2`` gives discriminated states ``|0>`` and
``|1>``. Defaults to ``2``.
* ``meas_return``: Measurement type, ``'single'`` or ``'avg'``. Defaults to ``'avg'``.
* ``shots``: Number of shots per experiment. Defaults to ``1024``.

There are three measurement levels that return the data, specified when using assemble.
Measurement level `0` gives the raw data.
Measurement level `1` gives complex numbers (IQ values).
Measurement level `2` gives the discriminated states, `|0>` and `|1>`.

**Simulation method**
**Simulation details**

The simulator uses the `zvode` differential equation solver method through `scipy`.
The simulator uses the ``zvode`` differential equation solver method through ``scipy``.
Simulation is performed in the rotating frame of the diagonal of the drift Hamiltonian
contained in the :class:`PulseSystemModel`. Measurements are performed in the `dressed basis`
of the drift Hamiltonian.

**Other options**

The `run` function additionally takes an argument `backend_options` for additional
customization. It accepts keys:
:meth:`PulseSimulator.run` takes an additional ``dict`` argument ``backend_options`` for
customization. Accepted keys:

* `'ode_options'`: a dictionary containing options to pass to the `zvode` solver. Accepted keys
for this option are `'atol'`, `'rtol'`, `'nsteps'`, `'max_step'`, `'num_cpus'`, `'norm_tol'`,
and `'norm_steps'`

**Default behaviors**

Defaults filled in for `assemble` parameters if not specified:
* `meas_level`: `2`
* `meas_return`: `'avg'`
* `shots`: `1024`
* ``'ode_options'``: A ``dict`` for ``zvode`` solver options. Accepted keys
are ``'atol'``, ``'rtol'``, ``'nsteps'``, ``'max_step'``, ``'num_cpus'``, ``'norm_tol'``,
and ``'norm_steps'``.
"""

DEFAULT_CONFIGURATION = {
Expand Down Expand Up @@ -133,7 +116,17 @@ def __init__(self, configuration=None, provider=None):
provider=provider)

def run(self, qobj, system_model, backend_options=None, validate=False):
"""Run a qobj on the backend."""
"""Run a qobj on system_model.

Args:
qobj (PulseQobj): Qobj for pulse Schedules to run
system_model (PulseSystemModel): Physical model to run simulation on
backend_options (dict): Other options
validate (bool): Flag for validation checks

Returns:
Result: results of simulation
"""
# Submit job
job_id = str(uuid.uuid4())
aer_job = AerJob(self, job_id, self._run_job, qobj, system_model,
Expand Down
3 changes: 3 additions & 0 deletions qiskit/providers/aer/openpulse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import numpy as np
from .qutip_lite.cy import pyxbuilder as pbldr

from .duffing_model_generators import duffing_system_model
from .pulse_system_model import PulseSystemModel

# Remove -Wstrict-prototypes from cflags
CFG_VARS = distutils.sysconfig.get_config_vars()
if "CFLAGS" in CFG_VARS:
Expand Down
126 changes: 84 additions & 42 deletions qiskit/providers/aer/openpulse/duffing_model_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,50 +27,92 @@ def duffing_system_model(dim_oscillators,
drive_strengths,
coupling_dict,
dt):
"""Returns a PulseSystemModel for a specified Duffing oscillator system.

Note: Frequencies are assumed to be in frequency units (as opposed to radial).

Single oscillators are specified by three parameters: frequency v, anharmonicity alpha, and
drive strength r, which enter into the Hamiltonian model via the terms:
pi*(2*v-alpha)*O + pi*alpha*O*O + 2*pi*r*X*D(t),
where O is the number operator, X is the drive operator, and D(t) is the signal of the
corresponding DriveChannel.

Couplings between oscillators are specified in the argument coupling_dict, with keys being
edges, and values being the coupling strenghts. Couplings enter the Hamiltonian model via an
exchange coupling term:
2*pi*j*(A0*C1+C0*A1),
where j is the coupling strength, and A0*C1+C0*A1 is the exchange coupling operator between the
two oscillators, where A is the annihiliation operator, and C is the creation operator.

For each pair of coupled oscillators, pulse ControlChannels are prepared for doing cross
resonance (CR) drives between oscillators. A CR drive on oscillators 0 with target oscillators
1 is modeled with the Hamiltonian term:
2*pi*r0*X0*U(t),
where r0 is the drive strength for oscillators 0, X0 is the drive operator for oscillators 0,
and U(t) is the signal for the CR drive. U(t) is set to have carrier frequency equal to that of
the target oscillators.

Indices for ControlChannels corresponding to CR drives can be retrieved from the returned
PulseSystemModel using the control_channel_index() method, with keys being tuples. E.g. the
index for the ControlChannel corresponding to a CR drive on oscillator 0 with target 1 is
given by system_model.control_channel_index((0,1)).
r"""Returns a :class:`PulseSystemModel` representing a physical model for a
collection of Duffing oscillators.

Args:
dim_oscillators (int): Dimension of truncation for each oscillator
oscillator_freqs (list): Oscillator frequencies in frequency units
anharm_freqs (list): Anharmonicity values in frequency units
drive_strengths (list): Drive strength values in frequency units
coupling_dict (dict): Specification of the coupling graph with keys being edges, and values
the coupling strengths in frequency units.
In the model, each individual oscillator is specified by the parameters:

* Frequency: :math:`\nu`, specified in the list ``oscillator_freqs``
* Anharmonicity: :math:`\alpha`, specified in the list ``anharm_freqs``, and
* Drive strength: :math:`r`, specified in the list ``drive_strengths``.

For each oscillator, the above parameters enter into the Hamiltonian via the terms:

.. math::
\pi(2 \nu - \alpha)a^\dagger a +
\pi \alpha (a^\dagger a)^2 + 2 \pi r D(t) (a + a^\dagger),

where :math:`a^\dagger` and :math:`a` are, respectively, the creation and annihilation
operators for the oscillator, and :math:`D(t)` is the drive signal for the oscillator.

Each coupling term between a pair of oscillators is specified by:

* Oscillator pair: :math:`(i,k)`, and
* Coupling strength: :math:`j`,

which are passed in the argument ``coupling_dict``, which is a ``dict`` with keys
being the ``tuple`` ``(i,k)``, and values the strength ``j``. Specifying a coupling
results in the Hamiltonian term:

.. math::
2 \pi j (a_i^\dagger a_k + a_i a_k^\dagger).

Finally, the returned :class:`PulseSystemModel` is setup for performing cross-resonance
drives between coupled qubits. The index for the :class:`ControlChannel` corresponding
to a particular cross-resonance drive channel is retreived by calling
:meth:`PulseSystemModel.control_channel_index` with the tuple ``(drive_idx, target_idx)``,
where ``drive_idx`` is the index of the oscillator being driven, and ``target_idx`` is
the target oscillator (see example below).

Note: In this model, all frequencies are in frequency units (as opposed to radial).

**Example**

Constructing a three Duffing Oscillator :class:``PulseSystemModel``.

For example:
.. code-block:: python

* ``{(0,1): 0.02, (1,3): 0.01}`` specifies a system in which
oscillators (0,1) are coupled with strength 0.02, and (1,3) are
coupled with strength 0.01
dt (float): Sample width for pulse instructions
# cutoff dimensions
dim_oscillators = 3

# single oscillator drift parameters
oscillator_freqs = [5.0e9, 5.1e9, 5.2e9]
anharm_freqs = [-0.33e9, -0.33e9, -0.33e9]

# drive strengths
drive_strengths = [0.02e9, 0.02e9, 0.02e9]

# specify coupling as a dictionary; here the qubit pair (0,1) is coupled with
# strength 0.002e9, and the qubit pair (1,2) is coupled with strength 0.001e9
coupling_dict = {(0,1): 0.002e9, (1,2): 0.001e9}

# time
dt = 1e-9

# create the model
three_qubit_model = duffing_system_model(dim_oscillators=dim_oscillators,
oscillator_freqs=oscillator_freqs,
anharm_freqs=anharm_freqs,
drive_strengths=drive_strengths,
coupling_dict=coupling_dict,
dt=dt)

In the above model, qubit pairs (0,1) and (1,2) are coupled. To perform a
cross-resonance drive on qubit 1 with target 0, use the :class:`ControlChannel`
with index:

.. code-block:: python

three_qubit_model.control_channel_index((1,0))

Args:
dim_oscillators (int): Dimension of truncation for each oscillator.
oscillator_freqs (list): Oscillator frequencies in frequency units.
anharm_freqs (list): Anharmonicity values in frequency units.
drive_strengths (list): Drive strength values in frequency units.
coupling_dict (dict): Coupling graph with keys being edges, and values
the coupling strengths in frequency units.
dt (float): Sample width for pulse instructions.

Returns:
PulseSystemModel: The generated Duffing system model
Expand Down Expand Up @@ -127,7 +169,7 @@ def duffing_system_model(dim_oscillators,
return PulseSystemModel(hamiltonian=hamiltonian_model,
u_channel_lo=u_channel_lo,
control_channel_labels=coupling_graph.sorted_two_way_graph,
qubit_list=oscillators,
subsystem_list=oscillators,
dt=dt)


Expand Down
Loading