Skip to content

Commit

Permalink
Added option to run pulse gates in spam estimation and some reformatting
Browse files Browse the repository at this point in the history
  • Loading branch information
haggaila committed Dec 25, 2023
1 parent 2b4809a commit 5233d88
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 95 deletions.
2 changes: 2 additions & 0 deletions project_experiments/experiment_routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ def run_experiment(
n_draws=int(10e6),
n_repeats=n_repeats,
user_qubit_groups=all_qubits,
b_pulse_gates=False,
)
# b_pulse_gates can be set to True for a more accurate estimation in presence of gate error
SPAM_exp = spam_model.build(backend)

qubit_estimation_exp = []
Expand Down
41 changes: 29 additions & 12 deletions project_experiments/library/bayesian/spam_1q_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from qiskit_experiments.framework import (
BaseExperiment,
ParallelExperiment,
BatchExperiment,
)

from project_experiments.partition import partition_qubits
Expand Down Expand Up @@ -52,6 +51,9 @@ class BayesianSPAMBuilder:
transpile_options: Transpile options.
distance: The graph distance parameter for parallelizing the qubits. A value of 1 indicates
all qubits in parallel, a value of 2 indicates next-nearest-neighbors are parallel, etc.
s_add_suffix: An optional suffix string to add to the experiment results.
b_pulse_gates: Whether to use pulse gates. Important if gate errors are not small
enough, and in order to meaningfully estimate gate errors.
"""

def __init__(
Expand All @@ -67,6 +69,8 @@ def __init__(
transpile_options: Optional[dict] = None,
user_qubit_groups: Optional[Sequence[Sequence[Sequence[int]]]] = None,
distance=2,
s_add_suffix: Optional[str] = "",
b_pulse_gates=False,
):
if gates is None:
gates = BayesianSPAMEstimator.BAYESIAN_CPCMG_GATES
Expand All @@ -87,35 +91,48 @@ def __init__(
self._transpile_options = transpile_options
self._user_qubit_groups = user_qubit_groups
self._distance = distance
self._s_add_suffix = s_add_suffix
self._b_pulse_gates = b_pulse_gates

def build(self, backend: Backend) -> [BaseExperiment]:
def build(self, backend: Backend, model=None) -> [BaseExperiment]:
"""Build the batch of parallel experiments according to the qubit groups, constructed
according to the requested `distance` parameter of the constructor.
Args:
backend: The backend whose connectivity is used to parallelize the experiments.
model: An optional BayesianSPAMEstimator to use - if None, one will be created
and initialized.
Returns:
The experiment for the device.
"""
model = BayesianSPAMEstimator(
gates=self.gates,
parameters=self.parameters,
prior_intervals=self.prior_intervals,
n_draws=self.n_draws,
n_x90p_power=self.n_x90p_power,
n_repeats=self.n_repeats,
)
model.prepare_Bayesian()
if model is None:
model = BayesianSPAMEstimator(
gates=self.gates,
parameters=self.parameters,
prior_intervals=self.prior_intervals,
n_draws=self.n_draws,
n_x90p_power=self.n_x90p_power,
n_repeats=self.n_repeats,
)
model.prepare_Bayesian()

qubit_groups = self._user_qubit_groups or partition_qubits(
backend, self._distance
)
faulty_qubits = backend.properties().faulty_qubits()
par_exps = []
for group in qubit_groups:
exps = []
for qubit in group:
exp = BayesianSPAMExperiment(qubit=qubit[0], model=model)
if qubit[0] in faulty_qubits:
continue
exp = BayesianSPAMExperiment(
qubit=qubit[0],
model=model,
add_suffix=self._s_add_suffix,
b_pulse_gates=self._b_pulse_gates,
)
if self._experiment_options:
exp.set_experiment_options(**self._experiment_options)
if self._analysis_options:
Expand Down
147 changes: 83 additions & 64 deletions project_experiments/library/bayesian/spam_1q_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class BayesianSPAMEstimator:
"y90p",
"y90m",
"x90p^2",
"x90p^5",
"x90m^5",
"y90p^5",
"y90m^5",
"x90p^4n",
"x90p^(4n+1)",
]
Expand All @@ -58,7 +62,7 @@ class BayesianSPAMEstimator:

NUM_SUPPORTED_PARAMS = len(SUPPORTED_PARAMETERS)
"""The number of parameters that are supported for estimation (as listed in
SUPPORTED_PARAMETERS)."""
SUPPORTED_PARAMETERS)."""

