I am trying to simulate Deutsch's algorithm, and I need to apply the oracle function matrix to my circuit.
3 Answers
[Note: this answer is outdated]
This is going to change somewhat radically in the next version of cirq, so I'll give an answer for both versions.
In v0.3, in order for a simulator to understand a custom gate, the gate must implement either cirq.CompositeGate
or cirq.KnownMatrix
. For your case, the simplest is to implement the matrix:
# assuming cirq v0.3
import cirq
import numpy as np
class Oracle(cirq.Gate, cirq.KnownMatrix):
def __init__(self, secret_state, qubit_count):
self.secret_state = secret_state
self.qubit_count = qubit_count
def matrix(self):
m = np.eye(1 << self.qubit_count)
m[self.secret_state, self.secret_state] = -1
return m
You can then use this oracle to simulate e.g. a Grover circuit and see that the secret state ends up with quite a high probability:
qs = cirq.LineQubit.range(5)
secret = 5
oracle = Oracle(secret, len(qs)).on(*qs)
diffusion = [
cirq.H.on_each(qs),
Oracle(0, len(qs)).on(*qs),
cirq.H.on_each(qs)
]
c = cirq.Circuit.from_ops(
cirq.H.on_each(qs),
[oracle, diffusion] * 4
)
output_vector = c.apply_unitary_effect_to_state()
print(np.round(abs(output_vector)**2, 3))
# [0 0 0 0 0 .999 0 0 ...]
# ^ big probability at offset 5
In the coming v0.4 the classes such as KnownMatrix
will be replaced by "magic methods" such as _unitary_
. (This is generally how things are supposed to be done in python.) One of those magic methods is _apply_unitary_to_tensor_
, which is used to enable faster simulation. With that method the oracle application can be simulated much much faster; in $O(q)$ time instead of $O(4^q)$ time assuming the oracle covers all of the qubits. We also happen to avoid the need to know the number of qubits ahead of time:
# assuming cirq v0.4
import cirq
class Oracle(cirq.Gate):
def __init__(self, secret_state):
self.secret_state = secret_state
def _apply_unitary_to_tensor_(self, target_tensor, available_buffer, axes):
s = cirq.slice_for_qubits_equal_to(axes, self.secret_state)
target_tensor[s] *= -1
return target_tensor
I searched for doing a custom gate on the Cirq documentation and here are the results :
Gate sets
The xmon simulator is designed to work with operations that are either a GateOperation applying an XmonGate, a CompositeOperation that decomposes (recursively) to XmonGates, or a 1-qubit or 2-qubit operation with a KnownMatrix. By default the xmon simulator uses an Extension defined in xgate_gate_extensions to try to resolve gates that are not XmonGates to XmonGates.
So if you are using a custom gate, there are multiple options for getting it to work with the simulator:
Define it directly as an XmonGate. Provide a CompositeGate made up of XmonGates. Supply an Exentension to the simulator which converts the gate to an XmonGate or to a CompositeGate which itself can be decomposed in XmonGates.
The other answers seem outdated, so here is an answer written for Cirq v0.7.0. Applying a unitary matrix is basically constructing a custom gate, and a simple example for a qubit is shown below. The gate below simply converts 0 -> 1
and 1 -> 0
, but more complex examples are possible.
import cirq
import numpy as np
class QubitPlusGate(cirq.SingleQubitGate):
def _unitary_(self):
# Our input is q = a[0] + b[1]
# And our output becomes q = b[0] + a[1]
return np.array([[0, 1],
[1, 0]])
def _circuit_diagram_info_(self, args):
return '[+1]'
# Create a single line qubit
q0 = cirq.GridQubit(0, 0)
circuit = cirq.Circuit(
QubitPlusGate().on(q0),
cirq.measure(q0)
)
# Simulate the circuit several times.
simulator = cirq.Simulator()
result = simulator.run(circuit, repetitions=10)
print("Results:")
print(result)
If you want to view more complex examples, it is best to dive into the source code of some gates supplied by cirq itself, but the implementation architecture is exactly the same.