Skip to content

Commit

Permalink
feat(vhdl): remove translatable protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
julianhoever committed Sep 7, 2022
1 parent 653835a commit 37412e8
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 104 deletions.
2 changes: 1 addition & 1 deletion elasticai/creator/examples/translate_lstm_linear_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def main() -> None:
)

code_repr = translator.generate_code(
translatable_layers=translatable_layers, translation_args=translation_args
vhdl_modules=translatable_layers, translation_args=translation_args
)

translator.save_code(code_repr=code_repr, path=args.build_dir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
)
from elasticai.creator.vhdl.number_representations import FixedPoint
from elasticai.creator.vhdl.translator.abstract.layers import (
Linear1dTranslatable,
Linear1dModule,
Linear1dTranslationArgs,
)


class Linear1dTranslatableTest(unittest.TestCase):
class Linear1dModuleTest(unittest.TestCase):
def setUp(self) -> None:
self.linear = Linear1dTranslatable(weight=[[1, 2, 3]], bias=[1])
self.linear = Linear1dModule(weight=[[1, 2, 3]], bias=[1])
self.translation_args = Linear1dTranslationArgs(
fixed_point_factory=FixedPoint.get_factory(total_bits=8, frac_bits=4)
)

def test_contains_all_needed_components(self) -> None:
vhdl_components = self.linear.translate(self.translation_args)
vhdl_components = self.linear.components(self.translation_args)

