Skip to content

Commit

Permalink
Release v0.6.0 (#285)
Browse files Browse the repository at this point in the history
# Release 0.6.0 (current release)

### New features

* Added a new method to discretize Wigner functions that revolves
Clenshaw summations. This method is expected to be fast and
reliable for systems with high number of excitations, for which the
pre-existing iterative method is known to be unstable. Users
can select their preferred methods by setting the value of
`Settings.DISCRETIZATION_METHOD` to either `interactive` (default) or
`clenshaw`.

* Added the `PhaseNoise(phase_stdev)` gate (non-Gaussian). Output is a
mixed state in Fock representation.
It is not based on a choi operator, but on a nonlinear transformation of
the density matrix.
  [(#275)](#275)

### Breaking changes

* The value of `hbar` can no longer be specified outside of `Settings`.
All the classes and
methods that allowed specifying its value as an input now retrieve it
directly from `Settings`.

* Certain attributes of `Settings` can no longer be changed after their
value is queried for the
  first time.

### Improvements

* Tensorflow bumped to v2.14 with poetry installation working out of the
box on Linux and Mac.
  [(#281)](#281)

### Bug fixes

* Fixed a bug about the variable names in functions (apply_kraus_to_ket,
apply_kraus_to_dm, apply_choi_to_ket, apply_choi_to_dm).
[(#271)](#271)

* Fixed a bug that was leading to an error when computing the Choi
representation of a unitary transformation.

### Documentation

### Contributors
[Filippo Miatto](https://github.com/ziofil), [Samuele
Ferracin](https://github.com/SamFerracin), [Yuan
Yao](https://github.com/sylviemonet), [Zeyue
Niu](https://github.com/zeyueN)

---------

Co-authored-by: Sebastián Duque Mesa <[email protected]>
Co-authored-by: JacobHast <[email protected]>
Co-authored-by: elib20 <[email protected]>
Co-authored-by: Luke Helt <[email protected]>
Co-authored-by: zeyueN <[email protected]>
Co-authored-by: Robbe De Prins <[email protected]>
Co-authored-by: Robbe De Prins (UGent-imec) <[email protected]>
Co-authored-by: Yuan <[email protected]>
Co-authored-by: Ryk <[email protected]>
Co-authored-by: Gabriele Gullì <[email protected]>
Co-authored-by: Yuan Yao <[email protected]>
Co-authored-by: SamFerracin <[email protected]>
Co-authored-by: Yuan Yao <[email protected]>
Co-authored-by: heltluke <[email protected]>
  • Loading branch information
15 people authored Oct 14, 2023
1 parent df94d49 commit 4d47e8e
Show file tree
Hide file tree
Showing 29 changed files with 2,525 additions and 1,626 deletions.
56 changes: 52 additions & 4 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
# Release 0.6.0 (current release)

### New features

* Added a new method to discretize Wigner functions that revolves Clenshaw summations. This method is expected to be fast and
reliable for systems with high number of excitations, for which the pre-existing iterative method is known to be unstable. Users
can select their preferred methods by setting the value of `Settings.DISCRETIZATION_METHOD` to either `interactive` (default) or
`clenshaw`.
[(#280)](https://github.com/XanaduAI/MrMustard/pull/280)

* Added the `PhaseNoise(phase_stdev)` gate (non-Gaussian). Output is a mixed state in Fock representation.
It is not based on a choi operator, but on a nonlinear transformation of the density matrix.
[(#275)](https://github.com/XanaduAI/MrMustard/pull/275)

### Breaking changes

* The value of `hbar` can no longer be specified outside of `Settings`. All the classes and
methods that allowed specifying its value as an input now retrieve it directly from `Settings`.
[(#278)](https://github.com/XanaduAI/MrMustard/pull/278)

* Certain attributes of `Settings` can no longer be changed after their value is queried for the
first time.
[(#278)](https://github.com/XanaduAI/MrMustard/pull/278)

### Improvements

* Tensorflow bumped to v2.14 with poetry installation working out of the box on Linux and Mac.
[(#281)](https://github.com/XanaduAI/MrMustard/pull/281)

### Bug fixes

* Fixed a bug about the variable names in functions (apply_kraus_to_ket, apply_kraus_to_dm, apply_choi_to_ket, apply_choi_to_dm).
[(#271)](https://github.com/XanaduAI/MrMustard/pull/271)

* Fixed a bug that was leading to an error when computing the Choi representation of a unitary transformation.
[(#283)](https://github.com/XanaduAI/MrMustard/pull/283)

* Fixed the internal function to calculate ABC of Bargmann representation (now corresponds to the literature) and other fixes to get the correct Fock tensor.
[(#255)](https://github.com/XanaduAI/MrMustard/pull/255)

### Documentation

### Contributors
[Filippo Miatto](https://github.com/ziofil), [Samuele Ferracin](https://github.com/SamFerracin), [Yuan Yao](https://github.com/sylviemonet), [Zeyue Niu](https://github.com/zeyueN)

---

# Release 0.5.0

### New features
Expand Down Expand Up @@ -35,7 +82,7 @@
# Or, in command line: `tensorboard --logdir={tb_cb.logdir}` and open link in browser.
```

* Gaussian states support a `bargmann` method for returning the bargmann representation.
* Gaussian states support a `bargmann` method for returning the bargmann representation.
[(#235)](https://github.com/XanaduAI/MrMustard/pull/235)

* The `ket` method of `State` now supports new keyword arguments `max_prob` and `max_photons`.
Expand All @@ -50,7 +97,7 @@
ket = Gaussian(2).ket(max_prob=0.99, max_photons=3)
```

* Gaussian transformations support a `bargmann` method for returning the bargmann representation.
* Gaussian transformations support a `bargmann` method for returning the bargmann representation.
[(#239)](https://github.com/XanaduAI/MrMustard/pull/239)

* BSGate.U now supports method='vanilla' (default) and 'schwinger' (slower, but stable to any cutoff)
Expand Down Expand Up @@ -82,10 +129,11 @@
[(#239)](https://github.com/XanaduAI/MrMustard/pull/239)

* More robust implementation of cutoffs for States.
[(#239)](https://github.com/XanaduAI/MrMustard/pull/239)

[(#239)](https://github.com/XanaduAI/MrMustard/pull/239)

* Dependencies and versioning are now managed using Poetry.
[(#257)](https://github.com/XanaduAI/MrMustard/pull/257)
[(#257)](https://github.com/XanaduAI/MrMustard/pull/257)

### Bug fixes

Expand Down
82 changes: 2 additions & 80 deletions mrmustard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,89 +14,11 @@

"""This is the top-most `__init__.py` file of MrMustard package."""

import numpy as np
import rich.table

from rich import print

from ._version import __version__


# pylint: disable=too-many-instance-attributes
class Settings:
"""Settings class."""

def __new__(cls): # singleton
if not hasattr(cls, "instance"):
cls.instance = super(Settings, cls).__new__(cls)
return cls.instance

def __init__(self):
self._backend = "tensorflow"
self.HBAR = 2.0
self.CHOI_R = 0.881373587019543 # np.arcsinh(1.0)
self.DEBUG = False
self.AUTOCUTOFF_PROBABILITY = 0.999 # capture at least 99.9% of the probability
self.AUTOCUTOFF_MAX_CUTOFF = 100
self.AUTOCUTOFF_MIN_CUTOFF = 1
self.CIRCUIT_DECIMALS = 3
# use cutoff=5 for each mode when determining if two transformations in fock repr are equal
self.EQ_TRANSFORMATION_CUTOFF = 3 # 3 is enough to include a full step of the rec relations
self.EQ_TRANSFORMATION_RTOL_FOCK = 1e-3
self.EQ_TRANSFORMATION_RTOL_GAUSS = 1e-6
# for the detectors
self.PNR_INTERNAL_CUTOFF = 50
self.HOMODYNE_SQUEEZING = 10.0
# misc
self.PROGRESSBAR = True
self._seed = np.random.randint(0, 2**31 - 1)
self.rng = np.random.default_rng(self._seed)
self.DEFAULT_BS_METHOD = "vanilla" # can be 'vanilla' or 'schwinger'

@property
def SEED(self):
"""Returns the seed value if set, otherwise returns a random seed."""
if self._seed is None:
self._seed = np.random.randint(0, 2**31 - 1)
self.rng = np.random.default_rng(self._seed)
return self._seed

@SEED.setter
def SEED(self, value):
"""Sets the seed value."""
self._seed = value
self.rng = np.random.default_rng(self._seed)

@property
def BACKEND(self):
"""The backend which is used.
Can be either ``'tensorflow'`` or ``'torch'``.
"""
return self._backend

@BACKEND.setter
def BACKEND(self, backend_name: str):
if backend_name not in ["tensorflow", "torch"]: # pragma: no cover
raise ValueError("Backend must be either 'tensorflow' or 'torch'")
self._backend = backend_name

# use rich.table to print the settings
def __repr__(self):
"""Returns a string representation of the settings."""
table = rich.table.Table(title="MrMustard Settings")
table.add_column("Setting")
table.add_column("Value")
table.add_row("BACKEND", self.BACKEND)
table.add_row("SEED", str(self.SEED))
for key, value in self.__dict__.items():
if key == key.upper():
table.add_row(key, str(value))
print(table)
return ""


settings = Settings()
"""Settings object."""
from .settings import *


def version():
Expand Down
28 changes: 19 additions & 9 deletions mrmustard/lab/abstract/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(
self._norm = _norm
if cov is not None and means is not None:
self.is_gaussian = True
self.is_hilbert_vector = np.allclose(gaussian.purity(self.cov, settings.HBAR), 1.0)
self.is_hilbert_vector = np.allclose(gaussian.purity(self.cov), 1.0)
self.num_modes = cov.shape[-1] // 2
elif eigenvalues is not None and symplectic is not None:
self.is_gaussian = True
Expand Down Expand Up @@ -140,7 +140,7 @@ def purity(self) -> float:
"""Returns the purity of the state."""
if self._purity is None:
if self.is_gaussian:
self._purity = gaussian.purity(self.cov, settings.HBAR)
self._purity = gaussian.purity(self.cov)
else:
self._purity = fock.purity(self._dm)
return self._purity
Expand Down Expand Up @@ -209,7 +209,10 @@ def fock(self) -> ComplexTensor:
r"""Returns the Fock representation of the state."""
if self._dm is None and self._ket is None:
_fock = fock.wigner_to_fock_state(
self.cov, self.means, shape=self.shape, return_dm=not self.is_hilbert_vector
self.cov,
self.means,
shape=self.shape,
return_dm=not self.is_hilbert_vector,
)
if self.is_mixed:
self._dm = _fock
Expand All @@ -223,7 +226,7 @@ def fock(self) -> ComplexTensor:
def number_means(self) -> RealVector:
r"""Returns the mean photon number for each mode."""
if self.is_gaussian:
return gaussian.number_means(self.cov, self.means, settings.HBAR)
return gaussian.number_means(self.cov, self.means)

return fock.number_means(tensor=self.fock, is_dm=self.is_mixed)

Expand All @@ -233,7 +236,7 @@ def number_cov(self) -> RealMatrix:
if not self.is_gaussian:
raise NotImplementedError("number_cov not yet implemented for non-gaussian states")

return gaussian.number_cov(self.cov, self.means, settings.HBAR)
return gaussian.number_cov(self.cov, self.means)

@property
def norm(self) -> float:
Expand Down Expand Up @@ -508,7 +511,9 @@ def __and__(self, other: State) -> State:
cov = gaussian.join_covs([self.cov, other.cov])
means = gaussian.join_means([self.means, other.means])
return State(
cov=cov, means=means, modes=self.modes + [m + self.num_modes for m in other.modes]
cov=cov,
means=means,
modes=self.modes + [m + self.num_modes for m in other.modes],
)

def __getitem__(self, item) -> State:
Expand Down Expand Up @@ -581,11 +586,15 @@ def __eq__(self, other) -> bool: # pylint: disable=too-many-return-statements
return True
try:
return np.allclose(
self.ket(cutoffs=other.cutoffs), other.ket(cutoffs=other.cutoffs), atol=1e-6
self.ket(cutoffs=other.cutoffs),
other.ket(cutoffs=other.cutoffs),
atol=1e-6,
)
except TypeError:
return np.allclose(
self.dm(cutoffs=other.cutoffs), other.dm(cutoffs=other.cutoffs), atol=1e-6
self.dm(cutoffs=other.cutoffs),
other.dm(cutoffs=other.cutoffs),
atol=1e-6,
)

def __rshift__(self, other: Transformation) -> State:
Expand Down Expand Up @@ -617,7 +626,8 @@ def __rmul__(self, other):
"""
if self.is_gaussian:
warnings.warn(
"scalar multiplication forces conversion to fock representation", UserWarning
"scalar multiplication forces conversion to fock representation",
UserWarning,
)
if self.is_pure:
return State(ket=self.ket() * other)
Expand Down
1 change: 1 addition & 0 deletions mrmustard/lab/abstract/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ def choi(self, cutoffs: Sequence[int]):
f"Invalid number of cutoffs: {len(cutoffs)} (expected {self.num_modes} or {4*self.num_modes})"
)
if self.is_unitary:
shape = shape[: 2 * self.num_modes]
U = self.U(shape[: self.num_modes])
Udual = self.U(shape[self.num_modes :])
return fock.U_to_choi(U, Udual)
Expand Down
82 changes: 74 additions & 8 deletions mrmustard/lab/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"""

from typing import List, Optional, Sequence, Tuple, Union

import numpy as np
from mrmustard import settings
from mrmustard.lab.abstract import Transformation
from mrmustard.lab.abstract import State, Transformation
from mrmustard.math import Math
from mrmustard.physics import fock, gaussian
from mrmustard.training import Parametrized
Expand All @@ -45,6 +45,7 @@
"Attenuator",
"Amplifier",
"AdditiveNoise",
"PhaseNoise",
]


Expand Down Expand Up @@ -92,7 +93,7 @@ def __init__(

@property
def d_vector(self):
return gaussian.displacement(self.x.value, self.y.value, settings.HBAR)
return gaussian.displacement(self.x.value, self.y.value)

def U(self, cutoffs: Sequence[int]):
r"""Returns the unitary representation of the Displacement gate using
Expand Down Expand Up @@ -787,11 +788,11 @@ def __init__(

@property
def X_matrix(self):
return gaussian.loss_XYd(self.transmissivity.value, self.nbar.value, settings.HBAR)[0]
return gaussian.loss_XYd(self.transmissivity.value, self.nbar.value)[0]

@property
def Y_matrix(self):
return gaussian.loss_XYd(self.transmissivity.value, self.nbar.value, settings.HBAR)[1]
return gaussian.loss_XYd(self.transmissivity.value, self.nbar.value)[1]


class Amplifier(Parametrized, Transformation):
Expand Down Expand Up @@ -844,11 +845,11 @@ def __init__(

@property
def X_matrix(self):
return gaussian.amp_XYd(self.gain.value, self.nbar.value, settings.HBAR)[0]
return gaussian.amp_XYd(self.gain.value, self.nbar.value)[0]

@property
def Y_matrix(self):
return gaussian.amp_XYd(self.gain.value, self.nbar.value, settings.HBAR)[1]
return gaussian.amp_XYd(self.gain.value, self.nbar.value)[1]


# pylint: disable=no-member
Expand Down Expand Up @@ -897,4 +898,69 @@ def __init__(

@property
def Y_matrix(self):
return gaussian.noise_Y(self.noise.value, settings.HBAR)
return gaussian.noise_Y(self.noise.value)


class PhaseNoise(Parametrized, Transformation):
r"""The phase noise channel.
The phase noise channel is a non-Gaussian transformation that is equivalent to
a random phase rotation.
Args:
phase_stdev (float or List[float]): the standard deviation of the (wrapped) normal
distribution for the angle of the rotation
modes (optional, list(int)): the single mode this gate is applied to (default [0])
"""

def __init__(
self,
phase_stdev: Union[Optional[float], Optional[List[float]]] = 0.0,
phase_stdev_trainable: bool = False,
phase_stdev_bounds: Tuple[Optional[float], Optional[float]] = (0.0, None),
modes: Optional[List[int]] = None,
):
super().__init__(
phase_stdev=phase_stdev,
phase_stdev_trainable=phase_stdev_trainable,
phase_stdev_bounds=phase_stdev_bounds,
)
self._modes = modes or [0]
self.is_unitary = False
self.is_gaussian = False
self.short_name = "P~"

# need to override primal because of the unconventional way
# the channel is defined in Fock representation
def primal(self, state):
idx = state.modes.index(self.modes[0])
if state.is_pure:
ket = state.ket()
dm = fock.ket_to_dm(ket)
else:
dm = state.dm()

# transpose dm so that the modes of interest are at the end
M = state.num_modes
indices = list(range(2 * M))
indices.remove(idx)
indices.remove(idx + M)
indices += [idx, idx + M]
dm = math.transpose(dm, indices)

coeff = math.cast(
math.exp(
-0.5
* self.phase_stdev.value**2
* math.arange(-dm.shape[-2] + 1, dm.shape[-1]) ** 2
),
dm.dtype,
)

for k in range(-dm.shape[-2] + 1, dm.shape[-1]):
diagonal = math.diag_part(dm, k=k)
diagonal *= coeff[k + dm.shape[-2] - 1]
dm = math.set_diag(dm, diagonal, k=k)

# transpose dm back to the original order
return State(dm=math.transpose(dm, np.argsort(indices)), modes=state.modes)
Loading

0 comments on commit 4d47e8e

Please sign in to comment.