This developer document explains what is expected of a gate or operation exposed by Cirq. In particular, we have a stricter standard than what is required of users of the library.
For a user of Cirq, specifying either a _unitary_
method or a _decompose_
method is sufficient to get a gate working.
Most other protocols will infer what they need from these two methods.
A gate specified in this way will not be particularly performant, but it will
work.
For internal gates, we also want high performance, and so we require several
other protocol methods to be implemented.
In general, the source of truth for what has to be implemented is enforced
by the cirq.testing.assert_implements_consistent_protocols
method.
This method verifies the following properties:
The class has a
__repr__
method that produces a python expression that evaluates to an object equal to the original value. The expression assumes thatcirq
,sympy
,numpy as np
, andpandas as pd
have been imported.If the class is unitary, it specifies a
_has_unitary_
method.The classes various protocols agree with each other. For example, the decomposition that
_decompose_
produces should have the same effect as the unitary produced by_unitary_
or the transformation applied by_apply_unitary_
.
If the gate is exposed by cirq/__init__.py
or another public module, other
tests will notice it and verify that it is serializable.
See the serialization guidelines.
There are several other informal constraints:
Large gates should have a
_decompose_
method that returns a composition of smaller gates. This allows optimizers and other tools that cannot understand the gate to break it into pieces that they do understand.Gates should specify a good
_circuit_diagram_info_
method. In some cases the default behavior of using__str__
is sufficient.Gates should have a good
__str__
method.If the
__repr__
is cumbersome, gates should specify a_repr_pretty_
method. This method will be used preferentially by Jupyter notebooks, ipython, etc.Gates should specify an
_apply_unitary_
method. This is not necessary for single or two qubit gates, but it is a huge performance difference for larger gates.Gates that take parameters (e.g. a rotation angle) should generally allow for those parameters to be sympy objects instead of floats, and implement corresponding
_is_parameterized_
and_resolve_parameters_
methods.Prefer creating a
Gate
over creating anOperation
. In some cases it makes sense to only have anOperation
, but these cases are generally surprising to users. If you have to use an operation, try to have the.gate
property of the operation can return something useful instead ofNone
.Consider adding interop methods like
_qasm_
. These methods will fallback to using things like_decompose_
, but the output is usually much better when specialized.