Skip to content

Commit

Permalink
Add /gate-cutting/combineResultsQuokka endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
mar-be committed Nov 7, 2023
1 parent 3a404c5 commit 39ffab1
Show file tree
Hide file tree
Showing 5 changed files with 384 additions and 49 deletions.
69 changes: 66 additions & 3 deletions app/gate_cutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ******************************************************************************
from app.wire_cutter import _get_circuit, automatic_gate_cut
from app.model.cutting_request import CutCircuitsRequest
from app.model.cutting_response import GateCutCircuitsResponse
from collections import defaultdict

import numpy as np
from circuit_knitting.cutting import partition_problem, generate_cutting_experiments
from qiskit.quantum_info import PauliList

from app.gate_cutting_reconstruct_distribution import reconstruct_distribution
from app.model.cutting_request import CutCircuitsRequest, CombineResultsRequest
from app.model.cutting_response import GateCutCircuitsResponse, CombineResultsResponse
from app.partition import get_partitions, get_partition_labels
from app.utils import counts_to_array
from app.wire_cutter import _get_circuit


def gate_cut_circuit(cutting_request: CutCircuitsRequest):
Expand All @@ -35,3 +44,57 @@ def gate_cut_circuit(cutting_request: CutCircuitsRequest):
raise ValueError(f"{cutting_request.method} is an unkown cutting method.")

return GateCutCircuitsResponse(format=cutting_request.circuit_format, **res)


def automatic_gate_cut(circuit, num_subcircuits, max_subcircuit_width, max_cuts):
partitions, _ = get_partitions(circuit, num_subcircuits[0], max_subcircuit_width)
partition_labels = get_partition_labels(partitions)
partitioned_problem = partition_problem(
circuit=circuit,
partition_labels=partition_labels,
observables=PauliList(["Z" * circuit.num_qubits]),
)
subexperiments, coefficients = generate_cutting_experiments(
circuits=partitioned_problem.subcircuits,
observables=partitioned_problem.subobservables,
num_samples=np.inf,
)

individual_subcircuits = []
subcircuit_labels = []

for label, circs in subexperiments.items():
for sub_circ in circs:
individual_subcircuits.append(sub_circ)
subcircuit_labels.append(label)

return {
"individual_subcircuits": individual_subcircuits,
"subcircuit_labels": subcircuit_labels,
"coefficients": coefficients,
"partition_labels": partition_labels,
}


def reconstruct_result(input_dict: CombineResultsRequest, quokka_format=False):
subcircuit_results_dict = defaultdict(list)
for label, res in zip(
input_dict.cuts["subcircuit_labels"], input_dict.subcircuit_results
):
subcircuit_results_dict[label].append(res)

result = reconstruct_distribution(
subcircuit_results_dict,
input_dict.cuts["coefficients"],
input_dict.cuts["partition_label"],
)
num_qubits = len(input_dict.cuts["partition_label"])

if not quokka_format:
result = counts_to_array(result, num_qubits)
else:
result = {
"{0:b}".format(key).zfill(num_qubits): val for key, val in result.items()
}