PARAM_BOUNDARIES = {
"pi_x": (-1.0, 1.0),
Expand All @@ -72,7 +76,7 @@ class BayesianSPAMEstimator:
"theta_x90p": (-np.pi / 4, np.pi / 4),
}
"""Boundaries of the intervals over which the prior (uniform) distribution of each parameter can
be defined."""
be defined."""

BAYESIAN_QPCM_PARAMETERS = ["x_0", "y_0", "z_0", "pi_z", "pi_0"]
"""The five parameters for estimation of Quantum Preparation and Classical Measurement errors."""
Expand All @@ -85,8 +89,19 @@ class BayesianSPAMEstimator:
[0.45, 0.6],
]
"""Five default parameter priors for estimation corresponding to the parameters defined in
BAYESIAN_QPCM_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""
BAYESIAN_QPCM_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""

BAYESIAN_QPCMp5_PRIORS = [
[-0.2, 0.2],
[-0.2, 0.2],
[0.82, 1.0],
[0.4, 0.55],
[0.45, 0.6],
]
"""Five default parameter priors for estimation corresponding to the parameters defined in
BAYESIAN_QPCM_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""

BAYESIAN_QPCMG_PARAMETERS = [
"x_0",
Expand All @@ -98,7 +113,7 @@ class BayesianSPAMEstimator:
"theta_x90p",
]
"""The seven parameters supported for estimation of Quantum Preparation / Classical Measurement
and Gate errors."""
and Gate errors."""

BAYESIAN_QPCMG_PRIORS = [
[-0.1, 0.1],
Expand All @@ -110,12 +125,12 @@ class BayesianSPAMEstimator:
[-0.01, 0.01],
]
"""Seven default parameter priors for estimation corresponding to parameters defined in
BAYESIAN_QPCMG_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""
BAYESIAN_QPCMG_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""

BAYESIAN_CPCMG_PARAMETERS = ["z_0", "pi_z", "pi_0", "epsilon_x90p", "theta_x90p"]
"""The five parameters supported for estimation of Classical Preparation / Classical Measurement
and Gate errors."""
and Gate errors."""

BAYESIAN_CPCMG_PRIORS = [
[0.82, 1.0],
Expand All @@ -125,16 +140,20 @@ class BayesianSPAMEstimator:
[-0.02, 0.02],
]
"""Seven default parameter priors for estimation corresponding to parameters defined in
BAYESIAN_CPCMG_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""
BAYESIAN_CPCMG_PARAMETERS. May not be suitable for all devices, if their manifested errors
are too large."""

BAYESIAN_DIRECT_GATES = ["id", "x", "x90p", "x90m", "y90p", "y90m"]
"""The six nonconcatenated gates used for estimation using a Bayesian estimation, without
gate errors."""
gate errors."""

BAYESIAN_QPCM_GATES = ["id", "x", "x90p", "x90m", "y90p", "y90m"]
"""The six nonconcatenated gates used for estimation using a Bayesian estimation, without
gate errors."""
gate errors."""

BAYESIAN_QPCMp5_GATES = ["id", "x90p^2", "x90p^5", "x90m^5", "y90p^5", "y90m^5"]
"""The six nonconcatenated gates used for estimation using a Bayesian estimation, without
gate errors."""

