aboutsummaryrefslogtreecommitdiff
path: root/python/pyarmnn/src/pyarmnn/_tensor/const_tensor.py
blob: 9735d7a63b5cb5d02e6894ee36e88c1ac9b1bed0 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# Copyright © 2019 Arm Ltd. All rights reserved.
# SPDX-License-Identifier: MIT
import numpy as np

from .._generated.pyarmnn import DataType_QuantisedAsymm8, DataType_QuantisedSymm16, DataType_Signed32, \
    DataType_Float32, DataType_Float16
from .._generated.pyarmnn import ConstTensor as AnnConstTensor, TensorInfo, Tensor


class ConstTensor(AnnConstTensor):
    """Creates a PyArmNN ConstTensor object.

    A ConstTensor is a Tensor with an immutable data store. Typically, a ConstTensor
    is used to input data into a network when running inference.

    This class overrides the swig generated Tensor class. The aim of
    this is to have an easy to use public API for the ConstTensor objects.

    """

    def __init__(self, *args):
        """
        Supported tensor data types:
            DataType_QuantisedAsymm8,
            DataType_QuantisedSymm16,
            DataType_Signed32,
            DataType_Float32,
            DataType_Float16

        Examples:
            Create empty ConstTensor
            >>> import pyarmnn as ann
            >>> ann.ConstTensor()

            Create ConstTensor given tensor info and input data
            >>> input_data = ... #  numpy array
            >>> ann.ConstTensor(ann.TensorInfo(...), input_data)

            Create ConstTensor from another ConstTensor i.e. copy ConstTensor
            >>> ann.ConstTensor(ann.ConstTensor())

            Create ConstTensor from tensor
            >>> ann.ConstTensor(ann.Tensor())

        Args:
            tensor (Tensor, optional): Create a ConstTensor from a Tensor.
            const_tensor (ConstTensor, optional): Create a ConstTensor from a ConstTensor i.e. copy.
            tensor_info (TensorInfo, optional): Tensor information.
            input_data (ndarray):   Numpy array. The numpy array will be transformed to a
                                    buffer according to type returned by `TensorInfo.GetDataType`.
                                    Input data values type must correspond to data type returned by
                                    `TensorInfo.GetDataType`.

        Raises:
            TypeError: Unsupported input data type.
            ValueError: Unsupported tensor data type and incorrect input data size.
        """
        self.__memory_area = None

        # TensorInfo as first argument and numpy array as second
        if len(args) > 1 and isinstance(args[0], TensorInfo):
            if isinstance(args[1], np.ndarray):
                self.__create_memory_area(args[0].GetDataType(), args[0].GetNumBytes(), args[0].GetNumElements(),
                                          args[1])
                super().__init__(args[0], self.__memory_area.data)
            else:
                raise TypeError('Data must be provided as a numpy array.')

        # copy constructor - reference to memory area is passed from copied const
        # tensor and armnn's copy constructor is called
        elif len(args) > 0 and isinstance(args[0], (ConstTensor, Tensor)):
            self.__memory_area = args[0].get_memory_area()
            super().__init__(args[0])

        # empty tensor
        elif len(args) == 0:
            super().__init__()

        else:
            raise ValueError('Incorrect number of arguments or type of arguments provided to create Const Tensor.')

    def __copy__(self) -> 'ConstTensor':
        """ Make copy of a const tensor.

        Make const tensor copyable using the python copy operation.

        Note:
            The tensor memory area is NOT copied. Instead, the new tensor maintains a
            reference to the same memory area as the old tensor.

        Example:
            Copy empty tensor
            >>> from copy import copy
            >>> import pyarmnn as ann
            >>> tensor = ann.ConstTensor()
            >>> copied_tensor = copy(tensor)

        Returns:
            Tensor: a copy of the tensor object provided.

        """
        return ConstTensor(self)

    @staticmethod
    def __check_size(data: np.ndarray, num_bytes: int, num_elements: int):
        """ Check the size of the input data against the number of bytes provided by tensor info.

        Args:
            data (ndarray): Input data.
            num_bytes (int): Number of bytes required by tensor info.
            num_elements: Number of elements required by tensor info.

        Raises:
            ValueError: number of bytes in input data does not match tensor info.

        """
        size_in_bytes = data.nbytes
        elements = data.size

        if size_in_bytes != num_bytes:
            raise ValueError(
                "ConstTensor requires {} bytes, {} provided. "
                "Is your input array data type ({}) aligned with TensorInfo?".format(num_bytes, size_in_bytes,
                                                                                     data.dtype))
        elif elements != num_elements:
            raise ValueError("ConstTensor requires {} elements, {} provided.".format(num_elements, elements))

    def __create_memory_area(self, data_type: int, num_bytes: int, num_elements: int, data: np.ndarray):
        """ Create the memory area used by the tensor to output its results.

        Args:
            data_type (int): The type of data that will be stored in the memory area.
                             See DataType_*.
            num_bytes (int): Determines the size of the memory area that will be created.
            num_elements (int): Determines number of elements in memory area.
            data (ndarray): Input data as numpy array.

        """
        np_data_type_mapping = {DataType_QuantisedAsymm8: np.uint8,
                                DataType_Float32: np.float32,
                                DataType_QuantisedSymm16: np.int16,
                                DataType_Signed32: np.int32,
                                DataType_Float16: np.float16}

        if data_type not in np_data_type_mapping:
            raise ValueError("The data type provided for this Tensor is not supported: {}".format(data_type))

        self.__check_size(data, num_bytes, num_elements)
        self.__memory_area = data
        self.__memory_area.flags.writeable = False

    def get_memory_area(self) -> np.ndarray:
        """ Get values that are stored by the tensor.

        Returns:
             ndarray: Tensor data (as numpy array).

        """
        return self.__memory_area