From acb02167c91d747e2e44aae78191b27afdbe5101 Mon Sep 17 00:00:00 2001 From: imagoulas Date: Tue, 7 May 2024 21:58:33 -0400 Subject: [PATCH] Fixed simplify_circuit with A gates --- src/qforte/circuit.cc | 24 ++++++++++++------------ src/qforte/gate.cc | 33 ++++++++++++++++++++------------- src/qforte/gate.h | 4 ++-- src/qforte/make_gate.cc | 2 ++ tests/test_circuit_simplify.py | 24 +++++++++++++++++++++--- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/qforte/circuit.cc b/src/qforte/circuit.cc index fa4ebff1..eec57716 100644 --- a/src/qforte/circuit.cc +++ b/src/qforte/circuit.cc @@ -174,7 +174,7 @@ int Circuit::get_num_cnots() const { if (gate.gate_id() == "CNOT" || gate.gate_id() == "cX" || gate.gate_id() == "aCNOT" || gate.gate_id() == "acX") { n_cnots++; - } else if (gate.gate_id() == "A") { + } else if (gate.gate_id() == "A" || gate.gate_id() == "SWAP") { n_cnots += 3; } } @@ -228,18 +228,18 @@ bool Circuit::is_pauli() const { void Circuit::simplify() { - std::unordered_set involutory_gates = {GateType::X, GateType::Y, GateType::Z, - GateType::cX, GateType::cY, GateType::cZ, - GateType::acX, GateType::H, GateType::SWAP}; + const std::unordered_set involutory_gates = {GateType::X, GateType::Y, GateType::Z, + GateType::cX, GateType::cY, GateType::cZ, + GateType::acX, GateType::H, GateType::SWAP}; - std::unordered_set parametrized_gates = {GateType::Rx, GateType::Ry, GateType::Rz, - GateType::R, GateType::cRz, GateType::cR}; + const std::unordered_set parametrized_gates = {GateType::Rx, GateType::Ry, GateType::Rz, + GateType::R, GateType::cRz, GateType::cR}; - std::unordered_set square_root_gates = {GateType::T, GateType::S, GateType::V, - GateType::cV}; + const std::unordered_set square_root_gates = {GateType::T, GateType::S, GateType::V, + GateType::cV}; - std::unordered_map simplify_square_root_gates = {{GateType::T, "S"}, {GateType::S, "Z"}, - {GateType::V, "X"}, {GateType::cV, "cX"}}; + const std::unordered_map simplify_square_root_gates = {{GateType::T, "S"}, {GateType::S, "Z"}, + {GateType::V, "X"}, {GateType::cV, "cX"}}; std::vector gate_indices_to_remove; @@ -275,7 +275,7 @@ void Circuit::simplify() { if (square_root_gates.find(gate1.gate_type()) != square_root_gates.end()) { gate_indices_to_remove.push_back(pos1); gates_[pos2] = - make_gate(simplify_square_root_gates[gate2.gate_type()], gates_[pos2].target(), gates_[pos2].control()); + make_gate(simplify_square_root_gates.at(gate2.gate_type()), gates_[pos2].target(), gates_[pos2].control()); break; } } @@ -285,7 +285,7 @@ void Circuit::simplify() { gate_indices_to_remove.push_back(pos2); break; } - if (phase_1qubit_gates.find(controlled_2qubit_to_1qubit_gate[gate1.gate_type()]) != phase_1qubit_gates.end()) { + if (phase_1qubit_gates.find(controlled_2qubit_to_1qubit_gate.at(gate1.gate_type())) != phase_1qubit_gates.end()) { gate_indices_to_remove.push_back(pos1); gates_[pos2] = make_gate(gates_[pos2].gate_id(), gates_[pos2].target(), gates_[pos2].control(), *gate1.parameter() + *gate2.parameter()); diff --git a/src/qforte/gate.cc b/src/qforte/gate.cc index aa21d9fa..11894014 100644 --- a/src/qforte/gate.cc +++ b/src/qforte/gate.cc @@ -203,7 +203,7 @@ struct pair_equal { } }; -std::unordered_set, pair_hash, pair_equal> pairs_of_commuting_1qubit_gates = { +const std::unordered_set, pair_hash, pair_equal> pairs_of_commuting_1qubit_gates = { {GateType::X, GateType::X}, {GateType::Rx, GateType::X}, {GateType::V, GateType::X}, {GateType::Y, GateType::Y}, {GateType::Ry, GateType::Y}, {GateType::Z, GateType::Z}, {GateType::S, GateType::Z}, {GateType::T, GateType::Z}, {GateType::Rz, GateType::Z}, @@ -215,16 +215,16 @@ std::unordered_set, pair_hash, pair_equal> pairs_o {GateType::V, GateType::V} }; -std::unordered_set diagonal_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::Rz, GateType::R}; +const std::unordered_set diagonal_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::Rz, GateType::R}; -std::unordered_set phase_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::R}; +const std::unordered_set phase_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::R}; -std::unordered_map controlled_2qubit_to_1qubit_gate = { +const std::unordered_map controlled_2qubit_to_1qubit_gate = { {GateType::cX, GateType::X}, {GateType::acX, GateType::X}, {GateType::cY, GateType::Y}, {GateType::cZ, GateType::Z}, {GateType::cRz, GateType::Rz}, {GateType::cR, GateType::R}, {GateType::cV, GateType::V} }; -std::unordered_set symmetrical_2qubit_gates = {GateType::cZ, GateType::cR, GateType::SWAP}; +const std::unordered_set symmetrical_2qubit_gates = {GateType::cZ, GateType::cR, GateType::SWAP}; std::pair evaluate_gate_interaction(const Gate& gate1, const Gate& gate2) { @@ -253,14 +253,14 @@ std::pair evaluate_gate_interaction(const Gate& gate1, const Gate& ga } if (product_nqubits == 2) { - if (gate1.gate_type() == GateType::SWAP || gate2.gate_type() == GateType::SWAP) { + if (gate1.gate_type() == GateType::SWAP || gate2.gate_type() == GateType::SWAP || gate1.gate_type() == GateType::A || gate2.gate_type() == GateType::A) { return {false, 0}; } const Gate& single_qubit_gate = (num_qubits_gate1 == 1) ? gate1 : gate2; const Gate& two_qubit_gate = (num_qubits_gate1 == 1) ? gate2 : gate1; if (single_qubit_gate.target() == two_qubit_gate.target()) { - std::pair pairGateType = std::make_pair(single_qubit_gate.gate_type(), controlled_2qubit_to_1qubit_gate[two_qubit_gate.gate_type()]); + std::pair pairGateType = std::make_pair(single_qubit_gate.gate_type(), controlled_2qubit_to_1qubit_gate.at(two_qubit_gate.gate_type())); return {pairs_of_commuting_1qubit_gates.find(pairGateType) != pairs_of_commuting_1qubit_gates.end(), 0}; } return {diagonal_1qubit_gates.find(single_qubit_gate.gate_type()) != diagonal_1qubit_gates.end(), 0}; @@ -268,20 +268,20 @@ std::pair evaluate_gate_interaction(const Gate& gate1, const Gate& ga if (product_nqubits == 4) { if (commonQubitCount == 1) { - if (gate1.gate_type() == GateType::SWAP || gate2.gate_type() == GateType::SWAP) { + if (gate1.gate_type() == GateType::SWAP || gate2.gate_type() == GateType::SWAP || gate1.gate_type() == GateType::A || gate2.gate_type() == GateType::A) { return {false, 0}; } if (gate1.control() == gate2.control()) { return {true, 0}; } if (gate1.target() == gate2.target()) { - std::pair pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate[gate1.gate_type()], controlled_2qubit_to_1qubit_gate[gate2.gate_type()]); + std::pair pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate.at(gate1.gate_type()), controlled_2qubit_to_1qubit_gate.at(gate2.gate_type())); return {pairs_of_commuting_1qubit_gates.find(pairGateType) != pairs_of_commuting_1qubit_gates.end(), 0}; } if (gate1.target() == gate2.control()) { - return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate[gate1.gate_type()]) != diagonal_1qubit_gates.end(), 0}; + return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate.at(gate1.gate_type())) != diagonal_1qubit_gates.end(), 0}; } - return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate[gate2.gate_type()]) != diagonal_1qubit_gates.end(), 0}; + return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate.at(gate2.gate_type())) != diagonal_1qubit_gates.end(), 0}; } else { if (symmetrical_2qubit_gates.find(gate1.gate_type()) != symmetrical_2qubit_gates.end() && symmetrical_2qubit_gates.find(gate2.gate_type()) != symmetrical_2qubit_gates.end()) { return {true, 2 * (gate1.gate_type() == gate2.gate_type())}; @@ -289,11 +289,18 @@ std::pair evaluate_gate_interaction(const Gate& gate1, const Gate& ga if (gate1.gate_type() == GateType::SWAP || gate2.gate_type() == GateType::SWAP) { return {false, 0}; } + if (gate1.gate_type() == GateType::A || gate2.gate_type() == GateType::A) { + if (symmetrical_2qubit_gates.find(gate1.gate_type()) != symmetrical_2qubit_gates.end() || symmetrical_2qubit_gates.find(gate2.gate_type()) != symmetrical_2qubit_gates.end()) { + return {true, 0}; + } else { + return {false, 0}; + } + } if (gate1.target() == gate2.target()) { - std::pair pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate[gate1.gate_type()], controlled_2qubit_to_1qubit_gate[gate2.gate_type()]); + std::pair pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate.at(gate1.gate_type()), controlled_2qubit_to_1qubit_gate.at(gate2.gate_type())); return {pairs_of_commuting_1qubit_gates.find(pairGateType) != pairs_of_commuting_1qubit_gates.end(), gate1.gate_type() == gate2.gate_type()}; } - return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate[gate1.gate_type()]) != diagonal_1qubit_gates.end() && diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate[gate2.gate_type()]) != diagonal_1qubit_gates.end(), 0}; + return {diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate.at(gate1.gate_type())) != diagonal_1qubit_gates.end() && diagonal_1qubit_gates.find(controlled_2qubit_to_1qubit_gate.at(gate2.gate_type())) != diagonal_1qubit_gates.end(), 0}; } } diff --git a/src/qforte/gate.h b/src/qforte/gate.h index c78023ba..28f4b912 100644 --- a/src/qforte/gate.h +++ b/src/qforte/gate.h @@ -152,7 +152,7 @@ Gate make_control_gate(size_t control, Gate& U); /// int: which simplification scheme can be applied std::pair evaluate_gate_interaction(const Gate& gate1, const Gate& gate2); -extern std::unordered_set phase_1qubit_gates; -extern std::unordered_map controlled_2qubit_to_1qubit_gate; +extern const std::unordered_set phase_1qubit_gates; +extern const std::unordered_map controlled_2qubit_to_1qubit_gate; #endif // _gate_h_ diff --git a/src/qforte/make_gate.cc b/src/qforte/make_gate.cc index e260a964..2945d5eb 100644 --- a/src/qforte/make_gate.cc +++ b/src/qforte/make_gate.cc @@ -109,6 +109,8 @@ Gate make_gate(std::string type, size_t target, size_t control, double parameter } else { if (type == "A") { + // The A gate is the particle-number preserving gate introduced in DOI: 10.1103/PhysRevA.98.022322. + // Its decomposition in elementary gates requires 3 CNOTs. std::complex c = std::cos(parameter); std::complex s = std::sin(parameter); std::complex gate[4][4]{ diff --git a/tests/test_circuit_simplify.py b/tests/test_circuit_simplify.py index e939a761..4fd7d5f8 100644 --- a/tests/test_circuit_simplify.py +++ b/tests/test_circuit_simplify.py @@ -4,9 +4,9 @@ import random one_qubit_gate_pool = ['X','Y','Z','Rx','Ry','Rz','H','S','T','R','V'] -two_qubit_gate_pool = ['CNOT', 'aCNOT', 'cY', 'cZ', 'cV', 'SWAP', 'cRz', 'cR'] +two_qubit_gate_pool = ['CNOT', 'aCNOT', 'cY', 'cZ', 'cV', 'SWAP', 'cRz', 'cR', 'A'] -parametrized_gates = {'Rx','Ry','Rz', 'R', 'cR', 'cRz'} +parametrized_gates = {'Rx','Ry','Rz', 'R', 'cR', 'cRz', 'A'} diagonal_1qubit_gates = {'T', 'S', 'Z', 'Rz', 'R'} @@ -137,7 +137,7 @@ def test_1qubit_and_2qubit_gate(self): two_qubit_gate = qf.gate(two_qubit_gate_type, 0, 1, parameter) else: two_qubit_gate = qf.gate(two_qubit_gate_type, 0, 1) - if two_qubit_gate_type == 'SWAP': + if two_qubit_gate_type == 'SWAP' or two_qubit_gate_type == 'A': assert(qf.evaluate_gate_interaction(one_qubit_gate_target, two_qubit_gate) == (False, 0)) assert(qf.evaluate_gate_interaction(one_qubit_gate_control, two_qubit_gate) == (False, 0)) continue @@ -190,6 +190,17 @@ def test_2qubit_gates(self): else: assert (qf.evaluate_gate_interaction(gate1, gate2e) == (False, 0)) assert (qf.evaluate_gate_interaction(gate1, gate2f) == (False, 0)) + elif 'A' in [gatetype1, gatetype2]: + assert (qf.evaluate_gate_interaction(gate1, gate2a) == (False, 0)) + assert (qf.evaluate_gate_interaction(gate1, gate2b) == (False, 0)) + assert (qf.evaluate_gate_interaction(gate1, gate2c) == (False, 0)) + assert (qf.evaluate_gate_interaction(gate1, gate2d) == (False, 0)) + if gatetype1 in symmetrical_2qubit_gates or gatetype2 in symmetrical_2qubit_gates: + assert (qf.evaluate_gate_interaction(gate1, gate2e) == (True, 0)) + assert (qf.evaluate_gate_interaction(gate1, gate2f) == (True, 0)) + else: + assert (qf.evaluate_gate_interaction(gate1, gate2e) == (False, 0)) + assert (qf.evaluate_gate_interaction(gate1, gate2f) == (False, 0)) else: if tuple(sorted([controlled_2qubit_to_1qubit_gate[gatetype1], controlled_2qubit_to_1qubit_gate[gatetype2]])) in pairs_of_commuting_1qubit_gates: assert (qf.evaluate_gate_interaction(gate1, gate2a) == (True, 0)) @@ -249,6 +260,13 @@ def test_simplify_involutory_gates(self): def test_simplify_parametrized_gates(self): for gatetype in parametrized_gates: + if gatetype == 'A': + circ = qf.Circuit() + circ.add(qf.gate(gatetype, 0, 1, 0.5)) + circ.add(qf.gate(gatetype, 0, 1, -0.2)) + circ.simplify() + assert len(circ.gates()) == 2 + continue if gatetype in one_qubit_gate_pool: circ = qf.Circuit() circ.add(qf.gate(gatetype, 0, 0, 0.5))