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

Refactor qr #179

Merged
merged 56 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
1f7b1a6
qubit routing first draft
vishal-ph Jan 11, 2023
795a2f0
Merge branch 'dev' into dev_greedyQR
vishal-ph Jan 12, 2023
73ee44b
first draft of greedyQR in OQ
vishal-ph Jan 17, 2023
c16ce72
stashing new updates to base structure of OQ to support QR
vishal-ph Jan 21, 2023
c44af41
working version of qr in oq
vishal-ph Jan 22, 2023
2e39a4d
updates to the design
vishal-ph Jan 25, 2023
6d2f2a3
conflicts fix
vishal-ph Feb 3, 2023
56ceec8
fixes
vishal-ph Feb 3, 2023
da8789d
added more fixes
vishal-ph Feb 3, 2023
80d36fc
added the missing jobs folder
vishal-ph Feb 3, 2023
c3337ba
Merge branch 'aws_jobs' into refactor_qr
vishal-ph Feb 6, 2023
c8eee26
more changes and fixes
vishal-ph Feb 6, 2023
73a6ca8
more fixes and changes
vishal-ph Feb 6, 2023
5c16f27
isinstance bug fixed
Feb 6, 2023
b0716cf
more fixes QR implementation
vishal-ph Feb 7, 2023
af2a1a2
finally working version of QR in OQ (remains to be tested)
vishal-ph Feb 13, 2023
7851c98
quick fix to final_mapping
vishal-ph Feb 13, 2023
df210c0
ixed some failing tests
vishal-ph Feb 14, 2023
bf327e3
more updates
vishal-ph Feb 14, 2023
d320fe2
new updates on pyquil and qiskit
vishal-ph Feb 15, 2023
03c024e
replaced backend.run with execute in qiskit backend
vishal-ph Feb 15, 2023
5b74ca2
Update azure device and qiskit backend
shahidee44 Feb 15, 2023
82e9d2b
pyquil readout register bugfix
vishal-ph Feb 15, 2023
3a33a80
Merge branch 'refactor_qr' of github.com:entropicalabs/openqaoa into …
vishal-ph Feb 15, 2023
cd00a26
bug in pyquil causing measurementissues for non-routed circuits
vishal-ph Feb 15, 2023
439e025
fix qubit indices for pyquil measure instructions
shaohenc Feb 16, 2023
9c2b08f
fix qubit indices for pyquil measure instructions for p > 1
shaohenc Feb 16, 2023
b46b4d8
Moved fix for p>1 to base backend object.
shaohenc Feb 16, 2023
c14f921
fixed measurement instructions
vishal-ph Feb 17, 2023
b0e769a
bugfix switching offqiskit routing
vishal-ph Feb 17, 2023
83c0339
Unit tests for qubit routing input/outpu
shaohenc Feb 17, 2023
7858898
minor name change
shaohenc Feb 17, 2023
2c92fc1
added qr support for qiskit qpu simulators, and addressed some comments
vishal-ph Feb 20, 2023
f56f62e
Merge branch 'aws_jobs' into refactor_qr
vishal-ph Feb 20, 2023
7db4b82
import bug fix
Feb 20, 2023
d74c962
remove file
Feb 20, 2023
f7967bb
change file name
Feb 20, 2023
8d6226e
prints
Feb 20, 2023
7f58406
test routing
Feb 20, 2023
9439ac1
Merge branch 'aws_jobs' into refactor_qr
vishal-ph Feb 20, 2023
22d4685
more tests
Feb 21, 2023
3e10f21
test workflows bug
Feb 21, 2023
486dc79
test routing
Feb 21, 2023
fa2619b
test routing
Feb 21, 2023
35c76dd
Bugfix: Relabelling for Custom Mixer Blocks
shahidee44 Feb 21, 2023
b190407
Update baseparams.py
Feb 21, 2023
32dd743
Update baseparams.py
Feb 21, 2023
70f3b8f
Update baseparams.py
Feb 21, 2023
a146777
Update bugfix and added unittest
shahidee44 Feb 21, 2023
8adedd6
Delete test_numerical_solutions.ipynb
Feb 21, 2023
557656f
Removed duplicate unittest
shahidee44 Feb 21, 2023
18510a4
Added more custom mixer unittests
shahidee44 Feb 22, 2023
d30939e
Rerun notebook example 1
shahidee44 Feb 22, 2023
65d8a23
Rerun example notebook 3
shahidee44 Feb 22, 2023
e5c1463
Added more unittests for custom mixers and updated old unittests
shahidee44 Feb 22, 2023
26f0c9d
fixed some comments and removed circuit_routing function
vishal-ph Feb 22, 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
479 changes: 225 additions & 254 deletions examples/01_workflows_example.ipynb

