Modifiers and Custom Gates


Gate Modifiers

Scotty provides two modifiers that can be applied to some gates: Controlled and Dagger.

Controlled implements the ControlGate trait. You can use it by setting the control index and the target gate. The target gate can be a controlled gate or a target gate (but not a swap gate). The simulator allows for as many qubits in between control and target qubit indexes as you want. You can also have qubits set in any order.

Dagger is a modifier that takes the complex conjugate transpose of a single-qubit gate matrix.

You can mix and match modifiers and gates in any way you see fit as long as the final controlled gate is a TargetGate:

Controlled(0, Dagger(Y(1))

Custom Gates

Scotty provides two abstractions for custom gates: DefGate and CompositeGate. DefGate lets you to define single-qubit gates with your own matrix.

Here is how you can define a simple square root of NOT gate:

import scotty.quantum.math.Complex

val matrix = Array(
  Array(Complex(0.5, 0.5), Complex(0.5, -0.5)),
  Array(Complex(0.5, -0.5), Complex(0.5, 0.5))
).toFloat

def SqrtNot(index: Int) = DefGate(matrix, index)

Now you can use SqrtNot as any other gate:

QuantumSimulator().run(Circuit(X(1), SqrtNot(1)))

Complex is a complex number representation provided by the framework. It has two Float parameters: one for the real part and one for the imaginary part. Gates use a more bare-bones representation of the matrix for performance reasons: Array[Array[Float]]. It represents complex numbers by putting real and imaginary float components next to each other. You can still define your gates with the Complex helper but you have to use the implicit toFloat method to convert your matrix array before passing it to DefGate.

To define a custom parametric gate use another DefGate constructor that takes a MatrixGen (which is a shorthand for type Seq[Float] => Matrix) and a list of Double parameters. Here’s an example of a custom phase shift gate:

import scotty.quantum.math.Complex

val matrix = (params: Seq[Double]) => Array(
  Array(Complex(1), Complex(0)),
  Array(Complex(0), Complex(Math.cos(params(0)), Math.sin(params(0))))
).toFloat

def QuarterPhase(phi: Double, index: Int) = DefGate(matrix, phi / 4, index)

Here’s how you can use it:

QuarterPhase(Math.PI, 0)

Finally, you can define CompositeGates to describe multi-qubit unitary gates. Some gates in the Scotty simulator use this technique. For example, the SWAP gate is implemented as a function that takes custom indexes as two parameters and returns a CompositeGate:

def SWAP(i0: Int, i1: Int) = CompositeGate(CNOT(i0, i1), CNOT(i1, i0), CNOT(i0, i1))

You can use this approach to define your own gates and unitary operations.