Skip to content

Commit

Permalink
documnetation
Browse files Browse the repository at this point in the history
  • Loading branch information
Rojods committed Jul 16, 2024
1 parent 97143ef commit ad87b49
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 157 deletions.
44 changes: 44 additions & 0 deletions docs/extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: default
title: IDeSyDe - Extensions
description: { { site.description } }
permalink: /extensions
usemathjax: true
---


# Extensions

This page contains two tutorials on how to construct extensions for IDeSyDe in two different manners:
one for JVM languages, but using Java as the main example; and one for a Rust embedded module.

## Java module

To create a new java module, first make sure that JDK and `gradle` are installed in your computer.
You can follow the steps from the [ForSyDe IO website](https://forsyde.github.io/forsyde-io/usage/#optional-downloading-sdk-gradle-and-jabba)
using SDKMan and Jabba.

Once `gradle` is installed, go to a folder in your computer where you wish to develop and build the module and issue:

```
gradle init
```

which ask you what type of project you wish to create and then create a handful of files in the directory that we will work with.
Ideally you should choose `application` as the most basic one, in case you do not fully know how to work with Maven/Gradle based JVM projects.
Among the questions asked, you can choose the default for most, except for the minimum java version, which should be 17.
Don’t worry if this number seems high to you, it is the latest LTS after 11, and it will be supported for many years;
it is just that Java moves slowly but surely, specially its using companies.

The most import file for now is `build.gradle`, which declaratively defines the project’s dependencies and also fetches them.
To fetch the framework parts of IDeSyDe, you need to first enable JitPack as a repository:

```
repositories {
maven { url '<https://jitpack.io>' }
}
```

And then add to the modules to the dependencies section of your build.gradle:

## Rust embedded module
244 changes: 122 additions & 122 deletions docs/extensions/decisionModel.md → docs/extensions_old/decisionModel.md
Original file line number Diff line number Diff line change
@@ -1,123 +1,123 @@
---
layout: default
permalink: /extensions/decisionModel
---

# Decision models

## Fundamentals

Let's start with how decision models are defined, taken from the [design space identification proposal paper](https://ieeexplore.ieee.org/abstract/document/9474082):

> Design space exploration requires models into "solvable" representations; such as genome encoding for genetic algorithms (GAs),
> constraint programs for constraint programming (CP), or linear mathematical formulas for mixed integer linear programming (MILP).
> These models are called decision models.
This tells us that besides the actual problem-specific parameters and methods that we want
to program, we need to add a handful "extras" for so that the identification procedure works properly.
In essence, these extras are defining what is the type of the elements ([`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#ElementT-0)) and the type of
the element relationships ([`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#ElementRelationT-0)) that this new @scaladoc[DecisionModel](idesyde.identification.DecisionModel)
is abstracting. Besides that, we need to provide how these custom types can be transformed to `String`s,
since that is the _de-facto_ common ground for unique identifiers that are also minimally understandable.
Finally and most importantly, we need to provide a set of [`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#coveredElements-0) and a set of [`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#coveredElementRelations-0).
This is fundamental because the identification procedure depends on this in order to: 1) terminate and converge to a unique value,
and 2) converge to the _correct_ value. Therefore, these choices are not to be taken lightly as they can mean
the difference between IDeSyDe working as expected and not.

However, this discussion also suggests a good starting point for an abstracted element: if they have to be
stringifiable to an unique `String` identifier, why not make the elements of the @scaladoc[DecisionModel](idesyde.identification.DecisionModel)
`String`s themselves? That's exactly the path taken with @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel).
We could still go on with a very custom element type (`ElementT`), but them we have to guarantee that every distinct element
in the model will always generate a unique identifier.

## The standard decision model

Building on top of @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel) adds a new dependency to our
new extensions: the `scala-common` module. Some might think that adding dependencies is always risky, but this one particular dependency
is *highly recomended*. The cde in this module
can greatly simplify life with IDeSyDe, since most of the "vendor-agnostic" stuff is in this module, including
the aforementioned @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel), but also other
children decision models encoding known design scenarios from the scientific and engineering literature.

Moving on, now the only required to implement the trait @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel)
is to return a set of strings and set of pairs of strings.

For example, let's take the decision model @scaladoc[SharedMemoryMultiCore](idesyde.identification.common.models.platform.SharedMemoryMultiCore) from `scala-common` , which abstracts a shared-memory multi-core hardware architecture. Although there are
a handful of parameters that describe many performance characteristics of the architecture, the covered elements are fewer; namely,
they are the processing elements, the memory elements and the communication elements:

@@snip [SharedMemoryMultiCore.scala](/scala-common/src/main/scala/idesyde/identification/common/models/platform/SharedMemoryMultiCore.scala) { #covering_documentation_example }

where the elements being aggregated are simply lists of strings.

## A step-by-step example

Now that we know to use @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel), let's do a step-by-step construction of a new decision model. In this tutorial, we shall
create the parameters that abstract nicely [Synchronous Dataflow (graphs)](https://ieeexplore.ieee.org/document/1458143) or SDF(Gs), for short.

Since SDFs are essentially labelled directed graphs, we can start with the graph part. Every directed graph must have a set of nodes and
a set of arcs, which are called actors and channels in SDF terminology. Thus, we could start writing the decision model class like:

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

Although this captures the graph part, now we are missing the "labelled" part of it: the data rates. Since SDFs have
fixed production and consumption rates, and they always exist for every channels, we could add to the decision simply via:

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
val production: Vector[Int],
val consumption: Vector[Int],
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

which would take care of it. Note that the covered elements have not changed! This does not mean that the decision model created
is wrong, because we added information that is conceptually identified from the same elements and their relations. That is, we "know"
from the model definition that the production and consumption rates are part of the channel definition, and therefore it is not
a problem that new information was added to the decision model _while_ the covered elements and relations remained the same.
A final key elements we are missing is the initial tokens present in each channel. Once more, since we were supposed to start with
this information, it makes sense that no new element is being covered by this decision model extension.

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
val production: Vector[Int],
val consumption: Vector[Int],
val numInitialTokens: Vector[Int]
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

And there we have it! The most basic decision model representing SDFs, without taking into consideration execution times, actors sizes etc.
One could checkout the actual decision model for SDFs in `scala-model`: @scaladoc[SDFApplication](idesyde.identification.common.models.sdf.SDFApplication) and see differences. This is because the SDF decision model in the common module also takes into consideration the practical
factors briefly mentioned in order to later perform design space exploration on top of this decision model.

That's it! This is how one would create a correcy decision model, @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel) specifically, so that it is a expected for the identification procedure. From here, it would now be
---
layout: default
permalink: /extensions/decisionModel
---

# Decision models

## Fundamentals

Let's start with how decision models are defined, taken from the [design space identification proposal paper](https://ieeexplore.ieee.org/abstract/document/9474082):

> Design space exploration requires models into "solvable" representations; such as genome encoding for genetic algorithms (GAs),
> constraint programs for constraint programming (CP), or linear mathematical formulas for mixed integer linear programming (MILP).
> These models are called decision models.
This tells us that besides the actual problem-specific parameters and methods that we want
to program, we need to add a handful "extras" for so that the identification procedure works properly.
In essence, these extras are defining what is the type of the elements ([`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#ElementT-0)) and the type of
the element relationships ([`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#ElementRelationT-0)) that this new @scaladoc[DecisionModel](idesyde.identification.DecisionModel)
is abstracting. Besides that, we need to provide how these custom types can be transformed to `String`s,
since that is the _de-facto_ common ground for unique identifiers that are also minimally understandable.
Finally and most importantly, we need to provide a set of [`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#coveredElements-0) and a set of [`coveredElements`](/$scaladoc.base_url$/idesyde/identification/DecisionModel.html#coveredElementRelations-0).
This is fundamental because the identification procedure depends on this in order to: 1) terminate and converge to a unique value,
and 2) converge to the _correct_ value. Therefore, these choices are not to be taken lightly as they can mean
the difference between IDeSyDe working as expected and not.

However, this discussion also suggests a good starting point for an abstracted element: if they have to be
stringifiable to an unique `String` identifier, why not make the elements of the @scaladoc[DecisionModel](idesyde.identification.DecisionModel)
`String`s themselves? That's exactly the path taken with @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel).
We could still go on with a very custom element type (`ElementT`), but them we have to guarantee that every distinct element
in the model will always generate a unique identifier.

## The standard decision model

Building on top of @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel) adds a new dependency to our
new extensions: the `scala-common` module. Some might think that adding dependencies is always risky, but this one particular dependency
is *highly recomended*. The cde in this module
can greatly simplify life with IDeSyDe, since most of the "vendor-agnostic" stuff is in this module, including
the aforementioned @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel), but also other
children decision models encoding known design scenarios from the scientific and engineering literature.

Moving on, now the only required to implement the trait @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel)
is to return a set of strings and set of pairs of strings.

For example, let's take the decision model @scaladoc[SharedMemoryMultiCore](idesyde.identification.common.models.platform.SharedMemoryMultiCore) from `scala-common` , which abstracts a shared-memory multi-core hardware architecture. Although there are
a handful of parameters that describe many performance characteristics of the architecture, the covered elements are fewer; namely,
they are the processing elements, the memory elements and the communication elements:

@@snip [SharedMemoryMultiCore.scala](/scala-common/src/main/scala/idesyde/identification/common/models/platform/SharedMemoryMultiCore.scala) { #covering_documentation_example }

where the elements being aggregated are simply lists of strings.

## A step-by-step example

Now that we know to use @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel), let's do a step-by-step construction of a new decision model. In this tutorial, we shall
create the parameters that abstract nicely [Synchronous Dataflow (graphs)](https://ieeexplore.ieee.org/document/1458143) or SDF(Gs), for short.

Since SDFs are essentially labelled directed graphs, we can start with the graph part. Every directed graph must have a set of nodes and
a set of arcs, which are called actors and channels in SDF terminology. Thus, we could start writing the decision model class like:

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

Although this captures the graph part, now we are missing the "labelled" part of it: the data rates. Since SDFs have
fixed production and consumption rates, and they always exist for every channels, we could add to the decision simply via:

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
val production: Vector[Int],
val consumption: Vector[Int],
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

which would take care of it. Note that the covered elements have not changed! This does not mean that the decision model created
is wrong, because we added information that is conceptually identified from the same elements and their relations. That is, we "know"
from the model definition that the production and consumption rates are part of the channel definition, and therefore it is not
a problem that new information was added to the decision model _while_ the covered elements and relations remained the same.
A final key elements we are missing is the initial tokens present in each channel. Once more, since we were supposed to start with
this information, it makes sense that no new element is being covered by this decision model extension.

```
final case class SDFApplication(
val actors: Vector[String],
val channelsSrcs: Vector[String],
val channelsDsts: Vector[String],
val production: Vector[Int],
val consumption: Vector[Int],
val numInitialTokens: Vector[Int]
) extends StandardDecisionModel {
val coveredElements = actors.toSet
val coveredElementRelations = channelsSrcs.zip(channelDsts).toSet
def uniqueIdentifier = "SDFApplication"
}
```

And there we have it! The most basic decision model representing SDFs, without taking into consideration execution times, actors sizes etc.
One could checkout the actual decision model for SDFs in `scala-model`: @scaladoc[SDFApplication](idesyde.identification.common.models.sdf.SDFApplication) and see differences. This is because the SDF decision model in the common module also takes into consideration the practical
factors briefly mentioned in order to later perform design space exploration on top of this decision model.

That's it! This is how one would create a correcy decision model, @scaladoc[StandardDecisionModel](idesyde.identification.common.StandardDecisionModel) specifically, so that it is a expected for the identification procedure. From here, it would now be
necessary to define @ref[identification rules](identRules.md) to actually put this decision model to some use.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit ad87b49

Please sign in to comment.