aboutsummaryrefslogtreecommitdiff
path: root/src/armnn/optimizations/MaxMinIntoBoundedRelu.hpp
blob: 99089f3a3e1223cf421ecc1f12595e2a4984dcf0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//
// Copyright © 2024 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//

#pragma once

#include "Optimization.hpp"

namespace armnn::optimizations
{

class MaxMinIntoBoundedReluImpl
{
public:
    /// Run for every exclusive connection between any Max & Min layers
    /// The Max, Min and its associated constant inputs will be removed, and replaced with a BoundedRelu Activation
    static void Run(Graph& graph, InputSlot& connection)
    {
        Layer& base = connection.GetConnectedOutputSlot()->GetOwningLayer();
        Layer& child = connection.GetOwningLayer();

        auto& maxLayer = *PolymorphicDowncast<ElementwiseBinaryLayer*>(&base);
        if (maxLayer.GetParameters().m_Operation != BinaryOperation::Maximum)
        {
            return;
        }
        auto& minLayer = *PolymorphicDowncast<ElementwiseBinaryLayer*>(&child);
        if (minLayer.GetParameters().m_Operation != BinaryOperation::Minimum)
        {
            return;
        }

        if (maxLayer.GetDataType() != minLayer.GetDataType())
        {
            return;
        }

        // get max and min values
        float_t maxValue;
        if (!GetValue(maxLayer, maxValue))
        {
            return;
        }
        float_t minValue;
        if (!GetValue(minLayer, minValue))
        {
            return;
        }

        // Save original parent output slot of the max layer
        OutputSlot& parentOut = *maxLayer.GetInputSlot(0).GetConnectedOutputSlot();

        // Insert activation layer between max layer and its parent layer
        ActivationDescriptor boundedReluDescriptor(ActivationFunction::BoundedReLu, minValue, maxValue);
        const std::string name = std::string("replaced-") + maxLayer.GetName() + std::string("-") + minLayer.GetName()
                               + std::string("-with-BoundedRelu");
        auto& boundedReluLayer = *graph.InsertNewLayer<ActivationLayer>(maxLayer.GetInputSlot(0),
                                                                        boundedReluDescriptor,
                                                                        name.c_str());

        // Reconnects with original parent.
        boundedReluLayer.GetOutputSlot().MoveAllConnections(parentOut);

        // Moves connections in min layer output to parent layer.
        // Min layer will be removed as it's left unconnected.
        // Max layer will be removed if left unconnected.
        minLayer.GetOutputSlot().MoveAllConnections(boundedReluLayer.GetOutputSlot());
    }

protected:
    MaxMinIntoBoundedReluImpl()  = default;
    ~MaxMinIntoBoundedReluImpl() = default;

private:
    static float_t GetConstTensorValue(Layer& layer)
    {
        auto& constLayer = *PolymorphicDowncast<ConstantLayer*>(&layer);
        switch (constLayer.GetDataType())
        {
            case DataType::Float32:
                return *constLayer.m_LayerOutput->GetConstTensor<float>();
            case DataType::BFloat16:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<BFloat16>());
            case DataType::Float16:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<half_float::half>());
            case DataType::QAsymmU8:
            case DataType::Boolean:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<uint8_t>());
            case DataType::QAsymmS8:
            case DataType::QSymmS8:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<int8_t>());
            case DataType::QSymmS16:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<int16_t>());
            case DataType::Signed32:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<int32_t>());
            case DataType::Signed64:
                return static_cast<float_t>(*constLayer.m_LayerOutput->GetConstTensor<int64_t>());
            default:
                throw InvalidArgumentException("No supported Data Type");
        }
    }

    static bool GetValue(Layer& layer, float_t& value)
    {
        Layer& input0 = layer.GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
        Layer& input1 = layer.GetInputSlot(1).GetConnectedOutputSlot()->GetOwningLayer();
        if (input0.GetType() == LayerType::Constant)
        {
            if (input0.GetOutputSlot(0).GetTensorInfo().GetNumElements() != 1)
            {
                return false;
            }
            value = GetConstTensorValue(input0);
        }
        else if (input1.GetType() == LayerType::Constant)
        {
            if (input1.GetOutputSlot(0).GetTensorInfo().GetNumElements() != 1)
            {
                return false;
            }
            value = GetConstTensorValue(input1);
        }
        else
        {
            return false;
        }
        return true;
    };
};

using MaxMinIntoBoundedRelu = OptimizeForExclusiveConnection<ElementwiseBinaryLayer,
                                                             ElementwiseBinaryLayer,
                                                             MaxMinIntoBoundedReluImpl>;

} // namespace armnn::optimizations