Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 16 additions & 12 deletions src/qc_compiler/mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class MitigationPlan:
subcircuit_sensitivity: Mapping from segment index to
sensitivity score.
total_shots: Total number of shots across all scales.
method: Mitigation method ('zne', 'pec', or 'cdr').
method: Mitigation method ('zne', 'adaptive', 'pec', or 'cdr').
segments: Number of circuit segments identified.
shots_per_segment: Mapping from segment index to shot count.
scales_per_segment: Mapping from segment index to list of
Expand Down Expand Up @@ -175,19 +175,21 @@ def create_plan(
variational sensitivity. If None, uses two-qubit
gate count as a proxy for sensitivity.
total_shots: Total shot budget.
method: Mitigation method ('zne', 'pec', or 'cdr').
method: Mitigation method ('zne', 'adaptive', 'pec', or 'cdr').
num_segments: Number of circuit segments. If None,
segments are determined by two-qubit gate boundaries.

Returns:
A MitigationPlan with allocation details.
"""
if method not in ("zne", "pec", "cdr"):
if method not in ("zne", "pec", "cdr", "adaptive"):
raise ValueError(
f"Unknown mitigation method '{method}'. "
"Use 'zne', 'pec', or 'cdr'."
"Use 'zne', 'pec', 'cdr', or 'adaptive'."
)

resolved_method = "zne" if method == "adaptive" else method

if circuit.num_qubits == 0 or circuit.depth() == 0:
return MitigationPlan(
noise_scales=[1.0],
Expand All @@ -208,7 +210,7 @@ def create_plan(
)

scales_per_segment = self._assign_noise_scales(
sensitivity, method
sensitivity, resolved_method
)

all_scales = sorted(
Expand Down Expand Up @@ -258,11 +260,13 @@ def execute(
Returns:
A MitigationResult with mitigated values.
"""
if plan.method == "zne":
resolved_method = "zne" if plan.method == "adaptive" else plan.method

if resolved_method == "zne":
return self._extrapolate_zne(plan, raw_values)
elif plan.method == "pec":
elif resolved_method == "pec":
return self._execute_pec(plan, raw_values)
elif plan.method == "cdr":
elif resolved_method == "cdr":
return self._execute_cdr(plan, raw_values)
else:
return MitigationResult(method=plan.method)
Expand Down Expand Up @@ -469,7 +473,7 @@ def _extrapolate_zne(
raw_values = self._simulate_values(plan)

if not scales or not raw_values:
return MitigationResult(method="zne")
return MitigationResult(method=plan.method)

if len(raw_values) == 1:
return MitigationResult(
Expand All @@ -478,7 +482,7 @@ def _extrapolate_zne(
noise_scales=scales,
extrapolation_coefficients=[1.0],
shots_used=plan.total_shots,
method="zne",
method=plan.method,
)

if len(raw_values) == 2:
Expand All @@ -501,7 +505,7 @@ def _extrapolate_zne(
noise_scales=scales[:2],
extrapolation_coefficients=[c1, c2] if abs(denom) >= 1e-12 else [1.0, 0.0],
shots_used=plan.total_shots,
method="zne",
method=plan.method,
)

coeffs = self._richardson_coefficients(scales[:len(raw_values)])
Expand All @@ -513,7 +517,7 @@ def _extrapolate_zne(
noise_scales=scales[:len(raw_values)],
extrapolation_coefficients=coeffs,
shots_used=plan.total_shots,
method="zne",
method=plan.method,
)

def _execute_pec(
Expand Down
10 changes: 2 additions & 8 deletions src/qc_compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,8 @@ def optimize(
)

if config.mitigation != "none":
mitigation_method = config.mitigation
if mitigation_method == "adaptive":
mitigation_method = "zne"
sub_mitigation = self.mitigation.create_plan(
sub_current, method=mitigation_method
sub_current, method=config.mitigation
)
sub_result.mitigation_plan = sub_mitigation
sub_result.passes_applied.append(
Expand Down Expand Up @@ -280,11 +277,8 @@ def optimize(

# Pass 5: Error Mitigation
if config.mitigation != "none":
mitigation_method = config.mitigation
if mitigation_method == "adaptive":
mitigation_method = "zne"
mitigation_plan = self.mitigation.create_plan(
current_circuit, method=mitigation_method
current_circuit, method=config.mitigation
)
result.mitigation_plan = mitigation_plan
passes_applied.append(f"mitigation:{config.mitigation}")
Expand Down
33 changes: 32 additions & 1 deletion tests/test_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,4 +439,35 @@ def test_zne_result_is_not_placeholder(self):
result = mitigation.execute(qc, plan)
assert result.placeholder is False
assert result.method == "zne"
assert plan.subcircuit_sensitivity is not None
assert plan.subcircuit_sensitivity is not None


class TestAdaptiveMitigation:
"""Regression tests for 'adaptive' mitigation method (issue #47)."""

def test_adaptive_creates_plan(self):
mitigation = AdaptiveErrorMitigation(CostModel())
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
plan = mitigation.create_plan(qc, method="adaptive")
assert plan.method == "adaptive"

def test_adaptive_executes_as_zne(self):
mitigation = AdaptiveErrorMitigation(CostModel())
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
plan = mitigation.create_plan(qc, method="adaptive")
result = mitigation.execute(qc, plan)
assert result.method == "adaptive"
assert result.placeholder is False

def test_adaptive_uses_zne_noise_scales(self):
mitigation = AdaptiveErrorMitigation(CostModel())
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
plan_adaptive = mitigation.create_plan(qc, method="adaptive")
plan_zne = mitigation.create_plan(qc, method="zne")
assert plan_adaptive.noise_scales == plan_zne.noise_scales
Loading