mirror of https://github.com/Qiskit/qiskit.git
Improve LinearFunction synthesis (#8568)
* move graysynth from transpiler.synthesis to synthesis.linear * add linear utilities for binary matrices: inverse matrix, random matrix * update random invertible binary matrix in random_cnotdihedral * update random invertible binary matrix in test_linear_function * fix deprecation warning in graysynth * style * update variable names * add a function with 4 options to synthesize a CX circuit * improve linear_utils code * refactor transpiler/test_synthesis to synthesis/test_gray_synthesis * split linear_utils into two files * style * add test/python/synthesis init file * unify linear utils code * add check_invertible_binary_matrix function * update import in high_level_synthesis transpiler pass * update transpose_cx_circ, add docstrings * minor fix in calc_inverse_matrix * add types in docstrings * add tests for linear synthesis functions * fix transpose_cx_circ function * add docstrings in linear_matrix_utils * update import in test * fix docs in linear matrix utils * add a test for invertible matrices * style changes * add release notes * fix random_invertible_binary_matrix * update following review comments * fix type hint * fix import * fix release notes * renmae cnot_synth to PMH_cnot_synth * add comments following review * fix release notes following review * add a test following review * rename cnot_synth to PMH_cnot_synth * rename to synth_cnot_count_full_pmh * format tests Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
08969a6c2a
commit
94bccd55a5
|
@ -16,6 +16,7 @@ from typing import Union, List, Optional
|
|||
import numpy as np
|
||||
from qiskit.circuit import QuantumCircuit, Gate
|
||||
from qiskit.circuit.exceptions import CircuitError
|
||||
from qiskit.synthesis.linear import check_invertible_binary_matrix
|
||||
|
||||
|
||||
class LinearFunction(Gate):
|
||||
|
@ -110,8 +111,7 @@ class LinearFunction(Gate):
|
|||
|
||||
# Optionally, check that the matrix is invertible
|
||||
if validate_input:
|
||||
det = np.linalg.det(linear) % 2
|
||||
if not np.allclose(det, 1):
|
||||
if not check_invertible_binary_matrix(linear):
|
||||
raise CircuitError(
|
||||
"A linear function must be represented by an invertible matrix."
|
||||
)
|
||||
|
@ -134,9 +134,9 @@ class LinearFunction(Gate):
|
|||
Returns:
|
||||
QuantumCircuit: A circuit implementing the evolution.
|
||||
"""
|
||||
from qiskit.transpiler.synthesis import cnot_synth
|
||||
from qiskit.synthesis.linear import synth_cnot_count_full_pmh
|
||||
|
||||
return cnot_synth(self.linear)
|
||||
return synth_cnot_count_full_pmh(self.linear)
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
|
|
|
@ -48,10 +48,9 @@ def random_cnotdihedral(num_qubits, seed=None):
|
|||
|
||||
# Random affine function
|
||||
# Random invertible binary matrix
|
||||
det = 0
|
||||
while np.allclose(det, 0) or np.allclose(det, 2):
|
||||
linear = rng.integers(2, size=(num_qubits, num_qubits))
|
||||
det = np.linalg.det(linear) % 2
|
||||
from qiskit.synthesis.linear import random_invertible_binary_matrix
|
||||
|
||||
linear = random_invertible_binary_matrix(num_qubits, seed=rng)
|
||||
elem.linear = linear
|
||||
|
||||
# Random shift
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2018.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Module containing cnot circuits and cnot-phase circuit synthesize."""
|
||||
|
||||
|
||||
from .graysynth import graysynth, synth_cnot_count_full_pmh
|
||||
from .linear_matrix_utils import (
|
||||
random_invertible_binary_matrix,
|
||||
calc_inverse_matrix,
|
||||
check_invertible_binary_matrix,
|
||||
)
|
|
@ -176,11 +176,11 @@ def graysynth(cnots, angles, section_size=2):
|
|||
else:
|
||||
sta.append([cnots1, list(set(ilist).difference([j])), qubit])
|
||||
sta.append([cnots0, list(set(ilist).difference([j])), qubit])
|
||||
qcir &= cnot_synth(state, section_size).inverse()
|
||||
qcir &= synth_cnot_count_full_pmh(state, section_size).inverse()
|
||||
return qcir
|
||||
|
||||
|
||||
def cnot_synth(state, section_size=2):
|
||||
def synth_cnot_count_full_pmh(state, section_size=2):
|
||||
"""
|
||||
This function is an implementation of the Patel–Markov–Hayes algorithm
|
||||
for optimal synthesis of linear reversible circuits, as specified by an
|
|
@ -0,0 +1,102 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2022.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Utility functions for handling linear reversible circuits."""
|
||||
|
||||
import copy
|
||||
from typing import Callable
|
||||
import numpy as np
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.exceptions import QiskitError
|
||||
from qiskit.circuit.exceptions import CircuitError
|
||||
from . import calc_inverse_matrix, check_invertible_binary_matrix
|
||||
|
||||
|
||||
def transpose_cx_circ(qc: QuantumCircuit):
|
||||
"""Takes a circuit having only CX gates, and calculates its transpose.
|
||||
This is done by recursively replacing CX(i, j) with CX(j, i) in all instructions.
|
||||
|
||||
Args:
|
||||
qc: a QuantumCircuit containing only CX gates.
|
||||
|
||||
Returns:
|
||||
QuantumCircuit: the transposed circuit.
|
||||
|
||||
Raises:
|
||||
CircuitError: if qc has a non-CX gate.
|
||||
"""
|
||||
transposed_circ = QuantumCircuit(qc.qubits, qc.clbits, name=qc.name + "_transpose")
|
||||
for instruction in reversed(qc.data):
|
||||
if instruction.operation.name != "cx":
|
||||
raise CircuitError("The circuit contains non-CX gates.")
|
||||
transposed_circ._append(instruction.replace(qubits=reversed(instruction.qubits)))
|
||||
return transposed_circ
|
||||
|
||||
|
||||
def optimize_cx_4_options(function: Callable, mat: np.ndarray, optimize_count: bool = True):
|
||||
"""Get the best implementation of a circuit implementing a binary invertible matrix M,
|
||||
by considering all four options: M,M^(-1),M^T,M^(-1)^T.
|
||||
Optimizing either the CX count or the depth.
|
||||
|
||||
Args:
|
||||
function: the synthesis function.
|
||||
mat: a binary invertible matrix.
|
||||
optimize_count: True if the number of CX gates in optimize, False if the depth is optimized.
|
||||
|
||||
Returns:
|
||||
QuantumCircuit: an optimized QuantumCircuit, has the best depth or CX count of the four options.
|
||||
|
||||
Raises:
|
||||
QiskitError: if mat is not an invertible matrix.
|
||||
"""
|
||||
if not check_invertible_binary_matrix(mat):
|
||||
raise QiskitError("The matrix is not invertible.")
|
||||
|
||||
qc = function(mat)
|
||||
best_qc = qc
|
||||
best_depth = qc.depth()
|
||||
best_count = qc.count_ops()["cx"]
|
||||
|
||||
for i in range(1, 4):
|
||||
mat_cpy = copy.deepcopy(mat)
|
||||
# i=1 inverse, i=2 transpose, i=3 transpose and inverse
|
||||
if i == 1:
|
||||
mat_cpy = calc_inverse_matrix(mat_cpy)
|
||||
qc = function(mat_cpy)
|
||||
qc = qc.inverse()
|
||||
elif i == 2:
|
||||
mat_cpy = np.transpose(mat_cpy)
|
||||
qc = function(mat_cpy)
|
||||
qc = transpose_cx_circ(qc)
|
||||
elif i == 3:
|
||||
mat_cpy = calc_inverse_matrix(np.transpose(mat_cpy))
|
||||
qc = function(mat_cpy)
|
||||
qc = transpose_cx_circ(qc)
|
||||
qc = qc.inverse()
|
||||
|
||||
new_depth = qc.depth()
|
||||
new_count = qc.count_ops()["cx"]
|
||||
# Prioritize count, and if it has the same count, then also consider depth
|
||||
better_count = (optimize_count and best_count > new_count) or (
|
||||
not optimize_count and best_depth == new_depth and best_count > new_count
|
||||
)
|
||||
# Prioritize depth, and if it has the same depth, then also consider count
|
||||
better_depth = (not optimize_count and best_depth > new_depth) or (
|
||||
optimize_count and best_count == new_count and best_depth > new_depth
|
||||
)
|
||||
|
||||
if better_count or better_depth:
|
||||
best_count = new_count
|
||||
best_depth = new_depth
|
||||
best_qc = qc
|
||||
|
||||
return best_qc
|
|
@ -0,0 +1,149 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2022.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Utility functions for handling binary matrices."""
|
||||
|
||||
from typing import Optional, Union
|
||||
import numpy as np
|
||||
from qiskit.exceptions import QiskitError
|
||||
|
||||
|
||||
def check_invertible_binary_matrix(mat: np.ndarray):
|
||||
"""Check that a binary matrix is invertible.
|
||||
|
||||
Args:
|
||||
mat: a binary matrix.
|
||||
|
||||
Returns:
|
||||
bool: True if mat in invertible and False otherwise.
|
||||
"""
|
||||
if len(mat.shape) != 2 or mat.shape[0] != mat.shape[1]:
|
||||
return False
|
||||
|
||||
mat = _gauss_elimination(mat)
|
||||
rank = _compute_rank_after_gauss_elim(mat)
|
||||
return rank == mat.shape[0]
|
||||
|
||||
|
||||
def random_invertible_binary_matrix(
|
||||
num_qubits: int, seed: Optional[Union[np.random.Generator, int]] = None
|
||||
):
|
||||
"""Generates a random invertible n x n binary matrix.
|
||||
|
||||
Args:
|
||||
num_qubits: the matrix size.
|
||||
seed: a random seed.
|
||||
|
||||
Returns:
|
||||
np.ndarray: A random invertible binary matrix of size num_qubits.
|
||||
"""
|
||||
if isinstance(seed, np.random.Generator):
|
||||
rng = seed
|
||||
else:
|
||||
rng = np.random.default_rng(seed)
|
||||
|
||||
rank = 0
|
||||
while rank != num_qubits:
|
||||
mat = rng.integers(2, size=(num_qubits, num_qubits))
|
||||
mat_gauss = mat.copy()
|
||||
mat_gauss = _gauss_elimination(mat_gauss)
|
||||
rank = _compute_rank_after_gauss_elim(mat_gauss)
|
||||
return mat
|
||||
|
||||
|
||||
def _gauss_elimination(mat, ncols=None, full_elim=False):
|
||||
"""Gauss elimination of a matrix mat with m rows and n columns.
|
||||
If full_elim = True, it allows full elimination of mat[:, 0 : ncols]
|
||||
Mutates and returns the matrix mat."""
|
||||
|
||||
# Treat the matrix A as containing integer values
|
||||
mat = np.array(mat, dtype=int, copy=False)
|
||||
|
||||
m = mat.shape[0] # no. of rows
|
||||
n = mat.shape[1] # no. of columns
|
||||
if ncols is not None:
|
||||
n = min(n, ncols) # no. of active columns
|
||||
|
||||
r = 0 # current rank
|
||||
k = 0 # current pivot column
|
||||
while (r < m) and (k < n):
|
||||
is_non_zero = False
|
||||
new_r = r
|
||||
for j in range(k, n):
|
||||
for i in range(r, m):
|
||||
if mat[i][j]:
|
||||
is_non_zero = True
|
||||
k = j
|
||||
new_r = i
|
||||
break
|
||||
if is_non_zero:
|
||||
break
|
||||
if not is_non_zero:
|
||||
return mat # A is in the canonical form
|
||||
|
||||
if new_r != r:
|
||||
mat[[r, new_r]] = mat[[new_r, r]]
|
||||
|
||||
if full_elim:
|
||||
for i in range(0, r):
|
||||
if mat[i][k]:
|
||||
mat[i] = mat[i] ^ mat[r]
|
||||
|
||||
for i in range(r + 1, m):
|
||||
if mat[i][k]:
|
||||
mat[i] = mat[i] ^ mat[r]
|
||||
r += 1
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
def calc_inverse_matrix(mat: np.ndarray, verify: bool = False):
|
||||
"""Given a square numpy(dtype=int) matrix mat, tries to compute its inverse.
|
||||
|
||||
Args:
|
||||
mat: a boolean square matrix.
|
||||
verify: if True asserts that the multiplication of mat and its inverse is the identity matrix.
|
||||
|
||||
Returns:
|
||||
np.ndarray: the inverse matrix.
|
||||
|
||||
Raises:
|
||||
QiskitError: if the matrix is not square.
|
||||
QiskitError: if the matrix is not invertible.
|
||||
"""
|
||||
|
||||
if mat.shape[0] != mat.shape[1]:
|
||||
raise QiskitError("Matrix to invert is a non-square matrix.")
|
||||
|
||||
n = mat.shape[0]
|
||||
# concatenate the matrix and identity
|
||||
mat1 = np.concatenate((mat, np.eye(n, dtype=int)), axis=1)
|
||||
mat1 = _gauss_elimination(mat1, None, full_elim=True)
|
||||
|
||||
r = _compute_rank_after_gauss_elim(mat1[:, 0:n])
|
||||
|
||||
if r < n:
|
||||
raise QiskitError("The matrix is not invertible.")
|
||||
|
||||
matinv = mat1[:, n : 2 * n]
|
||||
|
||||
if verify:
|
||||
mat2 = np.dot(mat, matinv) % 2
|
||||
assert np.array_equal(mat2, np.eye(n))
|
||||
|
||||
return matinv
|
||||
|
||||
|
||||
def _compute_rank_after_gauss_elim(mat):
|
||||
"""Given a matrix A after Gaussian elimination, computes its rank
|
||||
(i.e. simply the number of nonzero rows)"""
|
||||
return np.sum(mat.any(axis=1))
|
|
@ -19,7 +19,7 @@ from qiskit.transpiler.basepasses import TransformationPass
|
|||
from qiskit.dagcircuit.dagcircuit import DAGCircuit
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.quantum_info import decompose_clifford
|
||||
from qiskit.transpiler.synthesis import cnot_synth
|
||||
from qiskit.synthesis.linear import synth_cnot_count_full_pmh
|
||||
from .plugin import HighLevelSynthesisPluginManager, HighLevelSynthesisPlugin
|
||||
|
||||
|
||||
|
@ -168,5 +168,5 @@ class DefaultSynthesisLinearFunction(HighLevelSynthesisPlugin):
|
|||
|
||||
def run(self, high_level_object, **options):
|
||||
"""Run synthesis for the given LinearFunction."""
|
||||
decomposition = cnot_synth(high_level_object.linear)
|
||||
decomposition = synth_cnot_count_full_pmh(high_level_object.linear)
|
||||
return decomposition
|
||||
|
|
|
@ -11,6 +11,3 @@
|
|||
# that they have been altered from the originals.
|
||||
|
||||
"""Module containing transpiler synthesize."""
|
||||
|
||||
|
||||
from .graysynth import graysynth, cnot_synth
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Add new utility functions to handle linear functions and circuits:
|
||||
* A utility function that checks whether a NxN binary matrix is invertible.
|
||||
* A utility function that computes an inverse of a NxN invertible binary matrix.
|
||||
* A utility function that calculates the transpose of a linear reversible circuit.
|
||||
* Implement the following "meta" idea for synthesizing linear functions:
|
||||
given an invertible binary matrix A and a synthesis algorithm f: A -> QuantumCircuit,
|
||||
in addition to applying f to A, we can also apply f to A^{-1} and invert the computed circuit,
|
||||
apply f to A^{T} and transpose the computed circuit, and to apply f to A^{T}^{-1} and to invert
|
||||
and transpose the computed circuit, thus leading to 4 generally different synthesized circuits for A,
|
||||
allowing to pick the best one in terms of depth or the number of gates.
|
||||
upgrade:
|
||||
- |
|
||||
Improve code structure by moving the internal code graysynth.py from qiskit/transpiler/synthesis
|
||||
to qiskit/synthesis/linear, and renaming cnot_synth to synth_cnot_count_full_pmh.
|
||||
fixes:
|
||||
- |
|
||||
Provide an internal utility function to generate a random NxN invertible binary matrix.
|
||||
This function is completely robust, as opposed to the previously implemented method based on
|
||||
computing determinant (via np.linalg.det) and checking that it's close to 1,
|
||||
where we already saw cases of floating point errors leading to an incorrect result (for N>100).
|
|
@ -22,6 +22,7 @@ from qiskit.circuit import QuantumCircuit
|
|||
from qiskit.circuit.library.standard_gates import CXGate, SwapGate
|
||||
from qiskit.circuit.library.generalized_gates import LinearFunction
|
||||
from qiskit.circuit.exceptions import CircuitError
|
||||
from qiskit.synthesis.linear import random_invertible_binary_matrix
|
||||
|
||||
from qiskit.quantum_info.operators import Operator
|
||||
|
||||
|
@ -51,23 +52,6 @@ def random_linear_circuit(num_qubits, num_gates, seed=None):
|
|||
return circ
|
||||
|
||||
|
||||
def random_invertible_binary_matrix(num_qubits, seed=None):
|
||||
"""Generates a random invertible n x n binary matrix."""
|
||||
|
||||
# This code is adapted from random_cnotdihedral
|
||||
if isinstance(seed, np.random.Generator):
|
||||
rng = seed
|
||||
else:
|
||||
rng = np.random.default_rng(seed)
|
||||
|
||||
det = 0
|
||||
while np.allclose(det, 0) or np.allclose(det, 2):
|
||||
binary_matrix = rng.integers(2, size=(num_qubits, num_qubits))
|
||||
det = np.linalg.det(binary_matrix) % 2
|
||||
|
||||
return binary_matrix
|
||||
|
||||
|
||||
@ddt
|
||||
class TestLinearFunctions(QiskitTestCase):
|
||||
"""Tests for clifford append gate functions."""
|
||||
|
@ -132,7 +116,7 @@ class TestLinearFunctions(QiskitTestCase):
|
|||
def test_patel_markov_hayes(self):
|
||||
"""Checks the explicit example from Patel-Markov-Hayes's paper."""
|
||||
|
||||
# This code is adapted from test_synthesis.py
|
||||
# This code is adapted from test_gray_synthesis.py
|
||||
binary_matrix = [
|
||||
[1, 1, 0, 0, 0, 0],
|
||||
[1, 0, 0, 1, 1, 0],
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2022.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Qiskit synthesis unit tests."""
|
|
@ -10,12 +10,14 @@
|
|||
# copyright notice, and modified files need to carry a notice indicating
|
||||
# that they have been altered from the originals.
|
||||
|
||||
"""Test synthesis algorithms"""
|
||||
"""Test cnot circuit and cnot-phase circuit synthesis algorithms"""
|
||||
|
||||
import unittest
|
||||
|
||||
from qiskit.circuit import QuantumCircuit, QuantumRegister
|
||||
from qiskit.quantum_info.operators import Operator
|
||||
from qiskit.extensions.unitary import UnitaryGate
|
||||
from qiskit.transpiler.synthesis import graysynth, cnot_synth
|
||||
from qiskit.synthesis.linear import graysynth, synth_cnot_count_full_pmh
|
||||
from qiskit.test import QiskitTestCase
|
||||
|
||||
|
||||
|
@ -237,7 +239,7 @@ class TestPatelMarkovHayes(QiskitTestCase):
|
|||
[1, 1, 0, 1, 1, 1],
|
||||
[0, 0, 1, 1, 1, 0],
|
||||
]
|
||||
c_patel = cnot_synth(state)
|
||||
c_patel = synth_cnot_count_full_pmh(state)
|
||||
unitary_patel = UnitaryGate(Operator(c_patel))
|
||||
|
||||
# Create the circuit displayed above:
|
||||
|
@ -262,3 +264,7 @@ class TestPatelMarkovHayes(QiskitTestCase):
|
|||
|
||||
# Check if the two circuits are equivalent
|
||||
self.assertEqual(unitary_patel, unitary_compare)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -0,0 +1,114 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2022.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Test linear reversible circuits synthesis functions."""
|
||||
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.circuit.library import LinearFunction
|
||||
from qiskit.synthesis.linear import (
|
||||
synth_cnot_count_full_pmh,
|
||||
random_invertible_binary_matrix,
|
||||
check_invertible_binary_matrix,
|
||||
calc_inverse_matrix,
|
||||
)
|
||||
from qiskit.synthesis.linear.linear_circuits_utils import transpose_cx_circ, optimize_cx_4_options
|
||||
from qiskit.test import QiskitTestCase
|
||||
|
||||
|
||||
class TestLinearSynth(QiskitTestCase):
|
||||
"""Test the linear reversible circuit synthesis functions."""
|
||||
|
||||
def test_lnn_circuit(self):
|
||||
"""Test the synthesis of a CX circuit with LNN connectivity."""
|
||||
|
||||
n = 5
|
||||
qc = QuantumCircuit(n)
|
||||
for i in range(n - 1):
|
||||
qc.cx(i, i + 1)
|
||||
mat = LinearFunction(qc).linear
|
||||
|
||||
for optimized in [True, False]:
|
||||
optimized_qc = optimize_cx_4_options(
|
||||
synth_cnot_count_full_pmh, mat, optimize_count=optimized
|
||||
)
|
||||
self.assertEqual(optimized_qc.depth(), 4)
|
||||
self.assertEqual(optimized_qc.count_ops()["cx"], 4)
|
||||
|
||||
def test_full_circuit(self):
|
||||
"""Test the synthesis of a CX circuit with full connectivity."""
|
||||
|
||||
n = 5
|
||||
qc = QuantumCircuit(n)
|
||||
for i in range(n):
|
||||
for j in range(i + 1, n):
|
||||
qc.cx(i, j)
|
||||
mat = LinearFunction(qc).linear
|
||||
|
||||
for optimized in [True, False]:
|
||||
optimized_qc = optimize_cx_4_options(
|
||||
synth_cnot_count_full_pmh, mat, optimize_count=optimized
|
||||
)
|
||||
self.assertEqual(optimized_qc.depth(), 4)
|
||||
self.assertEqual(optimized_qc.count_ops()["cx"], 4)
|
||||
|
||||
def test_transpose_circ(self):
|
||||
"""Test the transpose_cx_circ() function."""
|
||||
n = 5
|
||||
mat = random_invertible_binary_matrix(n, seed=1234)
|
||||
qc = synth_cnot_count_full_pmh(mat)
|
||||
transposed_qc = transpose_cx_circ(qc)
|
||||
transposed_mat = LinearFunction(transposed_qc).linear.astype(int)
|
||||
self.assertTrue((mat.transpose() == transposed_mat).all())
|
||||
|
||||
def test_example_circuit(self):
|
||||
"""Test the synthesis of an example CX circuit which provides different CX count
|
||||
and depth for different optimization methods."""
|
||||
|
||||
qc = QuantumCircuit(9)
|
||||
qc.swap(8, 7)
|
||||
qc.swap(7, 6)
|
||||
qc.cx(5, 6)
|
||||
qc.cx(6, 5)
|
||||
qc.swap(4, 5)
|
||||
qc.cx(3, 4)
|
||||
qc.cx(4, 3)
|
||||
qc.swap(2, 3)
|
||||
qc.cx(1, 2)
|
||||
qc.cx(2, 1)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(1, 0)
|
||||
mat = LinearFunction(qc).linear
|
||||
|
||||
optimized_qc = optimize_cx_4_options(synth_cnot_count_full_pmh, mat, optimize_count=True)
|
||||
self.assertEqual(optimized_qc.depth(), 17)
|
||||
self.assertEqual(optimized_qc.count_ops()["cx"], 20)
|
||||
|
||||
optimized_qc = optimize_cx_4_options(synth_cnot_count_full_pmh, mat, optimize_count=False)
|
||||
self.assertEqual(optimized_qc.depth(), 15)
|
||||
self.assertEqual(optimized_qc.count_ops()["cx"], 23)
|
||||
|
||||
def test_invertible_matrix(self):
|
||||
"""Test the functions for generating a random invertible matrix and inverting it."""
|
||||
n = 5
|
||||
mat = random_invertible_binary_matrix(n, seed=1234)
|
||||
out = check_invertible_binary_matrix(mat)
|
||||
mat_inv = calc_inverse_matrix(mat, verify=True)
|
||||
mat_out = np.dot(mat, mat_inv) % 2
|
||||
self.assertTrue(np.array_equal(mat_out, np.eye(n)))
|
||||
self.assertTrue(out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Reference in New Issue