BAYESIAN_CPCMG_GATES = ["id", "x90p^2", "x90p", "x90m", "x90p^4n", "x90p^(4n+1)"]
"""The six gates used for estimation using a Bayesian estimation, without gate errors."""
Expand Down Expand Up @@ -191,54 +210,6 @@ def __init__(
self.n_repeats = n_repeats
self.cube = None

def get_1q_circuits(self, qubit) -> List[QuantumCircuit]:
"""Return a list of experiment circuits.
Returns:
A list of :class:`QuantumCircuit`.
Raises:
Exception: In case of unsupported gates requested.
"""
gates = self.gates

circuits = []
pi_2 = np.pi / 2
for _, s_gate in enumerate(gates):
circ = QuantumCircuit(1, 1)

if s_gate == "x":
circ.x(qubit)
elif s_gate[0:4] == "x90p":
if s_gate == "x90p":
n_len = 1
elif s_gate == "x90p^2":
n_len = 2
elif s_gate == "x90p^4n":
n_len = 4 * self.n_x90p_power
elif s_gate == "x90p^(4n+1)":
n_len = 4 * self.n_x90p_power + 1
else:
raise Exception(f"Unknown/unsupported instruction {s_gate}.")
for _ in range(n_len):
circ.rx(pi_2, qubit)
elif s_gate == "x90m":
circ.rx(-pi_2, qubit)
elif s_gate == "y90p":
circ.ry(pi_2, qubit)
elif s_gate == "y90m":
circ.ry(pi_2, -qubit)
elif s_gate == "id":
pass
else:
raise Exception(f"Unknown/unsupported instruction {s_gate}.")
circ.measure(0, 0)

circ.metadata = {"experiment_type": "Bayesian-spam-1q", "qubits": [qubit]}
circuits.append(circ)

return circuits

def prepare_Bayesian(self):
"""Precomputes the Monte Carlo cube used for Bayesian estimation, enforcing constraints.
With Python being an interpreted language and this computation being a bottle neck of the
Expand Down Expand Up @@ -468,28 +439,28 @@ def prepare_Bayesian(self):
+ vals[ipiy] * vals[iy0]
+ vals[ipiz] * vals[iz0]
)
elif s_gate == "x90p" or s_gate == "x90p^(4n+1)":
elif s_gate == "x90p" or s_gate == "x90p^(4n+1)" or s_gate == "x90p^5":
p = (
vals_pi0
+ vals[ipix] * vals[ix0]
- vals[ipiy] * vals[iz0]
+ vals[ipiz] * vals[iy0]
)
elif s_gate == "x90m":
elif s_gate == "x90m" or s_gate == "x90m^5":
p = (
vals_pi0
+ vals[ipix] * vals[ix0]
+ vals[ipiy] * vals[iz0]
- vals[ipiz] * vals[iy0]
)
elif s_gate == "y90p":
elif s_gate == "y90p" or s_gate == "y90p^5":
p = (
vals_pi0
+ vals[ipix] * vals[iz0]
+ vals[ipiy] * vals[iy0]
- vals[ipiz] * vals[ix0]
)
elif s_gate == "y90m":
elif s_gate == "y90m" or s_gate == "y90m^5":
p = (
vals_pi0
- vals[ipix] * vals[iz0]
Expand Down Expand Up @@ -639,3 +610,51 @@ def estimate_Bayesian(
result["cube"] = cube
result["P"] = P
return result

# def get_1q_circuits(self, qubit) -> List[QuantumCircuit]:
# """Return a list of experiment circuits.
#
# Returns:
# A list of :class:`QuantumCircuit`.
#
# Raises:
# Exception: In case of unsupported gates requested.
# """
# gates = self.gates
#
# circuits = []
# pi_2 = np.pi / 2
# for _, s_gate in enumerate(gates):
# circ = QuantumCircuit(1, 1)
#
# if s_gate == "x":
# circ.x(qubit)
# elif s_gate[0:4] == "x90p":
# if s_gate == "x90p":
# n_len = 1
# elif s_gate == "x90p^2":
# n_len = 2
# elif s_gate == "x90p^4n":
# n_len = 4 * self.n_x90p_power
# elif s_gate == "x90p^(4n+1)":
# n_len = 4 * self.n_x90p_power + 1
# else:
# raise Exception(f"Unknown/unsupported instruction {s_gate}.")
# for _ in range(n_len):
# circ.rx(pi_2, qubit)
# elif s_gate == "x90m":
# circ.rx(-pi_2, qubit)
# elif s_gate == "y90p":
# circ.ry(pi_2, qubit)
# elif s_gate == "y90m":
# circ.ry(pi_2, -qubit)
# elif s_gate == "id":
# pass
# else:
# raise Exception(f"Unknown/unsupported instruction {s_gate}.")
# circ.measure(0, 0)
#
# circ.metadata = {"experiment_type": "Bayesian-spam-1q", "qubits": [qubit]}
# circuits.append(circ)
#
# return circuits
Loading

0 comments on commit 5233d88

Please sign in to comment.