Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
tic
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wenyuanbo
tic
Commits
db4be63c
Commit
db4be63c
authored
Jun 22, 2018
by
Tianqi Chen
Committed by
GitHub
Jun 22, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[TOPI] Numpy consistency: always broadcast binary op. (#1321)
parent
90db723d
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
564 additions
and
793 deletions
+564
-793
docs/Doxyfile
+1
-1
docs/api/python/topi.rst
+20
-17
nnvm/python/nnvm/top/nn.py
+3
-3
nnvm/python/nnvm/top/tensor.py
+2
-2
nnvm/src/top/tensor/broadcast.cc
+6
-6
nnvm/src/top/tensor/elemwise.cc
+4
-4
python/tvm/expr.py
+1
-1
python/tvm/generic.py
+16
-0
tests/python/unittest/test_lang_tensor_overload_op.py
+10
-11
topi/include/topi/broadcast.h
+170
-122
topi/include/topi/elemwise.h
+0
-49
topi/include/topi/nn/l2_normalize.h
+10
-10
topi/include/topi/nn/local_response_norm.h
+8
-7
topi/python/topi/broadcast.py
+162
-156
topi/python/topi/cpp.py
+0
-40
topi/python/topi/generic_op_impl.py
+9
-47
topi/python/topi/math.py
+6
-3
topi/python/topi/tensor.py
+0
-49
topi/src/topi.cc
+38
-53
topi/tests/python/test_topi_broadcast.py
+97
-53
topi/tests/python/test_topi_tensor.py
+0
-44
topi/tests/python_cpp/test_topi_broadcast.py
+0
-114
topi/tests/python_cpp/test_topi_transform.py
+1
-1
No files found.
docs/Doxyfile
View file @
db4be63c
...
...
@@ -1934,7 +1934,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION =
NO
MACRO_EXPANSION =
YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
...
...
docs/api/python/topi.rst
View file @
db4be63c
...
...
@@ -31,8 +31,6 @@ List of operators
topi.take
topi.full
topi.full_like
topi.greater
topi.less
topi.nn.relu
topi.nn.leaky_relu
topi.nn.dilate
...
...
@@ -49,12 +47,16 @@ List of operators
topi.sum
topi.min
topi.broadcast_to
topi.broadcast_add
topi.broadcast_sub
topi.broadcast_mul
topi.broadcast_div
topi.broadcast_maximum
topi.broadcast_minimum
topi.add
topi.subtract
topi.multiply
topi.divide
topi.mod
topi.maximum
topi.minimum
topi.power
topi.greater
topi.less
topi.image.resize
...
...
@@ -94,19 +96,20 @@ topi
.. autofunction:: topi.take
.. autofunction:: topi.full
.. autofunction:: topi.full_like
.. autofunction:: topi.greater
.. autofunction:: topi.less
.. autofunction:: topi.max
.. autofunction:: topi.sum
.. autofunction:: topi.min
.. autofunction:: topi.broadcast_to
.. autofunction:: topi.broadcast_add
.. autofunction:: topi.broadcast_sub
.. autofunction:: topi.broadcast_mul
.. autofunction:: topi.broadcast_div
.. autofunction:: topi.broadcast_maximum
.. autofunction:: topi.broadcast_minimum
.. autofunction:: topi.add
.. autofunction:: topi.subtract
.. autofunction:: topi.multiply
.. autofunction:: topi.divide
.. autofunction:: topi.mod
.. autofunction:: topi.maximum
.. autofunction:: topi.minimum
.. autofunction:: topi.power
.. autofunction:: topi.greater
.. autofunction:: topi.less
topi.nn
~~~~~~~
...
...
nnvm/python/nnvm/top/nn.py
View file @
db4be63c
...
...
@@ -105,7 +105,7 @@ def compute_conv2d(attrs, inputs, _):
bias
=
inputs
[
2
]
expand_axis
=
1
if
layout
==
"NCHW"
else
0
bias
=
topi
.
expand_dims
(
bias
,
axis
=
expand_axis
,
num_newaxis
=
2
)
out
=
topi
.
broadcast_
add
(
out
,
bias
)
out
=
topi
.
add
(
out
,
bias
)
return
out
@reg.register_schedule
(
"conv2d"
)
...
...
@@ -146,7 +146,7 @@ def compute_contrib_conv2d_NCHWc(attrs, inputs, _):
if
attrs
.
get_bool
(
"use_bias"
):
bias
=
inputs
[
2
]
bias
=
topi
.
expand_dims
(
bias
,
axis
=
1
,
num_newaxis
=
2
)
out
=
topi
.
broadcast_
add
(
out
,
bias
)
out
=
topi
.
add
(
out
,
bias
)
return
out
@reg.register_schedule
(
"_contrib_conv2d_NCHWc"
)
...
...
@@ -181,7 +181,7 @@ def compute_conv2d_transpose(attrs, inputs, _):
if
attrs
.
get_bool
(
"use_bias"
):
bias
=
inputs
[
2
]
bias
=
topi
.
expand_dims
(
bias
,
axis
=
1
,
num_newaxis
=
2
)
out
=
topi
.
broadcast_
add
(
out
,
bias
)
out
=
topi
.
add
(
out
,
bias
)
output_padding
=
attrs
.
get_int_tuple
(
"output_padding"
)
out
=
topi
.
nn
.
pad
(
out
,
\
[
0
,
0
,
0
,
0
],
[
0
,
0
,
output_padding
[
0
],
output_padding
[
1
]])
...
...
nnvm/python/nnvm/top/tensor.py
View file @
db4be63c
...
...
@@ -244,7 +244,7 @@ reg.register_schedule("ones_like", _fschedule_elemwise)
@reg.register_compute
(
"greater"
)
def
compute_greater
(
_
,
inputs
,
out_info
):
"""Compute definition of greater"""
return
topi
.
tensor
.
greater
(
inputs
[
0
],
inputs
[
1
],
'float32'
)
return
topi
.
greater
(
inputs
[
0
],
inputs
[
1
])
.
astype
(
'float32'
)
reg
.
register_pattern
(
"greater"
,
OpPattern
.
ELEMWISE
)
reg
.
register_schedule
(
"greater"
,
_fschedule_elemwise
)
...
...
@@ -252,7 +252,7 @@ reg.register_schedule("greater", _fschedule_elemwise)
@reg.register_compute
(
"less"
)
def
compute_less
(
_
,
inputs
,
out_info
):
"""Compute definition of less"""
return
topi
.
tensor
.
less
(
inputs
[
0
],
inputs
[
1
],
'float32'
)
return
topi
.
less
(
inputs
[
0
],
inputs
[
1
])
.
astype
(
'float32'
)
reg
.
register_pattern
(
"less"
,
OpPattern
.
ELEMWISE
)
reg
.
register_schedule
(
"less"
,
_fschedule_elemwise
)
...
...
nnvm/src/top/tensor/broadcast.cc
View file @
db4be63c
...
...
@@ -200,7 +200,7 @@ inline bool BinaryBroadcastCorrectLayout(const NodeAttrs& attrs,
return
true
;
}
#define NNVM_REGISTER_BINARY_BROADCAST_OP(name
)
\
#define NNVM_REGISTER_BINARY_BROADCAST_OP(name
, TOPIOp)
\
NNVM_REGISTER_OP(name) \
.set_num_inputs(2) \
.set_num_outputs(1) \
...
...
@@ -217,13 +217,13 @@ inline bool BinaryBroadcastCorrectLayout(const NodeAttrs& attrs,
const Array<Tensor>& inputs, \
const Array<Tensor>& out_info) { \
return Array<Tensor>{ \
topi::
name(inputs[0], inputs[1]) };
\
topi::
TOPIOp(inputs[0], inputs[1]) };
\
}) \
.add_argument("lhs", "Tensor", "first input") \
.add_argument("rhs", "Tensor", "second input")
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_add
)
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_add
,
add
)
.
add_alias
(
"__add_symbol__"
)
.
describe
(
R"code(Returns element-wise sum of the input arrays with broadcasting.
...
...
@@ -241,7 +241,7 @@ Example::
)code"
NNVM_ADD_FILELINE
);
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_sub
)
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_sub
,
subtract
)
.
add_alias
(
"__sub_symbol__"
)
.
describe
(
R"code(Returns element-wise difference of the input arrays with broadcasting.
...
...
@@ -259,7 +259,7 @@ Example::
)code"
NNVM_ADD_FILELINE
);
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_mul
)
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_mul
,
multiply
)
.
add_alias
(
"__mul_symbol__"
)
.
describe
(
R"code(Returns element-wise product of the input arrays with broadcasting.
...
...
@@ -276,7 +276,7 @@ Example::
)code"
NNVM_ADD_FILELINE
);
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_div
)
NNVM_REGISTER_BINARY_BROADCAST_OP
(
broadcast_div
,
divide
)
.
add_alias
(
"__div_symbol__"
)
.
describe
(
R"code(Returns element-wise division of the input arrays with broadcasting.
...
...
nnvm/src/top/tensor/elemwise.cc
View file @
db4be63c
...
...
@@ -230,7 +230,7 @@ NNVM_REGISTER_ELEMWISE_BINARY_OP(elemwise_add)
"FTVMCompute"
,
[](
const
NodeAttrs
&
attrs
,
const
Array
<
Tensor
>&
inputs
,
const
Array
<
Tensor
>&
out_info
)
{
return
Array
<
Tensor
>
{
topi
::
broadcast_
add
(
inputs
[
0
],
inputs
[
1
])
};
return
Array
<
Tensor
>
{
topi
::
add
(
inputs
[
0
],
inputs
[
1
])
};
})
.
set_attr
<
FGradient
>
(
"FGradient"
,
[](
const
NodePtr
&
n
,
...
...
@@ -253,7 +253,7 @@ NNVM_REGISTER_ELEMWISE_BINARY_OP(elemwise_sub)
"FTVMCompute"
,
[](
const
NodeAttrs
&
attrs
,
const
Array
<
Tensor
>&
inputs
,
const
Array
<
Tensor
>&
out_info
)
{
return
Array
<
Tensor
>
{
topi
::
broadcast_sub
(
inputs
[
0
],
inputs
[
1
])
};
return
Array
<
Tensor
>
{
topi
::
subtract
(
inputs
[
0
],
inputs
[
1
])
};
})
.
set_attr
<
FGradient
>
(
"FGradient"
,
[](
const
NodePtr
&
n
,
...
...
@@ -276,7 +276,7 @@ NNVM_REGISTER_ELEMWISE_BINARY_OP(elemwise_mul)
"FTVMCompute"
,
[](
const
NodeAttrs
&
attrs
,
const
Array
<
Tensor
>&
inputs
,
const
Array
<
Tensor
>&
out_info
)
{
return
Array
<
Tensor
>
{
topi
::
broadcast_mul
(
inputs
[
0
],
inputs
[
1
])
};
return
Array
<
Tensor
>
{
topi
::
multiply
(
inputs
[
0
],
inputs
[
1
])
};
})
.
set_attr
<
FGradient
>
(
"FGradient"
,
[](
const
NodePtr
&
n
,
...
...
@@ -301,7 +301,7 @@ NNVM_REGISTER_ELEMWISE_BINARY_OP(elemwise_div)
"FTVMCompute"
,
[](
const
NodeAttrs
&
attrs
,
const
Array
<
Tensor
>&
inputs
,
const
Array
<
Tensor
>&
out_info
)
{
return
Array
<
Tensor
>
{
topi
::
broadcast_div
(
inputs
[
0
],
inputs
[
1
])
};
return
Array
<
Tensor
>
{
topi
::
divide
(
inputs
[
0
],
inputs
[
1
])
};
})
.
set_attr
<
FGradient
>
(
"FGradient"
,
[](
const
NodePtr
&
n
,
...
...
python/tvm/expr.py
View file @
db4be63c
...
...
@@ -137,7 +137,7 @@ class ExprOp(object):
expr : Expr
Expression with new type
"""
return
_
make
.
static_cast
(
dtype
,
self
)
return
_
generic
.
cast
(
self
,
dtype
)
class
EqualOp
(
NodeGeneric
,
ExprOp
):
...
...
python/tvm/generic.py
View file @
db4be63c
...
...
@@ -79,3 +79,19 @@ def divide(lhs, rhs):
The result Expr of divide operaton.
"""
return
_make
.
Div
(
lhs
,
rhs
)
def
cast
(
src
,
dtype
):
"""Generic cast operator.
Parameters
----------
src : object
The source operand.
Returns
-------
op : tvm.Expr
The result Expr of divide operaton.
"""
return
_make
.
static_cast
(
dtype
,
src
)
tests/python/unittest/test_lang_tensor_overload_op.py
View file @
db4be63c
...
...
@@ -15,11 +15,11 @@ def test_operator_type_and_tags():
assert
isinstance
(
k
+
n
,
tvm
.
expr
.
Expr
)
assert
isinstance
(
n
+
n
,
tvm
.
expr
.
Expr
)
assert
isinstance
(
k
+
A
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
A
+
k
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
n
+
A
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
A
+
n
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
A
+
A
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
k
+
A
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
A
+
k
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
n
+
A
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
A
+
n
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
A
+
A
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
k
+
B
,
tvm
.
tensor
.
Tensor
)
assert
isinstance
(
B
+
k
,
tvm
.
tensor
.
Tensor
)
...
...
@@ -33,8 +33,8 @@ def test_operator_type_and_tags():
assert
(
B
+
k
)
.
op
.
tag
==
topi
.
tag
.
ELEMWISE
assert
(
n
+
B
)
.
op
.
tag
==
topi
.
tag
.
ELEMWISE
assert
(
B
+
n
)
.
op
.
tag
==
topi
.
tag
.
ELEMWISE
assert
(
A
+
B
)
.
op
.
tag
==
topi
.
tag
.
ELEMWISE
assert
(
B
+
A
)
.
op
.
tag
==
topi
.
tag
.
ELEMWISE
assert
(
A
+
B
)
.
op
.
tag
==
topi
.
tag
.
BROADCAST
assert
(
B
+
A
)
.
op
.
tag
==
topi
.
tag
.
BROADCAST
assert
(
B
+
B
)
.
op
.
tag
==
topi
.
tag
.
BROADCAST
assert
isinstance
(
k
+
B2
,
tvm
.
expr
.
Expr
)
...
...
@@ -42,8 +42,8 @@ def test_operator_type_and_tags():
assert
isinstance
(
n
+
B2
,
tvm
.
expr
.
Expr
)
assert
isinstance
(
B2
+
n
,
tvm
.
expr
.
Expr
)
assert
isinstance
(
B2
+
B2
,
tvm
.
expr
.
Expr
)
assert
isinstance
(
B2
+
A
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
A
+
B2
,
tvm
.
expr
.
Exp
r
)
assert
isinstance
(
B2
+
A
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
A
+
B2
,
tvm
.
tensor
.
Tenso
r
)
assert
isinstance
(
B2
+
B
,
tvm
.
tensor
.
Tensor
)
assert
isinstance
(
B
+
B2
,
tvm
.
tensor
.
Tensor
)
...
...
@@ -246,4 +246,4 @@ if __name__ == "__main__":
test_combination
()
test_tensor_scalar_bop
()
test_broadcast_bop
()
test_conv2d_scalar_bop
()
\ No newline at end of file
test_conv2d_scalar_bop
()
topi/include/topi/broadcast.h
View file @
db4be63c
...
...
@@ -7,7 +7,7 @@
#define TOPI_BROADCAST_H_
#include <string>
#include <algorithm>
#include "topi/detail/broadcast.h"
#include "topi/detail/constant_utils.h"
#include "topi/tags.h"
...
...
@@ -47,167 +47,215 @@ inline tvm::Tensor broadcast_to(const tvm::Tensor& t,
tag
);
}
#define TOPI_DEFINE_BCAST_OP(Name, ComputeRule) \
inline tvm::Expr Name(const tvm::Expr& a, \
const tvm::Expr& b) { \
ComputeRule; \
} \
inline tvm::Tensor Name(const tvm::Tensor& A, \
const tvm::Tensor& B, \
std::string name = "tensor", \
std::string tag = kBroadcast) { \
auto l = [](tvm::Expr a, tvm::Expr b) { ComputeRule; }; \
return detail::WithBroadcast(l, A, B, name, tag); \
} \
inline tvm::Tensor Name(const tvm::Tensor& A, \
const tvm::Expr& B, \
std::string name = "tensor", \
std::string tag = kElementWise) { \
auto l = [](tvm::Expr a, tvm::Expr b) { ComputeRule; }; \
return compute(A->shape, [&](const ::tvm::Array<::tvm::Var>& i) { \
return l(A(i), B); \
}, name, tag); \
} \
inline tvm::Tensor Name(const tvm::Expr& A, \
const tvm::Tensor& B, \
std::string name = "tensor", \
std::string tag = kElementWise) { \
auto l = [&](tvm::Expr a, tvm::Expr b) { ComputeRule; }; \
return compute(B->shape, [&](const ::tvm::Array<::tvm::Var>& i) { \
return l(A, B(i)); \
}, name, tag); \
}
#define TOPI_DEFINE_OP_OVERLOAD(Name, OpName) \
inline tvm::Tensor Name(const tvm::Tensor& A, \
const tvm::Tensor& B) { \
return topi::OpName(A, B); \
} \
inline tvm::Tensor Name(const tvm::Expr& A, \
const tvm::Tensor& B) { \
return topi::OpName(A, B); \
} \
inline tvm::Tensor Name(const tvm::Tensor& A, \
const tvm::Expr& B) { \
return topi::OpName(A, B); \
}
/*!
* \brief Creates an operation that performs pointwise addition of 2 tensors
* and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
* \fn add
* \brief Compute A + B with auto-broadcasting.
*
* \param A The first tensor
to add
* \param B The second tensor
to add
* \param A The first tensor
, or Expr
* \param B The second tensor
, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return
A Tensor whose op member is a pointwise addition with broadcast
* \return
The result.
*/
inline
tvm
::
Tensor
broadcast_add
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
a
+
b
;
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
TOPI_DEFINE_BCAST_OP
(
add
,
{
return
a
+
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
+
,
add
);
/*!
* \brief Creates an operation that performs pointwise subtraction of 2 tensors
* and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
* \fn subtract
* \brief Compute A - B with auto-broadcasting.
*
* \param A The first tensor
* \param B The second tensor
to subtract from the first
* \param A The first tensor
, or Expr
* \param B The second tensor
, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return
A Tensor whose op member is a pointwise subtraction with broadcast
* \return
The result.
*/
inline
tvm
::
Tensor
broadcast_sub
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
a
-
b
;
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
TOPI_DEFINE_BCAST_OP
(
subtract
,
{
return
a
-
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
-
,
subtract
);
/*!
* \brief Creates an operation that performs pointwise multiplication of 2
* tensors and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
* \fn multiply
* \brief Compute A * B with auto-broadcasting.
*
* \param A The first tensor
to multiply
* \param B The second tensor
to multiply
* \param A The first tensor
, or Expr
* \param B The second tensor
, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return
A Tensor whose op member is a pointwise multiplication with broadcast
* \return
The result.
*/
inline
tvm
::
Tensor
broadcast_mul
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
a
*
b
;
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
TOPI_DEFINE_BCAST_OP
(
multiply
,
{
return
a
*
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
*
,
multiply
);
/*!
* \brief Creates an operation that performs pointwise division of 2 tensors
* and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
* \fn divide
* \brief Compute A / B with auto-broadcasting.
*
* \param A The first tensor
* \param B The second tensor
to divide the first tensor with
* \param A The first tensor
, or Expr
* \param B The second tensor
, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return
A Tensor whose op member is a pointwise division with broadcast
* \return
The result.
*/
inline
tvm
::
Tensor
broadcast_div
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
a
/
b
;
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
TOPI_DEFINE_BCAST_OP
(
divide
,
{
return
a
/
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
/
,
divide
);
/*!
* \brief Creates an operation that performs pointwise modulo remainder of 2
* tensors and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
* \fn mod
* \brief Compute A % B with auto-broadcasting.
*
* \param A The first tensor
* \param B The second tensor
to compute A % B
* \param A The first tensor
, or Expr
* \param B The second tensor
, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return A Tensor whose op member is a pointwise modulo remainder with
* broadcast
* \return The result.
*/
inline
tvm
::
Tensor
broadcast_mod
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
a
%
b
;
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
TOPI_DEFINE_BCAST_OP
(
mod
,
{
return
a
%
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
%
,
mod
);
/*!
* \brief Creates an operation that performs pointwise maximum of 2 tensors
* and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
*
* \param A The first tensor
* \param B The second tensor
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return A Tensor whose op member is a pointwise maximum with broadcast
*/
inline
tvm
::
Tensor
broadcast_maximum
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
tvm
::
max
(
a
,
b
);
};
// NOLINT(*)
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
* \fn maximum
* \brief Compute maximum(A, B) with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
maximum
,
{
return
tvm
::
max
(
a
,
b
);
});
/*!
* \brief Creates an operation that performs pointwise minimum of 2 tensors
* and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
*
* \param A The first tensor
* \param B The second tensor
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return A Tensor whose op member is a pointwise minimum with broadcast
*/
inline
tvm
::
Tensor
broadcast_minimum
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
tvm
::
min
(
a
,
b
);
};
// NOLINT(*)
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
* \fn minimum
* \brief Compute minimum(A, B) with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
minimum
,
{
return
tvm
::
min
(
a
,
b
);
});
/*!
* \brief Creates an operation that raises one tensor to the power of another
* pointwise and broadcasts them into a common compatible shape where necessary,
* according to numpy's rules
*
* \param A The first tensor
* \param B The second tensor to compute pow(A, B)
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return A Tensor whose op member is a pointwise pow with
* broadcast
*/
inline
tvm
::
Tensor
broadcast_pow
(
const
tvm
::
Tensor
&
A
,
const
tvm
::
Tensor
&
B
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kBroadcast
)
{
auto
l
=
[
&
](
tvm
::
Expr
a
,
tvm
::
Expr
b
)
{
return
tvm
::
pow
(
a
,
b
);
};
return
detail
::
WithBroadcast
(
l
,
A
,
B
,
name
,
tag
);
}
* \fn power
* \brief Compute power(A, B) with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
power
,
{
return
tvm
::
pow
(
a
,
b
);
});
/*!
* \fn left_shift
* \brief Compute A << B with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
left_shift
,
{
return
a
<<
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
<<
,
left_shift
);
/*!
* \fn right_shift
* \brief Compute A >> B with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
right_shift
,
{
return
a
>>
b
;
});
TOPI_DEFINE_OP_OVERLOAD
(
operator
>>
,
right_shift
);
/*!
* \fn greater
* \brief Compute (A > B) with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
greater
,
{
return
(
a
>
b
);
});
/*!
* \fn less
* \brief Compute (A < B) with auto-broadcasting.
*
* \param A The first tensor, or Expr
* \param B The second tensor, or Expr
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return The result.
*/
TOPI_DEFINE_BCAST_OP
(
less
,
{
return
(
a
<
b
);
});
}
// namespace topi
...
...
topi/include/topi/elemwise.h
View file @
db4be63c
...
...
@@ -69,55 +69,6 @@ inline Tensor negative(const Tensor& x,
}
/*!
* \brief Creates an operation that raises each element of tensor x to power y
*
* \param x The input tensor
* \param y The exponent
* \param name The name of the operation
* \param tag The tag to mark the operation
*
* \return A Tensor whose op member is the pow operation
*/
inline
Tensor
pow
(
const
Tensor
&
x
,
const
Expr
&
y
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
kElementWise
)
{
return
compute
(
x
->
shape
,
[
&
](
const
Array
<
Var
>&
i
)
{
return
tvm
::
pow
(
x
(
i
),
y
);
},
name
,
tag
);
}
/*!
* \brief Creates an operation that performs pointwise left shift by n bits
*
* \param x The input tensor
* \param n The number of bits to shift by
*
* \return A Tensor whose op member is the left shift operation
*/
inline
Tensor
operator
<<
(
const
Tensor
&
x
,
const
Expr
&
n
)
{
return
compute
(
x
->
shape
,
[
&
](
const
Array
<
Var
>&
i
)
{
return
x
(
i
)
<<
n
;
},
"tensor"
,
kElementWise
);
}
/*!
* \brief Creates an operation that performs pointwise right shift by n bits
*
* \param x The input tensor
* \param n The number of bits to shift by
*
* \return A Tensor whose op member is the right shift operation
*/
inline
Tensor
operator
>>
(
const
Tensor
&
x
,
const
Expr
&
n
)
{
return
compute
(
x
->
shape
,
[
&
](
const
Array
<
Var
>&
i
)
{
return
x
(
i
)
>>
n
;
},
"tensor"
,
kElementWise
);
}
/*!
* \brief Creates an operation that clips each element of a tensor to
* the interval [a_min, a_max]
*
...
...
topi/include/topi/nn/l2_normalize.h
View file @
db4be63c
...
...
@@ -26,20 +26,20 @@ using namespace tvm;
* \return A Tensor whose op member is the l2 normalization operation
*/
inline
Tensor
l2_normalize
(
const
Tensor
&
data
,
float
eps
,
const
Array
<
Expr
>&
axis
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
"l2_normalize"
)
{
float
eps
,
const
Array
<
Expr
>&
axis
,
std
::
string
name
=
"tensor"
,
std
::
string
tag
=
"l2_normalize"
)
{
CHECK_EQ
(
data
->
shape
.
size
(),
4
)
<<
"L2 normalization requires 4-D input"
;
auto
input_shape
=
data
->
shape
;
Tensor
dot_value
=
pow
(
data
,
static_cast
<
float
>
(
2
.
0
));
Tensor
dot_value
=
topi
::
power
(
data
,
static_cast
<
float
>
(
2
.
0
));
Tensor
sum_value
=
topi
::
sum
(
dot_value
,
axis
,
true
);
Tensor
expand_sum
=
topi
::
broadcast_to
(
sum_value
,
input_shape
);
return
topi
::
broadcast_div
(
data
,
topi
::
sqrt
(
tvm
::
compute
(
expand_sum
->
shape
,
[
&
](
const
Array
<
Var
>&
i
){
return
(
max
(
expand_sum
(
i
),
eps
));
},
name
=
name
,
tag
=
tag
)));
return
topi
::
divide
(
data
,
topi
::
sqrt
(
tvm
::
compute
(
expand_sum
->
shape
,
[
&
](
const
Array
<
Var
>&
i
){
return
(
max
(
expand_sum
(
i
),
eps
));
},
name
=
name
,
tag
=
tag
)));
}
}
// namespace nn
}
// namespace topi
...
...
topi/include/topi/nn/local_response_norm.h
View file @
db4be63c
...
...
@@ -63,13 +63,14 @@ inline Tensor lrn(const Tensor& data,
{
rxs
});
});
}
auto
sqrt_sum_up
=
tvm
::
compute
(
input_shape
,
[
&
](
Var
i
,
Var
j
,
Var
k
,
Var
l
)
{
return
tvm
::
pow
(
bias
+
(
alpha
*
sqr_sum
(
i
,
j
,
k
,
l
)
/
size
),
beta
);
});
return
topi
::
broadcast_div
(
data
,
sqrt_sum_up
);
auto
sqrt_sum_up
=
tvm
::
compute
(
input_shape
,
[
&
](
Var
i
,
Var
j
,
Var
k
,
Var
l
)
{
return
tvm
::
pow
(
bias
+
(
alpha
*
sqr_sum
(
i
,
j
,
k
,
l
)
/
size
),
beta
);
});
return
topi
::
divide
(
data
,
sqrt_sum_up
);
}
}
// namespace nn
}
// namespace topi
...
...
topi/python/topi/broadcast.py
View file @
db4be63c
# pylint: disable=no-member,consider-using-enumerate
"""Broadcast operators"""
from
__future__
import
absolute_import
as
_abs
import
tvm
from
.
import
tag
from
.util
import
get_const_tuple
,
equal_const_int
,
get_const_int
from
.
import
cpp
as
_cpp
def
_get_bcast_info
(
original_shape
,
target_shape
):
"""Get the broadcasting info.
def
broadcast_to
(
data
,
shape
):
"""Broadcast the src to the target shape
We follows the numpy broadcasting rule.
See also https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
bcast_info = _get_bcast_info(original_shape, target_shape)
In bcast_info:
-1 means to the padding dim
0 means to to be the same as the original shape
1 means to the broadcasted dim
Parameters
----------
data : tvm.Tensor
The input data
shape : list or tuple
The target shape to be broadcasted.
Returns
-------
ret : tvm.Tensor
"""
return
_cpp
.
broadcast_to
(
data
,
shape
)
E.g
original: (2, 1, 5), target: (2, 4, 5) => bcast_info: (0, 1, 0)
original: (2, 5), target: (4, 2, 5) => bcast_info: (-1, 0, 0)
original: (1, 5), target: (4, 2, 5) => bcast_info: (-1, 1, 0)
def
add
(
lhs
,
rhs
):
"""Addition with auto-broadcasting
Parameters
----------
original_shape : tuple of tvm.expr.IntImm
The original shape before broadcasting
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
_cpp
.
add
(
lhs
,
rhs
)
def
subtract
(
lhs
,
rhs
):
"""Subtraction with auto-broadcasting
target_shape : tuple
The target shape
Parameters
----------
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
bcast_info : list
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
assert
len
(
target_shape
)
>=
len
(
original_shape
)
bcast_info
=
[
-
1
for
_
in
range
(
len
(
target_shape
))]
original_shape
=
[
original_shape
[
i
]
for
i
in
range
(
len
(
original_shape
))]
original_shape
=
original_shape
[::
-
1
]
target_shape
=
target_shape
[::
-
1
]
for
i
in
range
(
len
(
original_shape
)):
if
equal_const_int
(
original_shape
[
i
],
target_shape
[
i
]):
bcast_info
[
i
]
=
0
elif
equal_const_int
(
original_shape
[
i
],
1
):
bcast_info
[
i
]
=
1
else
:
raise
ValueError
(
"Original Shape: {} cannot be broadcast to {}"
.
format
(
original_shape
[::
-
1
],
target_shape
[::
-
1
]))
bcast_info
=
bcast_info
[::
-
1
]
return
bcast_info
def
_get_binary_op_bcast_shape
(
lhs_shape
,
rhs_shape
):
"""Get the shape after binary broadcasting.
We will strictly follow the broadcasting rule in numpy.
return
_cpp
.
subtract
(
lhs
,
rhs
)
def
multiply
(
lhs
,
rhs
):
"""Multiplication with auto-broadcasting
Parameters
----------
lhs_shape : tuple
rhs_shape : tuple
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret_shape : tuple
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
ret_shape
=
[]
if
len
(
lhs_shape
)
>
len
(
rhs_shape
):
lhs_shape
,
rhs_shape
=
rhs_shape
,
lhs_shape
for
ptr
in
range
(
len
(
rhs_shape
)):
if
ptr
<
len
(
lhs_shape
):
l_val
,
r_val
=
lhs_shape
[
len
(
lhs_shape
)
-
1
-
ptr
],
\
rhs_shape
[
len
(
rhs_shape
)
-
1
-
ptr
]
assert
(
l_val
==
1
or
r_val
==
1
or
l_val
==
r_val
),
\
"Shape is NOT broadcastable, lhs=
%
s, rhs=
%
s"
\
%
(
str
(
lhs_shape
),
str
(
rhs_shape
))
ret_shape
.
append
(
max
(
l_val
,
r_val
))
else
:
ret_shape
.
append
(
rhs_shape
[
len
(
rhs_shape
)
-
1
-
ptr
])
ret_shape
=
ret_shape
[::
-
1
]
return
ret_shape
@tvm.tag_scope
(
tag
=
tag
.
BROADCAST
)
def
broadcast_to
(
data
,
shape
):
"""Broadcast the src to the target shape
return
_cpp
.
multiply
(
lhs
,
rhs
)
We follows the numpy broadcasting rule.
See also https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
def
divide
(
lhs
,
rhs
):
"""Division with auto-broadcasting
Parameters
----------
data : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
shape : list or tuple
Returns
-------
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
_cpp
.
divide
(
lhs
,
rhs
)
def
mod
(
lhs
,
rhs
):
"""Modulus with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
def
_bcast_to_arg_eval
(
data
,
bcast_info
,
*
indices
):
indices_tuple
=
[]
for
i
,
ind
in
enumerate
(
indices
):
if
bcast_info
[
i
]
==
0
:
indices_tuple
.
append
(
ind
)
elif
bcast_info
[
i
]
==
1
:
indices_tuple
.
append
(
0
)
return
data
[
tuple
(
indices_tuple
)]
original_shape
=
data
.
shape
shape
=
[
get_const_int
(
i
)
for
i
in
shape
]
bcast_info
=
_get_bcast_info
(
original_shape
=
original_shape
,
target_shape
=
shape
)
ret
=
tvm
.
compute
(
shape
,
lambda
*
indices
:
_bcast_to_arg_eval
(
data
,
bcast_info
,
*
indices
),
name
=
data
.
name
+
"_broadcast"
)
return
ret
@tvm.tag_scope
(
tag
=
tag
.
BROADCAST
)
def
broadcast_binary_op
(
lhs
,
rhs
,
func
,
name
=
"bop"
):
"""Binary operands that will automatically broadcast the inputs
return
_cpp
.
mod
(
lhs
,
rhs
)
We follows the numpy broadcasting rule.
See also https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
def
maximum
(
lhs
,
rhs
):
"""Take element-wise maximum of two tensors with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
func : function
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
def
_inner_arg_eval
(
lhs
,
rhs
,
lhs_bcast_info
,
rhs_bcast_info
,
func
,
*
indices
):
lhs_indices
=
[]
rhs_indices
=
[]
for
i
,
ind
in
enumerate
(
indices
):
if
lhs_bcast_info
[
i
]
==
0
:
lhs_indices
.
append
(
ind
)
elif
lhs_bcast_info
[
i
]
==
1
:
lhs_indices
.
append
(
0
)
if
rhs_bcast_info
[
i
]
==
0
:
rhs_indices
.
append
(
ind
)
elif
rhs_bcast_info
[
i
]
==
1
:
rhs_indices
.
append
(
0
)
return
func
(
lhs
[
tuple
(
lhs_indices
)],
rhs
[
tuple
(
rhs_indices
)])
ret_shape
=
_get_binary_op_bcast_shape
(
get_const_tuple
(
lhs
.
shape
),
get_const_tuple
(
rhs
.
shape
))
lhs_bcast_info
=
_get_bcast_info
(
original_shape
=
lhs
.
shape
,
target_shape
=
ret_shape
)
rhs_bcast_info
=
_get_bcast_info
(
original_shape
=
rhs
.
shape
,
target_shape
=
ret_shape
)
ret
=
tvm
.
compute
(
ret_shape
,
lambda
*
indices
:
_inner_arg_eval
(
lhs
,
rhs
,
lhs_bcast_info
,
rhs_bcast_info
,
func
,
*
indices
),
name
=
lhs
.
name
+
"_"
+
rhs
.
name
+
"_"
+
name
)
return
ret
def
broadcast_add
(
lhs
,
rhs
):
"""Binary addition with auto-broadcasting
return
_cpp
.
maximum
(
lhs
,
rhs
)
def
minimum
(
lhs
,
rhs
):
"""Take element-wise maximum of two tensors with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
lambda
a
,
b
:
a
+
b
,
"add"
)
return
_cpp
.
minimum
(
lhs
,
rhs
)
def
broadcast_mul
(
lhs
,
rhs
):
"""
Binary multiplication
with auto-broadcasting
def
power
(
lhs
,
rhs
):
"""
Power
with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
lambda
a
,
b
:
a
*
b
,
"mul"
)
return
_cpp
.
power
(
lhs
,
rhs
)
def
broadcast_div
(
lhs
,
rhs
):
"""
Binary division
with auto-broadcasting
def
left_shift
(
lhs
,
rhs
):
"""
Left shift
with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
lambda
a
,
b
:
a
/
b
,
"div"
)
return
_cpp
.
left_shift
(
lhs
,
rhs
)
def
broadcast_sub
(
lhs
,
rhs
):
"""
Binary subtraction
with auto-broadcasting
def
right_shift
(
lhs
,
rhs
):
"""
Right shift
with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
lambda
a
,
b
:
a
-
b
,
"sub"
)
return
_cpp
.
left_shift
(
lhs
,
rhs
)
def
broadcast_maximum
(
lhs
,
rhs
):
"""
Take element-wise maximum of two tensors
with auto-broadcasting
def
greater
(
lhs
,
rhs
):
"""
Compute (lhs>rhs)
with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
tvm
.
max
,
"maximum"
)
return
_cpp
.
greater
(
lhs
,
rhs
)
def
broadcast_minimum
(
lhs
,
rhs
):
"""
Take element-wise minimum of two tensors
with auto-broadcasting
def
less
(
lhs
,
rhs
):
"""
Compute (lhs<rhs)
with auto-broadcasting
Parameters
----------
lhs : tvm.Tensor
rhs : tvm.Tensor
lhs : tvm.Tensor or Expr
The left operand
rhs : tvm.Tensor or Expr
The right operand
Returns
-------
ret : tvm.Tensor
ret : tvm.Tensor or Expr
Returns Expr if both operands are Expr.
Otherwise returns Tensor.
"""
return
broadcast_binary_op
(
lhs
,
rhs
,
tvm
.
min
,
"minimum"
)
return
_cpp
.
less
(
lhs
,
rhs
)
topi/python/topi/cpp.py
View file @
db4be63c
...
...
@@ -5,7 +5,6 @@ import ctypes
from
imp
import
new_module
as
_new_module
from
tvm._ffi.function
import
_init_api_prefix
from
tvm._ffi
import
libinfo
import
tvm
as
_tvm
def
_get_lib_names
():
if
sys
.
platform
.
startswith
(
'win32'
):
...
...
@@ -35,7 +34,6 @@ def _create_module(name):
return
mod
# pylint: disable-msg=C0103
nn
=
_create_module
(
"nn"
)
_init_api_prefix
(
"topi.cpp.nn"
,
"topi.nn"
)
generic
=
_create_module
(
"generic"
)
...
...
@@ -52,41 +50,3 @@ yolo2 = _create_module("vision.yolo2")
_init_api_prefix
(
"topi.cpp.vision.yolo2"
,
"topi.vision.yolo2"
)
image
=
_create_module
(
"image"
)
_init_api_prefix
(
"topi.cpp.image"
,
"topi.image"
)
class
IntVector
(
object
):
"""Handle to std::vector<int> instance """
_tvm_tcode
=
27
def
__init__
(
self
,
handle
):
self
.
handle
=
handle
def
__del__
(
self
):
_tvm
.
nd
.
free_extension_handle
(
self
.
handle
,
27
)
@property
def
_tvm_handle
(
self
):
return
self
.
handle
.
value
def
__getitem__
(
self
,
idx
):
return
ivec_get
(
self
,
idx
)
_tvm
.
register_extension
(
IntVector
,
IntVector
)
class
Target
(
object
):
"""Handle to C++ Target instance """
_tvm_tcode
=
28
def
__init__
(
self
,
handle
):
self
.
handle
=
handle
def
__del__
(
self
):
_tvm
.
nd
.
free_extension_handle
(
self
.
handle
,
28
)
@property
def
_tvm_handle
(
self
):
return
self
.
handle
.
value
def
__getitem__
(
self
,
idx
):
return
ivec_get
(
self
,
idx
)
_tvm
.
register_extension
(
Target
,
Target
)
topi/python/topi/generic_op_impl.py
View file @
db4be63c
...
...
@@ -3,10 +3,10 @@
from
__future__
import
absolute_import
as
_abs
import
tvm
from
.
import
broadcast
as
_broadcast
from
.
import
tag
from
.
import
math
as
_math
def
_make_bop
(
elementwise_bop
,
broadcast_bop
,
orig_bop
):
def
_make_bop
(
broadcast_bop
,
orig_bop
):
"""Make a specific overloaded binary operator of Tensor when applicable;
apply the original operator if it is not supposed to be overloaded.
...
...
@@ -23,9 +23,6 @@ def _make_bop(elementwise_bop, broadcast_bop, orig_bop):
Parameters
----------
elementwise_bop : operator function
Operator for element-wise tensor-scalar operation, for rule (2).
broadcast_bop : operator function
Operator for broadcast tensor-tensor operation, for rule (1).
...
...
@@ -66,36 +63,9 @@ def _make_bop(elementwise_bop, broadcast_bop, orig_bop):
tvm.Expr (otherwise)
The result of {op} operation.
"""
def
_get_rank
(
x
):
"""Get the rank of a value.
If x is Tensor, then return its rank;
if x is scalar_like (i.e., numeric types, Expr, or TensorSlice), return 0;
otherwise, return -1.
"""
if
isinstance
(
x
,
tvm
.
tensor
.
Tensor
):
return
len
(
x
.
shape
)
elif
isinstance
(
x
,
(
int
,
float
,
tvm
.
expr
.
Expr
,
tvm
.
tensor
.
TensorSlice
)):
return
0
return
-
1
rl
=
_get_rank
(
lhs
)
rr
=
_get_rank
(
rhs
)
if
rl
==
-
1
or
rr
==
-
1
or
(
rl
==
0
and
rr
==
0
):
if
not
isinstance
(
lhs
,
tvm
.
tensor
.
Tensor
)
and
not
isinstance
(
rhs
,
tvm
.
tensor
.
Tensor
):
return
orig_bop
(
lhs
,
rhs
)
elif
rl
>
0
and
rr
>
0
:
return
broadcast_bop
(
lhs
,
rhs
)
elif
rl
==
0
:
f
=
lambda
*
i
:
elementwise_bop
(
lhs
,
rhs
(
*
i
))
with
tvm
.
tag_scope
(
tag
=
tag
.
ELEMWISE
):
return
tvm
.
compute
(
rhs
.
shape
,
f
,
"tensor_"
+
name
)
elif
rr
==
0
:
f
=
lambda
*
i
:
elementwise_bop
(
lhs
(
*
i
),
rhs
)
with
tvm
.
tag_scope
(
tag
=
tag
.
ELEMWISE
):
return
tvm
.
compute
(
lhs
.
shape
,
f
,
"tensor_"
+
name
)
else
:
raise
AssertionError
(
"Cannot reach this line."
)
return
broadcast_bop
(
lhs
,
rhs
)
_tensor_bop_impl
.
__doc__
=
_tensor_bop_impl
.
__doc__
.
format
(
op
=
name
)
return
_tensor_bop_impl
...
...
@@ -106,18 +76,10 @@ def _bind_generic_ops():
__op_priority__
=
1
if
__op_priority__
>
tvm
.
generic
.
__op_priority__
:
tvm
.
generic
.
__op_priority__
=
__op_priority__
tvm
.
generic
.
add
=
_make_bop
(
lambda
x
,
y
:
x
+
y
,
_broadcast
.
broadcast_add
,
tvm
.
generic
.
add
)
tvm
.
generic
.
subtract
=
_make_bop
(
lambda
x
,
y
:
x
-
y
,
_broadcast
.
broadcast_sub
,
tvm
.
generic
.
subtract
)
tvm
.
generic
.
multiply
=
_make_bop
(
lambda
x
,
y
:
x
*
y
,
_broadcast
.
broadcast_mul
,
tvm
.
generic
.
multiply
)
tvm
.
generic
.
divide
=
_make_bop
(
lambda
x
,
y
:
x
/
y
,
_broadcast
.
broadcast_div
,
tvm
.
generic
.
divide
)
tvm
.
generic
.
add
=
_make_bop
(
_broadcast
.
add
,
tvm
.
generic
.
add
)
tvm
.
generic
.
subtract
=
_make_bop
(
_broadcast
.
subtract
,
tvm
.
generic
.
subtract
)
tvm
.
generic
.
multiply
=
_make_bop
(
_broadcast
.
multiply
,
tvm
.
generic
.
multiply
)
tvm
.
generic
.
divide
=
_make_bop
(
_broadcast
.
divide
,
tvm
.
generic
.
divide
)
tvm
.
generic
.
cast
=
_math
.
cast
_bind_generic_ops
()
topi/python/topi/math.py
View file @
db4be63c
...
...
@@ -258,14 +258,14 @@ def clip(x, a_min, a_max):
return
tvm
.
compute
(
x
.
shape
,
_compute
)
@tvm.tag_scope
(
tag
=
tag
.
ELEMWISE
)
def
cast
(
x
,
dtype
):
"""Cast input to specified data type.
Parameters
----------
x : tvm.Tensor
x : tvm.Tensor
or Expr
Input argument.
dtype : str
Data type.
...
...
@@ -274,4 +274,7 @@ def cast(x, dtype):
y : tvm.Tensor
The result.
"""
return
tvm
.
compute
(
x
.
shape
,
lambda
*
i
:
x
(
*
i
)
.
astype
(
dtype
))
if
isinstance
(
x
,
tvm
.
tensor
.
Tensor
):
return
tvm
.
compute
(
x
.
shape
,
lambda
*
i
:
x
(
*
i
)
.
astype
(
dtype
),
tag
=
tag
.
ELEMWISE
)
return
tvm
.
make
.
static_cast
(
dtype
,
x
)
topi/python/topi/tensor.py
View file @
db4be63c
...
...
@@ -68,52 +68,3 @@ def full_like(x, fill_value):
"""
dtype
=
x
.
dtype
return
tvm
.
compute
(
x
.
shape
,
lambda
*
i
:
tvm
.
const
(
fill_value
,
dtype
))
@tvm.tag_scope
(
tag
=
tag
.
ELEMWISE
)
def
greater
(
lhs
,
rhs
,
out_type
=
tvm
.
int8
):
"""Compare two input tensors element-wise and return an mask tensor
which contains 1 if lhs > rhs holds else 0
Parameters
----------
lhs : tvm.Tensor
Left input argument.
rhs : tvm.Tensor
Right argument.
out_type: str
Output data type. Default is int8
Returns
-------
y : tvm.Tensor
The result.
"""
return
tvm
.
compute
(
lhs
.
shape
,
lambda
*
i
:
tvm
.
select
(
lhs
(
*
i
)
>
rhs
(
*
i
),
tvm
.
const
(
1
,
out_type
),
tvm
.
const
(
0
,
out_type
)))
@tvm.tag_scope
(
tag
=
tag
.
ELEMWISE
)
def
less
(
lhs
,
rhs
,
out_type
=
tvm
.
int8
):
"""Compare two input tensors element-wise and return an mask tensor
which contains 1 if lhs < rhs holds else 0
Parameters
----------
lhs : tvm.Tensor
Left input argument.
rhs : tvm.Tensor
Right argument.
out_type: str
Output data type. Default is int8
Returns
-------
y : tvm.Tensor
The result.
"""
return
tvm
.
compute
(
lhs
.
shape
,
lambda
*
i
:
tvm
.
select
(
lhs
(
*
i
)
<
rhs
(
*
i
),
tvm
.
const
(
1
,
out_type
),
tvm
.
const
(
0
,
out_type
)))
topi/src/topi.cc
View file @
db4be63c
...
...
@@ -67,51 +67,55 @@ Array<Expr> ArrayOrInt(TVMArgValue arg) {
}
}
inline
bool
IsTensorType
(
TVMArgValue
arg
)
{
return
(
arg
.
type_code
()
==
kNodeHandle
&&
arg
.
node_sptr
()
->
is_type
<
tvm
::
TensorNode
>
());
}
TVM_REGISTER_GLOBAL
(
"topi.TEST_create_target"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
tvm
::
Target
::
create
(
args
[
0
]);
});
/* Ops from broadcast.h */
#define TOPI_REGISTER_BCAST_OP(OpName, Op) \
TVM_REGISTER_GLOBAL(OpName) \
.set_body([](TVMArgs args, TVMRetValue *rv) { \
bool lhs_is_tensor = IsTensorType(args[0]); \
bool rhs_is_tensor = IsTensorType(args[1]); \
if (lhs_is_tensor && rhs_is_tensor) { \
*rv = Op(args[0].operator tvm::Tensor(), \
args[1].operator tvm::Tensor()); \
} else if (!lhs_is_tensor && rhs_is_tensor) { \
*rv = Op(args[0].operator tvm::Expr(), \
args[1].operator tvm::Tensor()); \
} else if (lhs_is_tensor && !rhs_is_tensor) { \
*rv = Op(args[0].operator tvm::Tensor(), \
args[1].operator tvm::Expr()); \
} else if (!lhs_is_tensor && !rhs_is_tensor) { \
*rv = Op(args[0].operator tvm::Expr(), \
args[1].operator tvm::Expr()); \
} \
}); \
TVM_REGISTER_GLOBAL
(
"topi.broadcast_to"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_to
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_add"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_add
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_sub"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_sub
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_mul"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_mul
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_div"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_div
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_maximum"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_maximum
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_minimum"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_minimum
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.broadcast_pow"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
broadcast_pow
(
args
[
0
],
args
[
1
]);
});
TOPI_REGISTER_BCAST_OP
(
"topi.add"
,
topi
::
add
);
TOPI_REGISTER_BCAST_OP
(
"topi.subtract"
,
topi
::
subtract
);
TOPI_REGISTER_BCAST_OP
(
"topi.multiply"
,
topi
::
multiply
);
TOPI_REGISTER_BCAST_OP
(
"topi.divide"
,
topi
::
divide
);
TOPI_REGISTER_BCAST_OP
(
"topi.mod"
,
topi
::
mod
);
TOPI_REGISTER_BCAST_OP
(
"topi.maximum"
,
topi
::
maximum
);
TOPI_REGISTER_BCAST_OP
(
"topi.minimum"
,
topi
::
minimum
);
TOPI_REGISTER_BCAST_OP
(
"topi.power"
,
topi
::
power
);
TOPI_REGISTER_BCAST_OP
(
"topi.left_shift"
,
topi
::
left_shift
);
TOPI_REGISTER_BCAST_OP
(
"topi.right_shift"
,
topi
::
right_shift
);
TOPI_REGISTER_BCAST_OP
(
"topi.greater"
,
topi
::
greater
);
TOPI_REGISTER_BCAST_OP
(
"topi.less"
,
topi
::
less
);
/* Ops from elemwise.h */
TVM_REGISTER_GLOBAL
(
"topi.exp"
)
...
...
@@ -149,25 +153,6 @@ TVM_REGISTER_GLOBAL("topi.negative")
*
rv
=
negative
(
args
[
0
]);
});
TVM_REGISTER_GLOBAL
(
"topi.pow"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
pow
(
args
[
0
],
args
[
1
]);
});
TVM_REGISTER_GLOBAL
(
"topi.left_shift"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
Tensor
lhs
=
args
[
0
];
Expr
rhs
=
args
[
1
];
*
rv
=
lhs
>>
rhs
;
});
TVM_REGISTER_GLOBAL
(
"topi.right_shift"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
Tensor
lhs
=
args
[
0
];
Expr
rhs
=
args
[
1
];
*
rv
=
lhs
<<
rhs
;
});
TVM_REGISTER_GLOBAL
(
"topi.clip"
)
.
set_body
([](
TVMArgs
args
,
TVMRetValue
*
rv
)
{
*
rv
=
clip
(
args
[
0
],
args
[
1
],
args
[
2
]);
...
...
topi/tests/python/test_topi_broadcast.py
View file @
db4be63c
...
...
@@ -4,10 +4,10 @@ import numpy as np
import
tvm
import
topi
def
verify_broadcast_to_ele
(
in_shape
,
out_shape
):
def
verify_broadcast_to_ele
(
in_shape
,
out_shape
,
fbcast
):
# Build the logic and compile the function
A
=
tvm
.
placeholder
(
shape
=
in_shape
,
name
=
"A"
)
B
=
topi
.
broadcast_to
(
A
,
out_shape
)
B
=
fbcast
(
A
,
out_shape
)
def
check_device
(
device
):
ctx
=
tvm
.
context
(
device
,
0
)
if
not
ctx
.
exist
:
...
...
@@ -32,24 +32,20 @@ def verify_broadcast_to_ele(in_shape, out_shape):
check_device
(
"rocm"
)
def
verify_broadcast_binary_ele
(
lhs_shape
,
rhs_shape
,
typ
=
"add"
):
def
verify_broadcast_binary_ele
(
lhs_shape
,
rhs_shape
,
ftopi
,
fnumpy
,
lhs_min
=-
100
,
lhs_max
=
100
,
rhs_min
=-
100
,
rhs_max
=
100
,
dtype
=
"float32"
):
# Build the logic and compile the function
A
=
tvm
.
placeholder
(
shape
=
lhs_shape
,
name
=
"A"
)
B
=
tvm
.
placeholder
(
shape
=
rhs_shape
,
name
=
"B"
)
if
typ
==
"add"
:
C
=
topi
.
broadcast_add
(
A
,
B
)
elif
typ
==
"sub"
:
C
=
topi
.
broadcast_sub
(
A
,
B
)
elif
typ
==
"div"
:
C
=
topi
.
broadcast_div
(
A
,
B
)
elif
typ
==
"mul"
:
C
=
topi
.
broadcast_mul
(
A
,
B
)
elif
typ
==
"maximum"
:
C
=
topi
.
broadcast_maximum
(
A
,
B
)
elif
typ
==
"minimum"
:
C
=
topi
.
broadcast_minimum
(
A
,
B
)
else
:
raise
NotImplementedError
A
=
(
tvm
.
var
(
"A"
,
dtype
=
dtype
)
if
lhs_shape
is
None
else
tvm
.
placeholder
(
shape
=
lhs_shape
,
name
=
"A"
,
dtype
=
dtype
))
B
=
(
tvm
.
var
(
"B"
,
dtype
=
dtype
)
if
rhs_shape
is
None
else
tvm
.
placeholder
(
shape
=
rhs_shape
,
name
=
"B"
,
dtype
=
dtype
))
C
=
ftopi
(
A
,
B
)
if
(
isinstance
(
A
,
tvm
.
expr
.
Expr
)
and
isinstance
(
B
,
tvm
.
expr
.
Expr
)):
assert
(
isinstance
(
C
,
tvm
.
expr
.
Expr
))
return
def
check_device
(
device
):
ctx
=
tvm
.
context
(
device
,
0
)
if
not
ctx
.
exist
:
...
...
@@ -58,54 +54,102 @@ def verify_broadcast_binary_ele(lhs_shape, rhs_shape, typ="add"):
print
(
"Running on target:
%
s"
%
device
)
with
tvm
.
target
.
create
(
device
):
s
=
topi
.
generic
.
schedule_broadcast
(
C
)
foo
=
tvm
.
build
(
s
,
[
A
,
B
,
C
],
device
,
name
=
"broadcast_binary"
+
"_"
+
typ
)
lhs_npy
=
np
.
random
.
uniform
(
size
=
lhs_shape
)
.
astype
(
A
.
dtype
)
rhs_npy
=
np
.
random
.
uniform
(
size
=
rhs_shape
)
.
astype
(
A
.
dtype
)
if
typ
==
"add"
:
out_npy
=
lhs_npy
+
rhs_npy
elif
typ
==
"sub"
:
out_npy
=
lhs_npy
-
rhs_npy
elif
typ
==
"div"
:
rhs_npy
=
np
.
abs
(
rhs_npy
)
+
0.001
out_npy
=
lhs_npy
/
rhs_npy
elif
typ
==
"mul"
:
out_npy
=
lhs_npy
*
rhs_npy
elif
typ
==
"maximum"
:
out_npy
=
np
.
maximum
(
lhs_npy
,
rhs_npy
)
elif
typ
==
"minimum"
:
out_npy
=
np
.
minimum
(
lhs_npy
,
rhs_npy
)
foo
=
tvm
.
build
(
s
,
[
A
,
B
,
C
],
device
,
name
=
"broadcast_binary"
+
"_"
+
ftopi
.
__name__
)
if
lhs_shape
is
None
:
lhs_npy
=
float
(
np
.
random
.
uniform
(
low
=
lhs_min
,
high
=
lhs_max
))
if
dtype
.
startswith
(
'int'
):
lhs_npy
=
int
(
lhs_npy
)
lhs_nd
=
lhs_npy
else
:
raise
NotImplementedError
lhs_nd
=
tvm
.
nd
.
array
(
lhs_npy
,
ctx
)
rhs_nd
=
tvm
.
nd
.
array
(
rhs_npy
,
ctx
)
out_nd
=
tvm
.
nd
.
array
(
np
.
empty
(
out_npy
.
shape
)
.
astype
(
B
.
dtype
),
ctx
)
lhs_npy
=
np
.
random
.
uniform
(
low
=
lhs_min
,
high
=
lhs_max
,
size
=
lhs_shape
)
.
astype
(
A
.
dtype
)
lhs_nd
=
tvm
.
nd
.
array
(
lhs_npy
,
ctx
)
if
rhs_shape
is
None
:
rhs_npy
=
float
(
np
.
random
.
uniform
(
low
=
rhs_min
,
high
=
rhs_max
))
if
dtype
.
startswith
(
'int'
):
lhs_npy
=
int
(
lhs_npy
)
rhs_nd
=
rhs_npy
else
:
rhs_npy
=
np
.
random
.
uniform
(
low
=
rhs_min
,
high
=
rhs_max
,
size
=
rhs_shape
)
.
astype
(
A
.
dtype
)
rhs_nd
=
tvm
.
nd
.
array
(
rhs_npy
,
ctx
)
out_npy
=
fnumpy
(
lhs_npy
,
rhs_npy
)
out_nd
=
tvm
.
nd
.
array
(
np
.
empty
(
out_npy
.
shape
)
.
astype
(
C
.
dtype
),
ctx
)
for
_
in
range
(
1
):
foo
(
lhs_nd
,
rhs_nd
,
out_nd
)
np
.
testing
.
assert_allclose
(
out_nd
.
asnumpy
(),
out_npy
,
rtol
=
1E-4
,
atol
=
1E-4
)
check_device
(
"vulkan"
)
check_device
(
"opencl"
)
check_device
(
"vulkan"
)
check_device
(
"cuda"
)
check_device
(
"metal"
)
check_device
(
"rocm"
)
def
test_broadcast_to
():
verify_broadcast_to_ele
((
1
,),
(
10
,))
verify_broadcast_to_ele
((),
(
10
,))
verify_broadcast_to_ele
((
1
,
1
,
5
,
4
),
(
3
,
4
,
4
,
4
,
5
,
4
))
verify_broadcast_to_ele
((
1
,
128
,
1
,
32
),
(
64
,
128
,
64
,
32
))
verify_broadcast_to_ele
((
1
,),
(
10
,),
topi
.
broadcast_to
)
verify_broadcast_to_ele
((),
(
10
,),
topi
.
broadcast_to
)
verify_broadcast_to_ele
((
1
,
1
,
5
,
4
),
(
3
,
4
,
4
,
4
,
5
,
4
),
topi
.
broadcast_to
)
verify_broadcast_to_ele
((
1
,
128
,
1
,
32
),
(
64
,
128
,
64
,
32
),
topi
.
broadcast_to
)
def
test_add
():
verify_broadcast_binary_ele
(
(
5
,
2
,
3
),
(
2
,
1
),
topi
.
add
,
np
.
add
)
def
test_subtract
():
verify_broadcast_binary_ele
(
(
5
,
2
,
3
),
(),
topi
.
subtract
,
np
.
subtract
)
verify_broadcast_binary_ele
(
(
5
,
2
,
3
),
None
,
topi
.
subtract
,
np
.
subtract
)
verify_broadcast_binary_ele
(
None
,
None
,
topi
.
subtract
,
np
.
subtract
)
verify_broadcast_binary_ele
(
(
1
,
32
),
(
64
,
32
),
topi
.
subtract
,
np
.
subtract
)
def
test_multiply
():
verify_broadcast_binary_ele
(
(
5
,
64
,
128
),
(
2
,
5
,
64
,
1
),
topi
.
multiply
,
np
.
multiply
)
def
test_divide
():
verify_broadcast_binary_ele
(
None
,
(
10
,),
topi
.
divide
,
np
.
divide
,
rhs_min
=
0.0001
)
verify_broadcast_binary_ele
(
(
2
,
3
,
1
,
32
),
(
64
,
32
),
topi
.
divide
,
np
.
divide
,
rhs_min
=
0.0001
)
def
test_maximum_minmum
():
verify_broadcast_binary_ele
(
(
32
,),
(
64
,
32
),
topi
.
maximum
,
np
.
maximum
)
verify_broadcast_binary_ele
(
(
1
,
2
,
2
,
1
,
32
),
(
64
,
32
),
topi
.
minimum
,
np
.
minimum
)
def
test_power
():
verify_broadcast_binary_ele
(
(
1
,
2
,
2
),
(
2
,),
topi
.
power
,
np
.
power
,
lhs_min
=
0.001
,
rhs_min
=
0.001
,
rhs_max
=
2
)
def
test_mod
():
verify_broadcast_binary_ele
(
(
1
,
2
,
2
),
(
2
,),
topi
.
mod
,
np
.
mod
,
lhs_min
=
0.001
,
rhs_min
=
1
,
dtype
=
"int32"
)
def
test_broadcast_binary
():
verify_broadcast_binary_ele
((
5
,
2
,
3
),
(
2
,
1
),
typ
=
"add"
)
verify_broadcast_binary_ele
((
5
,
2
,
3
),
(),
typ
=
"add"
)
verify_broadcast_binary_ele
((
5
,
64
,
128
),
(
2
,
5
,
64
,
1
),
typ
=
"mul"
)
verify_broadcast_binary_ele
((
2
,
3
,
1
,
32
),
(
64
,
32
),
typ
=
"div"
)
verify_broadcast_binary_ele
((
1
,
32
),
(
64
,
32
),
typ
=
"sub"
)
verify_broadcast_binary_ele
((
32
,),
(
64
,
32
),
typ
=
"maximum"
)
verify_broadcast_binary_ele
((
1
,
2
,
2
,
1
,
32
),
(
64
,
32
),
typ
=
"minimum"
)
def
test_cmp
():
# explicit specify the output type
def
greater
(
x
,
y
):
return
topi
.
greater
(
x
,
y
)
.
astype
(
"int8"
)
def
less
(
x
,
y
):
return
topi
.
less
(
x
,
y
)
.
astype
(
"int8"
)
verify_broadcast_binary_ele
(
(
1
,
2
,
2
),
(
2
,),
greater
,
np
.
greater
)
verify_broadcast_binary_ele
(
(
2
,
1
,
2
),
(
2
,
3
,
1
),
less
,
np
.
less
)
if
__name__
==
"__main__"
:
test_broadcast_binary
()
test_cmp
()
test_mod
()
test_add
()
test_subtract
()
test_multiply
()
test_divide
()
test_maximum_minmum
()
test_power
()
test_broadcast_to
()
topi/tests/python/test_topi_tensor.py
View file @
db4be63c
...
...
@@ -69,44 +69,6 @@ def verify_full(shape, dtype, fill_value):
check_device
(
device
)
def
verify_comparator
(
shape
,
dtype
,
out_type
=
'int8'
):
A
=
tvm
.
placeholder
(
shape
,
dtype
,
name
=
"A"
)
B
=
tvm
.
placeholder
(
shape
,
dtype
,
name
=
"B"
)
C
=
topi
.
less
(
A
,
B
)
s_less
=
tvm
.
create_schedule
([
C
.
op
])
D
=
tvm
.
placeholder
(
shape
,
dtype
,
name
=
"D"
)
E
=
tvm
.
placeholder
(
shape
,
dtype
,
name
=
"E"
)
F
=
topi
.
greater
(
D
,
E
,
out_type
)
s_greater
=
tvm
.
create_schedule
([
F
.
op
])
@memoize
(
"topi.tests.test_topi_indicator"
)
def
get_ref_data
():
return
[
np
.
random
.
uniform
(
0
,
10
,
size
=
shape
)
.
astype
(
dtype
),
np
.
random
.
uniform
(
0
,
10
,
size
=
shape
)
.
astype
(
dtype
)]
[
np_l
,
np_r
]
=
get_ref_data
()
def
check_device
(
device
):
if
not
tvm
.
module
.
enabled
(
device
):
print
(
"Skip because
%
s is not enabled"
%
device
)
return
ctx
=
tvm
.
context
(
device
,
0
)
out
=
tvm
.
nd
.
array
(
np
.
zeros
(
shape
,
dtype
=
out_type
),
ctx
)
tvm_l
=
tvm
.
nd
.
array
(
np_l
,
ctx
)
tvm_r
=
tvm
.
nd
.
array
(
np_r
,
ctx
)
f
=
tvm
.
build
(
s_less
,
[
A
,
B
,
C
],
device
,
name
=
"less"
)
f
(
tvm_l
,
tvm_r
,
out
)
np
.
testing
.
assert_allclose
(
out
.
asnumpy
(),
np
.
less
(
np_l
,
np_r
)
.
astype
(
out_type
),
rtol
=
1e-5
)
f
=
tvm
.
build
(
s_greater
,
[
D
,
E
,
F
],
device
,
name
=
"greater"
)
f
(
tvm_l
,
tvm_r
,
out
)
np
.
testing
.
assert_allclose
(
out
.
asnumpy
(),
np
.
greater
(
np_l
,
np_r
)
.
astype
(
out_type
),
rtol
=
1e-5
)
for
device
in
[
"llvm"
]:
check_device
(
device
)
def
test_elemwise_sum
():
verify_elemwise_sum
(
1
,
"float32"
)
verify_elemwise_sum
(
5
,
"float32"
)
...
...
@@ -118,12 +80,6 @@ def test_full():
verify_full
((
10
,),
"int32"
,
7
)
def
test_comparator
():
verify_comparator
((
3
,
4
,
5
),
"float32"
)
verify_comparator
((
7
,),
"int32"
)
verify_comparator
((
3
,
4
,
5
),
"float32"
,
"int8"
)
if
__name__
==
"__main__"
:
test_elemwise_sum
()
test_full
()
test_comparator
()
topi/tests/python_cpp/test_topi_broadcast.py
deleted
100644 → 0
View file @
90db723d
"""Test code for broadcasting operators."""
import
os
import
numpy
as
np
import
tvm
import
topi
def
verify_broadcast_to_ele
(
in_shape
,
out_shape
):
# Build the logic and compile the function
A
=
tvm
.
placeholder
(
shape
=
in_shape
,
name
=
"A"
)
B
=
topi
.
cpp
.
broadcast_to
(
A
,
out_shape
)
def
check_device
(
device
):
if
not
tvm
.
module
.
enabled
(
device
):
print
(
"Skip because
%
s is not enabled"
%
device
)
return
print
(
"Running on target:
%
s"
%
device
)
target
=
topi
.
cpp
.
TEST_create_target
(
device
)
s
=
topi
.
cpp
.
cuda
.
schedule_injective
(
target
,
[
B
])
ctx
=
tvm
.
context
(
device
,
0
)
foo
=
tvm
.
build
(
s
,
[
A
,
B
],
device
,
name
=
"broadcast_to"
)
data_npy
=
np
.
random
.
uniform
(
size
=
in_shape
)
.
astype
(
A
.
dtype
)
out_npy
=
np
.
broadcast_to
(
data_npy
,
out_shape
)
data_nd
=
tvm
.
nd
.
array
(
data_npy
,
ctx
)
out_nd
=
tvm
.
nd
.
array
(
np
.
empty
(
out_shape
)
.
astype
(
B
.
dtype
),
ctx
)
for
_
in
range
(
1
):
foo
(
data_nd
,
out_nd
)
np
.
testing
.
assert_allclose
(
out_nd
.
asnumpy
(),
out_npy
)
check_device
(
"opencl"
)
check_device
(
"cuda"
)
#check_device("metal")
#check_device("rocm")
def
verify_broadcast_binary_ele
(
lhs_shape
,
rhs_shape
,
typ
=
"add"
):
# Build the logic and compile the function
A
=
tvm
.
placeholder
(
shape
=
lhs_shape
,
name
=
"A"
)
B
=
tvm
.
placeholder
(
shape
=
rhs_shape
,
name
=
"B"
)
if
typ
==
"add"
:
C
=
topi
.
cpp
.
broadcast_add
(
A
,
B
)
elif
typ
==
"sub"
:
C
=
topi
.
cpp
.
broadcast_sub
(
A
,
B
)
elif
typ
==
"div"
:
C
=
topi
.
cpp
.
broadcast_div
(
A
,
B
)
elif
typ
==
"mul"
:
C
=
topi
.
cpp
.
broadcast_mul
(
A
,
B
)
elif
typ
==
"maximum"
:
C
=
topi
.
cpp
.
broadcast_maximum
(
A
,
B
)
elif
typ
==
"minimum"
:
C
=
topi
.
cpp
.
broadcast_minimum
(
A
,
B
)
elif
typ
==
"pow"
:
C
=
topi
.
cpp
.
broadcast_pow
(
A
,
B
)
else
:
raise
NotImplementedError
def
check_device
(
device
):
if
not
tvm
.
module
.
enabled
(
device
):
print
(
"Skip because
%
s is not enabled"
%
device
)
return
print
(
"Running on target:
%
s"
%
device
)
target
=
topi
.
cpp
.
TEST_create_target
(
device
)
s
=
topi
.
cpp
.
cuda
.
schedule_injective
(
target
,
[
C
])
ctx
=
tvm
.
context
(
device
,
0
)
foo
=
tvm
.
build
(
s
,
[
A
,
B
,
C
],
device
,
name
=
"broadcast_binary"
+
"_"
+
typ
)
lhs_npy
=
np
.
random
.
uniform
(
size
=
lhs_shape
)
.
astype
(
A
.
dtype
)
rhs_npy
=
np
.
random
.
uniform
(
size
=
rhs_shape
)
.
astype
(
A
.
dtype
)
if
typ
==
"add"
:
out_npy
=
lhs_npy
+
rhs_npy
elif
typ
==
"sub"
:
out_npy
=
lhs_npy
-
rhs_npy
elif
typ
==
"div"
:
rhs_npy
=
np
.
abs
(
rhs_npy
)
+
0.001
out_npy
=
lhs_npy
/
rhs_npy
elif
typ
==
"mul"
:
out_npy
=
lhs_npy
*
rhs_npy
elif
typ
==
"maximum"
:
out_npy
=
np
.
maximum
(
lhs_npy
,
rhs_npy
)
elif
typ
==
"minimum"
:
out_npy
=
np
.
minimum
(
lhs_npy
,
rhs_npy
)
elif
typ
==
"pow"
:
out_npy
=
lhs_npy
**
rhs_npy
else
:
raise
NotImplementedError
lhs_nd
=
tvm
.
nd
.
array
(
lhs_npy
,
ctx
)
rhs_nd
=
tvm
.
nd
.
array
(
rhs_npy
,
ctx
)
out_nd
=
tvm
.
nd
.
array
(
np
.
empty
(
out_npy
.
shape
)
.
astype
(
B
.
dtype
),
ctx
)
for
_
in
range
(
1
):
foo
(
lhs_nd
,
rhs_nd
,
out_nd
)
np
.
testing
.
assert_allclose
(
out_nd
.
asnumpy
(),
out_npy
,
rtol
=
1E-4
,
atol
=
1E-4
)
check_device
(
"opencl"
)
check_device
(
"cuda"
)
#check_device("metal")
#check_device("rocm")
def
test_broadcast_to
():
verify_broadcast_to_ele
((
1
,),
(
10
,))
verify_broadcast_to_ele
((),
(
10
,))
verify_broadcast_to_ele
((
1
,
1
,
5
,
4
),
(
3
,
4
,
4
,
4
,
5
,
4
))
verify_broadcast_to_ele
((
1
,
128
,
1
,
32
),
(
64
,
128
,
64
,
32
))
def
test_broadcast_binary
():
verify_broadcast_binary_ele
((
5
,
2
,
3
),
(
2
,
1
),
typ
=
"add"
)
verify_broadcast_binary_ele
((
5
,
2
,
3
),
(),
typ
=
"add"
)
verify_broadcast_binary_ele
((
5
,
64
,
128
),
(
2
,
5
,
64
,
1
),
typ
=
"mul"
)
verify_broadcast_binary_ele
((
2
,
3
,
1
,
32
),
(
64
,
32
),
typ
=
"div"
)
verify_broadcast_binary_ele
((
1
,
32
),
(
64
,
32
),
typ
=
"sub"
)
verify_broadcast_binary_ele
((
32
,),
(
64
,
32
),
typ
=
"maximum"
)
verify_broadcast_binary_ele
((
1
,
2
,
2
,
1
,
32
),
(
64
,
32
),
typ
=
"minimum"
)
verify_broadcast_binary_ele
((
1
,
32
),
(
64
,
32
),
typ
=
"pow"
)
if
__name__
==
"__main__"
:
test_broadcast_to
()
test_broadcast_binary
()
topi/tests/python_cpp/test_topi_transform.py
View file @
db4be63c
...
...
@@ -243,7 +243,7 @@ def verify_concatenate_broadcast(shapes, axis, rhs_shape):
for
i
,
shape
in
enumerate
(
shapes
):
tensor_l
.
append
(
tvm
.
placeholder
(
shape
,
name
=
"A"
+
str
(
i
)))
out_tensor
=
topi
.
cpp
.
concatenate
(
tensor_l
,
axis
)
C
=
topi
.
cpp
.
broadcast_add
(
out_tensor
,
B
)
C
=
out_tensor
+
B
def
check_device
(
device
):
ctx
=
tvm
.
context
(
device
,
0
)
if
not
ctx
.
exist
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment