diff --git a/src/na/zoned/decomposer/NativeGateDecomposer.cpp b/src/na/zoned/decomposer/NativeGateDecomposer.cpp index 4e6c7cb55..7b69f379c 100644 --- a/src/na/zoned/decomposer/NativeGateDecomposer.cpp +++ b/src/na/zoned/decomposer/NativeGateDecomposer.cpp @@ -110,9 +110,9 @@ auto NativeGateDecomposer::getU3AnglesFromQuaternion(const Quaternion& quat) if (std::fabs(quat[0]) > epsilon || std::fabs(quat[3]) > epsilon) { theta = 2. * std::atan2(std::sqrt(quat[2] * quat[2] + quat[1] * quat[1]), std::sqrt(quat[0] * quat[0] + quat[3] * quat[3])); - qc::fp alpha_1 = std::atan2(quat[3], quat[0]); // phi+ lambda + qc::fp alpha_1 = std::atan2(quat[3], quat[0]); // (phi+ lambda) /2 if (std::fabs(quat[1]) > epsilon || std::fabs(quat[2]) > epsilon) { - qc::fp alpha_2 = -1 * std::atan2(quat[1], quat[2]); + qc::fp alpha_2 = -1 * std::atan2(quat[1], quat[2]); //(phi-lambda)/2 phi = alpha_1 + alpha_2; // phi lambda = alpha_1 - alpha_2; } else { @@ -313,6 +313,9 @@ auto NativeGateDecomposer::shortest_path_to_start( -> std::pair, double> { std::vector, double>> possible_paths = {}; // Check if leaf nodes are reached + + // TODO: Check if Path cost takes into account edge weight from current node + // to next node!!! for (auto edge : subproblem_graph.get_adjacent(current_node)) { if (leaf_nodes.contains(edge.first)) { possible_paths.push_back({std::pair, double>( @@ -545,7 +548,6 @@ auto NativeGateDecomposer::max_theta( } return max_cost; } -// TODO: This only ever gives the first Moment????? auto NativeGateDecomposer::sift( DiGraph>>& circuit, std::vector v, size_t nQubits) @@ -557,22 +559,18 @@ auto NativeGateDecomposer::sift( std::set v_rem = std::set(v.begin(), v.end()); std::set removed = std::set(); + // We traverse the graph rather than v_rem to use the graph's topological + // ordering for (auto node = 0; node < circuit.size(); node++) { - if (v_rem.contains(node)) { // TODO: SORT V_rem??? Needs to be a topological + if (v_rem.contains(node)) { auto op = circuit.get_Node_Value(node); - std::set op_qubits = std::set(); - std::set used_qubits; if (std::holds_alternative(op)) { - used_qubits = {std::get(op).qubit}; + op_qubits = {std::get(op).qubit}; } else { - used_qubits = {std::get>(op)[0], - std::get>(op)[1]}; - } - - for (auto qubit : used_qubits) { - op_qubits.insert(qubit); + op_qubits = {std::get>(op)[0], + std::get>(op)[1]}; } if (removed.size() < nQubits && disjunct(removed, op_qubits)) { if (std::holds_alternative(op)) { @@ -580,8 +578,6 @@ auto NativeGateDecomposer::sift( removed.insert(std::get(op).qubit); } else { v_p.push_back(node); - // Add something to make it only pick one 2-Qubit gate per Qubit per - // moment??? } } else { v_r.push_back(node); @@ -676,7 +672,6 @@ auto NativeGateDecomposer::schedule_remaining( // TODO: Check if subproblem has been computed std::size_t id = std::hash, 3>>{}(v); if (memo.contains(id)) { - // Wrong memory Call!!! std::size_t sub_node = memo.at(id).first; double edge_weight = memo.at(id).second[1]; cost = memo.at(id).second[0]; @@ -708,7 +703,6 @@ auto NativeGateDecomposer::schedule_remaining( for (const auto& val : args) { auto new_node = add_node_to_sub_prob_graph(v[0], val.first[0], val.second, subproblem_graph, prev_node); - // USdingg v_new is incorrect!! need adjusted one for arg temp_cost = schedule_remaining({val.first[1], val.first[2], val.first[3]}, circuit, subproblem_graph, new_node, nQubits, check_final_cond, memo) + val.second; if (temp_cost < min_cost) { @@ -747,8 +741,8 @@ auto NativeGateDecomposer::schedule_theta_opt( std::pair, std::vector>({}, {})); std::map>> memo = {}; - auto cost = schedule_remaining(v, circuit, sub_prob_graph, base_node, - nQubits_, config_.check_final_cond, memo); + auto cost = schedule_remaining(v, circuit, sub_prob_graph, base_node, nQubits, + config_.check_final_cond, memo); // TODO: Create Schedule from Subproblem Graph std::pair>, std::vector> final_circuit = build_schedule(circuit, sub_prob_graph); diff --git a/test/na/zoned/test_theta_opt_scheduler.cpp b/test/na/zoned/test_theta_opt_scheduler.cpp index a022a20a1..252c237e4 100644 --- a/test/na/zoned/test_theta_opt_scheduler.cpp +++ b/test/na/zoned/test_theta_opt_scheduler.cpp @@ -312,19 +312,24 @@ TEST_F(ThetaOptTest, NextMomentsCond3Test) { TEST_F(ThetaOptTest, RecursionBaseTest) { // Circuit - // ┌─────────────────┐ ┌───────┐ ┌─────────────────┐ - // q_0: ──┤ U(PI,Pi/2,PI/4) ├─────────■───┤ X ├─────────■───┤ - // U(PI/2,PI/2,PI) ├── + // ┌─────────────────┐ ┌───────┐ + // q_0: ──┤ U(PI,Pi/2,PI/4) ├─────────■───┤ X ├─────────■─ ─ ─ // └─────────────────┘ │ └───────┘ │ - // └─────────────────┘ // │ ┌───────┐ │ - // q_1: ──────────────────────────■───■───┤ Y - // ├─────────│─────────────────────── + // q_1: ──────────────────────────■───■───┤ Y ├─────────│─ ─ ─ // │ └───────┘ │ // ┌───────────────────┐ │ ┌────────────────┐ │ - // q_2: ──┤ U(PI/4,PI/4,PI/4) ├───■───┤ U(PI/2,0,PI/2) - // ├────■─────────────────────── + // q_2: ──┤ U(PI/4,PI/4,PI/4) ├───■───┤ U(PI/2,0,PI/2) ├────■─ ─ ─ // └───────────────────┘ └────────────────┘ + // + // ┌─────────────────┐ + // q_0: ─ ─ ─┤ U(PI/2,PI/2,PI) ├── + // └─────────────────┘ + // + // q_1: ─ ─ ────────────────────── + // + // q_2: ─ ─ ────────────────────── + size_t n = 3; qc::QuantumComputation qc(n); qc.u(qc::PI, qc::PI_2, qc::PI_4, 0); @@ -353,9 +358,48 @@ TEST_F(ThetaOptTest, RecursionBaseTest) { auto result = NativeGateDecomposer::schedule_remaining( v, graph, subproblem_graph, 0, n, false, memo); + EXPECT_EQ(result, 5 * qc::PI_2); + + EXPECT_EQ(subproblem_graph.size(), 1 + 6); + auto t = subproblem_graph.get_adjacent(0); + EXPECT_THAT(subproblem_graph.get_adjacent(0), + ::testing::UnorderedElementsAre(::testing::Pair(1, qc::PI), + ::testing::Pair(4, qc::PI_4))); + EXPECT_THAT(subproblem_graph.get_Node_Value(1).first, ::testing::IsEmpty()); EXPECT_THAT(subproblem_graph.get_Node_Value(1).second, - ::testing::ElementsAre(0, 1)); + ::testing::UnorderedElementsAre(0, 1)); + EXPECT_THAT(subproblem_graph.get_adjacent(1), + ::testing::UnorderedElementsAre(::testing::Pair(2, qc::PI))); + EXPECT_THAT(subproblem_graph.get_Node_Value(2).first, + ::testing::UnorderedElementsAre(2, 4)); + EXPECT_THAT(subproblem_graph.get_Node_Value(2).second, + ::testing::UnorderedElementsAre(3, 5, 6)); + EXPECT_THAT(subproblem_graph.get_adjacent(2), + ::testing::UnorderedElementsAre(::testing::Pair(3, qc::PI_2))); + EXPECT_THAT(subproblem_graph.get_Node_Value(3).first, + ::testing::UnorderedElementsAre(7)); + EXPECT_THAT(subproblem_graph.get_Node_Value(3).second, + ::testing::UnorderedElementsAre(8)); + EXPECT_THAT(subproblem_graph.get_adjacent(3), ::testing::IsEmpty()); + + EXPECT_THAT(subproblem_graph.get_Node_Value(4).first, ::testing::IsEmpty()); + EXPECT_THAT(subproblem_graph.get_Node_Value(4).second, + ::testing::UnorderedElementsAre(1)); + EXPECT_THAT(subproblem_graph.get_adjacent(4), + ::testing::UnorderedElementsAre(::testing::Pair(5, qc::PI))); + EXPECT_THAT(subproblem_graph.get_Node_Value(5).first, + ::testing::UnorderedElementsAre(2)); + EXPECT_THAT(subproblem_graph.get_Node_Value(5).second, + ::testing::UnorderedElementsAre(0, 3)); + EXPECT_THAT(subproblem_graph.get_adjacent(5), + ::testing::UnorderedElementsAre(::testing::Pair(6, qc::PI))); + EXPECT_THAT(subproblem_graph.get_Node_Value(6).first, + ::testing::UnorderedElementsAre(4)); + EXPECT_THAT(subproblem_graph.get_Node_Value(6).second, + ::testing::UnorderedElementsAre(5, 6)); + EXPECT_THAT(subproblem_graph.get_adjacent(6), + ::testing::UnorderedElementsAre(::testing::Pair(3,qc::PI_2))); } @@ -482,38 +526,119 @@ TEST_F(ThetaOptTest, BuildScheduleTest) { EXPECT_EQ(schedule.first.at(3).at(0).qubit,0); EXPECT_THAT(schedule.first.at(3).at(0).angles,::testing::ElementsAre(one_qubit_gates.at(3).at(0).angles[0], - one_qubit_gates.at(3).at(0).angles[1],one_qubit_gates.at(3).at(0).angles[2])); - + one_qubit_gates.at(3).at(0).angles[1], + one_qubit_gates.at(3).at(0).angles[2])); } TEST_F(ThetaOptTest, CompleteTestSmall) { // Circuit - // ┌───────┐ ┌───────┐ ┌───────┐ - // q_0: ──┤ X ├───■───────┤ Z ├───■───┤ Y ├─ - // └───────┘ │ └───────┘ │ └───────┘ - // │ ┌───────┐ │ - // q_1: ──────────────■───■───┤ X ├───│───────────── - // │ └───────┘ │ - // ┌───────┐ │ ┌───────┐ │ - // q_2: ──┤ X ├───────■───┤ Y ├───■───────────── - // └───────┘ └───────┘ + // ┌─────────────────┐ ┌───────┐ + // q_0: ──┤ U(PI,PI/2,PI/4) ├─────────■───┤ X ├─────────■─ ─ ─ + // └─────────────────┘ │ └───────┘ │ + // │ ┌───────┐ │ + // q_1: ──────────────────────────■───■───┤ Y ├─────────│─ ─ ─ + // │ └───────┘ │ + // ┌───────────────────┐ │ ┌────────────────┐ │ + // q_2: ──┤ U(PI/4,PI/4,PI/4) ├───■───┤ U(PI/2,0,PI/2) ├────■─ ─ ─ + // └───────────────────┘ └────────────────┘ + // + // ┌─────────────────┐ + // q_0: ─ ─ ─┤ U(PI/2,PI/2,PI) ├── + // └─────────────────┘ + // + // q_1: ─ ─ ────────────────────── + // + // q_2: ─ ─ ────────────────────── size_t n = 3; qc::QuantumComputation qc(n); - qc.x(0); - qc.x(2); - qc.cz(0, 1); + qc.u(qc::PI, qc::PI_2, qc::PI_4, 0); + qc.u(qc::PI_4, qc::PI_4, qc::PI_4, 2); qc.cz(1, 2); - qc.z(0); - qc.x(1); - qc.y(2); + qc.cz(0, 1); + qc.x(0); + qc.y(1); + qc.u(qc::PI_2, 0.0, qc::PI_2, 2); qc.cz(0, 2); qc.y(0); + qc.u(qc::PI_2, qc::PI_2, qc::PI, 0); + + auto schedule = scheduler.schedule(qc); + auto one_qubit_gates = NativeGateDecomposer::transformToU3(schedule.first, n); + auto theta_opt_schedule = + decomposer.schedule_theta_opt({one_qubit_gates, schedule.second}, n); + + EXPECT_EQ(theta_opt_schedule.first.size(), 4); + EXPECT_EQ(theta_opt_schedule.second.size(), 3); + + EXPECT_EQ(theta_opt_schedule.first.at(0).size(), 2); + EXPECT_EQ(theta_opt_schedule.first.at(1).size(), 0); + EXPECT_EQ(theta_opt_schedule.first.at(2).size(), 3); + EXPECT_EQ(theta_opt_schedule.first.at(3).size(), 1); + + EXPECT_EQ(theta_opt_schedule.second.at(0).size(), 1); + EXPECT_EQ(theta_opt_schedule.second.at(1).size(), 1); + EXPECT_EQ(theta_opt_schedule.second.at(2).size(), 1); + + EXPECT_EQ(theta_opt_schedule.first.at(0).at(0).qubit, 0); + // TODO: DOUBLE Nears!!! + EXPECT_THAT( + theta_opt_schedule.first.at(0).at(0).angles, + ::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(0).at(0).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(0).at(0).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(0).at(0).angles[2], + epsilon))); + EXPECT_EQ(theta_opt_schedule.first.at(0).at(1).qubit, 2); + EXPECT_THAT( + theta_opt_schedule.first.at(0).at(1).angles, + ::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(0).at(1).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(0).at(1).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(0).at(1).angles[2], + epsilon))); + + EXPECT_THAT(theta_opt_schedule.second.at(0).front(), + ::testing::ElementsAre(1, 2)); + + EXPECT_THAT(theta_opt_schedule.second.at(1).front(), + ::testing::ElementsAre(0, 1)); + // QUAT + EXPECT_EQ(theta_opt_schedule.first.at(2).at(0).qubit, 2); + EXPECT_THAT( + theta_opt_schedule.first.at(2).at(0).angles, + ::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(1).at(0).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(1).at(0).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(1).at(0).angles[2], + epsilon))); + EXPECT_EQ(theta_opt_schedule.first.at(2).at(1).qubit, 0); + EXPECT_THAT( + theta_opt_schedule.first.at(2).at(1).angles, + ::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(2).at(0).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(2).at(0).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(2).at(0).angles[2], + epsilon))); + EXPECT_EQ(theta_opt_schedule.first.at(2).at(2).qubit, 1); + EXPECT_THAT( + theta_opt_schedule.first.at(2).at(2).angles, + ::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(2).at(1).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(2).at(1).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(2).at(1).angles[2], epsilon))); + + EXPECT_THAT(theta_opt_schedule.second.at(2).front(),::testing::ElementsAre(0,2)); + EXPECT_EQ(theta_opt_schedule.first.at(3).at(0).qubit,0); + EXPECT_THAT(theta_opt_schedule.first.at(3).at(0).angles,::testing::ElementsAre( + ::testing::DoubleNear(one_qubit_gates.at(3).at(0).angles[0], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(3).at(0).angles[1], epsilon), + ::testing::DoubleNear(one_qubit_gates.at(3).at(0).angles[2], epsilon))); } TEST_F(ThetaOptTest, CompleteTestBig) { -//Circuit BIG + //Circuit BIG } } // namespace na::zoned \ No newline at end of file