Skip to content

Commit

Permalink
Expose plugin options (#12107)
Browse files Browse the repository at this point in the history
* fix docstring

* import

* exposing additional plugin arguments

* tests

* lint

* release notes
  • Loading branch information
alexanderivrii committed Apr 22, 2024
1 parent 497603b commit cedf030
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 7 deletions.
4 changes: 1 addition & 3 deletions qiskit/synthesis/linear/cnot_synth.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ def synth_cnot_count_full_pmh(
Args:
state: :math:`n \\times n` boolean invertible matrix, describing
the state of the input circuit
section_size: The size of each section, used in the
Patel–Markov–Hayes algorithm [1]. ``section_size`` must be a factor of the number
of qubits.
section_size: The size of each section in the Patel–Markov–Hayes algorithm [1].
Returns:
QuantumCircuit: a CX-only circuit implementing the linear transformation.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/synthesis/linear/linear_circuits_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import copy
from typing import Callable
import numpy as np
from qiskit import QuantumCircuit
from qiskit.circuit import QuantumCircuit
from qiskit.exceptions import QiskitError
from qiskit.circuit.exceptions import CircuitError
from . import calc_inverse_matrix, check_invertible_binary_matrix
Expand Down
84 changes: 81 additions & 3 deletions qiskit/transpiler/passes/synthesis/high_level_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,15 @@

from typing import Optional, Union, List, Tuple

import numpy as np
import rustworkx as rx

from qiskit.circuit.operation import Operation
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit import ControlFlowOp, ControlledGate, EquivalenceLibrary
from qiskit.circuit.library import LinearFunction
from qiskit.transpiler.passes.utils import control_flow
from qiskit.transpiler.target import Target
from qiskit.transpiler.coupling import CouplingMap
Expand All @@ -163,7 +165,12 @@
synth_clifford_ag,
synth_clifford_bm,
)
from qiskit.synthesis.linear import synth_cnot_count_full_pmh, synth_cnot_depth_line_kms
from qiskit.synthesis.linear import (
synth_cnot_count_full_pmh,
synth_cnot_depth_line_kms,
calc_inverse_matrix,
)
from qiskit.synthesis.linear.linear_circuits_utils import transpose_cx_circ
from qiskit.synthesis.permutation import (
synth_permutation_basic,
synth_permutation_acg,
Expand Down Expand Up @@ -720,11 +727,43 @@ class KMSSynthesisLinearFunction(HighLevelSynthesisPlugin):
This plugin name is :``linear_function.kms`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
The plugin supports the following plugin-specific options:
* use_inverted: Indicates whether to run the algorithm on the inverse matrix
and to invert the synthesized circuit.
In certain cases this provides a better decomposition then the direct approach.
* use_transposed: Indicates whether to run the algorithm on the transposed matrix
and to invert the order oF CX gates in the synthesized circuit.
In certain cases this provides a better decomposition than the direct approach.
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given LinearFunction."""
decomposition = synth_cnot_depth_line_kms(high_level_object.linear)

if not isinstance(high_level_object, LinearFunction):
raise TranspilerError(
"PMHSynthesisLinearFunction only accepts objects of type LinearFunction"
)

use_inverted = options.get("use_inverted", False)
use_transposed = options.get("use_transposed", False)

mat = high_level_object.linear.astype(int)

if use_transposed:
mat = np.transpose(mat)
if use_inverted:
mat = calc_inverse_matrix(mat)

decomposition = synth_cnot_depth_line_kms(mat)

if use_transposed:
decomposition = transpose_cx_circ(decomposition)
if use_inverted:
decomposition = decomposition.inverse()

return decomposition


Expand All @@ -733,11 +772,50 @@ class PMHSynthesisLinearFunction(HighLevelSynthesisPlugin):
This plugin name is :``linear_function.pmh`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
The plugin supports the following plugin-specific options:
* section size: The size of each section used in the Patel–Markov–Hayes algorithm [1].
* use_inverted: Indicates whether to run the algorithm on the inverse matrix
and to invert the synthesized circuit.
In certain cases this provides a better decomposition then the direct approach.
* use_transposed: Indicates whether to run the algorithm on the transposed matrix
and to invert the order oF CX gates in the synthesized circuit.
In certain cases this provides a better decomposition than the direct approach.
References:
1. Patel, Ketan N., Igor L. Markov, and John P. Hayes,
*Optimal synthesis of linear reversible circuits*,
Quantum Information & Computation 8.3 (2008): 282-294.
`arXiv:quant-ph/0302002 [quant-ph] <https://arxiv.org/abs/quant-ph/0302002>`_
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given LinearFunction."""
decomposition = synth_cnot_count_full_pmh(high_level_object.linear)

if not isinstance(high_level_object, LinearFunction):
raise TranspilerError(
"PMHSynthesisLinearFunction only accepts objects of type LinearFunction"
)

section_size = options.get("section_size", 2)
use_inverted = options.get("use_inverted", False)
use_transposed = options.get("use_transposed", False)

mat = high_level_object.linear.astype(int)

if use_transposed:
mat = np.transpose(mat)
if use_inverted:
mat = calc_inverse_matrix(mat)

decomposition = synth_cnot_count_full_pmh(mat, section_size=section_size)

if use_transposed:
decomposition = transpose_cx_circ(decomposition)
if use_inverted:
decomposition = decomposition.inverse()

return decomposition


Expand Down
21 changes: 21 additions & 0 deletions releasenotes/notes/add-linear-plugin-options-b8a0ffe70dfe1676.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
features:
- |
The :class:`.KMSSynthesisLinearFunction` plugin for synthesizing
:class:`~qiskit.circuit.library.LinearFunction` objects now accepts
two additional options ``use_inverted`` and ``use_transposed``.
These option modify the matrix on which the underlying synthesis algorithm runs
by possibly inverting and/or transposing it, and then suitably adjust
the synthesized circuit. By varying these options, we generally get different
synthesized circuits, and in cases may obtain better results than for
their default values.
- |
The :class:`.PMHSynthesisLinearFunction` plugin for synthesizing
:class:`~qiskit.circuit.library.LinearFunction` objects now accepts
several additional options. The option ``section_size`` is passed to the underlying
synthesis method. The options ``use_inverted`` and ``use_transposed``
modify the matrix on which the underlying synthesis algorithm runs
by possibly inverting and/or transposing it, and then suitably adjust
the synthesized circuit. By varying these options, we generally get different
synthesized circuits, and in cases may obtain better results than for
their default values.
129 changes: 129 additions & 0 deletions test/python/transpiler/test_high_level_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
)
from qiskit.circuit.library.generalized_gates import LinearFunction
from qiskit.quantum_info import Clifford
from qiskit.synthesis.linear import random_invertible_binary_matrix
from qiskit.transpiler.passes.synthesis.plugin import (
HighLevelSynthesisPlugin,
HighLevelSynthesisPluginManager,
Expand Down Expand Up @@ -508,6 +509,134 @@ def test_qubits_get_passed_to_plugins(self):
pm_use_qubits_true.run(qc)


class TestPMHSynthesisLinearFunctionPlugin(QiskitTestCase):
"""Tests for the PMHSynthesisLinearFunction plugin for synthesizing linear functions."""

@staticmethod
def construct_linear_circuit(num_qubits: int):
"""Construct linear circuit."""
qc = QuantumCircuit(num_qubits)
for i in range(1, num_qubits):
qc.cx(i - 1, i)
return qc

def test_section_size(self):
"""Test that the plugin takes the section size argument into account."""

mat = random_invertible_binary_matrix(7, seed=1234)
qc = QuantumCircuit(7)
qc.append(LinearFunction(mat), [0, 1, 2, 3, 4, 5, 6])

with self.subTest("section_size_1"):
hls_config = HLSConfig(linear_function=[("pmh", {"section_size": 1})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 22)
self.assertEqual(qct.depth(), 20)

with self.subTest("section_size_2"):
hls_config = HLSConfig(linear_function=[("pmh", {"section_size": 2})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 23)
self.assertEqual(qct.depth(), 19)

with self.subTest("section_size_3"):
hls_config = HLSConfig(linear_function=[("pmh", {"section_size": 3})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 23)
self.assertEqual(qct.depth(), 17)

def test_invert_and_transpose(self):
"""Test that the plugin takes the use_inverted and use_transposed arguments into account."""

linear_function = LinearFunction(self.construct_linear_circuit(7))

qc = QuantumCircuit(7)
qc.append(linear_function, [0, 1, 2, 3, 4, 5, 6])

with self.subTest("default"):
hls_config = HLSConfig(linear_function=[("pmh", {})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 12)
self.assertEqual(qct.depth(), 8)

with self.subTest("invert"):
hls_config = HLSConfig(linear_function=[("pmh", {"use_inverted": True})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 6)
self.assertEqual(qct.depth(), 6)

with self.subTest("transpose"):
hls_config = HLSConfig(linear_function=[("pmh", {"use_transposed": True})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 6)
self.assertEqual(qct.depth(), 6)

with self.subTest("invert_and_transpose"):
hls_config = HLSConfig(
linear_function=[("pmh", {"use_inverted": True, "use_transposed": True})]
)
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 6)
self.assertEqual(qct.depth(), 6)


class TestKMSSynthesisLinearFunctionPlugin(QiskitTestCase):
"""Tests for the KMSSynthesisLinearFunction plugin for synthesizing linear functions."""

@staticmethod
def construct_linear_circuit(num_qubits: int):
"""Construct linear circuit."""
qc = QuantumCircuit(num_qubits)
for i in range(1, num_qubits):
qc.cx(i - 1, i)
return qc

def test_invert_and_transpose(self):
"""Test that the plugin takes the use_inverted and use_transposed arguments into account."""

linear_function = LinearFunction(self.construct_linear_circuit(7))

qc = QuantumCircuit(7)
qc.append(linear_function, [0, 1, 2, 3, 4, 5, 6])

with self.subTest("default"):
hls_config = HLSConfig(linear_function=[("kms", {})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 100)
self.assertEqual(qct.depth(), 34)

with self.subTest("invert"):
hls_config = HLSConfig(linear_function=[("kms", {"use_inverted": True})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 101)
self.assertEqual(qct.depth(), 35)

with self.subTest("transpose"):
hls_config = HLSConfig(linear_function=[("kms", {"use_transposed": True})])
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 84)
self.assertEqual(qct.depth(), 31)

with self.subTest("invert_and_transpose"):
hls_config = HLSConfig(
linear_function=[("kms", {"use_inverted": True, "use_transposed": True})]
)
qct = HighLevelSynthesis(hls_config=hls_config)(qc)
self.assertEqual(LinearFunction(qct), LinearFunction(qc))
self.assertEqual(qct.size(), 87)
self.assertEqual(qct.depth(), 32)


class TestTokenSwapperPermutationPlugin(QiskitTestCase):
"""Tests for the token swapper plugin for synthesizing permutation gates."""

Expand Down

0 comments on commit cedf030

Please sign in to comment.