Source code for mrmustard.lab.transformations.phasenoise

# Copyright 2024 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""The class representing a Phase noise channel."""

from __future__ import annotations

import numpy as np

from mrmustard import math
from mrmustard.lab.circuit_components import CircuitComponent
from mrmustard.parameters import Parameter
from mrmustard.physics.ansatz.array_ansatz import ArrayAnsatz
from mrmustard.physics.wires import Wires

from .base import Channel

__all__ = ["PhaseNoise"]


[docs] class PhaseNoise(Channel): r"""The Phase noise channel. This class represents the application of a random phase. The distributiuon of the phase is assumed to be a Gaussian with mean zero, and standard deviation `phase_stdev`. >>> from mrmustard.lab import PhaseNoise, Coherent, DM >>> phase_noise = PhaseNoise(0, phase_stdev=0.5) >>> assert isinstance(Coherent(0, 1) >> phase_noise, DM) Args: mode: The mode the channel is applied to. phase_stdev: The standard deviation of the random phase noise. name: A name for the channel. If not provided, the class name will be used. .. details:: The Fock representation is connected to the Fourier coefficients of the distribution. """ short_name = "P~" def __init__( self, mode: int | tuple[int], phase_stdev: float | Parameter = 0.0, name: str | None = None, ): mode = (mode,) if not isinstance(mode, tuple) else mode name = name if name is not None else self.__class__.__name__ super().__init__( wires=Wires( modes_in_bra=set(mode), modes_out_bra=set(mode), modes_in_ket=set(mode), modes_out_ket=set(mode), ), name=name, ) self.parameters["phase_stdev"] = Parameter.from_cc_init( phase_stdev, "float64", f"{self.name}/phase_stdev" ) def __custom_rrshift__(self, other: CircuitComponent) -> CircuitComponent: r"""Since PhaseNoise admits a particularly nice form in the Fock basis, we have implemented its right-shift operation separately. Args: other: the component other than the PhaseNoise object that is present in the contraction Output: the result of the contraction. """ if not other.wires.bra or not other.wires.ket: other = other.contract(other.adjoint) other = other.to_fock() array = other.fock_array() mode_indices = np.indices(other.ansatz.core_shape) for mode in self.modes: phase_factors = math.exp( -0.5 * (mode_indices[mode] - mode_indices[other.n_modes + mode]) ** 2 * self.parameters.phase_stdev.value**2, ) array *= phase_factors return CircuitComponent._from_attributes( ArrayAnsatz(array, batch_dims=other.ansatz.batch_dims), other.wires, self.name, )