diff --git a/.gitignore b/.gitignore index cda62f7..958ac74 100644 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,7 @@ Thumbs.db /distro/main.fmf /distro/plans/main.fmf /distro/tests/main.fmf +experiments/ /docs/**/build .vs diff --git a/include/backend/dd/DDSimDiagnostics.hpp b/include/backend/dd/DDSimDiagnostics.hpp index 7174f72..f7ae200 100644 --- a/include/backend/dd/DDSimDiagnostics.hpp +++ b/include/backend/dd/DDSimDiagnostics.hpp @@ -16,6 +16,7 @@ struct DDDiagnostics { DDSimulationState* simulationState; std::map> zeroControls; + std::map> nonZeroControls; }; size_t dddiagnosticsGetNumQubits(Diagnostics* self); @@ -25,6 +26,8 @@ Result dddiagnosticsGetDataDependencies(Diagnostics* self, size_t instruction, bool* instructions); Result dddiagnosticsGetInteractions(Diagnostics* self, size_t beforeInstruction, size_t qubit, bool* qubitsAreInteracting); +Result dddiagnosticsGetZeroControlInstructions(Diagnostics* self, + bool* instructions); size_t dddiagnosticsPotentialErrorCauses(Diagnostics* self, ErrorCause* output, size_t count); diff --git a/include/backend/diagnostics.h b/include/backend/diagnostics.h index 3f0ac9c..5bef392 100644 --- a/include/backend/diagnostics.h +++ b/include/backend/diagnostics.h @@ -27,6 +27,7 @@ struct Diagnostics { bool* instructions); Result (*getInteractions)(Diagnostics* self, size_t beforeInstruction, size_t qubit, bool* qubitsAreInteracting); + Result (*getZeroControlInstructions)(Diagnostics* self, bool* instructions); size_t (*potentialErrorCauses)(Diagnostics* self, ErrorCause* output, size_t count); }; diff --git a/src/backend/dd/DDSimDiagnostics.cpp b/src/backend/dd/DDSimDiagnostics.cpp index 55685bd..af1edbc 100644 --- a/src/backend/dd/DDSimDiagnostics.cpp +++ b/src/backend/dd/DDSimDiagnostics.cpp @@ -35,6 +35,8 @@ Result createDDDiagnostics(DDDiagnostics* self, DDSimulationState* state) { self->interface.getInstructionCount = dddiagnosticsGetInstructionCount; self->interface.getInteractions = dddiagnosticsGetInteractions; self->interface.getDataDependencies = dddiagnosticsGetDataDependencies; + self->interface.getZeroControlInstructions = + dddiagnosticsGetZeroControlInstructions; self->interface.potentialErrorCauses = dddiagnosticsPotentialErrorCauses; return self->interface.init(&self->interface); @@ -213,6 +215,10 @@ size_t tryFindZeroControls(DDDiagnostics* diagnostics, size_t instruction, if (diagnostics->zeroControls.find(i) == diagnostics->zeroControls.end()) { continue; } + if (diagnostics->nonZeroControls.find(i) != + diagnostics->nonZeroControls.end()) { + continue; + } const auto& zeroControls = diagnostics->zeroControls[i]; if (!zeroControls.empty()) { outputs[index].type = ErrorCauseType::ControlAlwaysZero; @@ -247,6 +253,20 @@ bool isAlwaysZero(const Statevector& sv, size_t qubit, bool checkOne = false) { return true; } +Result dddiagnosticsGetZeroControlInstructions(Diagnostics* self, + bool* instructions) { + auto* ddd = toDDDiagnostics(self); + const Span instructionSpan(instructions, + dddiagnosticsGetInstructionCount(self)); + for (size_t i = 0; i < dddiagnosticsGetInstructionCount(self); i++) { + instructionSpan[i] = + (ddd->nonZeroControls.find(i) == ddd->nonZeroControls.end()) && + (ddd->zeroControls.find(i) != ddd->zeroControls.end()); + } + + return OK; +} + void dddiagnosticsOnStepForward(DDDiagnostics* diagnostics, size_t instruction) { auto* ddsim = diagnostics->simulationState; @@ -273,6 +293,12 @@ void dddiagnosticsOnStepForward(DDDiagnostics* diagnostics, diagnostics->zeroControls[instruction] = std::set(); } diagnostics->zeroControls[instruction].insert(qubit); + } else { + if (diagnostics->nonZeroControls.find(instruction) == + diagnostics->nonZeroControls.end()) { + diagnostics->nonZeroControls[instruction] = std::set(); + } + diagnostics->nonZeroControls[instruction].insert(qubit); } } } diff --git a/src/mqt/debug/pydebug.pyi b/src/mqt/debug/pydebug.pyi index 0a4303a..0e9b40d 100644 --- a/src/mqt/debug/pydebug.pyi +++ b/src/mqt/debug/pydebug.pyi @@ -91,6 +91,7 @@ class Diagnostics: def get_instruction_count(self) -> int: ... def get_data_dependencies(self, instruction: int) -> list[int]: ... def get_interactions(self, before_instruction: int, qubit: int) -> list[int]: ... + def get_zero_control_instructions(self) -> list[int]: ... def potential_error_causes(self) -> list[ErrorCause]: ... def create_ddsim_simulation_state() -> SimulationState: ... diff --git a/src/python/InterfaceBindings.cpp b/src/python/InterfaceBindings.cpp index 05b5676..a34f8e4 100644 --- a/src/python/InterfaceBindings.cpp +++ b/src/python/InterfaceBindings.cpp @@ -291,6 +291,21 @@ void bindDiagnostics(py::module& m) { } return result; }) + .def("get_zero_control_instructions", + [](Diagnostics* self) { + std::vector instructions(self->getInstructionCount(self)); + // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) + checkOrThrow(self->getZeroControlInstructions( + self, reinterpret_cast(instructions.data()))); + // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) + std::vector result; + for (size_t i = 0; i < instructions.size(); i++) { + if (instructions[i] != 0) { + result.push_back(i); + } + } + return result; + }) .def("potential_error_causes", [](Diagnostics* self) { size_t nextSize = 10; while (true) { diff --git a/test/circuits/zero-controls-with-jumps.qasm b/test/circuits/zero-controls-with-jumps.qasm new file mode 100644 index 0000000..60686fc --- /dev/null +++ b/test/circuits/zero-controls-with-jumps.qasm @@ -0,0 +1,19 @@ +qreg q[3]; + +x q[0]; + +gate test1 q0, q1 { + cx q0, q1; +} + +gate test2 q0, q1 { + cx q0, q1; +} + +test1 q[2], q[1]; +test1 q[1], q[2]; + +test2 q[0], q[2]; +test2 q[1], q[2]; + +cx q[1], q[2]; diff --git a/test/python/test_diagnosis.py b/test/python/test_diagnosis.py index e9beab0..facba39 100644 --- a/test/python/test_diagnosis.py +++ b/test/python/test_diagnosis.py @@ -70,3 +70,11 @@ def test_missing_interaction() -> None: causes = [x for x in s.get_diagnostics().potential_error_causes() if x.type == ErrorCauseType.MissingInteraction] assert len(causes) == 1 assert causes[0].instruction == 4 + + +def test_zero_control_listing() -> None: + """Test the zero-control list.""" + s = load_instance("control-always-zero") + s.run_simulation() + zero_controls = s.get_diagnostics().get_zero_control_instructions() + assert zero_controls == [3, 12] diff --git a/test/test_diagnostics.cpp b/test/test_diagnostics.cpp index ca264e2..d93b430 100644 --- a/test/test_diagnostics.cpp +++ b/test/test_diagnostics.cpp @@ -161,3 +161,13 @@ TEST_F(DiagnosticsTest, RequestZeroProblems) { diagnostics->potentialErrorCauses(diagnostics, problems.data(), 0); ASSERT_EQ(count, 0); } + +TEST_F(DiagnosticsTest, ZeroControlsWithJumps) { + loadFromFile("zero-controls-with-jumps"); + state->runSimulation(state); + std::array zeroControls{}; + diagnostics->getZeroControlInstructions(diagnostics, zeroControls.data()); + for (size_t i = 0; i < zeroControls.size(); i++) { + ASSERT_FALSE(zeroControls.at(i) ^ (i == 3 || i == 12)); + } +}