ConditionalSqueezing

class hybridlane.ConditionalSqueezing(z, phi, wires, id=None)

Bases: hybridlane.ops.mixins.HybridOperation, hybridlane.ops.mixins.FockRepresentation

Qubit-conditioned squeezing gate \(CS(\zeta)\)

\[\begin{split}CS(\zeta) &= \exp\left[\frac{1}{2}\sigma_z (\zeta^* a^2 - \zeta (\ad)^2)\right] \\ &= \begin{pmatrix} S(\zeta) & 0 \\ 0 & S^\dagger(\zeta) \end{pmatrix}\end{split}\]

where \(\zeta = ze^{i2\theta} \in \mathbb{C}\) (Box IV.3 of [1]).

Details:

  • Number of wires: 2

  • Wire arguments: [qubit, qumode]

  • Number of parameters: 2

  • Number of dimensions per parameter: (0, 0)

This is the qubit-conditioned version of the Squeezing gate.

>>> hl.qcond(hl.S(0.5, 0.0, wires=1), control_wires=0)
ConditionalSqueezing(0.5, 0.0, wires=[0, 1])

Its representation in the Fock basis can be obtained with:

>>> CS(0.5, 0.0, wires=(0, 1)).fock_matrix({0: 2, 1: 3})
array([[ 0.9381+0.j,  0.    +0.j,  0.3462+0.j,  0.    +0.j,  0.    +0.j,
         0.    +0.j],
       [ 0.    +0.j,  1.    +0.j,  0.    +0.j,  0.    +0.j,  0.    +0.j,
         0.    +0.j],
       [-0.3462+0.j,  0.    +0.j,  0.9381+0.j,  0.    +0.j,  0.    +0.j,
         0.    +0.j],
       [ 0.    +0.j,  0.    +0.j,  0.    +0.j,  0.9381-0.j,  0.    -0.j,
        -0.3462-0.j],
       [ 0.    +0.j,  0.    +0.j,  0.    +0.j,  0.    -0.j,  1.    -0.j,
         0.    -0.j],
       [ 0.    +0.j,  0.    +0.j,  0.    +0.j,  0.3462-0.j,  0.    -0.j,
         0.9381-0.j]])

There exists a decomposition in terms of ConditionalRotation and Squeezing gates

\[CS(\zeta) = CR(\pi/2)~S(i\zeta)~CR(-\pi/2)\]

See also

Squeezing

References

Parameters:
  • z (pennylane.typing.TensorLike)

  • phi (pennylane.typing.TensorLike)

  • wires (pennylane.wires.WiresLike)

  • id (str | None)

num_params = 2

Number of trainable parameters that the operator depends on.

By default, this property returns as many parameters as were used for the operator creation. If the number of parameters for an operator subclass is fixed, this property can be overwritten to return the fixed value.

Returns:

number of parameters

Return type:

int

num_wires = 2

Number of wires the operator acts on.

num_qumodes = 1

The number of qumodes the gate acts on

ndim_params = (0, 0)

Number of dimensions per trainable parameter of the operator.

By default, this property returns the numbers of dimensions of the parameters used for the operator creation. If the parameter sizes for an operator subclass are fixed, this property can be overwritten to return the fixed value.

Returns:

Number of dimensions for each trainable parameter.

Return type:

tuple

resource_keys

The set of parameters that affects the resource requirement of the operator.

All decomposition rules for this operator class are expected to have a resource function that accepts keyword arguments that match these keys exactly. The resource_rep() function will also expect keyword arguments that match these keys when called with this operator type.

The default implementation is an empty set, which is suitable for most operators.

See also

resource_params()

pow(n)

A list of new operators equal to this one raised to the given power. This method is used to simplify Pow instances created by pow() or op ** power.

Operator.pow can be optionally defined by Operator developers, while pow() or op ** power are the entry point for constructing generic powers to exponents.

Parameters:
Returns:

list[Operator]

>>> class MyClass(qp.operation.Operator):
...
...     def pow(self, z):
...         return [MyClass(self.data[0]*z, self.wires)]
...
>>> op = MyClass(0.5, 0) ** 2
>>> op
MyClass(0.5, wires=[0])**2
>>> op.decomposition()
[MyClass(1.0, wires=[0])]
>>> op.simplify()
MyClass(1.0, wires=[0])
adjoint()

Create an operation that is the adjoint of this one. Used to simplify Adjoint operators constructed by adjoint().

