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

removed mapomatic dependency #117

Merged
merged 5 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
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
Loading