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

Keep resets at beginning of circuits #10591

Merged
merged 17 commits into from
Aug 24, 2023
Merged
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
# Custom extensions
# ---------------------------------------------------------------------------------------


Copy link
Member

Choose a reason for hiding this comment

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

black on the docs/conf.py file introduced this.

def add_versions_to_config(_app, config):
"""Add a list of old documentation versions that should have links generated to them into the
context, so the theme can use them to generate a sidebar."""
Expand Down
2 changes: 0 additions & 2 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from qiskit.transpiler.passes import NoiseAdaptiveLayout
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure
from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure
from qiskit.transpiler.preset_passmanagers import common
Expand Down Expand Up @@ -81,7 +80,6 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
pass_manager_config.unitary_synthesis_plugin_config,
pass_manager_config.hls_config,
)
init.append(RemoveResetInZeroState())
init.append(OptimizeSwapBeforeMeasure())
init.append(RemoveDiagonalGatesBeforeMeasure())
else:
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
target is not None and target.get_non_global_operation_names(strict_direction=True)
):
pre_optimization = common.generate_pre_op_passmanager(
target, coupling_map, remove_reset_in_zero=True
target, coupling_map, remove_reset_in_zero=False
)
else:
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True)
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=False)
Copy link
Member

Choose a reason for hiding this comment

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

Doing this is no-op, with the arguments set like this it returns an empty PassManager. It'd probably be more efficient to just default pre_optimization to None and only set it if we have a coupling map or target available, to run the gate direction passes.

Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't have thought that would have an effect on performance, though, and keeping it like this means potentially a bit less coupling between the components? It doesn't feel like the call site needs/ought to know that this combination of options returns an empty object.

Copy link
Member

Choose a reason for hiding this comment

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

It`ll save a few hundred ns at most. It's why I only left a comment and didn't block it because it's probably fine like this. I just wanted to call it out because this whole branch logic was written to support the pre optimization construction that was done prior to the introduction of stages. Where before the optimization loop the passmanagers either ran gate direction and optionally remove reset depending on optimization level or just remove reset in zero if there are no connectivity constraints. That all feels unnecessary now if we're never running remove resets anymore.


optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=1
Expand Down
6 changes: 4 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
if (coupling_map and not coupling_map.is_symmetric) or (
target is not None and target.get_non_global_operation_names(strict_direction=True)
):
pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True)
pre_optimization = common.generate_pre_op_passmanager(
target, coupling_map, remove_reset_in_zero=False
)
else:
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True)
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=False)

optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=2
Expand Down
6 changes: 4 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
if (coupling_map and not coupling_map.is_symmetric) or (
target is not None and target.get_non_global_operation_names(strict_direction=True)
):
pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True)
pre_optimization = common.generate_pre_op_passmanager(
target, coupling_map, remove_reset_in_zero=False
)
else:
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True)
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=False)

sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=3
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/keep-initial-resets-a5f75cb16c8edb57.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
upgrade:
- |
Disables the use of :class:`.RemoveResetInZeroState` in the preset passmanagers.
This better aligns circuits to the notion of arbitrary initial states
unless explicitly set to zeros with resets.
27 changes: 16 additions & 11 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
SXGate,
U1Gate,
U2Gate,
U3Gate,
UGate,
XGate,
)
Expand Down Expand Up @@ -802,20 +801,26 @@ def test_move_measurements(self):
)
self.assertTrue(is_last_measure)

def test_initialize_reset_should_be_removed(self):
"""The reset in front of initializer should be removed when zero state"""
@data(0, 1, 2, 3)
def test_init_resets_kept_preset_passmanagers(self, optimization_level):
"""Test initial resets kept at all preset transpilation levels"""
num_qubits = 5
qc = QuantumCircuit(num_qubits)
qc.reset(range(num_qubits))

num_resets = transpile(qc, optimization_level=optimization_level).count_ops()["reset"]
self.assertEqual(num_resets, num_qubits)

@data(0, 1, 2, 3)
def test_initialize_reset_is_not_removed(self, optimization_level):
"""The reset in front of initializer should NOT be removed at beginning"""
qr = QuantumRegister(1, "qr")
qc = QuantumCircuit(qr)
qc.initialize([1.0 / math.sqrt(2), 1.0 / math.sqrt(2)], [qr[0]])
qc.initialize([1.0 / math.sqrt(2), -1.0 / math.sqrt(2)], [qr[0]])

expected = QuantumCircuit(qr)
expected.append(U3Gate(np.pi / 2, 0, 0), [qr[0]])
expected.reset(qr[0])
expected.append(U3Gate(np.pi / 2, -np.pi, 0), [qr[0]])

after = transpile(qc, basis_gates=["reset", "u3"], optimization_level=1)
self.assertEqual(after, expected, msg=f"after:\n{after}\nexpected:\n{expected}")
after = transpile(qc, basis_gates=["reset", "u3"], optimization_level=optimization_level)
self.assertEqual(after.count_ops()["reset"], 2, msg=f"{after}\n does not have 2 resets.")

def test_initialize_FakeMelbourne(self):
"""Test that the zero-state resets are remove in a device not supporting them."""
Expand All @@ -828,7 +833,7 @@ def test_initialize_FakeMelbourne(self):
out_dag = circuit_to_dag(out)
reset_nodes = out_dag.named_nodes("reset")

self.assertEqual(reset_nodes, [])
self.assertEqual(len(reset_nodes), 3)
nonhermitian marked this conversation as resolved.
Show resolved Hide resolved

def test_non_standard_basis(self):
"""Test a transpilation with a non-standard basis"""
Expand Down