Skip to content
Snippets Groups Projects
Commit ef778f09 authored by Olivier BICHLER's avatar Olivier BICHLER
Browse files

Merged with main

parents d376906b 71763994
No related branches found
No related tags found
No related merge requests found
Showing
with 636 additions and 73 deletions
......@@ -27,6 +27,8 @@ build:ubuntu_python:
- python3 -m pip install virtualenv
- virtualenv venv
- source venv/bin/activate
# Numpy dependancy for unit test
- python3 -m pip install numpy
- export AIDGE_INSTALL=`pwd`/install
- python3 -m pip install .
artifacts:
......
......@@ -66,6 +66,10 @@ endif()
target_compile_features(${module_name} PRIVATE cxx_std_14)
# -fvisibility=hidden required by pybind11
target_compile_options(${module_name} PUBLIC
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-fvisibility=hidden>)
target_compile_options(${module_name} PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-Wall -Wextra -Wold-style-cast -Winline -pedantic -Werror=narrowing -Wshadow $<$<BOOL:${WERROR}>:-Werror>>)
......
......@@ -6,16 +6,19 @@ You can find here the C++ code of the Core library of Aidge.
## Pip installation
To install aidge_core using pip, make sure to set the desired install path :
``` bash
export AIDGE_INSTALL = '<path_to_aidge>/install'
```
Then run in your python environnement :
To install aidge_core using pip, run the following command in your python environnement :
``` bash
pip install . -v
```
**Note:** you can specify a custom install folder by setting an environment variable:
``` bash
export AIDGE_INSTALL='<path_to_aidge>/install'
```
## Standard C++ Compilation
Create two directories ``build`` and ``ìnstall``.
......
......@@ -30,36 +30,77 @@ class test_operator_binding(unittest.TestCase):
self.assertNotEqual(gop.name(), "")
def test_param_bool(self):
self.generic_operator.add_parameter("bool", True)
self.assertEqual(self.generic_operator.get_parameter("bool"), True)
self.generic_operator.add_attr("bool", True)
self.assertEqual(self.generic_operator.has_attr("bool"), True)
self.assertEqual(self.generic_operator.get_attr("bool"), True)
self.assertEqual(self.generic_operator.get_attr_type("bool"), "bool")
self.assertEqual(self.generic_operator.get_attrs_name(), {"bool"})
self.generic_operator.del_attr("bool")
self.assertEqual(self.generic_operator.has_attr("bool"), False)
self.assertEqual(len(self.generic_operator.get_attrs_name()), 0)
def test_param_int(self):
self.generic_operator.add_parameter("int", 1)
self.assertEqual(self.generic_operator.get_parameter("int"), 1)
self.generic_operator.add_attr("int", 1)
self.assertEqual(self.generic_operator.get_attr("int"), 1)
def test_param_float(self):
self.generic_operator.add_parameter("float", 2.0)
self.assertEqual(self.generic_operator.get_parameter("float"), 2.0)
self.generic_operator.add_attr("float", 2.0)
self.assertEqual(self.generic_operator.get_attr("float"), 2.0)
def test_param_str(self):
self.generic_operator.add_parameter("str", "value")
self.assertEqual(self.generic_operator.get_parameter("str"), "value")
self.generic_operator.add_attr("str", "value")
self.assertEqual(self.generic_operator.get_attr("str"), "value")
def test_param_l_int(self):
self.generic_operator.add_parameter("l_int", [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
self.assertEqual(self.generic_operator.get_parameter("l_int"), [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
self.generic_operator.add_attr("l_int", [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
self.assertEqual(self.generic_operator.get_attr("l_int"), [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
def test_param_l_bool(self):
self.generic_operator.add_parameter("l_bool", [True, False, False, True])
self.assertEqual(self.generic_operator.get_parameter("l_bool"), [True, False, False, True])
self.generic_operator.add_attr("l_bool", [True, False, False, True])
self.assertEqual(self.generic_operator.get_attr("l_bool"), [True, False, False, True])
def test_param_l_float(self):
self.generic_operator.add_parameter("l_float", [2.0, 1.0])
self.assertEqual(self.generic_operator.get_parameter("l_float"), [2.0, 1.0])
self.generic_operator.add_attr("l_float", [2.0, 1.0])
self.assertEqual(self.generic_operator.get_attr("l_float"), [2.0, 1.0])
def test_param_l_str(self):
self.generic_operator.add_parameter("l_str", ["ok"])
self.assertEqual(self.generic_operator.get_parameter("l_str"), ["ok"])
self.generic_operator.add_attr("l_str", ["ok"])
self.assertEqual(self.generic_operator.get_attr("l_str"), ["ok"])
def test_dynamicattribute_binding(self):
# Check original C++ attributes are binded
attrs = aidge_core.test_DynamicAttributes_binding()
self.assertEqual(attrs.has_attr("a"), True)
self.assertEqual(attrs.get_attr("a"), 42)
self.assertEqual(attrs.has_attr("b"), True)
self.assertEqual(attrs.get_attr("b"), "test")
self.assertEqual(attrs.has_attr("c"), True)
self.assertEqual(attrs.get_attr("c"), [True, False, True])
self.assertEqual(attrs.get_attrs_name(), {"a", "b", "c"})
self.assertEqual(attrs.has_attr("d"), False)
# Add Python attributes
attrs.add_attr("d", 18.56)
self.assertEqual(attrs.get_attr("d"), 18.56)
self.assertEqual(attrs.has_attr("d"), True)
self.assertEqual(attrs.get_attrs_name(), {"a", "b", "c", "d"})
self.assertEqual(attrs.has_attr("e"), False)
# Check that added Python attribute is accessible in C++
# Return the value of an attribute named "d" of type float64 (double in C++)
self.assertEqual(aidge_core.test_DynamicAttributes_binding_check(attrs), 18.56)
attrs.set_attr("d", 23.89)
self.assertEqual(aidge_core.test_DynamicAttributes_binding_check(attrs), 23.89)
def test_compute_output_dims(self):
in_dims=[25, 25]
input = aidge_core.Producer(in_dims, name="In")
genOp = aidge_core.GenericOperator("genOp", 1, 1, 1, name="genOp")
_ = aidge_core.sequential([input, genOp])
self.assertListEqual(genOp.get_operator().output(0).dims(), [])
genOp.get_operator().set_compute_output_dims(lambda x:x)
genOp.get_operator().compute_output_dims()
self.assertListEqual(genOp.get_operator().output(0).dims(), in_dims)
if __name__ == '__main__':
unittest.main()
\ No newline at end of file
unittest.main()
......@@ -11,7 +11,7 @@ SPDX-License-Identifier: EPL-2.0
import unittest
import aidge_core
class test_parameters(unittest.TestCase):
class test_attributes(unittest.TestCase):
"""Very basic test to make sure the python APi is not broken.
Can be remove in later stage of the developpement.
"""
......@@ -27,21 +27,21 @@ class test_parameters(unittest.TestCase):
out_channels = 8
k_dims = [2, 2]
conv_op = aidge_core.Conv2D(in_channels , out_channels, k_dims).get_operator()
self.assertEqual(conv_op.get("InChannels"), in_channels)
self.assertEqual(conv_op.get("OutChannels"), out_channels)
self.assertEqual(conv_op.get("KernelDims"), k_dims)
self.assertEqual(conv_op.get_attr("InChannels"), in_channels)
self.assertEqual(conv_op.get_attr("OutChannels"), out_channels)
self.assertEqual(conv_op.get_attr("KernelDims"), k_dims)
def test_fc(self):
out_channels = 8
nb_bias = True
fc_op = aidge_core.FC(out_channels, nb_bias).get_operator()
self.assertEqual(fc_op.get("OutChannels"), out_channels)
self.assertEqual(fc_op.get("NoBias"), nb_bias)
self.assertEqual(fc_op.get_attr("OutChannels"), out_channels)
self.assertEqual(fc_op.get_attr("NoBias"), nb_bias)
def test_matmul(self):
out_channels = 8
matmul_op = aidge_core.Matmul(out_channels).get_operator()
self.assertEqual(matmul_op.get("OutChannels"), out_channels)
matmul_op = aidge_core.MatMul(out_channels).get_operator()
self.assertEqual(matmul_op.get_attr("OutChannels"), out_channels)
def test_producer_1D(self):
dims = [5]
......@@ -71,7 +71,7 @@ class test_parameters(unittest.TestCase):
def test_leaky_relu(self):
negative_slope = 0.25
leakyrelu_op = aidge_core.LeakyReLU(negative_slope).get_operator()
self.assertEqual(leakyrelu_op.get("NegativeSlope"), negative_slope)
self.assertEqual(leakyrelu_op.get_attr("NegativeSlope"), negative_slope)
if __name__ == '__main__':
unittest.main()
"""
Copyright (c) 2023 CEA-List
This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0.
SPDX-License-Identifier: EPL-2.0
"""
import unittest
import aidge_core
class test_recipies(unittest.TestCase):
"""
"""
def setUp(self):
pass
def tearDown(self):
pass
def test_remove_flatten(self):
graph_view = aidge_core.sequential([
aidge_core.GenericOperator("Flatten", 1, 1, 1, name="Flatten0"),
aidge_core.FC(50, name='0')
])
old_nodes = graph_view.get_nodes()
aidge_core.remove_flatten(graph_view)
self.assertTrue(len(graph_view.get_nodes()) == len(old_nodes) - 1)
self.assertTrue("Flatten0" not in [i.name for i in graph_view.get_nodes()])
self.assertTrue(all([i in old_nodes for i in graph_view.get_nodes()]))
def test_fuse_matmul_add(self):
matmul0 = aidge_core.GenericOperator("MatMul", 1, 2, 1, name="MatMul0")
add0 = aidge_core.Add(name="Add0")
matmul1 = aidge_core.GenericOperator("MatMul", 1, 2, 1, name="MatMul1")
add1 = aidge_core.Add(name="Add1")
graph_view = aidge_core.sequential([matmul0, add0, matmul1, add1])
w0 = aidge_core.Producer([1, 1], name="W0")
w0.add_child(matmul0, 0, 1)
graph_view.add(w0)
b0 = aidge_core.Producer([1], name="B0")
b0.add_child(add0, 0, 1)
graph_view.add(b0)
w1 = aidge_core.Producer([1, 1], name="W1")
w1.add_child(matmul1, 0, 1)
graph_view.add(w1)
b1 = aidge_core.Producer([1], name="B1")
b1.add_child(add1, 0, 1)
graph_view.add(b1)
old_nodes = graph_view.get_nodes()
aidge_core.fuse_mul_add(graph_view)
self.assertTrue(len(graph_view.get_nodes()) == len(old_nodes) - 2)
self.assertTrue("MatMul0" not in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("Add0" not in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("MatMul1" not in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("Add1" not in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("W0" in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("B0" in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("W1" in [i.name() for i in graph_view.get_nodes()])
self.assertTrue("B1" in [i.name() for i in graph_view.get_nodes()])
# TODO : Vérifier que FC bien crée
if __name__ == '__main__':
unittest.main()
"""
Copyright (c) 2023 CEA-List
This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0.
SPDX-License-Identifier: EPL-2.0
"""
import unittest
import aidge_core
from functools import reduce
import numpy as np
class test_tensor(unittest.TestCase):
"""
"""
def setUp(self):
pass
def tearDown(self):
pass
def test_getcoord_getidx(self):
dims = [2,2,2]
size = reduce((lambda x, y: x*y), dims)
np_array = np.arange(size).reshape(dims)
t = aidge_core.Tensor(np_array)
for i in range(size):
coord = t.get_coord(i)
idx = t.get_idx(coord)
self.assertEqual(idx, i)
if __name__ == '__main__':
unittest.main()
......@@ -33,16 +33,18 @@
#include "aidge/operator/ConvDepthWise.hpp"
#include "aidge/operator/FC.hpp"
#include "aidge/operator/GenericOperator.hpp"
#include "aidge/operator/Matmul.hpp"
#include "aidge/operator/MatMul.hpp"
#include "aidge/operator/MaxPooling.hpp"
#include "aidge/operator/MetaOperator.hpp"
#include "aidge/operator/Operator.hpp"
#include "aidge/operator/Producer.hpp"
#include "aidge/operator/ReLU.hpp"
#include "aidge/operator/Softmax.hpp"
#include "aidge/operator/Scaling.hpp"
#include "aidge/scheduler/Scheduler.hpp"
#include "aidge/utils/CParameter.hpp"
#include "aidge/utils/Parameter.hpp"
#include "aidge/utils/Attributes.hpp"
#include "aidge/utils/StaticAttributes.hpp"
#include "aidge/utils/DynamicAttributes.hpp"
#include "aidge/utils/Recipies.hpp"
#include "aidge/utils/Registrar.hpp"
#include "aidge/utils/Types.h"
......
......@@ -14,11 +14,13 @@
#include <cstddef>
#include <vector>
#include <memory>
#include "aidge/utils/Types.h"
namespace Aidge {
class OperatorImpl {
public:
virtual void forward(){};
virtual void backward(){};
......
......@@ -27,6 +27,9 @@ public:
{
printf("Cannot set raw pointer for backend %s\n", mBackend);
};
virtual void* getRaw(std::size_t /*idx*/)=0;
virtual std::size_t scalarSize() const = 0; // Size of one scalar (in bytes)
constexpr const char *backend() const { return mBackend; }
virtual ~TensorImpl() = default;
......
......@@ -12,7 +12,7 @@
#ifndef AIDGE_DATA_H_
#define AIDGE_DATA_H_
#include "aidge/utils/Parameter.hpp"
#include "aidge/utils/Attributes.hpp"
namespace Aidge {
enum class DataType {
......
......@@ -446,18 +446,33 @@ class Tensor : public Data,
*/
bool empty() const { return mDims.empty(); }
template <typename expectedType, std::array<std::size_t, 1>::size_type DIM>
constexpr expectedType &get(std::array<std::size_t, DIM> idx) {
assert(DIM == mDims.size());
assert(mImpl);
std::size_t unfoldedIdx = 0;
for (std::size_t i = 0; i < DIM - std::size_t(1); ++i) {
unfoldedIdx = (unfoldedIdx + idx[i]) * mDims[i + 1];
}
unfoldedIdx += idx[DIM - 1];
return static_cast<expectedType *>(mImpl->rawPtr())[unfoldedIdx];
template <typename expectedType>
expectedType& get(std::size_t idx){
// TODO : add assert expected Type compatible with datatype
// TODO : add assert idx < Size
return *reinterpret_cast<expectedType *>(mImpl->getRaw(idx));
}
template <typename expectedType>
expectedType& get(std::vector<std::size_t> coordIdx){
return get<expectedType>(getIdx(coordIdx));
}
template <typename expectedType>
void set(std::size_t idx, expectedType value){
// TODO : add assert expected Type compatible with datatype
// TODO : add assert idx < Size
void* dataPtr = mImpl->getRaw(idx);
std::memcpy(dataPtr, &value, sizeof(expectedType));
}
template <typename expectedType>
void set(std::vector<std::size_t> coordIdx, expectedType value){
set<expectedType>(getIdx(coordIdx), value);
}
std::string toString() {
if (dims().empty()) { return "{}"; }
std::string res;
......@@ -559,6 +574,42 @@ class Tensor : public Data,
return mGrad;
}
/**
* @brief From the the 1D index, return the coordinate of an element in the tensor.
*
* @param flatIdx 1D index of the value considering a flatten tensor.
* @return std::vector<DimSize_t>
*/
std::vector<std::size_t> getCoord(std::size_t flatIdx) const {
std::vector<std::size_t> coordIdx = std::vector<std::size_t>(mDims.size());
std::size_t idx = flatIdx;
for (std::size_t i = mDims.size() - 1; i > 0; --i){
coordIdx[i] = (idx % mDims[i]);
idx/=mDims[i];
}
coordIdx[0] = idx % mDims[0];
return coordIdx;
}
/**
* @brief From the coordinate returns the 1D index of an element in the tensor.
*
* @param coordIdx Coordinate to an element in the tensor
* @return DimSize_t
*/
std::size_t getIdx(std::vector<std::size_t> coordIdx) const {
// std::size_t flatIdx = 0;
// std::size_t stride = 1;
std::size_t flatIdx = 0;
assert(coordIdx.size() == mDims.size() && "Coordinates does not match number of dimensions");
std::size_t i = 0;
for(; i < mDims.size() - 1; ++i){
assert(coordIdx[i] < mDims[i] && "Coordinates dimensions does not fit the dimensions of the tensor");
flatIdx = (flatIdx + coordIdx[i]) * mDims[i + 1];
}
return flatIdx + coordIdx[i];
}
private:
///\bug not protected against overflow
std::size_t computeSize() {
......
......@@ -320,8 +320,20 @@ public:
void link(std::string name1_inID, std::string name2_outID);
void insert(Node &newNode, Node &inNode, std::initializer_list<Node> outNodes,
IOIndex_t tensorIdx);
/**
* @brief Insert a node (newParentNode) as a parent of the passed node (childNode).
*
* @param childNode Node that gets a new parent.
* @param newParentNode Inserted Node.
* @param childInputTensorIdx Index of the input Tensor for the childNode linked to the inserted Node output.
* @param newParentInputTensorIdx Index of the input Tensor for the newParentNode linked to the former parent of childNode.
* @param newParentOutputTensorIdx Index of the output Tensor for the newParentNode linked to the childNode's input Tensor.
*/
void insertParent(NodePtr childNode,
NodePtr newParentNode,
IOIndex_t childInputTensorIdx,
IOIndex_t newParentInputTensorIdx,
IOIndex_t newParentOutputTensorIdx);
/**
* @brief Replace the current GraphView with the set of given Nodes if possible
......@@ -336,6 +348,37 @@ public:
*/
void updateOutputNodes();
/**
* @brief Clone the GraphView with shared Operators. It is a new GraphView, with cloned Nodes, but the new Nodes refer to the same Operators as the original ones.
* @return std::shared_ptr<GraphView>
*/
inline std::shared_ptr<GraphView> cloneSharedOperators() const {
return cloneCallback(&Node::cloneSharedOperators);
}
/**
* @brief Clone the GraphView with shared Producers. All the other Operators are copied.
* @return std::shared_ptr<GraphView>
*/
inline std::shared_ptr<GraphView> cloneSharedProducers() const {
return cloneCallback(&Node::cloneSharedProducers);
}
/**
* @brief Clone the GraphView. Everything is cloned: Nodes and Operators.
* @return std::shared_ptr<GraphView>
*/
inline std::shared_ptr<GraphView> clone() const {
return cloneCallback(&Node::clone);
}
/**
* @brief Clone the current GraphView using a callback function for the Node cloning, allowing to specify how each Node should be cloned or replaced by another Node type, or removed (i.e. replaced by identity). When a Node is removed, the clone() method automatically finds the next valid parent in line, going backward in the graph and connects it if that makes sense without ambiguity (effectively treating the removed Node as an identity operation).
* @param cloneNode Callback function to clone a node
* @return std::shared_ptr<GraphView>
*/
std::shared_ptr<GraphView> cloneCallback(NodePtr(*cloneNode)(NodePtr)) const;
private:
///////////////////////////////////////////////////////
// TENSOR MANAGEMENT
......
......@@ -350,6 +350,55 @@ public:
*/
void resetConnections(bool includeLearnableParam = false);
///////////////////////////////////////////////////////
// CLONE
///////////////////////////////////////////////////////
/**
* @brief Clone the current Node. The Operator attribute of the new Node is not copied but shared with the current Node. The new node has no connection.
* @return NodePtr
*/
NodePtr cloneSharedOperators() const;
/**
* @brief Clone the Node. Every attribute is copied, even Operator pointer except for Producers for which it is shared. The new Node has no connection.
* @return NodePtr
*/
NodePtr cloneSharedProducers() const;
/**
* @brief Clone the Node and its Operator. The new Node has no connection.
* @return NodePtr
*/
NodePtr clone() const;
/**
* @brief Callback function to clone the Node keeping the same Operator object instance. The new Node has no connection.
* @param node Node to clone.
* @return NodePtr
*/
static NodePtr cloneSharedOperators(NodePtr node) {
return node->cloneSharedOperators();
}
/**
* @brief Callback function to clone the Node. Every attribute is copied, even Operator pointer except for Producers for which it is shared. The new Node has no connection.
* @param node Node to clone.
* @return NodePtr
*/
static NodePtr cloneSharedProducers(NodePtr node) {
return node->cloneSharedProducers();
}
/**
* @brief Callback function to clone the Node and its Operator. The new Node has no connection.
* @param node Node to clone.
* @return NodePtr
*/
static NodePtr clone(NodePtr node) {
return node->clone();
}
private:
///////////////////////////////////////////////////////
// OPERATORS
......
/**
* \file execTime.hpp
* \brief execTime structure
* \version file 1.0.0
* \date Creation 27 June 2023
* \date 27 June 2023
* \par ChangeLog
* \par
* v1.0.0, 27 June 2023<br>
* - Initial version.
* \author mn271187, ik243221
* \copyright
* Copyright (c) 2023 CEA, LIST, Embedded Artificial Intelligence Laboratory. All
* rights reserved.
*/
#ifndef execTime_H_
#define execTime_H_
#include "aidge/operator/Operator.hpp"
#include "aidge/hook/hook.hpp"
#include <memory>
#include <chrono>
#include <vector>
namespace Aidge {
class ExecTime : public Hook {
private:
std::vector<std::chrono::high_resolution_clock::time_point> registeredTimes = std::vector<std::chrono::high_resolution_clock::time_point>();
public:
ExecTime(const std::shared_ptr<Operator> op) : Hook(op) {}
~ExecTime() = default;
void call() override final {
registeredTimes.push_back(std::chrono::high_resolution_clock::now());
}
static std::shared_ptr<ExecTime> create(const std::shared_ptr<Operator> op)
{
return std::make_shared<ExecTime>(op);
}
std::vector<std::chrono::high_resolution_clock::time_point> getTimes() {
return registeredTimes;
}
std::chrono::high_resolution_clock::time_point getTime(size_t idx) {
return registeredTimes[idx];
}
};
namespace {
static Registrar<Hook> registrarHook_ExecTime({"execution_time"}, Aidge::ExecTime::create);
}
}
#endif /* execTime_H_ */
\ No newline at end of file
/**
* \file Hook.hpp
* \brief Hook structure
* \version file 1.0.0
* \date Creation 27 June 2023
* \date 27 June 2023
* \par ChangeLog
* \par
* v1.0.0, 27 June 2023<br>
* - Initial version.
* \author mn271187, ik243221
* \copyright
* Copyright (c) 2023 CEA, LIST, Embedded Artificial Intelligence Laboratory. All
* rights reserved.
*/
#ifndef Hook_H_
#define Hook_H_
#include "aidge/utils/Attributes.hpp"
#include "aidge/utils/Registrar.hpp"
#include <memory>
namespace Aidge {
class Operator;
class Hook : public Registrable<Hook, std::tuple<std::string>, std::shared_ptr<Hook>(const std::shared_ptr<Operator>)> {
//class Hook : public Registrable<Hook, std::tuple<std::string>, std::shared_ptr<Hook>(const std::shared_ptr<Operator>)>{
protected:
const std::shared_ptr<Operator> mOperator;
public:
Hook(std::shared_ptr<Operator> op) : mOperator(op) {}
virtual ~Hook();
virtual void call() = 0;
};
}
#endif /* Hook_H_ */
\ No newline at end of file
/**
* \file execTime.hpp
* \brief execTime structure
* \version file 1.0.0
* \date Creation 27 June 2023
* \date 27 June 2023
* \par ChangeLog
* \par
* v1.0.0, 27 June 2023<br>
* - Initial version.
* \author ik243221
* \copyright
* Copyright (c) 2023 CEA, LIST, Embedded Artificial Intelligence Laboratory. All
* rights reserved.
*/
#ifndef AIDGE_CORE_HOOK_OUTPUTRANGE_H_
#define AIDGE_CORE_HOOK_OUTPUTRANGE_H_
#include "aidge/operator/Operator.hpp"
#include "aidge/hook/hook.hpp"
#include <memory>
#include <chrono>
#include <vector>
#include <cmath>
namespace Aidge {
class OutputRange : public Hook {
private:
std::vector<float> registeredOutputs = std::vector<float>();
public:
OutputRange(const std::shared_ptr<Operator> op) : Hook(op) {}
~OutputRange() = default;
void call() override final {
//std::cout << "call() outputRange hook " << std::endl;
//this assumes there is only 1 output possible
std::shared_ptr<Tensor> tensor = mOperator->getOutput(0);
//tensor->print();
//std::cout << "call() outputRange hook : tensor printed" << std::endl;
float max_value = 0.;
float * casted_tensor = static_cast<float *>(tensor->getImpl()->rawPtr());
//find the absolute max value in the tensor, save it to registered outputs
for(std::size_t i = 0; i < tensor->size(); ++i) {
//std::cout << "call() outputRange hook : casted_tensor[i] = " << casted_tensor[i] << std::endl;
if(std::abs(casted_tensor[i]) > max_value){
max_value = std::abs(casted_tensor[i]);
}
}
//std::cout << "call() outputRange hook : max_value = " << max_value << std::endl;
registeredOutputs.push_back(max_value);
}
static std::shared_ptr<OutputRange> create(const std::shared_ptr<Operator> op)
{
return std::make_shared<OutputRange>(op);
}
std::vector<float> getOutputs() {
return registeredOutputs;
}
float getOutput(size_t idx) {
return registeredOutputs[idx];
}
};
namespace {
static Registrar<Hook> registrarHook_OutputRange({"output_range"}, Aidge::OutputRange::create);
}
}
#endif /* outputRange_H_ */
\ No newline at end of file
......@@ -32,14 +32,13 @@ class Add_Op : public Operator,
public:
// FIXME: change accessibility
std::array<std::shared_ptr<Tensor>, NUM> mInputs;
const std::shared_ptr<Tensor> mOutput = std::make_shared<Tensor>(shared_from_this());
const std::shared_ptr<Tensor> mOutput = std::make_shared<Tensor>();
public:
static constexpr const char* Type = "Add";
constexpr Add_Op()
: Operator(Type),
mOutput(std::make_shared<Tensor>())
: Operator(Type)
{
assert(NUM > 0 && "Add should have at least one input");
for (std::size_t i = 0; i<NUM; ++i) {
......@@ -48,6 +47,31 @@ public:
setDatatype(DataType::Float32);
}
/**
* @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated).
* @param op Operator to copy.
*/
Add_Op(const Add_Op<NUM>& op)
: Operator(Type),
mOutput(std::make_shared<Tensor>(*op.mOutput))
{
// cpy-ctor
assert(NUM > 0 && "Add should have at least one input");
for (std::size_t i = 0; i<NUM; ++i) {
mInputs[i] = std::make_shared<Tensor>();
}
setDatatype(op.mOutput->dataType());
mImpl = op.mImpl ? Registrar<Add_Op<NUM>>::create(mOutput->getImpl()->backend())(*this) : nullptr;
}
/**
* @brief Clone the operator using its copy-constructor.
* @see Operator::Add_Op
*/
std::shared_ptr<Operator> clone() const override {
return std::make_shared<Add_Op>(*this);
}
// Data operator[](const char* inputName) override final {
// std::shared_ptr<Tensor> in = (strcmp(inputName, "data")) ? mInputs[0] :
// (strcmp(inputName, "weight") ? mInputs[1] :
......
......@@ -21,17 +21,17 @@
#include "aidge/graph/Node.hpp"
#include "aidge/operator/Operator.hpp"
#include "aidge/operator/Producer.hpp"
#include "aidge/utils/Parameter.hpp"
#include "aidge/utils/StaticAttributes.hpp"
#include "aidge/utils/Registrar.hpp"
#include "aidge/utils/Types.h"
namespace Aidge {
enum class AvgPoolingParam { StrideDims, KernelDims };
enum class AvgPoolingAttr { StrideDims, KernelDims };
template <DimIdx_t DIM>
class AvgPooling_Op : public Operator,
public Registrable<AvgPooling_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const AvgPooling_Op<DIM> &)>,
public Parameterizable<AvgPoolingParam,
public StaticAttributes<AvgPoolingAttr,
std::array<DimSize_t, DIM>,
std::array<DimSize_t, DIM>> {
private:
......@@ -44,20 +44,42 @@ public:
AvgPooling_Op() = delete;
using Parameterizable_ = Parameterizable<AvgPoolingParam,
using Attributes_ = StaticAttributes<AvgPoolingAttr,
std::array<DimSize_t, DIM>,
std::array<DimSize_t, DIM>>;
template <AvgPoolingParam e>
using param = typename Parameterizable_::template param<e>;
template <AvgPoolingAttr e>
using attr = typename Attributes_::template attr<e>;
constexpr AvgPooling_Op(const std::array<DimSize_t, DIM> &kernel_dims,
const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1))
: Operator(Type),
Parameterizable_(param<AvgPoolingParam::StrideDims>(stride_dims),
param<AvgPoolingParam::KernelDims>(kernel_dims)) {
Attributes_(attr<AvgPoolingAttr::StrideDims>(stride_dims),
attr<AvgPoolingAttr::KernelDims>(kernel_dims)) {
setDatatype(DataType::Float32);
}
/**
* @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated).
* @param op Operator to copy.
*/
AvgPooling_Op(const AvgPooling_Op<DIM>& op)
: Operator(Type),
Attributes_(op),
mOutput(std::make_shared<Tensor>(*op.mOutput))
{
// cpy-ctor
setDatatype(op.mOutput->dataType());
mImpl = op.mImpl ? Registrar<AvgPooling_Op<DIM>>::create(mOutput->getImpl()->backend())(*this) : nullptr;
}
/**
* @brief Clone the operator using its copy-constructor.
* @see Operator::AvgPooling_Op
*/
std::shared_ptr<Operator> clone() const override {
return std::make_shared<AvgPooling_Op<DIM>>(*this);
}
constexpr void associateInput(const IOIndex_t inputIdx, std::shared_ptr<Data> data) override final {
assert(inputIdx < 1 && "operators supports only 3 inputs");
(void) inputIdx; // avoid unused warning
......@@ -70,11 +92,11 @@ public:
if (!mInput->empty()) {
std::array<DimSize_t, DIM + 2> outputDims = {};
for (std::size_t dim = 0; dim < this->template get<AvgPoolingParam::KernelDims>().size() ; ++dim) {
for (std::size_t dim = 0; dim < this->template getAttr<AvgPoolingAttr::KernelDims>().size() ; ++dim) {
outputDims[dim+2] = 1 + static_cast<DimSize_t>(
std::floor(static_cast<float>(mInput->dims()[dim+2] -
this->template get<AvgPoolingParam::KernelDims>()[dim]) /
static_cast<float>(this->template get<AvgPoolingParam::StrideDims>()[dim])));
this->template getAttr<AvgPoolingAttr::KernelDims>()[dim]) /
static_cast<float>(this->template getAttr<AvgPoolingAttr::StrideDims>()[dim])));
}
outputDims[1] = mInput->dims()[1];
outputDims[0] = mInput->dims()[0];
......@@ -159,7 +181,7 @@ inline std::shared_ptr<Node> AvgPooling(
namespace {
template <>
const char *const EnumStrings<Aidge::AvgPoolingParam>::data[] = {"StrideDims",
const char *const EnumStrings<Aidge::AvgPoolingAttr>::data[] = {"StrideDims",
"KernelDims"};
}
......
......@@ -21,17 +21,17 @@
#include "aidge/graph/Node.hpp"
#include "aidge/operator/Operator.hpp"
#include "aidge/operator/Producer.hpp"
#include "aidge/utils/Parameter.hpp"
#include "aidge/utils/StaticAttributes.hpp"
#include "aidge/utils/Registrar.hpp"
namespace Aidge {
enum class BatchNormParam { Epsilon, Momentum };
enum class BatchNormAttr { Epsilon, Momentum };
template <DimIdx_t DIM>
class BatchNorm_Op : public Operator,
public Registrable<BatchNorm_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const BatchNorm_Op<DIM> &)>,
public Parameterizable<BatchNormParam, float, float> {
public StaticAttributes<BatchNormAttr, float, float> {
public:
// FIXME: change accessibility
std::array<std::shared_ptr<Tensor>, 5> mInputs = {std::make_shared<Tensor>(), std::make_shared<Tensor>(),
......@@ -44,18 +44,40 @@ public:
BatchNorm_Op() = delete;
using Parameterizable_ = Parameterizable<BatchNormParam, float, float>;
template <BatchNormParam e>
using param = typename Parameterizable_::template param<e>;
using Attributes_ = StaticAttributes<BatchNormAttr, float, float>;
template <BatchNormAttr e>
using attr = typename Attributes_::template attr<e>;
constexpr BatchNorm_Op(float epsilon, float momentum)
: Operator(Type),
Parameterizable_(param<BatchNormParam::Epsilon>(epsilon),
param<BatchNormParam::Momentum>(momentum)),
Attributes_(attr<BatchNormAttr::Epsilon>(epsilon),
attr<BatchNormAttr::Momentum>(momentum)),
mOutput(std::make_shared<Tensor>()) {
setDatatype(DataType::Float32);
}
/**
* @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its input tensors (the new operator has no input associated).
* @param op Operator to copy.
*/
BatchNorm_Op(const BatchNorm_Op<DIM>& op)
: Operator(Type),
Attributes_(op),
mOutput(std::make_shared<Tensor>(*op.mOutput))
{
// cpy-ctor
setDatatype(op.mOutput->dataType());
mImpl = op.mImpl ? Registrar<BatchNorm_Op<DIM>>::create(mOutput->getImpl()->backend())(*this) : nullptr;
}
/**
* @brief Clone the operator using its copy-constructor.
* @see Operator::BatchNorm_Op
*/
std::shared_ptr<Operator> clone() const override {
return std::make_shared<BatchNorm_Op<DIM>>(*this);
}
// Data operator[](const char* inputName) override final {
// std::shared_ptr<Tensor> in = (strcmp(inputName, "data")) ? mInputs[0] :
// (strcmp(inputName, "weight") ? mInputs[1] :
......@@ -76,7 +98,6 @@ public:
if (!mInputs[0]->empty()) {
for (std::size_t i = nbDataInputs(); i < nbInputs(); ++i) {
if(mInputs[i]->size() != mInputs[0]->dims()[1]) {
assert(!mInputs[0]->hasImpl() && "Incompatible size with already implemented learnable parameter");
mInputs[i]->resize(std::array<DimSize_t, 1>({mInputs[0]->dims()[1]}));
}
}
......@@ -157,7 +178,7 @@ inline std::shared_ptr<Node> BatchNorm(const float epsilon = 1.0e-5F,
namespace {
template <>
const char *const EnumStrings<Aidge::BatchNormParam>::data[] = { "Epsilon", "Momentum" };
const char *const EnumStrings<Aidge::BatchNormAttr>::data[] = { "Epsilon", "Momentum" };
}
#endif //AIDGE_CORE_OPERATOR_BATCHNORM_H_
\ No newline at end of file
#endif //AIDGE_CORE_OPERATOR_BATCHNORM_H_
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