Adjointed operations are the conjugated and transposed version of the original operation. Adjointed ops are equivalent to the inverted operation for unitary gates.

Operator.adjoint can be optionally defined by Operator developers, while adjoint() is the entry point for constructing generic adjoint representations.

Returns:

The adjointed operation.

>>> class MyClass(qp.operation.Operator):
...
...     def adjoint(self):
...         return self
...
>>> op = qp.adjoint(MyClass(wires=0))
>>> op
Adjoint(MyClass(wires=[0]))
>>> op.decomposition()
[MyClass(wires=[0])]
>>> op.simplify()
MyClass(wires=[0])
simplify()

Reduce the depth of nested operators to the minimum.

Returns:

simplified operator

Return type:

.Operator

label(decimals=None, base_label=None, cache=None)

A customizable string representation of the operator.

Parameters:
  • decimals=None (int) – If None, no parameters are included. Else, specifies how to round the parameters.

  • base_label=None (str) – overwrite the non-parameter component of the label

  • cache=None (dict) – dictionary that carries information between label calls in the same drawing

Returns:

label to use in drawings

Return type:

str

Example:

>>> op = qp.RX(1.23456, wires=0)
>>> op.label()
'RX'
>>> op.label(base_label="my_label")
'my_label'
>>> op = qp.RX(1.23456, wires=0)
>>> op.label()
'RX'
>>> op.label(decimals=2)
'RX\n(1.23)'
>>> op.label(base_label="my_label")
'my_label'
>>> op.label(decimals=2, base_label="my_label")
'my_label\n(1.23)'

If the operation has a matrix-valued parameter and a cache dictionary is provided, unique matrices will be cached in the 'matrices' key list. The label will contain the index of the matrix in the 'matrices' list.

>>> op2 = qp.QubitUnitary(np.eye(2), wires=0)
>>> cache = {'matrices': []}
>>> op2.label(cache=cache)
'U\n(M0)'
>>> cache['matrices']
[tensor([[1., 0.],
 [0., 1.]], requires_grad=True)]
>>> op3 = qp.QubitUnitary(np.eye(4), wires=(0,1))
>>> op3.label(cache=cache)
'U\n(M1)'
>>> cache['matrices']
[tensor([[1., 0.],
        [0., 1.]], requires_grad=True),
tensor([[1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 0.],
        [0., 0., 0., 1.]], requires_grad=True)]
static compute_fock_matrix(wire_dims, z, theta)

Computes the Fock matrix representation of the gate

This method should be implemented by any gate to define its representation in the Fock basis. It should return a dense matrix of shape (d, d), where \(d = \prod_i \text{wire\_dims}_i\) is the total dimension of the Hilbert space of the gate.

Details:

The wire_dims argument provides the dimension of each wire in the canonical wire order, the same order as self.wires.

An example using the CR gate, which has wire types [qubit, qumode], using dimension 2 for the qubit and a dimension of 3 for the qumode:

>>> hl.CR.compute_fock_matrix((2, 3), 0.5)
array([[1.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.9689-0.2474j, 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.8776-0.4794j, 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 1.    -0.j    ,
        0.    -0.j    , 0.    -0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    -0.j    ,
        0.9689+0.2474j, 0.    -0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    -0.j    ,
        0.    -0.j    , 0.8776+0.4794j]])

If parameters are tensors of a deep learning framework, the returned matrix should also be of the same framework so that it can be used in differentiable computations. If the gate is nonparametric, this should return a NumPy ndarray.

Using the same example as above with a JAX array as a parameter, the resulting matrix is a differentiable JAX Array:

>>> hl.CR.compute_fock_matrix((2, 3), jnp.array(0.5))
Array([[1.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.9689-0.2474j, 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.8776-0.4794j, 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 1.    -0.j    ,
        0.    -0.j    , 0.    -0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    -0.j    ,
        0.9689+0.2474j, 0.    -0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    -0.j    ,
        0.    -0.j    , 0.8776+0.4794j]], dtype=complex128)
Parameters:
  • wire_dims (tuple[int, Ellipsis]) – The dimension of each wire in the order of self.wires

  • *params – The parameters of the gate

  • **hyperparams – The hyperparameters of the gate

Returns:

The Fock dense matrix representation of the gate, matching the interface of the parameters.

Return type:

pennylane.typing.TensorLike