Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HLS with coupling map #9250

Closed
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6f82985
initial commit
alexanderivrii Dec 1, 2022
646ca0d
Adding functionality to linear functions LNN plugin
alexanderivrii Dec 5, 2022
57b1c7b
Futher improvements
alexanderivrii Dec 6, 2022
38a66a5
remove prints, black and lint
alexanderivrii Dec 6, 2022
7c05f15
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 6, 2022
2c6692c
adding missing break
alexanderivrii Dec 6, 2022
243027c
Adding an alternate way to specify HLS method
alexanderivrii Dec 7, 2022
a560566
improving performance of _hamiltonian_paths
alexanderivrii Dec 7, 2022
ea2ff5e
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 7, 2022
482450e
Adding tests for linear function synthesis plugins
alexanderivrii Dec 7, 2022
07e4881
release notes
alexanderivrii Dec 7, 2022
8363767
taking union of two dicts that also works for python 3.7
alexanderivrii Dec 7, 2022
038d335
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 20, 2022
33c9764
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 21, 2022
33f7e5d
adding test for LinearFunction permute method
alexanderivrii Dec 21, 2022
e044be1
renaming optimize_cx_4_options to _optimize_cx_4_options
alexanderivrii Dec 21, 2022
a87d495
Adding documentation for two linear function synthesis plugin + bug fix
alexanderivrii Dec 21, 2022
f5631a9
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 22, 2022
6a1ab8d
Fixing depth computation of linear circuits, renaming auxiliary funct…
alexanderivrii Dec 22, 2022
af6658f
renaming for lint
alexanderivrii Dec 22, 2022
cf8b9e1
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Dec 27, 2022
1266e1f
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Jan 10, 2023
a324253
Adding tests as per review comments
alexanderivrii Jan 10, 2023
37aa551
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Apr 3, 2023
13cbc6e
fixing the order of arguments and adding Target
alexanderivrii Apr 4, 2023
0e9b95a
fix
alexanderivrii Apr 5, 2023
c5599df
remove print statement
alexanderivrii Apr 5, 2023
4e1b1e0
remove print statement
alexanderivrii Apr 5, 2023
9738a21
Merge branch 'main' into hls-with-coupling-map
alexanderivrii Jul 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions qiskit/circuit/library/generalized_gates/linear_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ def permutation_pattern(self):
locs = np.where(linear == 1)
return locs[1]

def permute(self, perm):
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
"""Returns a linear function obtained by permuting rows and columns of a given linear function
using the permutation pattern ``perm``.
"""
mat = self.linear
nq = mat.shape[0]
permuted_mat = np.zeros(mat.shape, dtype=bool)
for i in range(nq):
for j in range(nq):
permuted_mat[i, j] = mat[perm[i], perm[j]]
return LinearFunction(permuted_mat)


def _linear_quantum_circuit_to_mat(qc: QuantumCircuit):
"""This creates a n x n matrix corresponding to the given linear quantum circuit."""
Expand Down
92 changes: 72 additions & 20 deletions qiskit/synthesis/linear/linear_circuits_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"""Utility functions for handling linear reversible circuits."""

import copy
from typing import Callable
from typing import Callable, List
import numpy as np
from qiskit import QuantumCircuit
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.exceptions import QiskitError
from qiskit.circuit.exceptions import CircuitError
from . import calc_inverse_matrix, check_invertible_binary_matrix
Expand Down Expand Up @@ -61,10 +61,25 @@ def optimize_cx_4_options(function: Callable, mat: np.ndarray, optimize_count: b
if not check_invertible_binary_matrix(mat):
raise QiskitError("The matrix is not invertible.")

circuits = _cx_circuits_4_options(function, mat)
best_qc = _choose_best_circuit(circuits, optimize_count)
return best_qc


def _cx_circuits_4_options(function: Callable, mat: np.ndarray) -> List[QuantumCircuit]:
"""Construct different circuits implementing a binary invertible matrix M,
by considering all four options: M,M^(-1),M^T,M^(-1)^T.

Args:
function: the synthesis function.
mat: a binary invertible matrix.

Returns:
List[QuantumCircuit]: constructed circuits.
"""
circuits = []
qc = function(mat)
best_qc = qc
best_depth = qc.depth()
best_count = qc.count_ops()["cx"]
circuits.append(qc)

for i in range(1, 4):
mat_cpy = copy.deepcopy(mat)
Expand All @@ -73,30 +88,67 @@ def optimize_cx_4_options(function: Callable, mat: np.ndarray, optimize_count: b
mat_cpy = calc_inverse_matrix(mat_cpy)
qc = function(mat_cpy)
qc = qc.inverse()
circuits.append(qc)
elif i == 2:
mat_cpy = np.transpose(mat_cpy)
qc = function(mat_cpy)
qc = transpose_cx_circ(qc)
circuits.append(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()
circuits.append(qc)

return circuits

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

def _choose_best_circuit(
circuits: List[QuantumCircuit], optimize_count: bool = True
) -> QuantumCircuit:
"""Returns the best quantum circuit either in terms of gate count or depth.

Args:
circuits: a list of quantum circuits
optimize_count: True if the number of CX gates is optimized, False if the depth is optimized.

Returns:
QuantumCircuit: the best quantum circuit out of the given circuits.
"""
best_qc = circuits[0]
for circuit in circuits[1:]:
if _compare_circuits(best_qc, circuit, optimize_count=optimize_count):
best_qc = circuit
return best_qc


def _compare_circuits(
qc1: QuantumCircuit, qc2: QuantumCircuit, optimize_count: bool = True
) -> bool:
"""Compares two quantum circuits either in terms of gate count or depth.

Args:
qc1: the first quantum circuit
qc2: the second quantum circuit
optimize_count: True if the number of CX gates is optimized, False if the depth is optimized.

Returns:
bool: ``False`` means that the first quantum circuit is "better", ``True`` means the second.
"""
# TODO: this is not fully correct if there are SWAP gates
count1 = qc1.size()
depth1 = qc1.depth()
count2 = qc2.size()
depth2 = qc2.depth()

# Prioritize count, and if it has the same count, then also consider depth
count2_is_better = (optimize_count and count1 > count2) or (
not optimize_count and depth1 == depth2 and count1 > count2
)
# Prioritize depth, and if it has the same depth, then also consider count
depth2_is_better = (not optimize_count and depth1 > depth2) or (
optimize_count and count1 == count2 and depth1 > depth2
)

return count2_is_better or depth2_is_better
Loading