Repository: Qiskit/qiskit

Issue:
`ConstrainedReschedule` raises an error with reset
### Environment

- **Qiskit Terra version**:  main
- **Python version**: 3.10.12
- **Operating system**: macOS 13.4.1

### What is happening?

`ConstrainedReschedule` raises `TranspilerError` with reset.

### How can we reproduce the issue?

```python
from qiskit import QuantumCircuit
from qiskit.providers.fake_provider import FakeBogotaV2
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import ALAPScheduleAnalysis, ConstrainedReschedule, TimeUnitConversion

backend = FakeBogotaV2()
target = backend.target
durations = target.durations()

circuit = QuantumCircuit(2)
circuit.cx(0, 1)
circuit.reset(0)

pm = PassManager(
    [
        TimeUnitConversion(durations),
        ALAPScheduleAnalysis(durations),
        ConstrainedReschedule(),
    ]
)

_ = pm.run(circuit)
```

output
```
Traceback (most recent call last):
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/runningpassmanager.py", line 226, in wrapper
    return meth(*meth_args, **meth_kwargs)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/runningpassmanager.py", line 148, in run
    return super().run(
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/passmanager/passrunner.py", line 235, in run
    passmanager_ir = self._run_pass_generic(
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/passmanager/passrunner.py", line 190, in _run_pass_generic
    passmanager_ir = self._run_pass_generic(
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/passmanager/passrunner.py", line 151, in _run_pass_generic
    passmanager_ir = self._run_base_pass(
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/runningpassmanager.py", line 188, in _run_base_pass
    pass_.run(FencedDAGCircuit(passmanager_ir))
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passes/scheduling/alignments/reschedule.py", line 241, in run
    self._push_node_back(dag, node)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passes/scheduling/alignments/reschedule.py", line 122, in _push_node_back
    raise TranspilerError(f"Unknown operation type for {repr(node)}.")
qiskit.transpiler.exceptions.TranspilerError: "Unknown operation type for DAGOpNode(op=Instruction(name='reset', num_qubits=1, num_clbits=0, params=[]), qargs=(Qubit(QuantumRegister(2, 'q'), 0),), cargs=())."

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passmanager.py", line 447, in wrapper
    return meth(*meth_args, **meth_kwargs)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passmanager.py", line 154, in run
    return super().run(
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/passmanager/passmanager.py", line 225, in run
    out_program = self._run_single_circuit(in_programs[0], callback, **metadata)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passmanager.py", line 173, in _run_single_circuit
    out_program = pass_runner.run(input_program, callback=callback, **metadata)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/runningpassmanager.py", line 228, in wrapper
    raise TranspilerError(ex.message) from ex
qiskit.transpiler.exceptions.TranspilerError: "Unknown operation type for DAGOpNode(op=Instruction(name='reset', num_qubits=1, num_clbits=0, params=[]), qargs=(Qubit(QuantumRegister(2, 'q'), 0),), cargs=())."

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/ima/tasks/3_2023/ntc/ntc-ibm-programs/tmp/432-min.py", line 22, in <module>
    _ = pm.run(circuit)
  File "/Users/ima/tasks/3_2023/qiskit/terra/qiskit/transpiler/passmanager.py", line 449, in wrapper
    raise TranspilerError(ex.message) from ex
qiskit.transpiler.exceptions.TranspilerError: "Unknown operation type for DAGOpNode(op=Instruction(name='reset', num_qubits=1, num_clbits=0, params=[]), qargs=(Qubit(QuantumRegister(2, 'q'), 0),), cargs=())."
```

### What should happen?

If `ConstrainedReschedule` does not support reset instruction, it would be great if the error message is more comprehensive rather than "Unknown operation type...".

### Any suggestions?

_No response_


Discussion:
Thanks @t-imamichi. It seems that running `ConstrainedReschedule` on circuits with resets became disallowed in my PR #10007, in particular due to the following code:
```
if isinstance(node.op, Gate):
    alignment = self.pulse_align
elif isinstance(node.op, Measure):
    alignment = self.acquire_align
elif isinstance(node.op, Delay) or getattr(node.op, "_directive", False):
    # Directive or delay. These can start at arbitrary time.
    alignment = None
else:
    raise TranspilerError(f"Unknown operation type for {repr(node)}.")
```
I don't know whether resets cannot be supported, or whether resets behave like `Measure`, or whether resets can start at arbitrary time. 

