Repository: facebookresearch/NeuralCompression (language: python)

Issue:
`HyperAnalysisTransformation` layer
```Python
from torch import Tensor
from torch.nn import Conv2d, Module, ReLU, Sequential


class HyperAnalysisTransformation(Module):
    def __init__(self, m: int, n: int):
        super(HyperAnalysisTransformation, self).__init__()

        self._modules = Sequential(
            Conv2d(m, n, (3,), (1,), 2),
            ReLU(inplace=True),
            Conv2d(n, n, (5,), (2,), 2),
            ReLU(inplace=True),
            Conv2d(n, n, (5,), (2,), 2),
        )

    def forward(self, x: Tensor) -> Tensor:
        return self._modules(x)
```


Discussion / hints:

Fix patch:
diff --git a/docs/source/layers.rst b/docs/source/layers.rst
index 202e38c9..b0138c7e 100644
--- a/docs/source/layers.rst
+++ b/docs/source/layers.rst
@@ -3,6 +3,14 @@ neuralcompression.layers
 
 .. currentmodule:: neuralcompression.layers
 
+
+HyperAnalysisTransformation2D
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: HyperAnalysisTransformation2D
+  :members: __getitem__
+  :special-members:
+
 HyperSynthesisTransformation2D
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/neuralcompression/layers/__init__.py b/neuralcompression/layers/__init__.py
index 66773c02..bc698e21 100644
--- a/neuralcompression/layers/__init__.py
+++ b/neuralcompression/layers/__init__.py
@@ -5,5 +5,6 @@
 LICENSE file in the root directory of this source tree.
 """
 
+from ._hyper_analysis_transformation_2d import HyperAnalysisTransformation2D
 from ._hyper_synthesis_transformation_2d import HyperSynthesisTransformation2D
 from .gdn import SimplifiedGDN, SimplifiedInverseGDN
diff --git a/neuralcompression/layers/_hyper_analysis_transformation_2d.py b/neuralcompression/layers/_hyper_analysis_transformation_2d.py
new file mode 100644
index 00000000..c33e2e39
--- /dev/null
+++ b/neuralcompression/layers/_hyper_analysis_transformation_2d.py
@@ -0,0 +1,41 @@
+"""
+Copyright (c) Facebook, Inc. and its affiliates.
+
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+"""
+
+from torch import Tensor
+from torch.nn import Conv2d, Module, ReLU, Sequential
+
+
+class HyperAnalysisTransformation2D(Module):
+    """Applies the 2D hyper analysis transformation over an input signal.
+
+    The hyper analysis transformation is used to generate a reconstructed
+    signal from a latent representation.
+
+    The method is described in:
+        | “Variational Image Compression with a Scale Hyperprior”
+        | Johannes Ballé, David Minnen, Saurabh Singh, Sung Jin Hwang,
+            Nick Johnston
+        | https://arxiv.org/abs/1802.01436
+
+    Args:
+        in_channels: number of channels in the input signal.
+        out_channels: number of channels produced by the transformation.
+    """
+
+    def __init__(self, in_channels: int, out_channels: int):
+        super(HyperAnalysisTransformation2D, self).__init__()
+
+        self.models = Sequential(
+            Conv2d(in_channels, out_channels, (3, 3), (1, 2), 2),
+            ReLU(inplace=True),
+            Conv2d(out_channels, out_channels, (5, 5), (2, 2), 2),
+            ReLU(inplace=True),
+            Conv2d(out_channels, out_channels, (5, 5), (2, 2), 2),
+        )
+
+    def forward(self, x: Tensor) -> Tensor:
+        return self.models(x)


Test patch:
diff --git a/tests/layers/test_hyper_analysis_transformation_2d.py b/tests/layers/test_hyper_analysis_transformation_2d.py
new file mode 100644
index 00000000..22bba609
--- /dev/null
+++ b/tests/layers/test_hyper_analysis_transformation_2d.py
@@ -0,0 +1,18 @@
+"""
+Copyright (c) Facebook, Inc. and its affiliates.
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+"""
+
+import torch
+from torch import Size
+
+from neuralcompression.layers import HyperAnalysisTransformation2D
+
+
+class TestHyperAnalysisTransformation:
+    transformation = HyperAnalysisTransformation2D(28, 28)
+
+    x = torch.rand((28, 28, 5, 5))
+
+    assert transformation(x).shape == Size([28, 28, 2, 1])