mirror of
https://github.com/Tencent/ncnn.git
synced 2026-10-22 16:55:13 +08:00
561 lines
13 KiB
C++
561 lines
13 KiB
C++
// Copyright 2025 Tencent
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "expression.h"
|
|
|
|
namespace ncnn {
|
|
|
|
int count_expression_blobs(const std::string& expr)
|
|
{
|
|
int count = 0;
|
|
|
|
std::string t;
|
|
for (size_t i = 0; i < expr.size(); i++)
|
|
{
|
|
char ch = expr[i];
|
|
|
|
if (ch == '(' || ch == ')' || ch == ',')
|
|
{
|
|
if (!t.empty())
|
|
{
|
|
if (t.size() == 2 && (t[0] >= '0' && t[0] <= '9') && (t[1] == 'w' || t[1] == 'h' || t[1] == 'd' || t[1] == 'c'))
|
|
{
|
|
int blob_index = t[0] - '0';
|
|
count = std::max(count, blob_index + 1);
|
|
}
|
|
|
|
t.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if NCNN_SIMPLESTL
|
|
t.resize(t.size() + 1);
|
|
t[t.size() - 1] = ch;
|
|
#else
|
|
t += ch;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!t.empty())
|
|
{
|
|
if (t.size() == 2 && (t[0] >= '0' && t[0] <= '9') && (t[1] == 'w' || t[1] == 'h' || t[1] == 'd' || t[1] == 'c'))
|
|
{
|
|
int blob_index = t[0] - '0';
|
|
count = std::max(count, blob_index + 1);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
struct typed_value
|
|
{
|
|
int type; // 0=i 1=f
|
|
union
|
|
{
|
|
int i;
|
|
float f;
|
|
};
|
|
|
|
typed_value()
|
|
: type(0), i(0)
|
|
{
|
|
}
|
|
typed_value(int _i)
|
|
: type(0), i(_i)
|
|
{
|
|
}
|
|
typed_value(float _f)
|
|
: type(1), f(_f)
|
|
{
|
|
}
|
|
|
|
int to_int()
|
|
{
|
|
if (type == 0)
|
|
return i;
|
|
|
|
// trunc by default
|
|
return (int)f;
|
|
}
|
|
};
|
|
|
|
int eval_list_expression(const std::string& expr, const std::vector<Mat>& blobs, std::vector<int>& outlist)
|
|
{
|
|
// /(0w,2),*(0h,2),0c
|
|
|
|
// split by , ( )
|
|
//
|
|
// /
|
|
// 0w
|
|
// 2
|
|
// -------------------
|
|
// *
|
|
// 0h
|
|
// 2
|
|
// -------------------
|
|
// 0c
|
|
// -------------------
|
|
|
|
// split by , ( )
|
|
|
|
// split into tokens
|
|
std::vector<std::string> tokens;
|
|
{
|
|
std::string t;
|
|
for (size_t i = 0; i < expr.size(); i++)
|
|
{
|
|
char ch = expr[i];
|
|
|
|
if (ch == '(' || ch == ')' || ch == ',')
|
|
{
|
|
if (!t.empty())
|
|
{
|
|
tokens.push_back(t);
|
|
t.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if NCNN_SIMPLESTL
|
|
t.resize(t.size() + 1);
|
|
t[t.size() - 1] = ch;
|
|
#else
|
|
t += ch;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!t.empty())
|
|
{
|
|
tokens.push_back(t);
|
|
}
|
|
}
|
|
|
|
// / 0w 2 * 0h 2 0c
|
|
|
|
// scan and stack
|
|
std::stack<typed_value> exprstack;
|
|
for (int i = (int)tokens.size() - 1; i >= 0; i--)
|
|
{
|
|
const std::string& t = tokens[i];
|
|
|
|
// NCNN_LOGE("t = %s", t.c_str());
|
|
|
|
// + - * / 0w 0h 0d 0c 12345
|
|
|
|
if (t.size() == 2 && (t[0] >= '0' && t[0] <= '9') && (t[1] == 'w' || t[1] == 'h' || t[1] == 'd' || t[1] == 'c'))
|
|
{
|
|
size_t blob_index = t[0] - '0';
|
|
if (blob_index >= blobs.size())
|
|
{
|
|
NCNN_LOGE("shape expression blob index %d out of bound!", (int)blob_index);
|
|
return -1;
|
|
}
|
|
|
|
const Mat& blob = blobs[blob_index].shape();
|
|
int size;
|
|
if (t[1] == 'w')
|
|
size = blob.w;
|
|
else if (t[1] == 'h')
|
|
size = blob.h;
|
|
else if (t[1] == 'd')
|
|
size = blob.d;
|
|
else // if (t[1] == 'c')
|
|
size = blob.c;
|
|
|
|
// NCNN_LOGE("t = %s => %d", t.c_str(), size);
|
|
|
|
exprstack.push(size);
|
|
}
|
|
else if (t == "+" || t == "-" || t == "*" || t == "//" || t == "max" || t == "min")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
typed_value tb = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
if (ta.type == 0 && tb.type == 0)
|
|
{
|
|
const int a = ta.i;
|
|
const int b = tb.i;
|
|
|
|
int r = 0;
|
|
if (t == "+")
|
|
{
|
|
r = a + b;
|
|
}
|
|
else if (t == "-")
|
|
{
|
|
r = a - b;
|
|
}
|
|
else if (t == "*")
|
|
{
|
|
r = a * b;
|
|
}
|
|
else if (t == "//")
|
|
{
|
|
if (b == 0)
|
|
{
|
|
NCNN_LOGE("expr divide by zero");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
r = a / b;
|
|
}
|
|
}
|
|
else if (t == "max")
|
|
{
|
|
r = std::max(a, b);
|
|
}
|
|
else // if (t == "min")
|
|
{
|
|
r = std::min(a, b);
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
else
|
|
{
|
|
const float a = ta.type == 0 ? ta.i : ta.f;
|
|
const float b = tb.type == 0 ? tb.i : tb.f;
|
|
|
|
float r = 0.f;
|
|
if (t == "+")
|
|
{
|
|
r = a + b;
|
|
}
|
|
else if (t == "-")
|
|
{
|
|
r = a - b;
|
|
}
|
|
else if (t == "*")
|
|
{
|
|
r = a * b;
|
|
}
|
|
else if (t == "//")
|
|
{
|
|
r = floorf(a / b);
|
|
}
|
|
else if (t == "max")
|
|
{
|
|
r = std::max(a, b);
|
|
}
|
|
else // if (t == "min")
|
|
{
|
|
r = std::min(a, b);
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
}
|
|
else if (t == "abs" || t == "neg" || t == "sign" || t == "square")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
if (ta.type == 0)
|
|
{
|
|
const int a = ta.i;
|
|
|
|
int r = 0;
|
|
if (t == "abs")
|
|
{
|
|
r = a > 0 ? a : -a;
|
|
}
|
|
else if (t == "neg")
|
|
{
|
|
r = -a;
|
|
}
|
|
else if (t == "sign")
|
|
{
|
|
r = a > 0 ? 1 : (a == 0 ? 0 : -1);
|
|
}
|
|
else // if (t == "square")
|
|
{
|
|
r = a * a;
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
else
|
|
{
|
|
const float a = ta.f;
|
|
|
|
float r = 0;
|
|
if (t == "abs")
|
|
{
|
|
r = fabsf(a);
|
|
}
|
|
else if (t == "neg")
|
|
{
|
|
r = -a;
|
|
}
|
|
else if (t == "sign")
|
|
{
|
|
r = a > 0.f ? 1 : (a == 0.f ? 0 : -1);
|
|
}
|
|
else // if (t == "square")
|
|
{
|
|
r = a * a;
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
}
|
|
else if (t == "trunc" || t == "ceil" || t == "floor" || t == "round")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
if (ta.type == 0)
|
|
{
|
|
const int a = ta.i;
|
|
exprstack.push(a);
|
|
}
|
|
else
|
|
{
|
|
const float a = ta.f;
|
|
|
|
int r = 0;
|
|
if (t == "trunc")
|
|
{
|
|
r = (int)a;
|
|
}
|
|
else if (t == "ceil")
|
|
{
|
|
r = (int)ceil(a);
|
|
}
|
|
else if (t == "floor")
|
|
{
|
|
r = (int)floor(a);
|
|
}
|
|
else // if (t == "round")
|
|
{
|
|
r = (int)round(a);
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
}
|
|
else if (t == "acos"
|
|
|| t == "acosh"
|
|
|| t == "asin"
|
|
|| t == "asinh"
|
|
|| t == "atan"
|
|
|| t == "atanh"
|
|
|| t == "cos"
|
|
|| t == "cosh"
|
|
|| t == "erf"
|
|
|| t == "exp"
|
|
|| t == "log"
|
|
|| t == "log10"
|
|
|| t == "reciprocal"
|
|
|| t == "rsqrt"
|
|
|| t == "sin"
|
|
|| t == "sinh"
|
|
|| t == "sqrt"
|
|
|| t == "tan"
|
|
|| t == "tanh")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
const float a = ta.type == 0 ? ta.i : ta.f;
|
|
|
|
float r = 0;
|
|
if (t == "acos")
|
|
{
|
|
r = acosf(a);
|
|
}
|
|
else if (t == "acosh")
|
|
{
|
|
r = acoshf(a);
|
|
}
|
|
else if (t == "asin")
|
|
{
|
|
r = asinf(a);
|
|
}
|
|
else if (t == "asinh")
|
|
{
|
|
r = asinhf(a);
|
|
}
|
|
else if (t == "atan")
|
|
{
|
|
r = atanf(a);
|
|
}
|
|
else if (t == "atanh")
|
|
{
|
|
r = atanhf(a);
|
|
}
|
|
else if (t == "cos")
|
|
{
|
|
r = cosf(a);
|
|
}
|
|
else if (t == "cosh")
|
|
{
|
|
r = coshf(a);
|
|
}
|
|
else if (t == "erf")
|
|
{
|
|
r = erff(a);
|
|
}
|
|
else if (t == "exp")
|
|
{
|
|
r = expf(a);
|
|
}
|
|
else if (t == "log")
|
|
{
|
|
r = logf(a);
|
|
}
|
|
else if (t == "log10")
|
|
{
|
|
r = log10f(a);
|
|
}
|
|
else if (t == "reciprocal")
|
|
{
|
|
r = 1.f / a;
|
|
}
|
|
else if (t == "rsqrt")
|
|
{
|
|
r = 1.f / sqrtf(a);
|
|
}
|
|
else if (t == "sin")
|
|
{
|
|
r = sinf(a);
|
|
}
|
|
else if (t == "sinh")
|
|
{
|
|
r = sinhf(a);
|
|
}
|
|
else if (t == "sqrt")
|
|
{
|
|
r = sqrtf(a);
|
|
}
|
|
else if (t == "tan")
|
|
{
|
|
r = tanf(a);
|
|
}
|
|
else // if (t == "tanh")
|
|
{
|
|
r = tanhf(a);
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
else if (t == "/"
|
|
|| t == "atan2"
|
|
|| t == "fmod"
|
|
|| t == "pow"
|
|
|| t == "remainder"
|
|
|| t == "logaddexp")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
typed_value tb = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
const float a = ta.type == 0 ? ta.i : ta.f;
|
|
const float b = tb.type == 0 ? tb.i : tb.f;
|
|
|
|
float r = 0.f;
|
|
if (t == "/")
|
|
{
|
|
r = a / b;
|
|
}
|
|
else if (t == "atan2")
|
|
{
|
|
r = atan2f(a, b);
|
|
}
|
|
else if (t == "fmod")
|
|
{
|
|
r = fmodf(a, b);
|
|
}
|
|
else if (t == "pow")
|
|
{
|
|
r = powf(a, b);
|
|
}
|
|
else if (t == "remainder")
|
|
{
|
|
r = fmodf(a, b);
|
|
if (a * b < 0)
|
|
r += b;
|
|
}
|
|
else // if (t == "logaddexp")
|
|
{
|
|
r = logf(expf(a) + expf(b));
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
else if (t == "and" || t == "or" || t == "xor" || t == "lshift" || t == "rshift")
|
|
{
|
|
typed_value ta = exprstack.top();
|
|
exprstack.pop();
|
|
typed_value tb = exprstack.top();
|
|
exprstack.pop();
|
|
|
|
// assert ta.type == 0 && tb.type == 0
|
|
|
|
const int a = ta.i;
|
|
const int b = tb.i;
|
|
|
|
int r = 0;
|
|
if (t == "and")
|
|
{
|
|
r = a & b;
|
|
}
|
|
else if (t == "or")
|
|
{
|
|
r = a | b;
|
|
}
|
|
else if (t == "xor")
|
|
{
|
|
r = a ^ b;
|
|
}
|
|
else if (t == "lshift")
|
|
{
|
|
r = a << b;
|
|
}
|
|
else // if (t == "rshift")
|
|
{
|
|
r = a >> b;
|
|
}
|
|
exprstack.push(r);
|
|
}
|
|
else
|
|
{
|
|
// literal
|
|
int vi;
|
|
float vf;
|
|
int nscani = sscanf(t.c_str(), "%d", &vi);
|
|
int nscanf = sscanf(t.c_str(), "%f", &vf);
|
|
if (nscani == 1 && nscanf == 1 && vi == vf)
|
|
{
|
|
exprstack.push(vi);
|
|
}
|
|
else if (nscanf == 1)
|
|
{
|
|
exprstack.push(vf);
|
|
}
|
|
else
|
|
{
|
|
NCNN_LOGE("malformed literal token %s", t.c_str());
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int size = exprstack.top().to_int();
|
|
exprstack.pop();
|
|
outlist.push_back(size);
|
|
while (!exprstack.empty())
|
|
{
|
|
size = exprstack.top().to_int();
|
|
exprstack.pop();
|
|
outlist.push_back(size);
|
|
}
|
|
|
|
// NCNN_LOGE("shape %s = %d %d", expr.c_str(), list[0], list[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace ncnn
|