Please notice that the previous answer may unnecessarily remove gates.
It depends on the order you insert them in your circuit.
For example, constructing the circuit you provided with the following code
qc = QuantumCircuit(2)
qc.sdg(0)
qc.h(0)
qc.y(1)
qc.h(1)
qc.s(1)
qc.cx(0, 1)
qc.cx(1, 0)
qc.h(0)
qc.s(0)
qc.h(0)
qc.h(1)
qc.s(1)
qc.h(1)
print(qc)
---
┌─────┐┌───┐ ┌───┐┌───┐┌───┐┌───┐
q_0: ┤ SDG ├┤ H ├───────■──┤ X ├┤ H ├┤ S ├┤ H ├
└┬───┬┘├───┤┌───┐┌─┴─┐└─┬─┘├───┤├───┤├───┤
q_1: ─┤ Y ├─┤ H ├┤ S ├┤ X ├──■──┤ H ├┤ S ├┤ H ├
└───┘ └───┘└───┘└───┘ └───┘└───┘└───┘
and after removing the last 2 layers with your method we get
┌─────┐┌───┐ ┌───┐┌───┐
q_0: ┤ SDG ├┤ H ├───────■──┤ X ├┤ H ├
└┬───┬┘├───┤┌───┐┌─┴─┐└─┬─┘└───┘
q_1: ─┤ Y ├─┤ H ├┤ S ├┤ X ├──■───────
└───┘ └───┘└───┘└───┘
Notice how the last 3 gates acting on qubit 1 disappear although we should keep one extra Hadamard.
For a more robust solution, we should work with a representation of the circuit that captures the topological dependencies present, i.e DAGCircuit
.
from qiskit.converters import circuit_to_dag, dag_to_circuit
dag = circuit_to_dag(qc)
layers = list(dag.multigraph_layers())
n_remove = 2
# the extra minus 1 since the last layer consists of output nodes (qubits and clbits).
for layer in layers[- n_remove - 1:]:
for node in layer:
if node.type == 'op':
dag.remove_op_node(node)
new_qc = dag_to_circuit(dag)
print(new_qc)
---
┌─────┐┌───┐ ┌───┐┌───┐
q_0: ┤ SDG ├┤ H ├───────■──┤ X ├┤ H ├
└┬───┬┘├───┤┌───┐┌─┴─┐└─┬─┘├───┤
q_1: ─┤ Y ├─┤ H ├┤ S ├┤ X ├──■──┤ H ├
└───┘ └───┘└───┘└───┘ └───┘