Skip to content

Commit

Permalink
Merge pull request #117 from nbronn/bug/mapomatic-upgrade
Browse files Browse the repository at this point in the history
removed mapomatic dependency
  • Loading branch information
kevinsung authored Aug 13, 2024
2 parents 19c6eea + 0aa9df2 commit 0d5416a
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 521 deletions.
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ classifiers = [
requires-python = ">=3.8"

dependencies = [
"mapomatic==0.9",
"qiskit==0.46.0",
"qiskit==1.1.1",
"qiskit-aer",
"qiskit-ibm-runtime",
]
Expand Down
29 changes: 14 additions & 15 deletions qiskit_research/utils/convenience.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import Gate
from qiskit.circuit.library import XGate
from qiskit.providers.backend import Backend
from qiskit.transpiler import PassManager
from qiskit.providers import Backend
from qiskit.transpiler import PassManager, Target
from qiskit.transpiler.passes.scheduling import ALAPScheduleAnalysis
from qiskit.transpiler.passes.scheduling.scheduling.base_scheduler import BaseScheduler

Expand All @@ -37,7 +37,7 @@

def add_dynamical_decoupling(
circuits: Union[QuantumCircuit, List[QuantumCircuit], List[List[QuantumCircuit]]],
backend: Backend,
target: Target,
dd_str: str,
scheduler: BaseScheduler = ALAPScheduleAnalysis,
add_pulse_cals: bool = False,
Expand All @@ -47,12 +47,12 @@ def add_dynamical_decoupling(
"""Add dynamical decoupling sequences and calibrations to circuits.
Adds dynamical decoupling sequences and the calibrations necessary
to run them on an IBM backend.
to run them on an IBM backend target.
Args:
circuits (Union[QuantumCircuit, List[QuantumCircuit], List[List[QuantumCircuit]]]):
input QuantumCircuit or sequences thereof.
backend (Backend): Backend to run on; gate timing is required for this method.
target (Target): Backend to run on; gate timing is required for this method.
dd_str (str): String describing DD sequence to use.
scheduler (BaseScheduler, optional): Scheduler, defaults to ALAPScheduleAnalysis.
add_pulse_cals (bool, optional): Add Pulse calibrations for non-basis
Expand All @@ -67,23 +67,22 @@ def add_dynamical_decoupling(
single or sequence type of QuantumCircuit, shceduled with DD sequences
inserted into idle times.
"""

pass_manager = PassManager(
list(
dynamical_decoupling_passes(
backend, dd_str, scheduler, urdd_pulse_num=urdd_pulse_num
target, dd_str, scheduler, urdd_pulse_num=urdd_pulse_num
)
)
)
if isinstance(circuits, QuantumCircuit) or isinstance(circuits[0], QuantumCircuit):
circuits_dd = pass_manager.run(circuits)
if add_pulse_cals:
add_pulse_calibrations(circuits_dd, backend, pulse_method=pulse_method)
add_pulse_calibrations(circuits_dd, target, pulse_method=pulse_method)
else:
circuits_dd = [pass_manager.run(circs) for circs in circuits]
if add_pulse_cals:
for circs_dd in circuits_dd:
add_pulse_calibrations(circs_dd, backend, pulse_method=pulse_method)
add_pulse_calibrations(circs_dd, target, pulse_method=pulse_method)

return circuits_dd

Expand Down Expand Up @@ -169,7 +168,7 @@ def add_pauli_twirls(
@overload
def scale_cr_pulses(
circuits: List[QuantumCircuit],
backend: Backend,
target: Target,
unroll_rzx_to_ecr: Optional[bool] = True,
force_zz_matches: Optional[bool] = True,
param_bind: Optional[dict] = None,
Expand All @@ -179,7 +178,7 @@ def scale_cr_pulses(
@overload
def scale_cr_pulses(
circuits: QuantumCircuit,
backend: Backend,
target: Target,
unroll_rzx_to_ecr: Optional[bool] = True,
force_zz_matches: Optional[bool] = True,
param_bind: Optional[dict] = None,
Expand All @@ -188,7 +187,7 @@ def scale_cr_pulses(

def scale_cr_pulses(
circuits,
backend,
target,
unroll_rzx_to_ecr: Optional[bool] = True,
force_zz_matches: Optional[bool] = True,
param_bind: Optional[dict] = None,
Expand All @@ -204,7 +203,7 @@ def scale_cr_pulses(
pass_manager = PassManager(
list(
cr_scaling_passes(
backend,
target,
templates,
unroll_rzx_to_ecr=unroll_rzx_to_ecr,
force_zz_matches=force_zz_matches,
Expand All @@ -217,15 +216,15 @@ def scale_cr_pulses(

def attach_cr_pulses(
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
backend: Backend,
target: Target,
param_bind: dict,
) -> Union[QuantumCircuit, List[QuantumCircuit]]:
"""
Scale circuits using Pulse scaling technique from
http://arxiv.org/abs/2012.11660. Binds parameters
in param_bind and attaches pulse gates.
"""
pass_manager = PassManager(list(pulse_attaching_passes(backend, param_bind)))
pass_manager = PassManager(list(pulse_attaching_passes(target, param_bind)))
return pass_manager.run(circuits)


Expand Down
195 changes: 24 additions & 171 deletions qiskit_research/utils/cost_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,181 +12,34 @@

from __future__ import annotations

from typing import List, Tuple

from qiskit.circuit import QuantumCircuit
from qiskit.providers.backend import Backend
from qiskit.qasm import pi

import numpy as np
from qiskit.converters import circuit_to_dag
from qiskit.transpiler import Target
from qiskit.transpiler.passes.layout.vf2_utils import (
build_average_error_map,
build_interaction_graph,
score_layout,
)


def cost_func_scaled_cr(
circ: QuantumCircuit,
layouts: List[List[int]],
backend: Backend,
) -> List[Tuple[List[int], float]]:
"""
A custom cost function that includes T1 and T2 computed during idle periods,
for an already transpiled and scheduled circuit circ
def avg_error_score(qc_isa: QuantumCircuit, target: Target) -> float:
"""Calculate average error score using vf2 utils
Parameters:
circ (QuantumCircuit): scheduled circuit of interest
backend (IBMQBackend): An IBM Quantum backend instance
Args:
qc_isa (QuantumCircuit): transpiled circuit
target (Target): backend target
Returns:
float: error
float: average error score determined by average error map
"""
out = []
props = backend.properties()
inst_sched_map = backend.defaults().instruction_schedule_map
dt = backend.configuration().dt
num_qubits = backend.configuration().num_qubits
t1s = [props.qubit_property(qq, "T1")[0] for qq in range(num_qubits)]
t2s = [props.qubit_property(qq, "T2")[0] for qq in range(num_qubits)]

error = 0.0
fid = 1.0
touched = set()
for layout in layouts:
for item in circ.data:
if item[0].num_qubits == 2:
q0 = circ.find_bit(item[1][0]).index
q1 = circ.find_bit(item[1][1]).index
if inst_sched_map.has("cx", qubits=[q0, q1]):
basis_2q_error = props.gate_error("cx", [q0, q1])
elif inst_sched_map.has("ecr", qubits=[q0, q1]):
basis_2q_error = props.gate_error("ecr", [q0, q1])
elif inst_sched_map.has("ecr", qubits=[q1, q0]):
basis_2q_error = props.gate_error("ecr", [q1, q0])
else:
print(
f"{backend.name} missing 2Q gate between qubit pair ({q0}, {q1})"
)

if item[0].name == "cx":
fid *= 1 - basis_2q_error
touched.add(q0)
touched.add(q1)

# if it is a scaled pulse derived from cx
elif item[0].name == "rzx":
cr_error = np.abs(float(item[0].params[0]) / (pi / 2)) * basis_2q_error

# assumes control qubit is actually control for cr
echo_error = props.gate_error("x", q0)

fid *= 1 - max(cr_error, 2 * echo_error)
elif item[0].name == "secr":
cr_error = (
np.abs((float(item[0].params[0])) / (pi / 2)) * basis_2q_error
)

# assumes control qubit is actually control for cr
echo_error = props.gate_error("x", q0)

fid *= 1 - max(cr_error, echo_error)

elif item[0].name in ["sx", "x"]:
q0 = circ.find_bit(item[1][0]).index
fid *= 1 - props.gate_error(item[0].name, q0)
touched.add(q0)

# if it is a dynamical decoupling pulse
elif item[0].name in ["xp", "xm", "y", "yp", "ym"]:
q0 = circ.find_bit(item[1][0]).index
fid *= 1 - props.gate_error("x", q0)
touched.add(q0)

elif item[0].name == "measure":
q0 = circ.find_bit(item[1][0]).index
fid *= 1 - props.readout_error(q0)
touched.add(q0)

elif item[0].name == "delay":
q0 = circ.find_bit(item[1][0]).index
# Ignore delays that occur before gates
# This assumes you are in ground state and errors
# do not occur.
if q0 in touched:
time = item[0].duration * dt
fid *= 1 - idle_error(time, t1s[q0], t2s[q0])

error = 1 - fid
out.append((layout, error))
return out


def idle_error(
time: float,
t1: float,
t2: float,
) -> float:
"""Compute the approx. idle error from T1 and T2
Parameters:
time (float): Delay time in sec
t1 (float): T1 time in sec
t2, (float): T2 time in sec
Returns:
float: Idle error
"""
t2 = min(t1, t2)
rate1 = 1 / t1
rate2 = 1 / t2
p_reset = 1 - np.exp(-time * rate1)
p_z = (1 - p_reset) * (1 - np.exp(-time * (rate2 - rate1))) / 2
return p_z + p_reset


def cost_func_ecr(
circ: QuantumCircuit,
layouts: List[List[int]],
backend: Backend,
) -> List[Tuple[List[int], float]]:
"""
A custom cost function that includes ECR gates in either direction
Parameters:
circ (QuantumCircuit): circuit of interest
layouts (list of lists): List of specified layouts
backend (IBMQBackend): An IBM Quantum backend instance
Returns:
list: Tuples of layout and cost
"""
out = []
inst_sched_map = backend.defaults().instruction_schedule_map
props = backend.properties()
for layout in layouts:
error = 0.0
fid = 1.0
touched = set()
for item in circ.data:
if item[0].name == "ecr":
q0 = layout[circ.find_bit(item[1][0]).index]
q1 = layout[circ.find_bit(item[1][1]).index]
if inst_sched_map.has("ecr", [q0, q1]):
fid *= 1 - props.gate_error("ecr", [q0, q1])
elif inst_sched_map.has("ecr", [q1, q0]):
fid *= 1 - props.gate_error("ecr", [q1, q0])
else:
print(
f"{backend.name} does not support {item[0].name} \
for qubit pair ({q0}, {q1})"
)
touched.add(q0)
touched.add(q1)

elif item[0].name in ["sx", "x"]:
q0 = layout[circ.find_bit(item[1][0]).index]
fid *= 1 - props.gate_error(item[0].name, q0)
touched.add(q0)

elif item[0].name == "measure":
q0 = layout[circ.find_bit(item[1][0]).index]
fid *= 1 - props.readout_error(q0)
touched.add(q0)

error = 1 - fid
out.append((layout, error))
return out
init_layout = qc_isa.layout.final_index_layout()
dag = circuit_to_dag(qc_isa)

layout = {idx: init_layout[idx] for idx in range(len(init_layout))}
avg_error_map = build_average_error_map(target, None, None)
im_graph, im_graph_node_map, reverse_im_graph_node_map, _ = build_interaction_graph(
dag, strict_direction=False
)
return score_layout(
avg_error_map, layout, im_graph_node_map, reverse_im_graph_node_map, im_graph
)
Loading

0 comments on commit 0d5416a

Please sign in to comment.