Skip to content

Commit

Permalink
Merge branch 'main' into support-sparsepauliop
Browse files Browse the repository at this point in the history
  • Loading branch information
splch committed May 31, 2024
2 parents f4148bd + 69b1627 commit 4e95b5b
Show file tree
Hide file tree
Showing 27 changed files with 858 additions and 218 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.8, 3.9, 3.11]
python-version: ["3.9", "3.10", "3.11"]
os: ["macOS-latest", "ubuntu-latest", "windows-latest"]
steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
First read the overall project contributing guidelines. These are all
included in the qiskit documentation:

https://qiskit.org/documentation/contributing_to_qiskit.html
https://github.com/Qiskit/qiskit/blob/main/CONTRIBUTING.md

## Contributing to Qiskit IonQ Provider

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ print(job.result().get_probabilities())

### Basis gates and transpilation

The IonQ provider provides access to the full IonQ Cloud backend, which includes its own transpilation and compilation pipeline. As such, IonQ provider backends have a broad set of "basis gates" that they will accept — effectively anything the IonQ API will accept. They are `ccx, ch, cnot, cp, crx, cry, crz, csx, cx, cy, cz, h, i, id, mcp, mct, mcx, measure, p, rx, rxx, ry, ryy, rz, s, sdg, swap, sx, sxdg, t, tdg, toffoli, x, y` and `z`.
The IonQ provider provides access to the full IonQ Cloud backend, which includes its own transpilation and compilation pipeline. As such, IonQ provider backends have a broad set of "basis gates" that they will accept — effectively anything the IonQ API will accept. The current supported gates can be found [on our docs site](https://docs.ionq.com/#tag/quantum_programs).

If you have circuits that you'd like to run on IonQ backends that use other gates than this (`u` or `iswap` for example), you will either need to manually rewrite the circuit to only use the above list, or use the Qiskit transpiler, per the example below. Please note that not all circuits can be automatically transpiled.

Expand Down
14 changes: 0 additions & 14 deletions docs/_templates/theme_variables.jinja

This file was deleted.

30 changes: 15 additions & 15 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
Expand Down Expand Up @@ -43,13 +42,14 @@

"""Sphinx doc build configuration."""
import qiskit_sphinx_theme
from qiskit_ionq.version import VERSION_INFO

# -- Project information -----------------------------------------------------

project = "Qiskit IonQ Provider" # pylint: disable=invalid-name
copyright = "2020, IonQ, Inc." # pylint: disable=invalid-name,redefined-builtin
author = "IonQ, Inc." # pylint: disable=invalid-name
release = "0.0.1" # pylint: disable=invalid-name
release = VERSION_INFO # pylint: disable=invalid-name


# -- General configuration ---------------------------------------------------
Expand All @@ -58,15 +58,15 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.napoleon',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
'sphinx.ext.extlinks',
'jupyter_sphinx',
'sphinx_panels',
'qiskit_sphinx_theme',
"sphinx.ext.napoleon",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.mathjax",
"sphinx.ext.viewcode",
"sphinx.ext.extlinks",
"jupyter_sphinx",
"sphinx_panels",
"qiskit_sphinx_theme",
]

# Add any paths that contain templates here, relative to this directory.
Expand All @@ -75,7 +75,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', '**.ipynb_checkpoints']
exclude_patterns = ["_build", "**.ipynb_checkpoints"]


# -- Options for HTML output -------------------------------------------------
Expand All @@ -89,13 +89,13 @@
autosummary_generate_overwrite = False

autodoc_default_options = {
'inherited-members': None,
"inherited-members": None,
}

autoclass_content = 'both'
autoclass_content = "both"
#
# Sphinx doc mappings
intersphinx_mapping = {
"qiskit-terra": ("https://qiskit.org/documentation/", None),
"qiskit-terra": ("https://docs.quantum.ibm.com/api/qiskit/", None),
"requests": ("https://requests.readthedocs.io/en/master/", None),
}
4 changes: 2 additions & 2 deletions example/example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
"\n",
"# add gates — here we're creating a simple bell pair\n",
"bell_qc.h(0)\n",
"bell_qc.cnot(0,1)\n",
"bell_qc.cx(0,1)\n",
"bell_qc.measure([0,1],[0,1])\n",
"\n",
"# draw the circuit to make sure it's what we expect\n",
Expand Down Expand Up @@ -169,7 +169,7 @@
"That said, there are a few things to note when running on IonQ backends: \n",
"1. IonQ backends do not allow arbitrary unitaries, mid-circuit resets or measurements, or multi-experiment jobs. In practice, this means using `reset`, `initialize`, `u` `u1`, `u2`, `u3`, `cu`, `cu1`, `cu2`, or `cu3` gates will throw an exception on submission, as will measuring mid-circuit, and submmitting jobs with multiple experiments.\n",
"2. while `barrier` is allowed for organizational and visualization purposes, the IonQ compiler does not see it as a compiler directive.\n",
"3. For the unitaries (`u`, `u1`, etc) and some other custom gates that [include appropriate decompositions](https://qiskit.org/documentation/stubs/qiskit.circuit.Instruction.html#qiskit.circuit.Instruction.add_decomposition), you can use the `transpile` method to rewrite your circuit into basis gates that the IonQ backends will take. See the example below.\n",
"3. For the unitaries (`u`, `u1`, etc) and some other custom gates that [include appropriate decompositions](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.Instruction#add_decomposition), you can use the `transpile` method to rewrite your circuit into basis gates that the IonQ backends will take. See the example below.\n",
"4. The IonQ simulator API natively produces ideal probabilities based on a statevector simulation, not counts. For ease of interoperability and portability, this package uses a simple random sampling to produce \"counts,\" and also exposes the true probabilities. The code samples below show this in more detail."
]
},
Expand Down
11 changes: 11 additions & 0 deletions qiskit_ionq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@

"""Provider for IonQ backends"""

import warnings

# warn if qiskit is not installed
try:
from qiskit.version import get_version_info # pylint: disable=unused-import
except ImportError as exc:
raise ImportError(
"Qiskit is not installed. Please install the latest version of Qiskit."
) from exc

from .ionq_provider import IonQProvider
from .version import __version__
from .ionq_gates import GPIGate, GPI2Gate, MSGate, ZZGate
from .constants import ErrorMitigation
from .ionq_equivalence_library import add_equivalences
2 changes: 1 addition & 1 deletion qiskit_ionq/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class IonQRetriableError(IonQError):

def __init__(self, cause):
self._cause = cause
super().__init__(getattr(cause, 'message', 'Unknown error'))
super().__init__(getattr(cause, "message", "Unknown error"))


