Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
8414ada
add new probability node
Oct 6, 2021
26b5b82
update hamiltonian tomo analysis logic and test
Oct 9, 2021
7a62da0
update error amplification analysis logic and test
Oct 11, 2021
482378c
update drag analysis logic
Oct 11, 2021
fa3537a
update rb data cache
Oct 11, 2021
0f7a504
black&lint
Oct 11, 2021
4c66e3a
add reno
Oct 11, 2021
1ddc994
add test for new probability
Oct 11, 2021
e225ff1
Merge branch 'main' of github.com:Qiskit/qiskit-experiments into upgr…
Oct 11, 2021
3211c4d
fix new test in main branch
Oct 11, 2021
3f64364
update error amplification analysis logic
Oct 11, 2021
1e8750f
fix error in the alpha_0 computation
Oct 11, 2021
cd7238a
set data driven test for fine amp module
Oct 11, 2021
d05a3b0
fix amp in fine amp analysis
Oct 11, 2021
f9a664a
black
Oct 11, 2021
10b00a0
minor update to fine amp guess logic
Oct 11, 2021
ac5b44a
Update qiskit_experiments/curve_analysis/standard_analysis/error_ampl…
nkanazawa1989 Oct 12, 2021
a6570a6
Update qiskit_experiments/curve_analysis/standard_analysis/error_ampl…
nkanazawa1989 Oct 12, 2021
bcb62c5
Update qiskit_experiments/curve_analysis/standard_analysis/error_ampl…
nkanazawa1989 Oct 12, 2021
b74c942
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 12, 2021
b85c159
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 12, 2021
eaa97de
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 12, 2021
a9a43c1
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 12, 2021
5484f99
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 12, 2021
b95d833
documentation update
Oct 12, 2021
e534cea
fix bug and add test for p = 0.5
Oct 12, 2021
c2dfbbe
update rb cache
Oct 12, 2021
3f61e3f
black
Oct 12, 2021
7e0efec
revert test accuracy change
Oct 12, 2021
4290bc4
fix probability test
Oct 12, 2021
aca3d6b
Update qiskit_experiments/data_processing/nodes.py
nkanazawa1989 Oct 13, 2021
f28c924
merge dirichlet probability with probability
Oct 13, 2021
4ee4383
update reno
Oct 13, 2021
62f7d82
update rb cache
Oct 13, 2021
194568b
fix description of outcome arg
Oct 13, 2021
08efb3d
fix data processor unittest
Oct 13, 2021
11f6c58
fix test
Oct 13, 2021
947f952
logic update (assume beta distribution)
Oct 18, 2021
b7c6c06
Merge branch 'main' of github.com:Qiskit/qiskit-experiments into upgr…
Oct 18, 2021
5351a81
update rb cache
Oct 18, 2021
1ceee7e
fix test
Oct 18, 2021
7c8b142
rb cache update
Oct 19, 2021
8c12a8d
Update docstring for Probability node
chriseclectic Oct 25, 2021
93276b7
Merge pull request #4 from chriseclectic/beta-prob
nkanazawa1989 Oct 25, 2021
9f3e946
update rb cache
Oct 25, 2021
da4bf5d
Merge branch 'main' of github.com:Qiskit/qiskit-experiments into upgr…
Oct 25, 2021
9fe8770
fix test
Oct 25, 2021
8291aa8
black
Oct 25, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import numpy as np

import qiskit_experiments.curve_analysis as curve
from qiskit_experiments.exceptions import CalibrationError


class ErrorAmplificationAnalysis(curve.CurveAnalysis):
Expand All @@ -32,7 +31,29 @@ class ErrorAmplificationAnalysis(curve.CurveAnalysis):

.. math::
y = \frac{{\rm amp}}{2}\cos\left(x[{\rm d}\theta + {\rm apg} ] \
+{\rm phase\_offset}\right)+{\rm base}
-{\rm phase\_offset}\right)+{\rm base}

The fit function can be transformed into
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

.. math::
y = \frac{{\rm amp}}{2} \left(\
\cos\right({\rm d}\theta \cdot x\left)\
\cos\right({\rm apg} \cdot x - {\rm phase\_offset}\left) -\
\sin\right({\rm d}\theta \cdot x\left)\
\sin\right({\rm apg} \cdot x - {\rm phase\_offset}\left)
\right) + {\rm base}