return CombineResultsResponse(result=result)
12 changes: 10 additions & 2 deletions app/gate_cutting_reconstruct_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _process_outcome_distribution(


def reconstruct_distribution(
results: SamplerResult | dict[Hashable, SamplerResult],
results: dict[Hashable, SamplerResult] | dict[Hashable, list],
coefficients: Sequence[tuple[float, WeightType]],
partition_labels: str,
) -> dict[int, float]:
Expand Down Expand Up @@ -107,14 +107,22 @@ def reconstruct_distribution(
for label in labels
}

if isinstance(results, dict) and isinstance(
results[next(iter(labels))], SamplerResult
):
results = {
label: [results[label].quasi_dists[i] for i in range(len(coefficients))]
for label in labels
}

# Reconstruct the probability distribution
for i, coeff in enumerate(coefficients):

coeff_result_dict = {}

for label in labels:
coeff_result_dict[label] = defaultdict(float)
quasi_probs = results[label].quasi_dists[i]
quasi_probs = results[label][i]
for outcome, quasi_prob in quasi_probs.items():
qpd_factor, meas_outcomes = _process_outcome_distribution(
qubits[label], outcome
Expand Down
193 changes: 193 additions & 0 deletions app/routes_gate_cutting.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@
from app.model.cutting_request import (
CutCircuitsRequestSchema,
CutCircuitsRequest,
CombineResultsRequest,
CombineResultsRequestQuokkaSchema,
)
from app.model.cutting_response import (
GateCutCircuitsResponseSchema,
CombineResultsResponseQuokkaSchema,
)

blp_gate_cutting = Blueprint(
Expand Down Expand Up @@ -54,3 +57,193 @@ def gate_cut_circuit(json: dict):
result = gate_cutter.gate_cut_circuit(CutCircuitsRequest(**json))
print("result", result)
return result


@blp_gate_cutting.route("/gate-cutting/combineResultsQuokka", methods=["POST"])
@blp_gate_cutting.arguments(
CombineResultsRequestQuokkaSchema,
example={
"circuit": 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[6];\nry(3.65201816844744) q[0];\nry(1.86914714247146) q[1];\ncx q[0],q[1];\nry(4.29668327383582) q[0];\nry(3.78320230781079) q[2];\ncx q[1],q[2];\nry(3.9348411572113) q[1];\nry(2.56591926281996) q[3];\ncx q[2],q[3];\nry(3.98698245166312) q[2];\nry(2.77957484387872) q[4];\ncx q[3],q[4];\nry(2.77046779740946) q[3];\nry(4.34760986661033) q[5];\ncx q[4],q[5];\nry(1.67983539831451) q[4];\nry(4.89050090588416) q[5];\n',
"subcircuit_results": [
{
"001": 0.019775390625,
"110": 0.131103515625,
"100": 0.05712890625,
"101": 0.10791015625,
"111": 0.138427734375,
"011": 0.251953125,
"000": 0.022216796875,
"010": 0.271484375,
},
{
"101": 0.106689453125,
"000": 0.0224609375,
"010": 0.258544921875,
"111": 0.14599609375,
"011": 0.259033203125,
"001": 0.0283203125,
"110": 0.123046875,
"100": 0.055908203125,
},
{
"1001": 0.0029296875,
"0000": 0.012939453125,
"1000": 0.008056640625,
"0110": 0.0810546875,
"1100": 0.001708984375,
"0100": 0.054443359375,
"0011": 0.02294921875,
"0001": 0.019775390625,
"0010": 0.013916015625,
"1110": 0.04736328125,
"1010": 0.24365234375,
"1011": 0.24267578125,
"0101": 0.109375,
"1111": 0.042236328125,
"0111": 0.096923828125,
},
{
"1000": 0.007080078125,
"1001": 0.003173828125,
"0010": 0.012939453125,
"0000": 0.011962890625,
"0101": 0.100341796875,
"0110": 0.075927734375,
"0011": 0.018310546875,
"0001": 0.021728515625,
"1100": 0.00146484375,
"0100": 0.052734375,
"1110": 0.048583984375,
"1010": 0.25341796875,
"1011": 0.2470703125,
"1101": 0.00048828125,
"1111": 0.04833984375,
"0111": 0.096435546875,
},
{
"011": 0.128662109375,
"111": 0.291015625,
"001": 0.041259765625,
"110": 0.228271484375,
"100": 0.03857421875,
"000": 0.0380859375,
"010": 0.143310546875,
"101": 0.0908203125,
},
{
"001": 0.0078125,
"100": 0.072265625,
"110": 0.00146484375,
"010": 0.382568359375,
"101": 0.13232421875,
"111": 0.0068359375,
"011": 0.396728515625,
},
{
"000": 0.0029296875,
"010": 0.00341796875,
"001": 0.013671875,
"110": 0.22119140625,
"100": 0.239501953125,
"111": 0.0927734375,
"011": 0.021484375,
"101": 0.405029296875,
},
{
"000": 0.001708984375,
"010": 0.003662109375,
"001": 0.014892578125,
"110": 0.224609375,
"100": 0.234375,
"111": 0.090576171875,
"011": 0.023681640625,
"101": 0.406494140625,
},
{
"111": 0.001953125,
"011": 0.004150390625,
"101": 0.131591796875,
"000": 0.00146484375,
"010": 0.007568359375,
"001": 0.002197265625,
"110": 0.36669921875,
"100": 0.484375,
},
{
"000": 0.001953125,
"100": 0.006103515625,
"110": 0.0693359375,
"001": 0.0283203125,
"111": 0.19091796875,
"011": 0.034423828125,
"101": 0.6689453125,
},
{
"0010": 0.0009765625,
"0000": 0.00244140625,
"1011": 0.00244140625,
"1010": 0.002197265625,
"1001": 0.00439453125,
"1111": 0.043212890625,
"1101": 0.052490234375,
"0001": 0.01318359375,
"0011": 0.016357421875,
"0111": 0.053466796875,
"1110": 0.0283203125,
"0100": 0.150390625,
"1100": 0.093017578125,
"0101": 0.35205078125,
"0110": 0.18505859375,
},
{
"1011": 0.00244140625,
"0000": 0.001953125,
"0010": 0.00146484375,
"1010": 0.003173828125,
"1111": 0.04345703125,
"1101": 0.052001953125,
"1001": 0.00390625,
"0011": 0.018310546875,
"0001": 0.014404296875,
"0110": 0.188720703125,
"1100": 0.09521484375,
"0100": 0.1494140625,
"1110": 0.027587890625,
"0111": 0.054931640625,
"0101": 0.343017578125,
},
],
"cuts": {
"subcircuit_labels": [
"A",
"A",
"A",
"A",
"A",
"A",
"B",
"B",
"B",
"B",
"B",
"B",
],
"coefficients": [
(0.5, 1),
(0.5, 1),
(0.5, 1),
(-0.5, 1),
(0.5, 1),
(-0.5, 1),
],
"partition_label": "AAABBB",
},
},
)
@blp_gate_cutting.response(200, CombineResultsResponseQuokkaSchema)
def combine_results(json: dict):
"""Recombine the results of the subcircuits from the gate cut."""
print("request combine", json)
return gate_cutter.reconstruct_result(
CombineResultsRequest(**json), quokka_format=True
)
42 changes: 0 additions & 42 deletions app/wire_cutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import pickle

import jsonpickle
import numpy as np
from circuit_knitting.cutting import partition_problem, generate_cutting_experiments
from circuit_knitting.cutting.cutqc import (
generate_summation_terms,
cut_circuit_wires,
Expand All @@ -34,7 +32,6 @@
measure_prob,
)
from qiskit import QuantumCircuit
from qiskit.quantum_info import PauliList
from qiskit.transpiler.passes import RemoveBarriers

from app.model.cutting_request import CutCircuitsRequest, CombineResultsRequest
Expand All @@ -43,7 +40,6 @@
CombineResultsResponse,
GateCutCircuitsResponse,
)
from app.partition import get_partitions, get_partition_labels
from app.utils import array_to_counts, counts_to_array, normalize_array


Expand Down Expand Up @@ -115,14 +111,6 @@ def cut_circuit(cutting_request: CutCircuitsRequest):
max_cuts=cutting_request.max_cuts,
num_subcircuits=cutting_request.num_subcircuits,
)
elif cutting_request.method == "automatic_gate_cutting":
res = automatic_gate_cut(
circuit,
num_subcircuits=cutting_request.num_subcircuits,
max_subcircuit_width=cutting_request.max_subcircuit_width,
max_cuts=cutting_request.max_cuts,
)
return GateCutCircuitsResponse(format=cutting_request.circuit_format, **res)
else:
res = cut_circuit_wires(
circuit,
Expand Down Expand Up @@ -201,36 +189,6 @@ def reconstruct_result(input_dict: CombineResultsRequest, quokka_format=False):
return CombineResultsResponse(result=res)


def automatic_gate_cut(circuit, num_subcircuits, max_subcircuit_width, max_cuts):
partitions, _ = get_partitions(circuit, num_subcircuits[0], max_subcircuit_width)
partition_labels = get_partition_labels(partitions)
partitioned_problem = partition_problem(
circuit=circuit,
partition_labels=partition_labels,
observables=PauliList(["Z" * circuit.num_qubits]),
)
subexperiments, coefficients = generate_cutting_experiments(
circuits=partitioned_problem.subcircuits,
observables=partitioned_problem.subobservables,
num_samples=np.inf,
)

individual_subcircuits = []
subcircuit_labels = []

for label, circs in subexperiments.items():
for sub_circ in circs:
individual_subcircuits.append(sub_circ)
subcircuit_labels.append(label)

return {
"individual_subcircuits": individual_subcircuits,
"subcircuit_labels": subcircuit_labels,
"coefficients": coefficients,
"partition_labels": partition_labels,
}


def convert_subcircuit_results(subcircuit_results, subcircuits):
converted_result = {}
for circ_fragment, frag_results in subcircuit_results.items():
Expand Down
Loading

0 comments on commit 39ffab1

Please sign in to comment.