Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for CCZ and CCX/TOF gates. #487

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion tensorflow_quantum/core/ops/circuit_execution_ops_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ def test_supported_gates_consistent(self, op_and_sim):
for qubit in qubits:
c += cirq.Circuit(cirq.Y(qubit)**0.125)

if gate_ref[gate] == 2:
if gate_ref[gate] == 3:
op_qubits = np.random.choice(qubits, size=3, replace=False)
c += cirq.Circuit(gate(*op_qubits))
elif gate_ref[gate] == 2:
op_qubits = np.random.choice(qubits, size=2, replace=False)
c += cirq.Circuit(gate(*op_qubits))
elif gate_ref[gate] == 1:
Expand Down
2 changes: 2 additions & 0 deletions tensorflow_quantum/core/serialize/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,9 @@ def _scalar_combiner(exponent, global_shift, exponent_scalar,
cirq.ZZPowGate: "ZZP",
cirq.HPowGate: "HP",
cirq.CZPowGate: "CZP",
cirq.CCZPowGate: "CCZP",
cirq.CNotPowGate: "CNP",
cirq.CCXPowGate: "CCXP",
cirq.SwapPowGate: "SP",
cirq.ISwapPowGate: "ISP",
}
Expand Down
41 changes: 41 additions & 0 deletions tensorflow_quantum/core/serialize/serializer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def _make_controlled_circuit(circuit, control_qubits, control_values):
def _get_circuit_proto_pairs():
q0 = cirq.GridQubit(0, 0)
q1 = cirq.GridQubit(0, 1)
q2 = cirq.GridQubit(0, 2)

pairs = [
# HPOW and aliases.
Expand Down Expand Up @@ -290,6 +291,26 @@ def _get_circuit_proto_pairs():
['exponent', 'exponent_scalar', 'global_shift'],
[1.0, 1.0, 0.0], ['0_0', '0_1'])),