When :math:`{\rm apg} \cdot x - {\rm phase\_offset} = (2n + 1) \pi/2` is satisfied,
above fit model can be simplified to
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

.. math::
y = \mp \frac{{\rm amp}}{2} \sin\left({\rm d}\theta \cdot x\right) + {\rm base}

In the limit :math:`{\rm d}\theta \ll 1`, the error can be estimated from curve data
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

.. math::
{\rm d}\theta \simeq \mp \frac{2(y - {\rm base})}{x \cdot {\rm amp}}


# section: fit_parameters
defpar \rm amp:
Expand All @@ -49,6 +70,8 @@ class ErrorAmplificationAnalysis(curve.CurveAnalysis):
desc: The angle offset in the gate that we wish to measure.
init_guess: Multiple initial guesses are tried ranging from -a to a
where a is given by :code:`max(abs(angle_per_gate), np.pi / 2)`.
Extra guesses are added based on curve data when either :math:`\rm amp` or
:math:`\rm base` is :math:`\pi/2`. See fit model for details.
bounds: [-pi, pi].

# section: note
Expand All @@ -63,7 +86,7 @@ class ErrorAmplificationAnalysis(curve.CurveAnalysis):
x,
amp=0.5 * amp,
freq=(d_theta + angle_per_gate) / (2 * np.pi),
phase=phase_offset,
phase=-phase_offset,
baseline=base,
),
plot_color="blue",
Expand All @@ -86,7 +109,6 @@ def _default_options(cls):
:math:`\pi/2` if the square-root of X gate is added before the repeated gates.
This is decided for the user in :meth:`set_schedule` depending on whether the
sx gate is included in the experiment.
number_of_guesses (int): The number of initial guesses to try.
max_good_angle_error (float): The maximum angle error for which the fit is
considered as good. Defaults to :math:`\pi/2`.
"""
Expand All @@ -96,7 +118,6 @@ def _default_options(cls):
default_options.ylabel = "Population"
default_options.angle_per_gate = None
default_options.phase_offset = 0.0
default_options.number_guesses = 21
default_options.max_good_angle_error = np.pi / 2

return default_options
Expand All @@ -115,8 +136,6 @@ def _generate_fit_guesses(
Raises:
CalibrationError: When ``angle_per_gate`` is missing.
"""
n_guesses = self._get_option("number_guesses")

