From edd5ab87c5ab6fb010a0c8efff5e19cb74eedbc8 Mon Sep 17 00:00:00 2001 From: xumingkuan Date: Sat, 8 Feb 2025 16:53:39 -0500 Subject: [PATCH] [verifier] [bug] Fix a bug when removing common prefix gates and do not generate hashing distribution in test_optimization_steps.h --- .../benchmark_optimization_steps.cpp | 7 +++ src/quartz/dataset/dataset.cpp | 7 +++ src/quartz/dataset/dataset.h | 13 +++++- src/quartz/verifier/verifier.cpp | 44 +++++++++++++++---- src/test/test_optimization_steps.h | 6 ++- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/benchmark/benchmark_optimization_steps.cpp b/src/benchmark/benchmark_optimization_steps.cpp index dbe8967a..05a9bda4 100644 --- a/src/benchmark/benchmark_optimization_steps.cpp +++ b/src/benchmark/benchmark_optimization_steps.cpp @@ -40,6 +40,8 @@ int main() { auto xfers = GraphXfer::get_all_xfers_from_ecc( &ctx, (kQuartzRootPath / "eccset/Nam_6_3_complete_ECC_set.json").string()); + int verified_count = 0; + int not_verified_count = 0; for (const auto &circuit : kCircuitNames) { freopen(((kQuartzRootPath / "benchmark-logs" / circuit).string() + ".log") .c_str(), @@ -57,9 +59,14 @@ int main() { /*verbose=*/false); if (verified) { std::cout << "All transformations are verified." << std::endl; + verified_count++; } else { std::cout << "Some transformation is not verified." << std::endl; + not_verified_count++; + std::cerr << circuit << " not verified." << std::endl; } } + std::cerr << verified_count << " cases verified, " << not_verified_count + << " cases not verified." << std::endl; return 0; } diff --git a/src/quartz/dataset/dataset.cpp b/src/quartz/dataset/dataset.cpp index cefdce06..9e3008c5 100644 --- a/src/quartz/dataset/dataset.cpp +++ b/src/quartz/dataset/dataset.cpp @@ -175,6 +175,13 @@ bool Dataset::insert(Context *ctx, std::unique_ptr dag) { return ret; } +bool Dataset::insert_with_hash(const CircuitSeqHashType &hash_value, + std::unique_ptr dag) { + bool ret = dataset.count(hash_value) == 0; + dataset[hash_value].push_back(std::move(dag)); + return ret; +} + bool Dataset::insert_to_nearby_set_if_exists(Context *ctx, std::unique_ptr dag) { const auto hash_value = dag->hash(ctx); diff --git a/src/quartz/dataset/dataset.h b/src/quartz/dataset/dataset.h index 3450a2f0..17cadeb3 100644 --- a/src/quartz/dataset/dataset.h +++ b/src/quartz/dataset/dataset.h @@ -40,7 +40,18 @@ class Dataset { // Returns true iff the hash value is new to the |dataset|. bool insert(Context *ctx, std::unique_ptr dag); - // Inserts the circuitseq to an existing set if the hash value plus or minus 1 + /** + * Insert a CircuitSeq with a pre-defined hash value. This function is used + * when the hash value cannot be computed, and should NOT be used in Quartz's + * generator. + * @param hash_value The hash value to be inserted. + * @param dag The CircuitSeq to be inserted. + * @return True iff the hash value is new to the |dataset|. + */ + bool insert_with_hash(const CircuitSeqHashType &hash_value, + std::unique_ptr dag); + + // Inserts the CircuitSeq to an existing set if the hash value plus or minus 1 // is found. Returns true iff there is no such existing set. bool insert_to_nearby_set_if_exists(Context *ctx, std::unique_ptr dag); diff --git a/src/quartz/verifier/verifier.cpp b/src/quartz/verifier/verifier.cpp index a831f4eb..018e501e 100644 --- a/src/quartz/verifier/verifier.cpp +++ b/src/quartz/verifier/verifier.cpp @@ -67,7 +67,7 @@ bool Verifier::equivalent(Context *ctx, const CircuitSeq *circuit1, std::unordered_set leftover_gates_start2; // (Partial) topological sort on circuit1 while (!wires_to_search.empty()) { - auto wire1 = wires_to_search.front(); + CircuitWire *wire1 = wires_to_search.front(); assert(wires_mapping.count(wire1) > 0); auto wire2 = wires_mapping[wire1]; wires_to_search.pop(); @@ -79,19 +79,31 @@ bool Verifier::equivalent(Context *ctx, const CircuitSeq *circuit1, [&qubit_blocked](CircuitWire *output_wire) { return qubit_blocked[output_wire->index]; })) { - // Block qubits of potential unmatched gates - for (auto &gate : wire1->output_gates) { - for (auto &output_wire : gate->output_wires) { - qubit_blocked[output_wire->index] = true; + // Block qubits of potential unmatched gates. + for (auto &gate1 : wire1->output_gates) { + // Use a for loop because |wire1->output_gates| can be empty. + for (auto &input_wire : gate1->input_wires) { + qubit_blocked[input_wire->index] = true; + // If |wires_mapping| does not have |input_wire|, this means that + // the qubit is already blocked before, and |leftover_gates_start2| + // already had an earlier gate at this qubit. + if (wires_mapping.count(input_wire) > 0) { + // Note that this input wire might be not in the search queue now. + // We need to manually add the gate in circuit2 to the frontier. + for (auto &gate : wires_mapping[input_wire]->output_gates) { + leftover_gates_start2.insert(gate); + } + } } // Use std::unordered_set to deduplicate, similarly hereinafter - leftover_gates_start1.insert(gate); + leftover_gates_start1.insert(gate1); } - for (auto &gate : wire2->output_gates) { - for (auto &output_wire : gate->output_wires) { + for (auto &gate2 : wire2->output_gates) { + // Use a for loop because |wire2->output_gates| can be empty. + for (auto &output_wire : gate2->output_wires) { qubit_blocked[output_wire->index] = true; } - leftover_gates_start2.insert(gate); + leftover_gates_start2.insert(gate2); } continue; } @@ -141,6 +153,20 @@ bool Verifier::equivalent(Context *ctx, const CircuitSeq *circuit1, // We should have removed the same number of gates assert(circuit1->get_num_gates() - c1->get_num_gates() == circuit2->get_num_gates() - c2->get_num_gates()); + if (verbose) { + std::cout << "Removed " << circuit1->get_num_gates() - c1->get_num_gates() + << " prefix gates from circuit1 and " + << circuit2->get_num_gates() - c2->get_num_gates() + << " prefix gates from circuit2." << std::endl; + std::cout << "Leftover gates from circuit1:" << std::endl; + for (auto &gate : leftover_gates_start1) { + std::cout << gate->to_string() << std::endl; + } + std::cout << "Leftover gates from circuit2:" << std::endl; + for (auto &gate : leftover_gates_start2) { + std::cout << gate->to_string() << std::endl; + } + } // Remove common last gates while (true) { diff --git a/src/test/test_optimization_steps.h b/src/test/test_optimization_steps.h index 6820c379..ce4cfd82 100644 --- a/src/test/test_optimization_steps.h +++ b/src/test/test_optimization_steps.h @@ -21,9 +21,11 @@ void test_optimization( std::cerr << "Parser failed" << std::endl; return; } - ctx->gen_input_and_hashing_dis(dag->get_num_qubits()); + // Do not generate (this is only for CircuitSeq::hash()) + // because the number of qubits can be large. + // ctx->gen_input_and_hashing_dis(dag->get_num_qubits()); - quartz::Graph graph(ctx, dag); + Graph graph(ctx, dag); std::cout << graph.total_cost() << " gates in circuit before optimizing." << std::endl; auto start = std::chrono::steady_clock::now();