Repository: IBM/aihwkit (language: python)

Issue:
Remove the numpy-specific tile
## Description and motivation

During the initial stages of the toolkit, a numpy-based tile was created, as an intermediate step for using `numpy` as the preferred format for the structures that are used between the layers. As it has been a while since the tile is strictly needed and both `rpucuda` and the upper layers have been centered around torch tensors, it is a good time for finally removing the tile.

## Proposed solution

Remove the numpy tiles and its tests.

## Alternatives and other information

<!-- A clear and concise description of any alternative solutions or features you've considered. -->



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d424a800..dd63e3ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,12 @@ The format is based on [Keep a Changelog], and this project adheres to
 * Faulty backward noise management error message removed for perfect backward
   and CUDA. (\#99)
 
+#### Removed
+
+* The legacy `NumpyAnalogTile` and `NumpyFloatingPointTile` tiles have been
+  finally removed. The regular, tensor-powered `aihwkit.simulator.tiles` tiles
+  contain all their functionality and numerous additions. (\#122)
+
 ## [0.2.1] - 2020/11/26
 
 * The `rpu_config` is now pretty-printed in a readable manner (excluding the
diff --git a/src/aihwkit/simulator/tiles/numpy.py b/src/aihwkit/simulator/tiles/numpy.py
deleted file mode 100644
index 29f46635..00000000
--- a/src/aihwkit/simulator/tiles/numpy.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
-#
-# This code is licensed under the Apache License, Version 2.0. You may
-# obtain a copy of this license in the LICENSE.txt file in the root directory
-# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
-#
-# Any modifications or derivative works of this code must retain this
-# copyright notice, and modified files need to carry a notice indicating
-# that they have been altered from the originals.
-
-# type: ignore
-
-"""High level analog tiles (numpy)."""
-
-from numpy import array
-from torch import from_numpy, Tensor
-
-from aihwkit.simulator.tiles import AnalogTile, FloatingPointTile
-
-
-class NumpyMixin:
-    """Helper for overriding forward, backward and update methods."""
-
-    def move_weights(self) -> Tensor:
-        """Move the tile weights to an external variable.
-
-        Move the weights currently stored in the tile to an externally
-        managed variable (so the internal references to the weights use the same
-        memory space as the returned array data).
-
-        Caution:
-            This is an advanced method, and assumes that the caller will be
-            responsible for managing the shared weights variable manually.
-            After calling this function, the tile will use the same pointer for
-            the internal weights: freeing the variable or modifying it manually
-            can result in several side effects.  Additionally, all tensor
-            functions applied to the external weights MUST be in-place or errors
-            will occur.
-
-        Caution:
-            Since the internal memory layout is transposed for CUDA the weight
-            matrix is transposed as well.
-
-        Returns:
-            Tensor: a ``[out_size, in_size (+ 1)]`` contiguous array that
-                contains the weights that uses the same memory addresses as the
-                internal pointer to the weights.
-        """
-        shared_weights = from_numpy(self.tile.get_weights())
-        if self.is_cuda:
-            shared_weights.cuda()
-        self.tile.set_shared_weights(shared_weights)
-        return shared_weights
-
-    def forward(self, x_input: array, is_test: bool = False) -> array:
-        """Perform the forward pass.
-
-        Args:
-            x_input: ``[N, in_size]`` matrix. If ``in_trans`` is set, transposed.
-            is_test: Whether to assume testing mode
-
-        Returns:
-            array: ``[N, out_size]`` matrix. If ``out_trans`` is set, transposed.
-        """
-        if self.is_cuda:
-            x_tensor = from_numpy(x_input.astype('float32')).cuda()
-            return self.tile.forward(x_tensor, self.bias,
-                                     self.in_trans, self.out_trans,
-                                     is_test).cpu().numpy()
-
-        return self.tile.forward_numpy(x_input, self.bias,
-                                       self.in_trans, self.out_trans,
-                                       is_test)
-
-    def backward(self, d_input: array) -> array:
-        """Perform the backward pass.
-
-        Args:
-            d_input: ``[N, out_size]`` matrix. If ``out_trans`` is set, transposed.
-
-        Returns:
-            array: ``[N, in_size]`` matrix. If ``in_trans`` is set, transposed.
-        """
-        if self.is_cuda:
-            d_tensor = from_numpy(d_input.astype('float32')).cuda()
-            return self.tile.backward(d_tensor, self.bias,
-                                      self.out_trans, self.in_trans).cpu().numpy()
-
-        return self.tile.backward_numpy(d_input, self.bias,
-                                        self.out_trans, self.in_trans)
-
-    def update(self, x_input: array, d_input: array) -> None:
-        """Perform the update pass.
-
-        Args:
-            x_input: ``[N, in_size]`` matrix. If ``in_trans`` is set, transposed.
-            d_input: ``[N, out_size]`` matrix. If ``out_trans`` is set, transposed.
-
-        Returns:
-            None.
-        """
-        if self.is_cuda:
-            x_tensor = from_numpy(x_input.astype('float32')).cuda()
-            d_tensor = from_numpy(d_input.astype('float32')).cuda()
-            return self.tile.update(x_tensor, d_tensor, self.bias,
-                                    self.in_trans, self.out_trans)
-
-        return self.tile.update_numpy(x_input, d_input, self.bias,
-                                      self.in_trans, self.out_trans)
-
-
-class NumpyFloatingPointTile(NumpyMixin, FloatingPointTile):
-    """Floating point tile (numpy)."""
-
-
-class NumpyAnalogTile(NumpyMixin, AnalogTile):
-    """Analog tile (numpy)."""


Test patch:
diff --git a/tests/test_bindings_tiles_numpy.py b/tests/test_bindings_tiles_numpy.py
deleted file mode 100644
index be6d5c8d..00000000
--- a/tests/test_bindings_tiles_numpy.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# (C) Copyright 2020, 2021 IBM. All Rights Reserved.
-#
-# This code is licensed under the Apache License, Version 2.0. You may
-# obtain a copy of this license in the LICENSE.txt file in the root directory
-# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
-#
-# Any modifications or derivative works of this code must retain this
-# copyright notice, and modified files need to carry a notice indicating
-# that they have been altered from the originals.
-
-"""Tests for the RPU array bindings."""
-
-from unittest import TestCase, skipIf
-
-from numpy import dot
-from numpy.random import uniform
-from numpy.testing import assert_array_almost_equal, assert_array_equal
-
-from torch import from_numpy
-
-from aihwkit.simulator.configs import SingleRPUConfig
-from aihwkit.simulator.configs.utils import (
-    IOParameters, UpdateParameters, PulseType
-)
-from aihwkit.simulator.tiles.numpy import NumpyFloatingPointTile, NumpyAnalogTile
-
-
-class NumpyFloatingPointTileTest(TestCase):
-    """Test NumpyFloatingPointTile."""
-
-    def get_tile(self, out_size, in_size):
-        """Return a tile of the specified dimensions with noisiness turned off."""
-        python_tile = NumpyFloatingPointTile(out_size, in_size)
-
-        # Set weights.
-        init_weights = uniform(-0.5, 0.5, size=(python_tile.out_size, python_tile.in_size))
-        python_tile.set_weights(from_numpy(init_weights))
-
-        return python_tile
-
-    def test_mimic_rpu_mac(self):
-        """Check using the update, forward and backward functions."""
-        n_rows = 6  # out_size aka d_size
-        n_cols = 5  # in_size aka x_size
-        m_batch = 4
-        lr = 0.1
-
-        python_tile = self.get_tile(n_rows, n_cols)
-        cpp_tile = python_tile.tile
-
-        cpp_tile.set_learning_rate(lr)
-
-        x = uniform(-1.2, 1.2, size=(m_batch, n_cols))
-        d = uniform(-0.1, 0.1, size=(m_batch, n_rows))
-
-        init_weights = cpp_tile.get_weights().copy()
-
-        # Perform forward.
-        y = cpp_tile.forward_numpy(x)
-        assert_array_almost_equal(y, dot(x, init_weights.T))
-
-        # Perform backward.
-        z = cpp_tile.backward_numpy(d)
-        assert_array_almost_equal(z, dot(d, init_weights))
-
-        # Perform update.
-        cpp_tile.update_numpy(x, d, bias=False)
-        post_rank_weights = cpp_tile.get_weights()
-        ref_weights = init_weights - lr*dot(d.T, x)
-
-        assert_array_almost_equal(post_rank_weights, ref_weights)
-
-    def test_mimic_rpu_mac_trans(self):
-        """Check using the update, forward and backward functions in
-        transposed mode."""
-        n_rows = 6
-        n_cols = 5
-        m_batch = 4
-        lr = 0.1
-
-        python_tile = self.get_tile(n_rows, n_cols)
-        python_tile.in_trans = True
-        python_tile.out_trans = True
-
-        cpp_tile = python_tile.tile
-        cpp_tile.set_learning_rate(lr)
-
-        x = uniform(-1.2, 1.2, size=(m_batch, n_cols))
-        d = uniform(-0.1, 0.1, size=(m_batch, n_rows))
-
-        init_weights = cpp_tile.get_weights().copy()
-
-        # Perform forward.
-        # Caution: x.T just a view! need to copy
-        y = cpp_tile.forward_numpy(x.T.copy(), x_trans=True, d_trans=True)
-        assert_array_almost_equal(y.T, dot(x, init_weights.T))
-
-        # Perform backward.
-        z = cpp_tile.backward_numpy(d.T.copy(), x_trans=True, d_trans=True).T
-        assert_array_almost_equal(z, dot(d, init_weights))
-
-        # Perform update.
-        cpp_tile.update_numpy(x.T.copy(), d.T.copy(), bias=False,
-                              x_trans=True, d_trans=True)
-        post_rank_weights = cpp_tile.get_weights()
-        ref_weights = init_weights - lr*dot(d.T, x)
-
-        assert_array_almost_equal(post_rank_weights, ref_weights)
-
-    @skipIf(True, "Currently not supported")
-    def test_move_weights_new_variable(self):
-        """Test moving the weights to a new shared variable."""
-        python_tile = self.get_tile(2, 3)
-        cpp_tile = python_tile.tile
-
-        def to_shape(mat):
-            if python_tile.is_cuda:
-                shape = mat.shape
-                return mat.T.copy().flatten().reshape(*shape)
-            return mat
-
-        # Read the weights.
-        initial_weights = cpp_tile.get_weights()
-
-        # Move the weights to a new shared variable.
-        weights_holder = python_tile.move_weights()
-        weights_clone = weights_holder.clone().cpu().numpy()
-        assert_array_equal(weights_clone, to_shape(initial_weights))
-
-        # Modify the shared variable manually.
-        weights_holder.add_(0.123)  # in place
-        current_weights = cpp_tile.get_weights()
-        weights_clone = weights_holder.clone().cpu().numpy()
-        assert_array_equal(weights_clone, to_shape(current_weights))
-
-        # Modify the weights manually.
-        cpp_tile.set_weights([[.2, .2, .2], [.3, .3, .3]])
-        current_weights = cpp_tile.get_weights()
-        weights_clone = weights_holder.clone().cpu().numpy()
-        assert_array_equal(weights_clone, to_shape(current_weights))
-
-
-class NumpyAnalogTileTest(NumpyFloatingPointTileTest):
-    """Test NumpyAnalogTile."""
-
-    def get_tile(self, out_size, in_size):
-        """Return a tile of the specified dimensions with noisiness turned off."""
-        rpu_config = SingleRPUConfig(
-            forward=IOParameters(is_perfect=True),
-            backward=IOParameters(is_perfect=True),
-            update=UpdateParameters(pulse_type=PulseType('None')),
-        )
-        python_tile = NumpyAnalogTile(out_size, in_size, rpu_config)
-
-        # Set weights.
-        init_weights = uniform(-0.5, 0.5, size=(python_tile.out_size, python_tile.in_size))
-        python_tile.set_weights(from_numpy(init_weights))
-
-        return python_tile