FockStateProjector

class hybridlane.FockStateProjector(n, wires, id=None)

Bases: pennylane.FockStateProjector, hybridlane.ops.mixins.Spectral, hybridlane.ops.mixins.FockRepresentation

The projector onto a multi-mode Fock state \(\ket{n_1, n_2, \ldots, n_m}\bra{n_1, n_2, \ldots, n_m}\)

When used with the expval() function, the expectation value \(\braket{\ket{n_1, n_2, \ldots, n_m}\bra{n_1, n_2, \ldots, n_m}}\) is returned. This corresponds to the probability of the system being in the Fock state \(\ket{n_1, n_2, \ldots, n_m}\).

Details:

  • Number of wires: Any

  • Wire arguments: [qumode, ...]

  • Number of parameters: Any

  • Number of dimensions per parameter: 0

The number of parameters must match the number of wires, with each parameter being an integer. For example, to measure the probability of a single mode being in state \(\ket{3}\), the operator \(\ket{3}\bra{3}\) would be instantiated as

>>> op = FockStateProjector(3, wires=0)

The corresponding matrix representation in the Fock basis would then be

>>> op.fock_matrix({0: 5})
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0.]])

The multi-mode operator \(\ket{1, 2}\bra{1, 2}\) would be instantiated as

>>> op = FockStateProjector([1, 2], wires=[0, 1])

with the corresponding matrix representation in the Fock basis given by

>>> op.fock_matrix({0: 3, 1: 3})
array([[0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.]])
property natural_basis
property num_wires

Number of wires the operator acts on.

static compute_diagonalizing_gates(*parameters, wires, **hyperparameters)

Sequence of gates that diagonalize the operator in the computational basis (static method).

Given the eigendecomposition \(O = U \Sigma U^{\dagger}\) where \(\Sigma\) is a diagonal matrix containing the eigenvalues, the sequence of diagonalizing gates implements the unitary \(U^{\dagger}\).

The diagonalizing gates rotate the state into the eigenbasis of the operator.

See also

diagonalizing_gates().

Parameters:
  • params (list) – trainable parameters of the operator, as stored in the parameters attribute

  • wires (Iterable[Any], Wires) – wires that the operator acts on

  • hyperparams (dict) – non-trainable hyperparameters of the operator, as stored in the hyperparameters attribute

  • parameters (pennylane.typing.TensorLike)

Returns:

list of diagonalizing gates

Return type:

list[.Operator]

fock_spectrum(*basis_states)

Provides a diagonal decomposition of the operator in the Fock basis

An observable that implements this method guarantees it can be written as

\[O = \sum_n f(n) \ket{n}\bra{n}\]

where \(n \in \mathbb{N}_0\).

Parameters:

basis_states – A set of tensors, in order of the wires, representing Fock basis states. Each tensor has shape (*batch_dim)

Returns:

The eigenvalue for each basis state sample, with shape (*batch_dim)

Return type:

Sequence[float]

static compute_fock_matrix(wire_dims, n)

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