Source code for mrmustard.lab_dev.transformations.base
# Copyright 2023 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.
"""
This module contains the base classes for the available unitaries and channels on quantum states.
In the docstrings defining the available unitaries we provide a definition in terms of
the symplectic matrix :math:`S` and the real vector :math:`d`. For deterministic Gaussian channels,
we use the two matrices :math:`X` and :math:`Y` and the vector :math:`d`. Additionally, we
provide the ``(A, b, c)`` triples that define the transformation in the Fock Bargmann
representation.
"""
from __future__ import annotations
from typing import Optional, Sequence
from mrmustard.utils.typing import ComplexMatrix, ComplexVector
from mrmustard import math
from mrmustard.lab_dev.utils import shape_check
from mrmustard.lab_dev.wires import Wires
from mrmustard.physics.representations import Bargmann
from ..circuit_components import CircuitComponent
__all__ = ["Transformation", "Unitary", "Channel"]
[docs]
class Transformation(CircuitComponent):
r"""
Base class for all transformations.
"""
[docs]
def inverse(self) -> Transformation:
r"""Returns the inverse of the transformation."""
if not isinstance(self.representation, Bargmann):
raise NotImplementedError("Only Bargmann representation is supported.")
if self.representation.ansatz.batch_size > 1:
raise NotImplementedError("Batched transformations are not supported.")
A, b, _ = self.dual.representation.conj().triple # apply X
almost_inverse = self.__class__.from_bargmann(
[0], (math.inv(A[0]), -math.inv(A[0]) @ b[0], 1 + 0j)
)
almost_identity = (
self >> almost_inverse
) # TODO: this is not efficient, need to get c from formula
invert_this_c = almost_identity.representation.c
actual_inverse = self.__class__.from_bargmann(
[0], (math.inv(A[0]), -math.inv(A[0]) @ b[0], 1 / invert_this_c)
)
return actual_inverse
[docs]
class Unitary(Transformation):
r"""
Base class for all unitary transformations.
Arguments:
name: The name of this transformation.
modes: The modes that this transformation acts on.
"""
def __init__(self, name: Optional[str] = None, modes: tuple[int, ...] = ()):
super().__init__(
name or "U" + "".join(str(m) for m in modes),
modes_in_ket=modes,
modes_out_ket=modes,
)
def __rshift__(self, other: CircuitComponent) -> CircuitComponent:
r"""
Contracts ``self`` and ``other`` as it would in a circuit, adding the adjoints when
they are missing.
Returns a ``Unitary`` when ``other`` is a ``Unitary``, a ``Channel`` when ``other`` is a
``Channel``, and a ``CircuitComponent`` otherwise.
"""
ret = super().__rshift__(other)
if isinstance(other, Unitary):
return Unitary._from_attributes("", ret.representation, ret.wires)
elif isinstance(other, Channel):
return Channel._from_attributes("", ret.representation, ret.wires)
return ret
def __repr__(self) -> str:
return super().__repr__().replace("CircuitComponent", "Unitary")
[docs]
@classmethod
def from_bargmann(
cls,
modes: Sequence[int],
triple: tuple[ComplexMatrix, ComplexVector, complex],
name: Optional[str] = None,
) -> Unitary:
A = math.astensor(triple[0])
b = math.astensor(triple[1])
c = math.astensor(triple[2])
shape_check(A, b, 2 * len(modes), "Bargmann")
s = set(modes)
return Unitary._from_attributes(name, Bargmann(A, b, c), Wires(set(), set(), s, s))
[docs]
class Channel(Transformation):
r"""
Base class for all non-unitary transformations.
Arguments:
name: The name of this transformation.
modes: The modes that this transformation acts on.
"""
def __init__(self, name: Optional[str] = None, modes: tuple[int, ...] = ()):
super().__init__(
name or "Ch" + "".join(str(m) for m in modes),
modes_in_ket=modes,
modes_out_ket=modes,
modes_in_bra=modes,
modes_out_bra=modes,
)
def __rshift__(self, other: CircuitComponent) -> CircuitComponent:
r"""
Contracts ``self`` and ``other`` as it would in a circuit, adding the adjoints when
they are missing.
Returns a ``Channel`` when ``other`` is a ``Unitary`` or a ``Channel``, and a
``CircuitComponent`` otherwise.
"""
ret = super().__rshift__(other)
if isinstance(other, (Unitary, Channel)):
return Channel._from_attributes("", ret.representation, ret.wires)
return ret
def __repr__(self) -> str:
return super().__repr__().replace("CircuitComponent", "Channel")
[docs]
@classmethod
def from_bargmann(
cls,
modes: Sequence[int],
triple: tuple[ComplexMatrix, ComplexVector, complex],
name: Optional[str] = None,
) -> Channel:
r"""Initialize a Channel from the given Bargmann ``(A, b, c)`` triple."""
A = math.astensor(triple[0])
b = math.astensor(triple[1])
c = math.astensor(triple[2])
shape_check(A, b, 4 * len(modes), "Bargmann")
s = set(modes)
return Channel._from_attributes(name, Bargmann(A, b, c), Wires(s, s, s, s))
_modules/mrmustard/lab_dev/transformations/base
Download Python script
Download Notebook
View on GitHub