Skip to content

Commit

Permalink
Add instructions on how to create a provider (Qiskit/qiskit-metapacka…
Browse files Browse the repository at this point in the history
…ge#131)

* moved Aer tutorial images to /docs/images/figures/

and all references to these images

* Have Makefile point to correct Aqua and Chemistry directories

Fixes Qiskit/qiskit-metapackage#88

* Revert "Have Makefile point to correct Aqua and Chemistry directories"

This reverts commit fee13ba87e1888c88a997430ee0a57969a9985ac.

* Create community_qiskit.rst

* Revert "Create community_qiskit.rst"

This reverts commit 5fd2cafd6d2e570488e9985f26d33a0f5088c581.

* update global install instructions

Motivation: to synthesize the mostly duplicate install instructions currently under each Qiskit element's doc section, which can then be removed.

Direct to CONTRIBUTING files in each element's repo for instructions to build from source, since this serves a niche persona and is lengthy.

Added images for steps that involve pointing and clicking through the web.

Rewrote material for additional clarity.

* removed Troubleshooting section

fixes Qiskit/qiskit-metapackage#97

* simplify the ibm q provider documentation

Fixes Qiskit/qiskit-metapackage#86

* removed Visualizations from global ToC, honed Installing QIskit

Fixes Qiskit/qiskit-metapackage#107 and fixes Qiskit/qiskit-metapackage#95

* added Contributin to Qiskit

fixes Qiskit/qiskit-metapackage#112

* remove Aer install instructions

fixes Qiskit/qiskit-metapackage#84

* Have the ToC do the work, instead of a landing page for Getting Started.

fixes Qiskit/qiskit-metapackage#84

* circuit drawing style in Getting Started

begins to address Qiskit/qiskit-metapackage#98

* adopting the term of art 'quantum cloud services'

* getting_started references wrong circuit drawing PNG file

Fixes Qiskit/qiskit-metapackage#129

* Add instructions on how to create a provider

fixes  Qiskit/qiskit-metapackage#123

* fix a typo in contributing_to_qiskit.rst

* update instructions on how to create a provider

addressing review by @yaelbh

* ipython3 to python code blocks
  • Loading branch information
derivation authored Feb 21, 2019
1 parent bf47f87 commit dfcba8f
Showing 1 changed file with 335 additions and 0 deletions.
335 changes: 335 additions & 0 deletions docs/contributing_to_qiskit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,338 @@ The Python API reference documentation is automatically generated from comments
.. code:: sh
make doc
Creating a Custom Provider
--------------------------

This example discusses how to approach the design and implementation of
a Qiskit provider, described in `Advanced Use of IBM Q
Devices <https://qiskit.org/documentation/advanced_use_of_ibm_q_devices.html>`__.
The objective of the provider in this example is to simulate circuits
made up entirely of Hadamard gates terminating with measurement of all
qubits.



Designing the Backend Class
^^^^^^^^^^^^^^^^^^^^^^^^^^^

To design a provider that simulates Hadamard gates, first determine the
elements of data and operations that form your *backend*, a simulator
(in this example) or real quantum computer responsible for running
circuits and returning results. A backend has at least these properties:

- ``name``
- ``configuration``
- ``status``
- ``provider``

You must be able to perform certain operations on a backend:

- Translate a circuit, described by a ``qobj`` (a quantum object) into
a form expected by your simulator
- Run your simulator
- Return a ``BaseJob`` object that contains the results of your
simulator

The ``run()`` method implements these operations.



**Implementing the Backend Class**

.. code:: python
from qiskit.providers import BaseBackend
from qiskit.providers.models import BackendConfiguration
from qiskit import qobj as qiskit_qobj
from qiskit.result import Result
# Inherit from qiskit.providers.BaseBackend
class HadamardSimulator(BaseBackend):
def __init__(self, provider=None):
configuration = {
'backend_name': 'hadamard_simulator',
'backend_version': '0.1.0',
'url': 'http://www.i_love_hadamard.com',
'simulator': True,
'local': True,
'description': 'Simulates only Hadamard gates',
# basis_gates must contain at least two gates
'basis_gates': ['h', 'x'],
'memory': True,
'n_qubits': 30,
'conditional': False,
'max_shots': 100000,
'open_pulse': False,
'gates': [
{
'name': 'TODO',
'parameters': [],
'qasm_def': 'TODO'
}
]
}
# The provider will be explained in a section below
super().__init__(
configuration=BackendConfiguration.from_dict(
configuration),
provider=provider)
def run(self, qobj):
# The job object will be explained in a section below
hadamard_job = HadamardJob(None)
# Simulate each circuit described by the qobj
experiment_results = []
for circuit_index, circuit \
in enumerate(qobj.experiments):
number_of_qubits = circuit.config.n_qubits
shots = qobj.config.shots
# Need to ensure that the circuit described by qobj
# only has gates our simulator can handle.
# We take this for granted here.
list_of_qubits = []
for operation in circuit.instructions:
if operation.name == 'h':
list_of_qubits.append(operation.qubits[0])
# Need to verify that all the qubits are measured,
# and to different classical registers.
# We take this for granted here.
# Run the Hadamard simulator, discussed below
counts = run_hadamard_simulator(number_of_qubits,
list_of_qubits, shots)
# Format results for printing
formatted_counts = {}
for i in range(2**number_of_qubits):
if counts[i] != 0:
formatted_counts[hex(i)] = counts[i]
experiment_results.append({
'name': circuit.header.name,
'success': True,
'shots': shots,
'data': {'counts': formatted_counts},
'header': circuit.header.as_dict()
})
# Return the simulation results in the job object
hadamard_job._result = Result.from_dict({
'results': experiment_results,
'backend_name': 'hadamard_simulator',
'backend_version': '0.1.0',
'qobj_id': '0',
'job_id': '0',
'success': True
})
return hadamard_job
Designing the Job Class
^^^^^^^^^^^^^^^^^^^^^^^

Job instances can be thought of as the “ticket” for a submitted job.
They find out the execution’s state at a given point in time (for
example, if the job is queued, running, or has failed) and also allow
control over the job.

The ``HadamardJob`` class stores information about itself and the
simulation results in the following properties:

- ``job_id``
- ``backend`` - The backend the job was run on

The ``HadamardJob`` class performs the following operations:

- ``result`` - get the result of a ``run`` on the backend
- ``status``
- ``cancel``
- ``submit``

In this example, we will only implement a method for the `result` operation.

**Implementing the Job Class**

Define a simple implementation of a job class that can merely return the
simulation results.

.. code:: python
from qiskit.providers import BaseJob
# Inherits from qiskit.providers.BaseJob
class HadamardJob(BaseJob):
def __init__(self, backend):
super().__init__(backend, 1)
def result(self):
return self._result
def cancel(self):
pass
def status(self):
pass
def submit(self):
pass
Designing the Provider Class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A provider is an entity that gives access to a group of different
backends. A provider must be able to

- return all backends known to it
- return a backend queried by name

The ``HadamardProvider`` class implements two methods:

- ``backends`` - Method that lists all known backends
- ``get_backend`` - Method that returns backends by name.



**Implementing the Provider Class**

.. code:: python
from qiskit.providers import BaseProvider
from qiskit.providers.providerutils import filter_backends
# Inherits from qiskit.providers.BaseProvider
class HadamardProvider(BaseProvider):
def __init__(self, *args, **kwargs):
super().__init__(args, kwargs)
# Populate the list of Hadamard backends
self._backends = [HadamardSimulator(provider=self)]
def get_backend(self, name=None, **kwargs):
return super().get_backend(name=name, **kwargs)
def backends(self, name=None, filters=None, **kwargs):
# pylint: disable=arguments-differ
backends = self._backends
if name:
backends = [backend for backend in backends
if backend.name() == name]
return filter_backends(
backends, filters=filters, **kwargs)
def __str__(self):
return 'HadamardProvider'
Implementing a Custom Simulator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The simulator accepts only a single quantum circuit, where all the gates
are Hadamard gates, and all qubits are measured at the end. The input
format is a list of qubits on whom Hadamard gates are applied. The
simulator returns the counts of each basis state, in the form of a list,
where the basis states are assumed to be ordered lexicographically.

.. code:: python
def run_hadamard_simulator(number_of_qubits, list_of_qubits, shots):
# For each qubit, store whether it is manipulated
# by an odd number of Hadamard gates
# Example: for run_hadamard_simulator(5, [3, 1, 3, 4], 100)
# we obtain hadamard_list:
# [0, 1, 0, 0, 1]
# because qubits 1 and 4 have
# an odd number of Hadamard gates.
hadamard_list = [0]*number_of_qubits
for qubit in list_of_qubits:
hadamard_list[qubit] = (1 + hadamard_list[qubit])%2
# Calculate the result for each basis state
result = [0]*(2**number_of_qubits)
for i in range(2**number_of_qubits):
# Example: when i is 2,
# the basis_state is 01000
basis_state = \
'{0:b}'.format(i).zfill(number_of_qubits)[::-1]
for qubit in range(number_of_qubits):
if (hadamard_list[qubit] == 0
and basis_state[qubit] == '1'):
result[i] = 0
break
if hadamard_list[qubit] == 1:
result[i] += int(
shots/(2**(1 + hadamard_list.count(1))))
return result
Using Custom Providers
^^^^^^^^^^^^^^^^^^^^^^

The following code runs two simulators on the same quantum circuit. The
simulators are accessed by their providers.

.. code:: python
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer
from qiskit.transpiler import PassManager
# Create a circuit with just Hadamards and measurements
qreg = QuantumRegister(4)
creg = ClassicalRegister(4)
qc = QuantumCircuit(qreg, creg)
qc.h(qreg[3])
qc.h(qreg[1])
qc.h(qreg[3])
qc.h(qreg[2])
qc.measure(qreg, creg)
# Use the custom provider to simulate the circuit
hadamard_provider = HadamardProvider()
hadamard_job = execute(qc, hadamard_provider.get_backend('hadamard_simulator'), pass_manager=PassManager(), shots=1024)
hadamard_result = hadamard_job.result()
# Use an Aer provider to compare and contrast
aer_job = execute(qc, Aer.get_backend('qasm_simulator'),
pass_manager=PassManager(), shots=1024)
aer_result = aer_job.result()
# Print the results of both providers
print('Hadamard simulator:')
print(hadamard_result.get_counts(qc))
print('Aer simulator:')
print(aer_result.get_counts(qc))
.. parsed-literal::
Hadamard simulator:
{'0100': 256, '0000': 256, '0010': 256, '0110': 256}
Aer simulator:
{'0100': 266, '0000': 252, '0010': 233, '0110': 273}

0 comments on commit dfcba8f

Please sign in to comment.