# CCZPow and aliases
(cirq.Circuit(cirq.CCZPowGate(exponent=0.3)(q0, q1, q2)),
_build_gate_proto("CCZP",
['exponent', 'exponent_scalar', 'global_shift'],
[0.3, 1.0, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(
cirq.CCZPowGate(exponent=sympy.Symbol('alpha'))(q0, q1, q2)),
_build_gate_proto("CCZP",
['exponent', 'exponent_scalar', 'global_shift'],
['alpha', 1.0, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(
cirq.CCZPowGate(exponent=3.1 * sympy.Symbol('alpha'))(q0, q1, q2)),
_build_gate_proto("CCZP",
['exponent', 'exponent_scalar', 'global_shift'],
['alpha', 3.1, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(cirq.CCZ(q0, q1, q2)),
_build_gate_proto("CCZP",
['exponent', 'exponent_scalar', 'global_shift'],
[1.0, 1.0, 0.0], ['0_0', '0_1', '0_2'])),

# CNOTPow and aliases
(cirq.Circuit(cirq.CNotPowGate(exponent=0.3)(q0, q1)),
_build_gate_proto("CNP",
Expand All @@ -309,6 +330,26 @@ def _get_circuit_proto_pairs():
['exponent', 'exponent_scalar', 'global_shift'],
[1.0, 1.0, 0.0], ['0_0', '0_1'])),

# CCXPow and aliases
(cirq.Circuit(cirq.CCXPowGate(exponent=0.3)(q0, q1, q2)),
_build_gate_proto("CCXP",
['exponent', 'exponent_scalar', 'global_shift'],
[0.3, 1.0, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(
cirq.CCXPowGate(exponent=sympy.Symbol('alpha'))(q0, q1, q2)),
_build_gate_proto("CCXP",
['exponent', 'exponent_scalar', 'global_shift'],
['alpha', 1.0, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(
cirq.CCXPowGate(exponent=3.1 * sympy.Symbol('alpha'))(q0, q1, q2)),
_build_gate_proto("CCXP",
['exponent', 'exponent_scalar', 'global_shift'],
['alpha', 3.1, 0.0], ['0_0', '0_1', '0_2'])),
(cirq.Circuit(cirq.TOFFOLI(q0, q1, q2)),
_build_gate_proto("CCXP",
['exponent', 'exponent_scalar', 'global_shift'],
[1.0, 1.0, 0.0], ['0_0', '0_1', '0_2'])),

# SWAPPow and aliases
(cirq.Circuit(cirq.SwapPowGate(exponent=0.3)(q0, q1)),
_build_gate_proto("SP",
Expand Down
31 changes: 31 additions & 0 deletions tensorflow_quantum/core/src/adj_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ void CreateGradientCircuit(
grad_gates->push_back(grad);
}

// Three qubit Eigen.
else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kCCZPowGate ||
circuit.gates[i].kind == qsim::Cirq::GateKind::kCCXPowGate) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good news! I fixed it Mike. The culprit was qsim::Cirq::GateKind::kCCX for Toffoli gate. It has its own enum value. :)

Please add the following line:

else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kCCZPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kCCXPowGate ||
             circuit.gates[i].kind == qsim::Cirq::GateKind::kCCX) {

and then I saw the following test passed.

bazel test tensorflow_quantum/python/differentiators:gradient_test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't think that is the fix :(. Like we talked about on our sync it is the parametershift differentiator that is having issues, not the adjoint differentiator. The tests fail somewhat randomly and unpredictably so If you can get the parametershift test to pass with --runs_per_test=50 and with many more circuits then I think that would be enough to say that the bug is fixed. When I run with this change I can still get the error if I change the number of circuits and ops.

bool swapq = circuit.gates[i].swapped;
PopulateGradientThreeEigen(
metadata[i].create_f3, metadata[i].symbol_values[0], i,
swapq ? circuit.gates[i].qubits[2] : circuit.gates[i].qubits[0],
circuit.gates[i].qubits[1],
swapq ? circuit.gates[i].qubits[0] : circuit.gates[i].qubits[2],
metadata[i].gate_params[0], metadata[i].gate_params[1],
metadata[i].gate_params[2], &grad);
grad_gates->push_back(grad);
}

// PhasedX
else if (circuit.gates[i].kind == qsim::Cirq::GateKind::kPhasedXPowGate) {
// Process potentially several symbols.
Expand Down Expand Up @@ -202,6 +216,23 @@ void PopulateGradientTwoEigen(
grad->grad_gates.push_back(left);
}

void PopulateGradientThreeEigen(
const std::function<qsim::Cirq::GateCirq<float>(unsigned int, unsigned int,
unsigned int, unsigned int,
float, float)>& create_f,
const std::string& symbol, unsigned int location, unsigned int qid,
unsigned int qid2, unsigned int qid3, float exp, float exp_s, float gs,
GradientOfGate* grad) {
grad->params.push_back(symbol);
grad->index = location;
auto left = create_f(0, qid, qid2, qid3, (exp + _GRAD_EPS) * exp_s, gs);
auto right = create_f(0, qid, qid2, qid3, (exp - _GRAD_EPS) * exp_s, gs);
Matrix8Diff(right.matrix,
left.matrix); // left's entries have right subtracted.
qsim::MatrixScalarMultiply(0.5 / _GRAD_EPS, left.matrix);
grad->grad_gates.push_back(left);
}

void PopulateGradientPhasedXPhasedExponent(const std::string& symbol,
unsigned int location,
unsigned int qid, float pexp,
Expand Down
16 changes: 16 additions & 0 deletions tensorflow_quantum/core/src/adj_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ void PopulateGradientTwoEigen(
const std::string& symbol, unsigned int location, unsigned int qid,
unsigned int qid2, float exp, float exp_s, float gs, GradientOfGate* grad);

void PopulateGradientThreeEigen(
const std::function<qsim::Cirq::GateCirq<float>(unsigned int, unsigned int,
unsigned int, unsigned int,
float, float)>& create_f,
const std::string& symbol, unsigned int location, unsigned int qid,
unsigned int qid2, unsigned int qid3, float exp, float exp_s, float gs,
GradientOfGate* grad);

// Note: all methods below expect gate qubit indices to have been swapped so
// qid < qid2.
void PopulateGradientPhasedXPhasedExponent(const std::string& symbol,
Expand Down Expand Up @@ -116,6 +124,14 @@ void Matrix4Diff(Array2& source, Array2& dest) {
}
}

// does matrix elementiwse subtraction dest -= source.
template <typename Array2>
void Matrix8Diff(Array2& source, Array2& dest) {
for (unsigned i = 0; i < 128; i++) {
dest[i] -= source[i];
}
}

} // namespace tfq

#endif // TFQ_CORE_SRC_ADJ_UTIL_H_
67 changes: 67 additions & 0 deletions tensorflow_quantum/core/src/adj_util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ void Matrix4Equal(const std::vector<float>& v,
}
}

void Matrix8Equal(const std::vector<float>& v,
const std::vector<float>& expected, float eps) {
for (int i = 0; i < 128; i++) {
EXPECT_NEAR(v[i], expected[i], eps);
}
}

typedef absl::flat_hash_map<std::string, std::pair<int, float>> SymbolMap;
typedef qsim::Cirq::GateCirq<float> QsimGate;
typedef qsim::Circuit<QsimGate> QsimCircuit;
Expand Down Expand Up @@ -167,6 +174,66 @@ INSTANTIATE_TEST_CASE_P(
&qsim::Cirq::ISwapPowGate<float>::Create,
&qsim::Cirq::SwapPowGate<float>::Create));

class ThreeQubitEigenFixture
: public ::testing::TestWithParam<
std::function<QsimGate(unsigned int, unsigned int, unsigned int,
unsigned int, float, float)>> {};

TEST_P(ThreeQubitEigenFixture, CreateGradientThreeEigen) {
QsimCircuit circuit;
std::vector<GateMetaData> metadata;
std::vector<std::vector<qsim::GateFused<QsimGate>>> fuses;
std::vector<GradientOfGate> grad_gates;

// Create a symbolized gate.
std::function<QsimGate(unsigned int, unsigned int, unsigned int, unsigned int,
float, float)>
given_f = GetParam();

circuit.num_qubits = 3;
circuit.gates.push_back(given_f(0, 0, 1, 2, 1.0, 2.0));
GateMetaData meta;
meta.index = 0;
meta.symbol_values.push_back("TheSymbol");
meta.placeholder_names.push_back(GateParamNames::kExponent);
meta.gate_params = {1.0, 1.0, 2.0};
meta.create_f3 = given_f;
metadata.push_back(meta);

CreateGradientCircuit(circuit, metadata, &fuses, &grad_gates);
EXPECT_EQ(grad_gates.size(), 1);
EXPECT_EQ(grad_gates[0].index, 0);
EXPECT_EQ(grad_gates[0].params.size(), 1);
EXPECT_EQ(grad_gates[0].params[0], "TheSymbol");

// fuse everything into 2 gates. One fuse before this gate and one after.
// both wind up being identity since this is the only gate.
EXPECT_EQ(fuses.size(), 2);

GradientOfGate tmp;
PopulateGradientThreeEigen(given_f, "TheSymbol", 0, 0, 1, 2, 1.0, 1.0, 2.0,
&tmp);

Matrix8Equal(tmp.grad_gates[0].matrix, grad_gates[0].grad_gates[0].matrix,
1e-4);

// Test with NO symbol.
metadata.clear();
meta.symbol_values.clear();
meta.placeholder_names.clear();
fuses.clear();
grad_gates.clear();

CreateGradientCircuit(circuit, metadata, &fuses, &grad_gates);
EXPECT_EQ(grad_gates.size(), 0);
EXPECT_EQ(fuses.size(), 1); // fuse everything into 1 gate.
}

INSTANTIATE_TEST_CASE_P(
ThreeQubitEigenTests, ThreeQubitEigenFixture,
::testing::Values(&qsim::Cirq::CCZPowGate<float>::Create,
&qsim::Cirq::CCXPowGate<float>::Create));

TEST(AdjUtilTest, CreateGradientPhasedX) {
QsimCircuit circuit;
std::vector<GateMetaData> metadata;
Expand Down
67 changes: 67 additions & 0 deletions tensorflow_quantum/core/src/circuit_parser_qsim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,58 @@ inline Status TwoEigenGate(
return Status::OK();
}

// three qubit eigen -> Create(time, q0, q1, q2, exp, gs)
inline Status ThreeEigenGate(
const Operation& op, const SymbolMap& param_map,
const std::function<QsimGate(unsigned int, unsigned int, unsigned int,
unsigned int, float, float)>& create_f,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
unsigned int q0, q1, q2;
float exp, exp_s, gs;
bool unused;
Status u;
unused = absl::SimpleAtoi(op.qubits(0).id(), &q0);
unused = absl::SimpleAtoi(op.qubits(1).id(), &q1);
unused = absl::SimpleAtoi(op.qubits(2).id(), &q2);

absl::optional<std::string> exponent_symbol;
u = ParseProtoArg(op, "exponent", param_map, &exp, &exponent_symbol);
if (!u.ok()) {
return u;
}
u = ParseProtoArg(op, "exponent_scalar", param_map, &exp_s);
if (!u.ok()) {
return u;
}
u = ParseProtoArg(op, "global_shift", param_map, &gs);
if (!u.ok()) {
return u;
}
auto gate = create_f(time, num_qubits - q0 - 1, num_qubits - q1 - 1,
num_qubits - q2 - 1, exp * exp_s, gs);

Status s = OptionalInsertControls(op, num_qubits, &gate);
if (!s.ok()) {
return s;
}
circuit->gates.push_back(gate);

// check for symbols and track metadata if needed.
if (metadata != nullptr) {
GateMetaData info;
info.index = circuit->gates.size() - 1;
info.gate_params = {exp, exp_s, gs};
info.create_f3 = create_f;
if (exponent_symbol.has_value()) {
info.symbol_values = {exponent_symbol.value()};
info.placeholder_names = {GateParamNames::kExponent};
}
metadata->push_back(info);
}
return Status::OK();
}

Status IGate(const Operation& op, const SymbolMap& param_map,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
Expand Down Expand Up @@ -365,13 +417,27 @@ Status CZGate(const Operation& op, const SymbolMap& param_map,
num_qubits, time, circuit, metadata);
}

Status CCZGate(const Operation& op, const SymbolMap& param_map,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
return ThreeEigenGate(op, param_map, &qsim::Cirq::CCZPowGate<float>::Create,
num_qubits, time, circuit, metadata);
}

Status CXGate(const Operation& op, const SymbolMap& param_map,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
return TwoEigenGate(op, param_map, &qsim::Cirq::CXPowGate<float>::Create,
num_qubits, time, circuit, metadata);
}

Status CCXGate(const Operation& op, const SymbolMap& param_map,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
return ThreeEigenGate(op, param_map, &qsim::Cirq::CCXPowGate<float>::Create,
num_qubits, time, circuit, metadata);
}

Status SwapGate(const Operation& op, const SymbolMap& param_map,
const unsigned int num_qubits, const unsigned int time,
QsimCircuit* circuit, std::vector<GateMetaData>* metadata) {
Expand Down Expand Up @@ -579,6 +645,7 @@ tensorflow::Status ParseAppendGate(const Operation& op,
{"ZP", &ZGate}, {"ZZP", &ZZGate},
{"CZP", &CZGate}, {"I2", &I2Gate},
{"CNP", &CXGate}, {"SP", &SwapGate},
{"CCZP", &CCZGate}, {"CCXP", &CCXGate},
{"ISP", &ISwapGate}, {"PXP", &PhasedXGate},
{"FSIM", &FsimGate}, {"PISP", &PhasedISwapGate}};

Expand Down
5 changes: 5 additions & 0 deletions tensorflow_quantum/core/src/circuit_parser_qsim.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ struct GateMetaData {
std::function<qsim::Cirq::GateCirq<float>(unsigned int, unsigned int,
unsigned int, float, float)>
create_f2;

// set only if gate is Three qubit Eigen gate.
std::function<qsim::Cirq::GateCirq<float>(
unsigned int, unsigned int, unsigned int, unsigned int, float, float)>
create_f3;
};

// parse a serialized Cirq program into a qsim representation.
Expand Down
Loading