FockRepresentation

class hybridlane.ops.mixins.FockRepresentation

Mixin for operators that define their representation in the Fock basis

static compute_fock_matrix(wire_dims, *params, **hyperparams)

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 (pennylane.typing.TensorLike) – The parameters of the gate

  • **hyperparams (pennylane.typing.TensorLike) – 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

fock_matrix(wire_dims, wire_order=None)

Computes the dense Fock matrix representation of the gate

The matrix returned has shape (d, d) where d is the total dimension of the Hilbert space, as determined from wire_dims.

Details:

Because operators are symbolic don’t know about device-level truncation, the user must provide the wire_dims argument to specify the dimension of each wire. Qubits should be explicitly set to dimension 2.

An example using the \(R\) gate with a Fock truncation of 3:

>>> hl.R(0.123, wires=0).fock_matrix({0: 3})
array([[1.    +0.j    , 0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.9924-0.1227j, 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.9699-0.2435j]])

If wire_order is not provided, it defaults to self.wires. If the order of the wires in wire_order differs from self.wires, the method automatically expands the matrix to the full Hilbert space and permutes the wires as necessary to match the requested order.

For example, defining the \(R\) gate to act on a composite qubit-qumode system with the qubit as wire 0 and the qumode as wire 1, the matrix will be expanded as \(I_2 \otimes R\):

>>> hl.R(0.123, wires=1).fock_matrix({0: 2, 1: 3}, wire_order=(0, 1))
array([[1.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.9924-0.1227j, 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.9699-0.2435j, 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.9924-0.1227j, 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.9699-0.2435j]])

Passing a different wire_order returns a permuted matrix \(R \otimes I_2\):

>>> hl.R(0.123, wires=1).fock_matrix({0: 2, 1: 3}, wire_order=(1, 0))
array([[1.    +0.j    , 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.9924-0.1227j, 0.    +0.j    ,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.9924-0.1227j,
        0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.9699-0.2435j, 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.    +0.j    , 0.    +0.j    ,
        0.    +0.j    , 0.9699-0.2435j]])

If the gate is nonparametric, the returned matrix will be a NumPy ndarray, but if the gate has parameters and they are tensors of a deep learning framework, the returned matrix will be of the same framework. This function is compatible with automatic differentiation and @jax.jit compilation provided the underlying compute_fock_matrix implementation is as well.

An example using JAX:

>>> hl.R(jnp.array(0.123), wires=0).fock_matrix({0: 3})
Array([[1.    +0.j    , 0.    +0.j    , 0.    +0.j    ],
       [0.    +0.j    , 0.9924-0.1227j, 0.    +0.j    ],
       [0.    +0.j    , 0.    +0.j    , 0.9699-0.2435j]],      dtype=complex128, weak_type=True)

Developers of new gates should prefer to implement compute_fock_matrix.

Parameters:
  • wire_dims (Mapping[Any, int])

  • wire_order (pennylane.wires.WiresLike | None)

Return type:

pennylane.typing.TensorLike