I agree that the error message is not very helpful.

Thank you for your information!

Resolving patch:
diff --git a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py
--- a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py
+++ b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py
@@ -17,6 +17,7 @@
 from qiskit.circuit.gate import Gate
 from qiskit.circuit.delay import Delay
 from qiskit.circuit.measure import Measure
+from qiskit.circuit.reset import Reset
 from qiskit.dagcircuit import DAGCircuit, DAGOpNode, DAGOutNode
 from qiskit.transpiler.basepasses import AnalysisPass
 from qiskit.transpiler.exceptions import TranspilerError
@@ -113,7 +114,7 @@ def _push_node_back(self, dag: DAGCircuit, node: DAGOpNode):
 
         if isinstance(node.op, Gate):
             alignment = self.pulse_align
-        elif isinstance(node.op, Measure):
+        elif isinstance(node.op, (Measure, Reset)):
             alignment = self.acquire_align
         elif isinstance(node.op, Delay) or getattr(node.op, "_directive", False):
             # Directive or delay. These can start at arbitrary time.
@@ -135,7 +136,7 @@ def _push_node_back(self, dag: DAGCircuit, node: DAGOpNode):
         # Compute shifted t1 of this node separately for qreg and creg
         new_t1q = this_t0 + node.op.duration
         this_qubits = set(node.qargs)
-        if isinstance(node.op, Measure):
+        if isinstance(node.op, (Measure, Reset)):
             # creg access ends at the end of instruction
             new_t1c = new_t1q
             this_clbits = set(node.cargs)
@@ -153,7 +154,7 @@ def _push_node_back(self, dag: DAGCircuit, node: DAGOpNode):
             # Compute next node start time separately for qreg and creg
             next_t0q = node_start_time[next_node]
             next_qubits = set(next_node.qargs)
-            if isinstance(next_node.op, Measure):
+            if isinstance(next_node.op, (Measure, Reset)):
                 # creg access starts after write latency
                 next_t0c = next_t0q + clbit_write_latency
                 next_clbits = set(next_node.cargs)
diff --git a/qiskit/transpiler/passes/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/base_scheduler.py
--- a/qiskit/transpiler/passes/scheduling/base_scheduler.py
+++ b/qiskit/transpiler/passes/scheduling/base_scheduler.py
@@ -11,11 +11,13 @@
 # that they have been altered from the originals.
 
 """Base circuit scheduling pass."""
+import warnings
+
 from qiskit.transpiler import InstructionDurations
 from qiskit.transpiler.basepasses import TransformationPass
 from qiskit.transpiler.passes.scheduling.time_unit_conversion import TimeUnitConversion
-from qiskit.dagcircuit import DAGOpNode, DAGCircuit
-from qiskit.circuit import Delay, Gate
+from qiskit.dagcircuit import DAGOpNode, DAGCircuit, DAGOutNode
+from qiskit.circuit import Delay, Gate, Measure, Reset
 from qiskit.circuit.parameterexpression import ParameterExpression
 from qiskit.transpiler.exceptions import TranspilerError
 from qiskit.transpiler.target import Target
@@ -269,6 +271,23 @@ def _get_node_duration(
         else:
             duration = node.op.duration
 
+        if isinstance(node.op, Reset):
+            warnings.warn(
+                "Qiskit scheduler assumes Reset works similarly to Measure instruction. "
+                "Actual behavior depends on the control system of your quantum backend. "
+                "Your backend may provide a plugin scheduler pass."
+            )
+        elif isinstance(node.op, Measure):
+            is_mid_circuit = not any(
+                isinstance(x, DAGOutNode) for x in dag.quantum_successors(node)
+            )
+            if is_mid_circuit:
+                warnings.warn(
+                    "Qiskit scheduler assumes mid-circuit measurement works as a standard instruction. "
+                    "Actual backend may apply custom scheduling. "
+                    "Your backend may provide a plugin scheduler pass."
+                )
+
         if isinstance(duration, ParameterExpression):
             raise TranspilerError(
                 f"Parameterized duration ({duration}) "


Test patch: