Skip to content
Snippets Groups Projects
Commit 2486dea3 authored by Yaman Umuroglu's avatar Yaman Umuroglu
Browse files

Merge branch 'feature/notebook_analysis_pass' of...

Merge branch 'feature/notebook_analysis_pass' of https://github.com/Xilinx/finn into feature/notebook_analysis_pass
parents 13597db0 e7a16792
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
# FINN - Transformation passes
--------------------------------------
* <font size="3">changes (transforms) the given graph</font>
* <font size="3">input: ModelWrapper</font>
* <font size="3">returns the changed model (ModelWrapper) and flag `model_was_changed`</font>
%% Cell type:markdown id: tags:
## General Information
-----------------------------
<font size="3">Transformation passes have a base class and must inherit from that. Transformations are meant to be applied using .transform function from the ModelWrapper. This function makes a deep copy of the input model by default. The next cell shows .transform of ModelWrapper. </font>
%% Cell type:markdown id: tags:
### .transform() from ModelWrapper
`def transform(self, transformation, make_deepcopy=True):
transformed_model = self
if make_deepcopy:
transformed_model = copy.deepcopy(self)
model_was_changed = True
while model_was_changed:
(transformed_model, model_was_changed) = transformation.apply(
transformed_model
)
return transformed_model`
%% Cell type:markdown id: tags:
<font size="3">When the function is called, the model, the name of the transformation and, if required, the flag make_deepcopy are passed. It is also possible not to make a copy of the model. In this case `make_deepcopy` must be set to False. Then the branch `if make_deepcopy:` would not be taken and no copy of the model would be made.
The unchanged model is first passed to the variable `transformed_model` to pass this variable on to the transformation later.
`model_was_changed` indicates whether the transformation needs to be applied more then once. Because it needs to be applied at least one time `model_was_changed` is first set to True and then depending on the return values of the transformation function the transformation can be applied more then once.
**Important**: Due to the structure of this function, `model_was_changed` must be set to False at some point. Otherwise the loop is infinite.
Each new transformation must correspond to the scheme of the base class and contain at least the function `apply(model)`, which returns the changed model and a bool value for `model_was_changed`.
</font>
%% Cell type:markdown id: tags:
### transformation base class
`from abc import ABC, abstractmethod`
`class Transformation(ABC):
def __init__(self):
super().__init__()`
` @abstractmethod
def apply(self, model):
pass`
<font size="3"> Base class is abstract class (`import ABC`) with only one abstract method (`apply()`) which gets the model as input. To show what a transformation should look like, the following example is taken from FINN. </font>
%% Cell type:markdown id: tags:
## Example -
## Example - ConvertSubToAdd
-----------------------------
<font size="3">text</font>
<font size="3">The transformation replaces all subtraction nodes in a model with addition nodes with appropriate sign.</font>
%% Cell type:markdown id: tags:
`from finn.transformation import Transformation`
`class ConvertSubToAdd(Transformation):
def apply(self, model):
graph = model.graph
for n in graph.node:
if n.op_type == "Sub":
A = model.get_initializer(n.input[1])
if A is not None:
n.op_type = "Add"
model.set_initializer(n.input[1], -A)
return (model, False)`
%% Cell type:markdown id: tags:
<font size="3">First the transformation class must be imported. Then a class can be created for the new transformation, which is derived from the base class. In this case the transformation has only the `apply()` function.
All nodes are checked by first extracting the graph from the model and then iterating over the node list. With the help of .op_type the operation type of the node can be checked, if the node is a subtraction node the condition `if n.op_type == "Sub"` is true. It may be that the subtraction input of the node has no value, this is checked with `model.get_initializer(n.input[1])`.
**Important:** FINN always assumes a certain order of inputs, this is especially important if you want to create additional custom operation nodes.
When the input is initialized, the operation type of the node is converted to `"Add"`, this can simply be done by using the equal sign. Then the sign of the initial value must be changed. For this the ModelWrapper function `.set_initializer` can be used.
At the end the changed model is returned and `model_was_changed` is set to False, because the transformation has to be executed only once.</font>
%% Cell type:code id: tags:
``` python
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment