Skip to content

Commit

Permalink
Add initial support for working with ion-trap devices (#3214)
Browse files Browse the repository at this point in the history
* Add partial transpiler support for targeting ion trap backends. (#3122)

* handle reset/barrier/measure (#3130)

* Do not unroll an Rx,Ry,Rxx circuit to U3,CX because of measure. (#3140)

* rebase ion-trap branch (#3153)

* Remove changelog and document automated creation (#3109)

* Remove changelog and document automated creation

With the introduction of the qiskit-bot release automation for terra we
no longer need to manually maintain or keep a changelog in repo. The
github release pages (which we were manually copy and pasting the
changelog into already) take the place of this file. For all future
releases the creation of this page is completely automatic and handled
by the bot. This works by using the git log and PR tags to figure out
which commits belong in the changelog summary. The commit msg summary
(the first line) is used for the changelog based on the tags. If a
longer explanation is necessary that should be handled in the reno
release notes.

Fixes #3077

* Add README section on changelog

* Make tweaks to readme for clarity

* The option fig.tight_layout is triggering warnings in some situations so it is being removed (#3123)

* no fig.tight_layout

* removing the rest of fig.tight_layout()

* check casteable types in Layout  (#3100)

* test

* cast in insinstance

* style

* layouts are already layouts :)

* Travis CI: The sudo: tag is deprecated on Travis (#3134)

https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration

* Fix a syntax error in QuantumCircuit.mirror. (#3138)

* Fix for #3106 gatefacecolor key on circuit diagrams (#3137)

* Issue #3106 gatefacecolor key has no effect on circuit diagrams

If the style property "gatefacecolor" is set to a different value than the default, this color is applied.

Fixes #3106

* Solved style problem

space before : eliminated

* Update plot_state_hinton() formatting (#3093)

Add axes to the image part. It looks like the image axes already exist for non-empty graphs. The change that I made here is to remove if np.any(dataimag != 0): if the desired behavior is to see the axes on graphs that don't contain any elements.

Replace rho with the latex rho

Use standard format for real and imag parts of rho

Fixes #2321

* Set barrier across multi-qubits correctly (#3042)

* Previously it was assumed that specified barrier has
  a single contiguous span across qubits. Qubits for the
  barrier need not be adjacent in the diagram. This commit
  handles this case by adding barrier across correct qubits.

* compute multiple short spans.

Fixes #2918

* Test pulse clipping fix Osx #1. (#3144)

* Remove superfluous reference to timeslots from assemble_schedules (#3139)

* Extend custom multiqubit gates over classical bits for mpl (#3062)

* Got it to extend over the clasical bits

* Fixed a small bug

* Removed a file accidentally modified

* Fix linter

* Deprecate allowing extra keys in circuit drawer style dict (#3105)

* test

* cast in insinstance

* style

* layouts are already layouts :)

* style['plotbarrier'] is being deprecated

* internal param for text drawer renamed to plot_barriers

* error when style contains unknown options

* better error message

* remove #3100, since it was not my intention to be on it

* style

* remove plotbarrier

* unused import

* replace error with warning

* release note

* rochester_layout (#3125)

* Use _append when args already converted and broadcast. (#2936)

* Remove line in init which had been commented out (#3150)

* Fix circuit drawing justification (#3061)

Fixes #2802

qiskit/visualization/utils.py _get_layered_instructions() was erroneous and did not justify left nor right correctly. I have reworked that code which underlies many visual representations of circuits to the user.

* refactored, needs more work on right justification

* Looks like this works for qiskit-terra issue #2802

* May fix first test failure for this pull request, while causing another?

* Draws conditionals on ClassicalRegister correctly now.

* - Occurs check for gate in gates leads to good test results.
- Added a few more self.maxDiff = None while debugging. Should they be pulled out?

* tabs and trailing spaces ;)

* removed the maxDiff's because they make the Style and lint test in Python 3.5 fail

* added tests
modified changelog

* Test case extended to include original issue reported in #2802

* oops, left a maxDiff=None in from testing. Fixed

* changes to utils.py requested by reviewer maddy-tod

* test cases split as requested by reviewer maddy-tod

* added circuit diagrams in comments to new tests

* removed note from changelog and made it a release note per @mtreinish

* made _LayerSpooler a private class

* Revert "Remove changelog and document automated creation (#3109)"

This reverts commit effad12.

* Make _LayerSpooler a subclass of list

The _LayerSpooler class was only ever used as list. While the class was
an independent type it basically just wrapped an internal list. THen
there were helper functions to access and manipulate that list. These
methods just duplicated what python already provides and the list class
itself already implements. This commit fixes this so the _LayerSpooler
itself is a subclass of list so when it's initialized you can just treat
it as the output list it was generating.

* Clean up release note, fix only, not prelude

* un reverted my revert of CONTRIBUTING.md

* refactored, needs more work on right justification

* Looks like this works for qiskit-terra issue #2802

* May fix first test failure for this pull request, while causing another?

* Draws conditionals on ClassicalRegister correctly now.

* - Occurs check for gate in gates leads to good test results.
- Added a few more self.maxDiff = None while debugging. Should they be pulled out?

* tabs and trailing spaces ;)

* removed the maxDiff's because they make the Style and lint test in Python 3.5 fail

* added tests
modified changelog

* Test case extended to include original issue reported in #2802

* oops, left a maxDiff=None in from testing. Fixed

* changes to utils.py requested by reviewer maddy-tod

* test cases split as requested by reviewer maddy-tod

* added circuit diagrams in comments to new tests

* removed note from changelog and made it a release note per @mtreinish

* made _LayerSpooler a private class

* Make _LayerSpooler a subclass of list

The _LayerSpooler class was only ever used as list. While the class was
an independent type it basically just wrapped an internal list. THen
there were helper functions to access and manipulate that list. These
methods just duplicated what python already provides and the list class
itself already implements. This commit fixes this so the _LayerSpooler
itself is a subclass of list so when it's initialized you can just treat
it as the output list it was generating.

* Clean up release note, fix only, not prelude

* put back missing section

* Add partial transpiler support for targeting ion trap backends. (#3122)

* handle reset/barrier/measure (#3130)

* Do not unroll an Rx,Ry,Rxx circuit to U3,CX because of measure. (#3140)

* Add global Molmer-Sorenson gate (#3149)

* add global ms gate

* clean up

* Update qiskit/extensions/standard/ms.py

Co-Authored-By: Kevin Krsulich <kevin@krsulich.net>

* Update qiskit/extensions/standard/ms.py

Co-Authored-By: Kevin Krsulich <kevin@krsulich.net>

* clean up

* Update qiskit/extensions/standard/ms.py

Co-Authored-By: Lauren Capelluto <laurencapelluto@gmail.com>

* Update MS docstring
  • Loading branch information
kdk authored Oct 11, 2019
1 parent 6e7cde4 commit 5270887
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 0 deletions.
1 change: 1 addition & 0 deletions qiskit/extensions/standard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@
from .cu3 import Cu3Gate
from .rzz import RZZGate
from .rxx import RXXGate
from .ms import MSGate
59 changes: 59 additions & 0 deletions qiskit/extensions/standard/ms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# 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
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=invalid-name

"""
Global Mølmer–Sørensen gate.
The Mølmer–Sørensen gate is native to ion-trap systems. The global MS can be
applied to multiple ions to entangle multiple qubits simultaneously.
In the two-qubit case, this is equivalent to an XX(theta) interaction,
and is thus reduced to the RXXGate.
"""


from qiskit.circuit import Gate
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import QuantumRegister
from qiskit.extensions.standard.rxx import RXXGate


class MSGate(Gate):
"""Global Molmer-Sorensen gate."""

def __init__(self, n_qubits, theta):
"""Create new MS gate."""
super().__init__("ms", n_qubits, [theta])

def _define(self):
definition = []
q = QuantumRegister(self.num_qubits, "q")
rule = []
for i in range(self.num_qubits):
for j in range(i+1, self.num_qubits):
rule += [(RXXGate(self.params[0]), [q[i], q[j]], [])]

for inst in rule:
definition.append(inst)
self.definition = definition


def ms(self, theta, qubits):
"""Apply MS to q1 and q2."""
return self.append(MSGate(len(qubits), theta), qubits)


QuantumCircuit.ms = ms
102 changes: 102 additions & 0 deletions qiskit/transpiler/passes/ms_basis_decomposer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# 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
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Pass for converting a circuit targeting U3,CX basis to Rx,Ry,Rxx."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.exceptions import QiskitError

from qiskit.converters import circuit_to_dag
from qiskit.extensions.standard import U3Gate, CnotGate

from qiskit.transpiler.passes import Unroller
from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer
from qiskit.quantum_info.synthesis.ion_decompose import cnot_rxx_decompose


class MSBasisDecomposer(TransformationPass):
"""
Convert a circuit in U3,CX to Rx,Ry,Rxx without unrolling or simplification.
"""

supported_input_gates = (U3Gate, CnotGate)
supported_basis_names = ('rx', 'ry', 'rxx', 'ms')

def __init__(self, basis):
"""
Args:
basis (list[str]): Target basis names, e.g. `['rx', 'ry', 'rxx', 'ms']` .
Raises:
QiskitError: if target basis is not [ 'rx', 'ry', 'rxx', 'ms' ]
"""
super().__init__()

self.basis = basis
self.requires = [Unroller(list(set(basis).union(['u3', 'cx'])))]

def run(self, dag):
"""Replace U3,CX nodes in input dag with equivalent Rx,Ry,Rxx gates.
Args:
dag(DAGCircuit): input dag
Raises:
QiskitError: if input dag includes gates outside U3,CX.
Returns:
DAGCircuit: output dag
"""

one_q_decomposer = OneQubitEulerDecomposer(basis='XYX')
cnot_decomposition = cnot_rxx_decompose()

for node in dag.op_nodes():
basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
if node.name in basic_insts:
# TODO: this is legacy behavior. basic_insts should be removed and these
# instructions should be part of the device-reported basis. Currently, no
# backend reports "measure", for example.
continue
if node.name in self.basis: # If already a base, ignore.
continue

if not isinstance(node.op, self.supported_input_gates):
raise QiskitError("Cannot convert the circuit to the given basis, %s. "
"No rule to expand instruction %s." %
(str(self.basis), node.op.name))

if isinstance(node.op, U3Gate):
replacement_circuit = one_q_decomposer(node.op)
elif isinstance(node.op, CnotGate):
# N.B. We can't circuit_to_dag once outside the loop because
# substitute_node_with_dag will modify the input DAG if the
# node to be replaced is conditional.

replacement_circuit = cnot_decomposition
else:
raise QiskitError("Unable to handle instruction (%s, %s)."
% (node.op.name, type(node.op)))

replacement_dag = circuit_to_dag(replacement_circuit)

# N.B. wires kwarg can be omitted for both 1Q and 2Q substitutions.
# For 1Q, one-to-one mapping is always correct. For 2Q,
# cnot_rxx_decompose follows convention of control as q[0], target
# as q[1], which matches qarg order in CX node.

dag.substitute_node_with_dag(node, replacement_dag)

return dag
17 changes: 17 additions & 0 deletions qiskit/transpiler/transpile_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
level_1_pass_manager,
level_2_pass_manager,
level_3_pass_manager)
from qiskit.transpiler.passes.ms_basis_decomposer import MSBasisDecomposer
from qiskit.transpiler.exceptions import TranspilerError


Expand All @@ -40,6 +41,19 @@ def transpile_circuit(circuit, transpile_config):

# or we choose an appropriate one based on desired optimization level (default: level 1)
else:
# Workaround for ion trap support: If basis gates includes
# Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis,
# first unroll to u3, cx, then run MSBasisDecomposer to target basis.
basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
device_insts = set(transpile_config.basis_gates).union(basic_insts)

ms_basis_swap = None
if 'rxx' in transpile_config.basis_gates and \
not device_insts >= circuit.count_ops().keys():
ms_basis_swap = transpile_config.basis_gates
transpile_config.basis_gates = list(set(['u3', 'cx']).union(
transpile_config.basis_gates))

level = transpile_config.optimization_level
if level is None:
level = 1
Expand All @@ -55,6 +69,9 @@ def transpile_circuit(circuit, transpile_config):
else:
raise TranspilerError("optimization_level can range from 0 to 3.")

if ms_basis_swap is not None:
pass_manager.append(MSBasisDecomposer(ms_basis_swap))

# Set a callback on the pass manager there is one
if getattr(transpile_config, 'callback', None):
pass_manager.callback = transpile_config.callback
Expand Down
56 changes: 56 additions & 0 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import math
import unittest
from unittest.mock import patch
from ddt import ddt, data

from qiskit import BasicAer
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
Expand All @@ -31,8 +32,10 @@
from qiskit.transpiler import PassManager
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements, CXDirection
from qiskit.quantum_info import Operator


@ddt
class TestTranspile(QiskitTestCase):
"""Test transpile function."""

Expand Down Expand Up @@ -636,3 +639,56 @@ def test_check_circuit_width(self):

with self.assertRaises(TranspilerError):
transpile(qc, coupling_map=cmap)

@data(0, 1, 2, 3)
def test_ms_unrolls_to_cx(self, optimization_level):
"""Verify a Rx,Ry,Rxx circuit transpile to a U3,CX target."""

qc = QuantumCircuit(2)
qc.rx(math.pi/2, 0)
qc.ry(math.pi/4, 1)
qc.rxx(math.pi/4, 0, 1)

out = transpile(qc, basis_gates=['u3', 'cx'], optimization_level=optimization_level)

self.assertTrue(Operator(qc).equiv(out))

@data(0, 1, 2, 3)
def test_ms_can_target_ms(self, optimization_level):
"""Verify a Rx,Ry,Rxx circuit can transpile to an Rx,Ry,Rxx target."""

qc = QuantumCircuit(2)
qc.rx(math.pi/2, 0)
qc.ry(math.pi/4, 1)
qc.rxx(math.pi/4, 0, 1)

out = transpile(qc, basis_gates=['rx', 'ry', 'rxx'], optimization_level=optimization_level)

self.assertTrue(Operator(qc).equiv(out))

@data(0, 1, 2, 3)
def test_cx_can_target_ms(self, optimization_level):
"""Verify a U3,CX circuit can transpiler to a Rx,Ry,Rxx target."""

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.rz(math.pi/4, [0, 1])

out = transpile(qc, basis_gates=['rx', 'ry', 'rxx'], optimization_level=optimization_level)

self.assertTrue(Operator(qc).equiv(out))

@data(0, 1, 2, 3)
def test_measure_doesnt_unroll_ms(self, optimization_level):
"""Verify a measure doesn't cause an Rx,Ry,Rxx circuit to unroll to U3,CX."""

qc = QuantumCircuit(2, 2)
qc.rx(math.pi/2, 0)
qc.ry(math.pi/4, 1)
qc.rxx(math.pi/4, 0, 1)
qc.measure([0, 1], [0, 1])

out = transpile(qc, basis_gates=['rx', 'ry', 'rxx'], optimization_level=optimization_level)

self.assertEqual(qc, out)

0 comments on commit 5270887

Please sign in to comment.