Skip to content

Commit

Permalink
Merge pull request #244 from imagoulas/fix_simplify_A_gate
Browse files Browse the repository at this point in the history
Fixed simplify_circuit with A gates
  • Loading branch information
muhan-zhang authored May 8, 2024
2 parents c569e90 + acb0216 commit e5eb9b7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 30 deletions.
24 changes: 12 additions & 12 deletions src/qforte/circuit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -228,18 +228,18 @@ bool Circuit::is_pauli() const {

void Circuit::simplify() {

std::unordered_set<GateType> involutory_gates = {GateType::X, GateType::Y, GateType::Z,
GateType::cX, GateType::cY, GateType::cZ,
GateType::acX, GateType::H, GateType::SWAP};
const std::unordered_set<GateType> involutory_gates = {GateType::X, GateType::Y, GateType::Z,
GateType::cX, GateType::cY, GateType::cZ,
GateType::acX, GateType::H, GateType::SWAP};

std::unordered_set<GateType> parametrized_gates = {GateType::Rx, GateType::Ry, GateType::Rz,
GateType::R, GateType::cRz, GateType::cR};
const std::unordered_set<GateType> parametrized_gates = {GateType::Rx, GateType::Ry, GateType::Rz,
GateType::R, GateType::cRz, GateType::cR};

std::unordered_set<GateType> square_root_gates = {GateType::T, GateType::S, GateType::V,
GateType::cV};
const std::unordered_set<GateType> square_root_gates = {GateType::T, GateType::S, GateType::V,
GateType::cV};

std::unordered_map<GateType, std::string> simplify_square_root_gates = {{GateType::T, "S"}, {GateType::S, "Z"},
{GateType::V, "X"}, {GateType::cV, "cX"}};
const std::unordered_map<GateType, std::string> simplify_square_root_gates = {{GateType::T, "S"}, {GateType::S, "Z"},
{GateType::V, "X"}, {GateType::cV, "cX"}};

std::vector<size_t> gate_indices_to_remove;

Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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());
Expand Down
33 changes: 20 additions & 13 deletions src/qforte/gate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ struct pair_equal {
}
};

std::unordered_set<std::pair<GateType, GateType>, pair_hash, pair_equal> pairs_of_commuting_1qubit_gates = {
const std::unordered_set<std::pair<GateType, GateType>, 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},
Expand All @@ -215,16 +215,16 @@ std::unordered_set<std::pair<GateType, GateType>, pair_hash, pair_equal> pairs_o
{GateType::V, GateType::V}
};

std::unordered_set<GateType> diagonal_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::Rz, GateType::R};
const std::unordered_set<GateType> diagonal_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::Rz, GateType::R};

std::unordered_set<GateType> phase_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::R};
const std::unordered_set<GateType> phase_1qubit_gates = {GateType::T, GateType::S, GateType::Z, GateType::R};

std::unordered_map<GateType, GateType> controlled_2qubit_to_1qubit_gate = {
const std::unordered_map<GateType, GateType> 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<GateType> symmetrical_2qubit_gates = {GateType::cZ, GateType::cR, GateType::SWAP};
const std::unordered_set<GateType> symmetrical_2qubit_gates = {GateType::cZ, GateType::cR, GateType::SWAP};

std::pair<bool, int> evaluate_gate_interaction(const Gate& gate1, const Gate& gate2) {

Expand Down Expand Up @@ -253,47 +253,54 @@ std::pair<bool, int> 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<GateType, GateType> pairGateType = std::make_pair(single_qubit_gate.gate_type(), controlled_2qubit_to_1qubit_gate[two_qubit_gate.gate_type()]);
std::pair<GateType, GateType> 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};
}

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<GateType, GateType> pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate[gate1.gate_type()], controlled_2qubit_to_1qubit_gate[gate2.gate_type()]);
std::pair<GateType, GateType> 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())};
}
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<GateType, GateType> pairGateType = std::make_pair(controlled_2qubit_to_1qubit_gate[gate1.gate_type()], controlled_2qubit_to_1qubit_gate[gate2.gate_type()]);
std::pair<GateType, GateType> 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};
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/qforte/gate.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Gate make_control_gate(size_t control, Gate& U);
/// int: which simplification scheme can be applied
std::pair<bool, int> evaluate_gate_interaction(const Gate& gate1, const Gate& gate2);

extern std::unordered_set<GateType> phase_1qubit_gates;
extern std::unordered_map<GateType, GateType> controlled_2qubit_to_1qubit_gate;
extern const std::unordered_set<GateType> phase_1qubit_gates;
extern const std::unordered_map<GateType, GateType> controlled_2qubit_to_1qubit_gate;

#endif // _gate_h_
2 changes: 2 additions & 0 deletions src/qforte/make_gate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<double> c = std::cos(parameter);
std::complex<double> s = std::sin(parameter);
std::complex<double> gate[4][4]{
Expand Down
24 changes: 21 additions & 3 deletions tests/test_circuit_simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit e5eb9b7

Please sign in to comment.