curve_data = self._data()
max_abs_y, _ = curve.guess.max_height(curve_data.y, absolute=True)
max_y, min_y = np.max(curve_data.y), np.min(curve_data.y)
Expand All @@ -128,15 +147,43 @@ def _generate_fit_guesses(
user_opt.p0.set_if_empty(amp=max_y - min_y)
user_opt.bounds.set_if_empty(amp=(-2 * max_abs_y, 2 * max_abs_y))

# Base the initial guess on the intended angle_per_gate.
angle_per_gate = self._get_option("angle_per_gate")

if angle_per_gate is None:
raise CalibrationError("The angle_per_gate was not specified in the analysis options.")
# Base the initial guess on the intended angle_per_gate and phase offset.
apg = self._get_option("angle_per_gate")
phi = self._get_option("phase_offset")

# Prepare logical guess for specific condition (often satisfied)

# The fit function can be transformed into
# y = A cos((d_theta + apg) x - phi)
# = A cos(d_theta x)cos(apg x - phi) - A sin(d_theta x)sin(apg x - phi)
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
# If apg x - phi = (2n + 1) pi / 2 is satisfied,
# y = ∓ A sin(d_theta x) ~ ∓ A d_theta x, when d_theta x << 1
# Finally, d_theta = ∓ y / (A x)

d_theta_guesses = []

offsets = apg * curve_data.x + phi
amp = user_opt.p0.get("amp", self._get_option("amp"))
for i in range(curve_data.x.size):
xi = curve_data.x[i]
yi = curve_data.y[i]
if np.isclose(offsets[i] % np.pi, np.pi / 2) and xi > 0:
# Condition satisfied: i.e. cos(apg x + phi) = 0
err = -np.sign(np.sin(offsets[i])) * (yi - user_opt.p0["base"]) / (0.5 * amp)
# Validate estimate. This is first order term of Maclaurin expansion.
if np.abs(err) < 0.5:
d_theta_guesses.append(err / xi)
else:
# Terminate guess generation because larger d_theta x will start to
# reduce net y value and underestimate the rotation.
break
Comment thread
nkanazawa1989 marked this conversation as resolved.

# Add naive guess for more coverage
guess_range = max(abs(apg), np.pi / 2)
d_theta_guesses.extend(np.linspace(-guess_range, guess_range, 21))
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

guess_range = max(abs(angle_per_gate), np.pi / 2)
options = []
for d_theta_guess in np.linspace(-guess_range, guess_range, n_guesses):
for d_theta_guess in d_theta_guesses:
new_opt = user_opt.copy()
new_opt.p0.set_if_empty(d_theta=d_theta_guess)
options.append(new_opt)
Expand Down
2 changes: 2 additions & 0 deletions qiskit_experiments/data_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
:toctree: ../stubs/

Probability
DirichletProbability
ToImag
ToReal
SVD
Expand All @@ -47,6 +48,7 @@
from .data_action import DataAction, TrainableDataAction
from .nodes import (
Probability,
DirichletProbability,
ToImag,
ToReal,
SVD,
Expand Down
81 changes: 81 additions & 0 deletions qiskit_experiments/data_processing/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,87 @@ def _population_error(self, counts_dict) -> Tuple[float, float]:
return p_mean, np.sqrt(p_var)


class DirichletProbability(Probability):
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
r"""Compute probability and variance from count dictionary.

This node is a subtype of :py:class:`~qiskit_experiments.data_processing.nodes.Probability`,
in which variance is computed based on a binomial distribution at the risk of zero variance
at probability at either zero or one.

This node avoid such singularity by assuming Dirichlet distribution with Bayes update
taking a prior distribution. Namely, a mean value is replaced by a mode that represents
the most likely value to be sampled
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

.. math::

p = \frac{\alpha_i - 1}{\alpha_0 + K}
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated

where :math:`\alpha_i = f_i + \theta_i`, :math:`\alpha_0 = \sum_{i=1}^K \alpha_i`,
:math:`\theta_i` is the prior distribution and :math:`f_i` is
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
the count of :math:`i`-th element of :math:`K` dimensional vector.
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
The variance is computed by

.. math::

v = \frac{E[x] (1 - E[x])}{\alpha_0 + 1}

where :math:`E[x] = \alpha_i / \alpha_0` is the mean value of the outcome of interest.
With a finite prior, this node always returns finite variance.
This saves us from the risk of unexpected zero division throughout the stack.
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
"""

def __init__(
self,
outcome: str = "1",
prior: Union[Dict[str, float], float] = 0.5,
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
validate: bool = True,
):
"""Initialize a counts to probability data conversion.

Args:
outcome: The bitstring for which to compute the probability which defaults to "1".
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After getting to here I think the whole distribution stuff in the into is a bit confusion now. The Dirichlet distribution if for computing a multi-variate probability distribution, so for example converting observed counts into an estimate of a probability distribution over all possible outcomes (2^n). If it is for a binary estimate this should be a beta distribution (which is the special case of 2-outcome dirichlet distribution).

The way the description of this class is worded it should be something that converts a full counts dict into a full probabilities dict + std error dict since the bayesian update is for a full distribution on all outcomes, not a distribution for a single outcome. The difference between estimating individual probabilities as 2-outcome distributions vs all probabilities as 2^n outcome distribution will result in slight differences in variances and what they mean.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the documentation for the option is confusing, i.e. it uses multivariable distribution but distribution defaults to 2-outcome. I updated the documentation and removed the default value in 194568b

prior: Prior distribution. This can be float value or dictionary keyed on
outcome bitstring. If n-bit label is provided in ``outcome``, the
dimension of the prior distribution, i.e. dictionary length, should be :math:`2^n`.
If a float value is applied, this applies a flat prior with the provided value.
By default, this assumes flat prior of 0.5 corresponding to the Jeffery's prior.
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
validate: If set to False the DataAction will not validate its input.

Raises:
DataProcessorError: When dimension of prior and expected parameter vector
doesn't match.
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
"""
self._dim = 2 ** len(outcome)
self._prior = prior
super().__init__(outcome=outcome, validate=validate)

if isinstance(prior, dict) and self._dim != len(prior):
raise DataProcessorError(
"Dimension of probability density function and prior distribution doesn't match."
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
)

def _population_error(self, counts_dict) -> Tuple[float, float]:
Comment thread
nkanazawa1989 marked this conversation as resolved.
Outdated
"""Helper method"""
shots = sum(counts_dict.values())
freq = counts_dict.get(self._outcome, 0.0)

if isinstance(self._prior, dict):
alpha_i = freq + self._prior[self._outcome]
alpha_0 = sum([v + self._prior[k] for k, v in counts_dict.items()])
else:
alpha_i = freq + self._prior
alpha_0 = shots + self._prior * self._dim

p_mean = alpha_i / alpha_0
p_var = p_mean * (1 - p_mean) / (alpha_0 + 1)
mode = (alpha_i - 1) / (alpha_0 + self._dim)

# If outcome count is zero, mode becomes tiny negative value with prior < 1
mode = max(0.0, mode)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the variable name. I though mode is the value that appears most often in the data. Since we have counts I would naively expect this to be 0 or 1. Should this be named first_moment or mean instead of mode?

Copy link
Copy Markdown
Collaborator Author

@nkanazawa1989 nkanazawa1989 Oct 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just referred to the definition here. According to the definition, alpha_i > 0 and it can be a negative value when alpha_i is less than 1 it says alpha_i > 1. So probably I use wrong definition. Please correct me if I miss something @oliverdial


return mode, np.sqrt(p_var)


class BasisExpectationValue(DataAction):
"""Compute expectation value of measured basis from probability.

Expand Down
10 changes: 5 additions & 5 deletions qiskit_experiments/data_processing/processor_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from qiskit_experiments.data_processing.exceptions import DataProcessorError
from qiskit_experiments.data_processing.data_processor import DataProcessor
from qiskit_experiments.data_processing.nodes import AverageData, Probability, SVD, MinMaxNormalize
from qiskit_experiments.data_processing import nodes


def get_processor(
Expand All @@ -38,16 +38,16 @@ def get_processor(
DataProcessorError: if the measurement level is not supported.
"""
if meas_level == MeasLevel.CLASSIFIED:
return DataProcessor("counts", [Probability("1")])
return DataProcessor("counts", [nodes.DirichletProbability("1")])

if meas_level == MeasLevel.KERNELED:
if meas_return == "single":
processor = DataProcessor("memory", [AverageData(axis=1), SVD()])
processor = DataProcessor("memory", [nodes.AverageData(axis=1), nodes.SVD()])
else:
processor = DataProcessor("memory", [SVD()])
processor = DataProcessor("memory", [nodes.SVD()])

if normalize:
processor.append(MinMaxNormalize())
processor.append(nodes.MinMaxNormalize())

return processor

Expand Down
15 changes: 9 additions & 6 deletions qiskit_experiments/library/calibration/analysis/drag_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,19 @@ def _generate_fit_guesses(
beta=(-freq_bound, freq_bound),
base=(-max_abs_y, max_abs_y),
)
user_opt.p0.set_if_empty(amp=0.5, base=0.5)
user_opt.p0.set_if_empty(base=0.5)

# Drag curves can sometimes be very flat, i.e. averages of y-data
# and min-max do not always make good initial guesses. We therefore add
# 0.5 to the initial guesses.
# 0.5 to the initial guesses. Note that we also set amp=-0.5 because the cosine function
# becomes +1 at zero phase, i.e. optimal beta, in which y data should become zero
# in discriminated measurement level.
options = []
for beta_guess in np.linspace(min_beta, max_beta, 20):
new_opt = user_opt.copy()
new_opt.p0.set_if_empty(beta=beta_guess)
options.append(new_opt)
for amp_guess in (0.5, -0.5):
for beta_guess in np.linspace(min_beta, max_beta, 20):
new_opt = user_opt.copy()
new_opt.p0.set_if_empty(amp=amp_guess, beta=beta_guess)
options.append(new_opt)

return options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ class FineAmplitudeAnalysis(ErrorAmplificationAnalysis):
"""

# The intended angle per gat of the gate being calibrated, e.g. pi for a pi-pulse.
__fixed_parameters__ = ["angle_per_gate", "phase_offset"]

# TODO remove amp from fixed parameter.
__fixed_parameters__ = ["angle_per_gate", "phase_offset", "amp"]
10 changes: 10 additions & 0 deletions qiskit_experiments/library/calibration/fine_amplitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ def _default_experiment_options(cls) -> Options:

return options

@classmethod
def _default_analysis_options(cls) -> Options:
"""Default analysis options."""
options = super()._default_analysis_options()

# TODO remove amp from fixed parameter.
options.amp = 1.0

return options

def __init__(self, qubit: int):
"""Setup a fine amplitude experiment on the given qubit.

Expand Down
2 changes: 1 addition & 1 deletion qiskit_experiments/library/calibration/fine_drag.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def _default_analysis_options(cls) -> Options:
options = super()._default_analysis_options()
options.normalization = True
options.angle_per_gate = 0.0
options.phase_offset = -np.pi / 2
options.phase_offset = np.pi / 2
options.amp = 1.0

return options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ def _default_options(cls):
"""Return the default analysis options."""
default_options = super()._default_options()
default_options.data_processor = dp.DataProcessor(
input_key="counts", data_actions=[dp.Probability("1"), dp.BasisExpectationValue()]
input_key="counts",
data_actions=[dp.DirichletProbability("1"), dp.BasisExpectationValue()],
)
default_options.curve_plotter = "mpl_multiv_canvas"
default_options.xlabel = "Flat top width"
Expand Down Expand Up @@ -266,21 +267,23 @@ def _generate_fit_guesses(

guesses = defaultdict(list)
for control in (0, 1):
# start from Z oscillation
x_data = self._data(series_name=f"x|c={control}")
y_data = self._data(series_name=f"y|c={control}")
z_data = self._data(series_name=f"z|c={control}")

omega_xyz = []
for data in (x_data, y_data, z_data):
ymin, ymax = np.percentile(data.y, [10, 90])
if ymax - ymin < 0.2:
# oscillation amplitude might be almost zero,
# then exclude from average because of lower SNR
continue
Comment on lines +276 to +280
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prompted this change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, I let the FFT module estimate a frequency regardless of the signal amplitude. In some test cases, especially when the signal is really weak, the guess module picks random frequency (because computed probability has been changed), and it hurts frequency guess of averaged oscillation frequency measured in x, y, z basis. So I decided to ignore the guess when SNR is likely low, i.e. it may pick some artifact.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! I wonder if using the actual SNR (min-max)/(sqrt(variance)) might be nice in case someone tries to fit a very small/off resonant rotation with this at some point, so that they can average a lot and still do it? Might be a separate PR

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is nice idea. What is the variance here? Is it the variance of y values np.std(data.y) or one computed from the sampling error data.y_err? If latter one, probably np.mean(data.y_err).

fft_freq = curve.guess.frequency(data.x, data.y)
# oscillation amplitude might be almost zero, then exclude from average
if fft_freq > 0:
omega_xyz.append(fft_freq)
omega_xyz.append(fft_freq)
if omega_xyz:
omega = 2 * np.pi * np.average(omega_xyz)
else:
omega = 0.0
omega = 1e-3

zmin, zmax = np.percentile(z_data.y, [10, 90])
theta = np.arccos(np.sqrt((zmax - zmin) / 2))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
from qiskit.quantum_info import Clifford
from qiskit.circuit import Gate

import qiskit_experiments.data_processing as dp
from qiskit_experiments.framework import BaseExperiment, ParallelExperiment, Options
from qiskit_experiments.curve_analysis.data_processing import probability
from .rb_analysis import RBAnalysis
from .clifford_utils import CliffordUtils
from .rb_utils import RBUtils
Expand Down Expand Up @@ -88,7 +88,12 @@ def __init__(

# Set configurable options
self.set_experiment_options(lengths=list(lengths), num_samples=num_samples)
self.set_analysis_options(data_processor=probability(outcome="0" * self.num_qubits))
self.set_analysis_options(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the get function of the data processor library?

Copy link
Copy Markdown
Collaborator Author

@nkanazawa1989 nkanazawa1989 Oct 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked but we cannot use the getter. This is a custom processor for RB, because outcome depends on the number of qubits, and it counts for label "0" instead of "1". Of course we can modify the getter, but this should be done in the separate PR.

data_processor=dp.DataProcessor(
input_key="counts",
data_actions=[dp.DirichletProbability(outcome="0" * self.num_qubits)],
)
)

# Set fixed options
self._full_sampling = full_sampling
Expand Down
Loading