# pylint: disable=no-member
Expand Down
79 changes: 50 additions & 29 deletions qiskit_ionq/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
# not the actual hardware basis gates for the system — we do our own transpilation pass.
# also not an exact/complete list of the gates IonQ's backend takes
# by name — please refer to IonQ docs for that.
#
# Some of these gates may be deprecated or removed in qiskit 1.0
ionq_basis_gates = [
"ccx",
"ch",
Expand Down Expand Up @@ -318,37 +320,36 @@ def get_register_sizes_and_labels(registers):
return sizes, labels


def compress_dict_to_metadata_string(metadata_dict): # pylint: disable=invalid-name
def compress_to_metadata_string(metadata): # pylint: disable=invalid-name
"""
Convert a dict to a compact string format (dumped, gzipped, base64 encoded)
Convert a metadata object to a compact string format (dumped, gzipped, base64 encoded)
for storing in IonQ API metadata
Parameters:
metadata_dict (dict): a dict with metadata relevant to building the results
object on a returned job.
metadata (dict or list): a dict or list of dicts with metadata relevant
to building the results object on a returned job.
Returns:
str: encoded string
"""
serialized = json.dumps(metadata_dict)
serialized = json.dumps(metadata)
compressed = gzip.compress(serialized.encode("utf-8"))
encoded = base64.b64encode(compressed)
encoded_string = encoded.decode()
return encoded_string
return encoded.decode()


def decompress_metadata_string_to_dict(input_string): # pylint: disable=invalid-name
def decompress_metadata_string(input_string): # pylint: disable=invalid-name
"""
Convert compact string format (dumped, gzipped, base64 encoded) from
IonQ API metadata back into a dict relevant to building the results object
on a returned job.
IonQ API metadata back into a dict or list of dicts relevant to building
the results object on a returned job.
Parameters:
input_string (str): compressed string format of metadata dict
Returns:
dict: decompressed metadata dict
dict or list: decompressed metadata dict or list of dicts
"""
if input_string is None:
return None
Expand Down Expand Up @@ -376,24 +377,39 @@ def qiskit_to_ionq(
passed_args = passed_args or {}
extra_query_params = extra_query_params or {}
extra_metadata = extra_metadata or {}
ionq_circ, _, meas_map = qiskit_circ_to_ionq_circ(circuit, backend.gateset())
creg_sizes, clbit_labels = get_register_sizes_and_labels(circuit.cregs)
qreg_sizes, qubit_labels = get_register_sizes_and_labels(circuit.qregs)
qiskit_header = compress_dict_to_metadata_string(
ionq_circs = []
multi_circuit = False
if isinstance(circuit, (list, tuple)):
multi_circuit = True
for circ in circuit:
ionq_circ, _, meas_map = qiskit_circ_to_ionq_circ(circ, backend.gateset())
ionq_circs.append((ionq_circ, meas_map))
else:
ionq_circs, _, meas_map = qiskit_circ_to_ionq_circ(circuit, backend.gateset())
circuit = [circuit]

metadata_list = [
{
"memory_slots": circuit.num_clbits, # int
"global_phase": circuit.global_phase, # float
"n_qubits": circuit.num_qubits, # int
"name": circuit.name, # str
"memory_slots": circ.num_clbits, # int
"global_phase": circ.global_phase, # float
"n_qubits": circ.num_qubits, # int
"name": circ.name, # str
# list of [str, int] tuples cardinality memory_slots
"creg_sizes": creg_sizes,
"creg_sizes": get_register_sizes_and_labels(circ.cregs)[0],
# list of [str, int] tuples cardinality memory_slots
"clbit_labels": clbit_labels,
"clbit_labels": get_register_sizes_and_labels(circ.cregs)[1],
# list of [str, int] tuples cardinality num_qubits
"qreg_sizes": qreg_sizes,
"qreg_sizes": get_register_sizes_and_labels(circ.qregs)[0],
# list of [str, int] tuples cardinality num_qubits
"qubit_labels": qubit_labels,
"qubit_labels": get_register_sizes_and_labels(circ.qregs)[1],
# custom metadata from the circuits
**({"metadata": circ.metadata} if circ.metadata else {}),
}
for circ in circuit
]

qiskit_header = compress_to_metadata_string(
metadata_list if multi_circuit else metadata_list[0]
)

target = backend.name()[5:] if backend.name().startswith("ionq") else backend.name()
Expand All @@ -407,21 +423,26 @@ def qiskit_to_ionq(
ionq_json = {
"target": target,
"shots": passed_args.get("shots"),
"name": circuit.name,
"name": ", ".join([c.name for c in circuit]),
"input": {
"format": "ionq.circuit.v0",
"gateset": backend.gateset(),
"qubits": circuit.num_qubits,
"circuit": ionq_circ,
"qubits": max(c.num_qubits for c in circuit),
},
"registers": {"meas_mapped": meas_map} if meas_map else {},
# store a couple of things we'll need later for result formatting
"metadata": {
"shots": str(passed_args.get("shots")),
"sampler_seed": str(passed_args.get("sampler_seed")),
"qiskit_header": qiskit_header,
},
}
if multi_circuit:
ionq_json["input"]["circuits"] = [
{"circuit": c, "registers": {"meas_mapped": m}} for c, m in ionq_circs
]
else:
ionq_json["input"]["circuit"] = ionq_circs
ionq_json["registers"] = {"meas_mapped": meas_map} if meas_map else {}
if target == "simulator":
ionq_json["noise"] = {
"model": passed_args.get("noise_model") or backend.options.noise_model,
Expand Down Expand Up @@ -487,7 +508,7 @@ def default(self, o):
__all__ = [
"qiskit_to_ionq",
"qiskit_circ_to_ionq_circ",
"compress_dict_to_metadata_string",
"decompress_metadata_string_to_dict",
"compress_to_metadata_string",
"decompress_metadata_string",
"get_user_agent",
]
Loading

0 comments on commit 4e95b5b

Please sign in to comment.