diff --git a/releasenotes/notes/fix_save_expval_truncation-2ea6b9224b22956e.yaml b/releasenotes/notes/fix_save_expval_truncation-2ea6b9224b22956e.yaml new file mode 100644 index 0000000000..41b1a9c8e4 --- /dev/null +++ b/releasenotes/notes/fix_save_expval_truncation-2ea6b9224b22956e.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fixes a small bug preventing the expval truncation mechanism from activating correctly. diff --git a/src/framework/circuit.hpp b/src/framework/circuit.hpp index eab35cd514..ef79862ee7 100644 --- a/src/framework/circuit.hpp +++ b/src/framework/circuit.hpp @@ -420,13 +420,14 @@ void Circuit::reset_metadata() { void Circuit::add_op_metadata(const Op &op) { has_conditional |= op.conditional; opset_.insert(op); - if (!qubitset_.empty() && - (op.type == OpType::save_expval || op.type == OpType::save_expval_var)) { + if (op.type == OpType::save_expval || op.type == OpType::save_expval_var) { for (int_t j = 0; j < op.expval_params.size(); j++) { const std::string &pauli = std::get<0>(op.expval_params[j]); for (int_t i = 0; i < op.qubits.size(); i++) { // add qubit with non-I operator - if (pauli[pauli.size() - 1 - i] != 'I') { + // we also add one qubit if the qubitset is empty to prevent edge cases + // with empty circuits + if (qubitset_.empty() || pauli[pauli.size() - 1 - i] != 'I') { qubitset_.insert(op.qubits[i]); } } @@ -693,6 +694,7 @@ void Circuit::remap_qubits(Op &op) const { } pauli = new_pauli; } + op.qubits.resize(qubitmap_.size()); for (int_t i = 0; i < qubitmap_.size(); i++) { op.qubits[i] = i; } diff --git a/test/terra/primitives/test_estimator_v2.py b/test/terra/primitives/test_estimator_v2.py index 988d8dc743..8c08d013f1 100644 --- a/test/terra/primitives/test_estimator_v2.py +++ b/test/terra/primitives/test_estimator_v2.py @@ -445,6 +445,33 @@ def test_truncate(self): result_5[0].data["evs"][0], result_2[0].data["evs"][0], delta=self._rtol ) + def test_truncate_large_backends(self): + """Test truncation allows us to run few-qubit circuits on many-qubit backends""" + N = 12 + qc = QuantumCircuit(N) + + qc.x(range(N)) + qc.h(range(N)) + + for kk in range(N // 2, 0, -1): + qc.ch(kk, kk - 1) + for kk in range(N // 2, N - 1): + qc.ch(kk, kk + 1) + + op = SparsePauliOp("Z" * N) + backend_127 = GenericBackendV2(num_qubits=127) + pm = generate_preset_pass_manager(backend=backend_127, optimization_level=1) + isa_circuit = pm.run(qc) + mapped_observable = op.apply_layout(isa_circuit.layout) + + ref_est = StatevectorEstimator() + ref_result = ref_est.run(pubs=[(qc, op)]).result() + + est = EstimatorV2() + res = est.run(pubs=[(isa_circuit, mapped_observable)]).result() + + self.assertAlmostEqual(ref_result[0].data.evs, res[0].data.evs) + if __name__ == "__main__": unittest.main()