target_components = [
(Linear1dComponent, "linear_1d.vhd"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
from unittest import TestCase

from elasticai.creator.vhdl.components.dual_port_2_clock_ram_component import (
from elasticai.creator.vhdl.components import (
DualPort2ClockRamComponent,
LSTMCommonComponent,
LSTMComponent,
RomComponent,
SigmoidComponent,
TanhComponent,
)
from elasticai.creator.vhdl.components.lstm_common_component import LSTMCommonComponent
from elasticai.creator.vhdl.components.lstm_component import (
LSTMComponent as LSTMComponent,
)
from elasticai.creator.vhdl.components.rom_component import RomComponent
from elasticai.creator.vhdl.components.sigmoid_component import SigmoidComponent
from elasticai.creator.vhdl.components.tanh_component import TanhComponent
from elasticai.creator.vhdl.number_representations import FixedPoint
from elasticai.creator.vhdl.translator.abstract.layers.lstm_translatable import (
LSTMTranslatable,
from elasticai.creator.vhdl.translator.abstract.layers import (
LSTMModule,
LSTMTranslationArgs,
)


class LSTMTranslatableTest(TestCase):
class LSTMModuleTest(TestCase):
def setUp(self) -> None:
self.lstm = LSTMTranslatable(
self.lstm = LSTMModule(
weights_ih=[[[1, 2], [3, 4], [5, 6], [7, 8]]],
weights_hh=[[[1], [2], [3], [4]]],
biases_ih=[[1, 2, 3, 4]],
Expand All @@ -33,11 +31,11 @@ def setUp(self) -> None:
)

def test_correct_number_of_components(self) -> None:
vhdl_components = list(self.lstm.translate(self.translation_args))
vhdl_components = list(self.lstm.components(self.translation_args))
self.assertEqual(len(vhdl_components), 13)

def test_contains_all_needed_components(self) -> None:
vhdl_components = self.lstm.translate(self.translation_args)
vhdl_components = self.lstm.components(self.translation_args)

target_components = [
(RomComponent, "wi_rom.vhd"),
Expand Down
26 changes: 13 additions & 13 deletions elasticai/creator/tests/vhdl/translator/pytorch/test_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import torch.nn

from elasticai.creator.vhdl.language import Code
from elasticai.creator.vhdl.translator.abstract.layers import LSTMTranslatable
from elasticai.creator.vhdl.translator.abstract.layers import LSTMModule
from elasticai.creator.vhdl.translator.build_function_mapping import (
BuildFunctionMapping,
)
Expand All @@ -18,7 +18,7 @@


@dataclass
class VHDLComponentMock:
class VHDLComponentMock(VHDLComponent):
name: str
code: list[str]

Expand All @@ -31,16 +31,16 @@ def __call__(self) -> Code:


@dataclass
class TranslatableMock:
components: list[VHDLComponent]
class VHDLModuleMock(VHDLModule):
vhdl_components: list[VHDLComponent]

def translate(self, args: Any) -> VHDLModule:
yield from self.components
def components(self, args: Any) -> list[VHDLComponent]:
yield from self.vhdl_components


def fake_build_function(module: torch.nn.Module) -> TranslatableMock:
return TranslatableMock(
components=[
def fake_build_function(module: torch.nn.Module) -> VHDLModuleMock:
return VHDLModuleMock(
vhdl_components=[
VHDLComponentMock(name="component1", code=["1", "2", "3"]),
VHDLComponentMock(name="component2", code=["4", "5", "6"]),
]
Expand Down Expand Up @@ -76,19 +76,19 @@ def test_translate_model_with_one_layer(self) -> None:
translated_model = list(translator.translate_model(model, self.build_mapping))

self.assertEqual(len(translated_model), 1)
self.assertEqual(type(translated_model[0]), TranslatableMock)
self.assertEqual(type(translated_model[0]), VHDLModuleMock)

def test_generate_code(self) -> None:
model = torch.nn.Sequential(torch.nn.LSTM(input_size=1, hidden_size=2))
translated_model = translator.translate_model(model, self.build_mapping)
modules = translator.generate_code(
translatable_layers=translated_model, translation_args=dict()
vhdl_modules=translated_model, translation_args=dict()
)

code = unpack_module_directories(modules)
expected_code = [
(
"0_TranslatableMock",
"0_VHDLModuleMock",
[
("component1", ["1", "2", "3"]),
("component2", ["4", "5", "6"]),
Expand All @@ -109,7 +109,7 @@ def __init__(self) -> None:
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.lstm_2(self.lstm_1(x))

def extract_input_hidden_size(lstm: LSTMTranslatable) -> tuple[int, int]:
def extract_input_hidden_size(lstm: LSTMModule) -> tuple[int, int]:
hidden_size = len(lstm.weights_hh[0][0])
input_size = len(lstm.weights_ih[0][0])
return input_size, hidden_size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import elasticai.creator.qat.layers as qtorch
from elasticai.creator.vhdl.language import Code
from elasticai.creator.vhdl.translator.abstract.translatable import Translatable
from elasticai.creator.vhdl.translator.build_function_mapping import (
BuildFunctionMapping,
)
Expand All @@ -21,23 +20,23 @@ def __call__(self) -> Code:


@dataclass
class MockTranslatable(Translatable):
components: list[VHDLComponent]
class MockModule(VHDLModule):
vhdl_components: list[VHDLComponent]

def translate(self, args: Any) -> VHDLModule:
return self.components
def components(self, args: Any) -> list[VHDLComponent]:
return self.vhdl_components


def mock_build_function1(layer: Any) -> Translatable:
return MockTranslatable([MockVHDLComponent()])
def mock_build_function1(layer: Any) -> VHDLModule:
return MockModule([MockVHDLComponent()])


def mock_build_function2(layer: Any) -> Translatable:
return MockTranslatable([MockVHDLComponent()])
def mock_build_function2(layer: Any) -> VHDLModule:
return MockModule([MockVHDLComponent()])


def mock_build_function3(layer: Any) -> Translatable:
return MockTranslatable([MockVHDLComponent()])
def mock_build_function3(layer: Any) -> VHDLModule:
return MockModule([MockVHDLComponent()])


class BuildFunctionMappingTest(unittest.TestCase):
Expand Down
36 changes: 20 additions & 16 deletions elasticai/creator/vhdl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ An incomplete class diagram illustrating this is the following:

```mermaid
classDiagram
class VHDLModule {
<<interface>>
+components(args: Any) Iterable VHDLComponent
}
class VHDLComponent {
<<interface>>
+String file_name
Expand All @@ -35,23 +39,23 @@ Static VHDL files that have no placeholders are represented by a `VHDLStaticComp
A layer (e.g. an LSTM layer) can consist of more than one VHDLComponent. For this reason we call a
`Iterable[VHDLComponent]` a `VHDLModule`.

## Translatables
## VHDLModules

In order to represent a layer independently of the machine learning framework used, every layer that needs to be
translated is represented as translatable. A `Translatable` class has a `translate` function that takes a
translated is represented as VHDLModule. A `VHDLModule` class has a `components` function that takes a
DTO (Data Transfer Object) which contains all necessary parameters from the user to translate the layer to VHDL and
returns a `VHDLModule` as the result of the translation.

An incomplete class diagram showing this for the `LSTMTranslatable` and `Linear1dTranslatable` is the following:
An incomplete class diagram showing this for the `LSTMModule` and `Linear1dModule` is the following:

```mermaid
classDiagram
class Translatable {
class VHDLModule {
<<interface>>
+translate(Any args) VHDLModule
+components(args: Any) Iterable VHDLComponent
}
class LSTMTranslatable {
class LSTMModule {
+weights_ih
+weights_hh
+biases_ih
Expand All @@ -65,7 +69,7 @@ class LSTMTranslationArgs {
+work_library_name
}
class Linear1dTranslatable {
class Linear1dModule {
+weight
+bias
+translate(Linear1dTranslationArgs args) VHDLModule
Expand All @@ -75,18 +79,18 @@ class Linear1dTranslationArgs {
+work_library_name
}
Translatable <|.. LSTMTranslatable
LSTMTranslatable -- LSTMTranslationArgs
Translatable <|.. Linear1dTranslatable
Linear1dTranslatable -- Linear1dTranslationArgs
VHDLModule <|.. LSTMModule
LSTMModule -- LSTMTranslationArgs
VHDLModule <|.. Linear1dModule
Linear1dModule -- Linear1dTranslationArgs
```

## Build Functions

To convert a layer from a particular machine learning framework to the corresponding translatable, we use build
To convert a layer from a particular machine learning framework to the corresponding VHDLModule, we use build
functions for each layer of the machine learning framework. A build function is a function that takes the current layer
object of the machine learning framework and returns a `Translatable` object. In this build function, all the
necessary parameters of the layer are extracted and these parameters are used to create the corresponding translatable
object of the machine learning framework and returns a `VHDLModule` object. In this build function, all the
necessary parameters of the layer are extracted and these parameters are used to create the corresponding VHDLModule
object. To translate a model of a machine learning framework, there is a `BuildFunctionMapping` that maps all supported
layers to their corresponding build function.

Expand All @@ -101,11 +105,11 @@ the functions `translate_model`, `generate_code` and `save_code` which can be fo
```mermaid
flowchart TD
start([Start]) ---> translate_model
translate_model ---> |"Iterator[Translatable]"| generate_code
translate_model ---> |"Iterator[VHDLModule]"| generate_code
generate_code ---> |"Iterator[CodeModule]"| save_code
save_code ---> stop([Stop])
model[/Model/] -.- translate_model
build_function_mapping[[BuildFunctionMapping]] <-.-> |Request BuildFunctions| translate_model
translation_args[/Translation arguments for each Translatable/] -.- generate_code
translation_args[/Translation arguments for each VHDLModule/] -.- generate_code
save_code -.-> saved_data[/Saved VHDL files/]
```
8 changes: 4 additions & 4 deletions elasticai/creator/vhdl/translator/abstract/layers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from elasticai.creator.vhdl.translator.abstract.layers.linear_1d_translatable import (
Linear1dTranslatable,
from elasticai.creator.vhdl.translator.abstract.layers.linear_1d_module import (
Linear1dModule,
Linear1dTranslationArgs,
)
from elasticai.creator.vhdl.translator.abstract.layers.lstm_translatable import (
LSTMTranslatable,
from elasticai.creator.vhdl.translator.abstract.layers.lstm_module import (
LSTMModule,
LSTMTranslationArgs,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from collections.abc import Iterable
from collections.abc import Iterable, Iterator
from dataclasses import dataclass, field
from itertools import chain
from typing import Callable
Expand All @@ -11,7 +11,7 @@
RomComponent,
)
from elasticai.creator.vhdl.number_representations import FixedPoint
from elasticai.creator.vhdl.vhdl_component import VHDLModule
from elasticai.creator.vhdl.vhdl_component import VHDLComponent, VHDLModule


@dataclass
Expand All @@ -21,11 +21,11 @@ class Linear1dTranslationArgs:


@dataclass
class Linear1dTranslatable:
class Linear1dModule(VHDLModule):
weight: list[list[float]]
bias: list[float]

def translate(self, args: Linear1dTranslationArgs) -> VHDLModule:
def components(self, args: Linear1dTranslationArgs) -> Iterator[VHDLComponent]:
def to_fp(values: Iterable[float]) -> list[FixedPoint]:
return list(map(args.fixed_point_factory, values))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass, field
from typing import Callable
from typing import Callable, Iterator

import numpy as np

Expand All @@ -12,8 +12,7 @@
TanhComponent,
)
from elasticai.creator.vhdl.number_representations import FixedPoint
from elasticai.creator.vhdl.translator.abstract.translatable import Translatable
from elasticai.creator.vhdl.vhdl_component import VHDLModule
from elasticai.creator.vhdl.vhdl_component import VHDLComponent, VHDLModule


@dataclass
Expand All @@ -25,7 +24,7 @@ class LSTMTranslationArgs:


@dataclass
class LSTMTranslatable(Translatable):
class LSTMModule(VHDLModule):
"""
Abstract representation of an LSTM layer that can be directly translated to an iterable of VHDLComponent objects.
Currently, no stacked LSTMs are supported (only single layer LSTMs are supported).
Expand Down Expand Up @@ -75,7 +74,7 @@ def _derive_input_and_hidden_size(self) -> tuple[int, int]:
_, hidden_size, input_size = np.shape(self.weights_ih)
return input_size, hidden_size // 4

def translate(self, args: LSTMTranslationArgs) -> VHDLModule:
def components(self, args: LSTMTranslationArgs) -> Iterator[VHDLComponent]:
def to_fp(values: list[float]) -> list[FixedPoint]:
return list(map(args.fixed_point_factory, values))

Expand Down
8 changes: 0 additions & 8 deletions elasticai/creator/vhdl/translator/abstract/translatable.py

This file was deleted.

4 changes: 2 additions & 2 deletions elasticai/creator/vhdl/translator/build_function_mapping.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from collections.abc import Mapping
from typing import Any, Callable, Iterator

from elasticai.creator.vhdl.translator.abstract.translatable import Translatable
from elasticai.creator.vhdl.vhdl_component import VHDLModule

BuildFunction = Callable[[Any], Translatable]
BuildFunction = Callable[[Any], VHDLModule]


class BuildFunctionMapping(Mapping[str, BuildFunction]):
Expand Down
Loading

0 comments on commit 37412e8

Please sign in to comment.