Large diffs are not rendered by default.

966 changes: 802 additions & 164 deletions examples/03_qaoa_on_qpus.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/04_qaoa_variational_parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
"name": "oq_reviews"
},
"language_info": {
"codemirror_mode": {
Expand Down
5 changes: 5 additions & 0 deletions src/openqaoa-azure/backends/devices.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List

from azure.quantum.qiskit import AzureQuantumProvider
from openqaoa.backends.devices_core import DeviceBase

Expand Down Expand Up @@ -96,3 +98,6 @@ def _check_provider_connection(self) -> bool:
)
)
return False

def connectivity(self) -> List[List[int]]:
return self.backend_device.configuration().coupling_map
5 changes: 4 additions & 1 deletion src/openqaoa-braket/backends/devices.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import numpy as np

from typing import List
from boto3.session import Session
from botocore.exceptions import NoRegionError
from braket.aws import AwsDevice
Expand Down Expand Up @@ -147,3 +147,6 @@ def _check_provider_connection(self) -> bool:
)
)
return False

def connectivity(self) -> List[List[int]]:
return self.backend_device.topology_graph.edges
75 changes: 42 additions & 33 deletions src/openqaoa-braket/backends/qaoa_braket_qpu.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
from typing import Optional, List
import warnings

from braket.circuits import Circuit
from braket.circuits.gates import H
Expand Down Expand Up @@ -47,32 +48,33 @@ class QAOAAWSQPUBackend(
used. This is False by default. Not all providers provide this feature.
"""

def __init__(
self,
qaoa_descriptor: QAOADescriptor,
device: DeviceAWS,
n_shots: int,
prepend_state: Optional[Circuit],
append_state: Optional[Circuit],
init_hadamard: bool,
cvar_alpha: float,
qubit_layout: List[int] = [],
disable_qubit_rewiring: bool = False,
):

QAOABaseBackendShotBased.__init__(
self,
qaoa_descriptor,
n_shots,
prepend_state,
append_state,
init_hadamard,
cvar_alpha,
)
def __init__(self,
qaoa_descriptor: QAOADescriptor,
device: DeviceAWS,
n_shots: int,
prepend_state: Optional[Circuit],
append_state: Optional[Circuit],
init_hadamard: bool,
cvar_alpha: float,
disable_qubit_rewiring: bool = False,
initial_qubit_mapping: Optional[List[int]] = None):

QAOABaseBackendShotBased.__init__(self,
qaoa_descriptor,
n_shots,
prepend_state,
append_state,
init_hadamard,
cvar_alpha,
)
QAOABaseBackendCloud.__init__(self, device)

self.qureg = self.qaoa_descriptor.qureg
self.qubit_layout = self.qureg if qubit_layout == [] else qubit_layout
self.qureg = list(range(self.n_qubits))
self.problem_reg = self.qureg[0:self.problem_qubits]
if self.initial_qubit_mapping is None:
self.initial_qubit_mapping = initial_qubit_mapping if initial_qubit_mapping is not None else list(range(self.n_qubits))
else:
warnings.warn("Ignoring the initial_qubit_mapping since the routing algorithm chose one")
self.disable_qubit_rewiring = disable_qubit_rewiring

if self.prepend_state:
Expand Down Expand Up @@ -145,15 +147,17 @@ def parametric_qaoa_circuit(self) -> Circuit:

# Initial state is all |+>
if self.init_hadamard:
for each_qubit in self.qubit_layout:
for each_qubit in self.problem_reg:
parametric_circuit += H.h(each_qubit)

self.braket_parameter_list = []
for each_gate in self.abstract_circuit:
angle_param = FreeParameter(str(each_gate.pauli_label))
self.braket_parameter_list.append(angle_param)
each_gate.rotation_angle = angle_param
decomposition = each_gate.decomposition("standard")
#if gate is of type mixer or cost gate, assign parameter to it
if each_gate.gate_label.type.value in ['mixer','cost']:
angle_param = FreeParameter(each_gate.gate_label.__repr__())
self.braket_parameter_list.append(angle_param)
each_gate.angle_value = angle_param
decomposition = each_gate.decomposition('standard')
# using the list above, construct the circuit
for each_tuple in decomposition:
gate = each_tuple[0]()
Expand All @@ -164,6 +168,7 @@ def parametric_qaoa_circuit(self) -> Circuit:
if self.append_state:
parametric_circuit += self.append_state

#TODO: needs to be fixed --> measurement operations on problem qubits
parametric_circuit += Probability.probability()

return parametric_circuit
Expand Down Expand Up @@ -233,10 +238,14 @@ def get_counts(self, params: QAOAVariationalBaseParams, n_shots=None) -> dict:
"An Error Occurred with the Task(s) sent to AWS."
)

# Expose counts
self.measurement_outcomes = counts
return counts

final_counts = counts
# if self.final_mapping is not None:
# final_counts = permute_counts_dictionary(final_counts,
# self.final_mapping)
# # Expose counts
self.measurement_outcomes = final_counts
return final_counts

def log_with_backend(self, metric_name: str, value, iteration_number) -> None:

"""
Expand Down
121 changes: 117 additions & 4 deletions src/openqaoa-core/algorithms/qaoa/qaoa_workflow.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import List, Callable, Optional
import requests
from .qaoa_result import QAOAResult
from ..workflow_properties import CircuitProperties
from ..baseworkflow import Workflow
from ...backends.devices_core import DeviceLocal
from ...backends.devices_core import DeviceLocal, DeviceBase
from ...backends.qaoa_backend import get_qaoa_backend
from ...problems import QUBO
from ...qaoa_components import (
Expand All @@ -13,6 +15,98 @@
from ...optimizers.qaoa_optimizer import get_optimizer


def circuit_routing(
device: DeviceBase,
problem_to_solve: List[List[int]],
initial_mapping: Optional[List[int]] = None,
routing_algo: str = "greedy",
) -> tuple:
"""Post the HTTP request to the routing API and retrieve routed
circuit

Parameters
----------
device: DeviceBase
The device for which to route the circuit
problem_to_solve: List[List[int]]
The QUBO problem to be run on QPU
initial_mapping: Optional[List[int]]
The initial selection of qubits, if provided by the user.
Defaults to the first n-qubits on the device if `None`
routing_algo: str
The algorithm to generate an optimal SWAP network

Returns
-------
tuple[List[List],List[bool],List[int]]
The first list specifies the qubit indices for operations,
the second list differentiates between Ising and SWAP gates,
and the final list returns the final qubit mapping.
"""

device_connecivity = device.connectivity()

# params for MCTS
params = (
{
"max_depth_simulation": 10,
"max_iterations": 10,
"n_simulations": 64,
"exploration_constant": 2,
"max_tree_depth": 5,
}
if routing_algo.lower() == "mcts"
else {}
)
query = {
"algorithm": routing_algo,
"device": device_connecivity,
"problem": problem_to_solve,
"initial_mapping": initial_mapping,
"params": params,
}

# post the query via HTTP
r = requests.post(url="http://127.0.0.1:8000/qubit_routing", json=query)

results = r.json()

gate_indices_list = results["gates_list"]
initial_mapping = results["initial_mapping"]
final_mapping = results["final_mapping"]
swap_mask = results["swap_mask"]

assert len(gate_indices_list) == len(
swap_mask
), "Incorrect output from qubit routing algorithm"
assert len(final_mapping) == len(
initial_mapping
), "Incorrect number of qubits in the final qubit layout"

all_qubits = []
for pair in gate_indices_list:
all_qubits.extend(pair)
all_qubits = set(all_qubits)

#append missing qubits from the initial_mapping
initial_mapping.extend([qubit for qubit in all_qubits if qubit not in initial_mapping])
#append missing qubits from the final_mapping
final_mapping.extend([qubit for qubit in all_qubits if qubit not in final_mapping])

initial_physical_to_logical_mapping = {
qubit:idx for idx,qubit in enumerate(initial_mapping)
}

for idx,pair in enumerate(gate_indices_list):
i,j = pair
logical_i, logical_j = initial_physical_to_logical_mapping[i],initial_physical_to_logical_mapping[j]
gate_indices_list[idx] = sorted([logical_i, logical_j])

final_logical_qubit_order = [initial_physical_to_logical_mapping[q] for q in final_mapping]

return (gate_indices_list, swap_mask, initial_physical_to_logical_mapping, final_logical_qubit_order)


class QAOA(Workflow):
"""
A class implementing a QAOA workflow end to end.
Expand Down Expand Up @@ -171,7 +265,12 @@ def set_circuit_properties(self, **kwargs):

return None

def compile(self, problem: QUBO = None, verbose: bool = False):
def compile(
self,
problem: QUBO = None,
verbose: bool = False,
routing_function: Optional[Callable] = None,
):
"""
Initialise the trainable parameters for QAOA according to the specified
strategies and by passing the problem statement
Expand All @@ -190,7 +289,17 @@ def compile(self, problem: QUBO = None, verbose: bool = False):
verbose: bool
Set True to have a summary of QAOA to displayed after compilation
"""

# if isinstance(routing_function,Callable):
# #assert that routing_function is supported only for Standard QAOA.
# if (
# self.backend_properties.append_state is not None or\
# self.backend_properties.prepend_state is not None or\
# self.circuit_properties.mixer_hamiltonian is not 'x' or\

# )

# connect to the QPU specified
self.device.check_connection()
# we compile the method of the parent class to genereate the id and
# check the problem is a QUBO object and save it
super().compile(problem=problem)
Expand All @@ -207,7 +316,11 @@ def compile(self, problem: QUBO = None, verbose: bool = False):
)

self.qaoa_descriptor = QAOADescriptor(
self.cost_hamil, self.mixer_hamil, p=self.circuit_properties.p
self.cost_hamil,
self.mixer_hamil,
p=self.circuit_properties.p,
routing_function=routing_function,
device=self.device,
)
self.variate_params = create_qaoa_variational_params(
qaoa_descriptor=self.qaoa_descriptor,
Expand Down
6 changes: 3 additions & 3 deletions src/openqaoa-core/algorithms/workflow_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ class BackendProperties(WorkflowProperties):
The value of the CVaR parameter.
noise_model: NoiseModel
The `qiskit` noise model to be used for the shot-based simulator.
qubit_layout: Union[List[int], numpy.ndarray]
initial_qubit_mapping: Union[List[int], numpy.ndarray]
Mapping from physical to logical qubit indices, used to eventually
construct the quantum circuit. For example, for a system composed by 3 qubits
`qubit_layout=[1,3,2]`, maps `1<->0`, `3<->1`, `2<->2`, where the left hand side is the physical qubit
Expand All @@ -229,7 +229,7 @@ def __init__(
n_shots: int = 100,
cvar_alpha: float = 1,
noise_model=None,
qubit_layout: Optional[Union[List[int], np.ndarray]] = None,
initial_qubit_mapping: Optional[Union[List[int], np.ndarray]] = None,
qiskit_simulation_method: Optional[str] = None,
seed_simulator: Optional[int] = None,
active_reset: Optional[bool] = None,
Expand All @@ -243,7 +243,7 @@ def __init__(
self.append_state = append_state
self.cvar_alpha = cvar_alpha
self.noise_model = noise_model
self.qubit_layout = qubit_layout
self.initial_qubit_mapping = initial_qubit_mapping
self.seed_simulator = seed_simulator
self.qiskit_simulation_method = qiskit_simulation_method
self.active_reset = active_reset
Expand Down
Loading