Unverified Commit e939ddcd authored by Shucai Xiao's avatar Shucai Xiao Committed by GitHub
Browse files

Op constructor c/python api (#636)



* code backup

* clang format

* add the python api for op construction

* clang format

* add converting a json string to a value and then pass to make_op

* fix a build error

* support kwargs decoder

* clang format

* code backup

* clang format

* temp code backup

* clang format

* temp code backup

* convert input string to the json format

* clang format

* remove unnecessary code

* fix review comments

* clang format

* add a comment

* add unit tests

* clang format

* fix cppcheck error

* fix review comments

* clang format

* rename two files

* refine an error report

* clang format

* remove unnecessary code

* fix a review comments

* add convert_to_json unit tests

* clang format

* fix cppcheck error

* fix cppcheck error

* add more unit tests

* clang format

* fix clang tidy error

* clang format

* fixed an test failure

* fix cppcheck error

* fix a bug

* fix cppcheck error

* fix jenkins build error

* fix a jenkin build error by wrap up string

* clang format

* another try

* clang format

* fix jenkins build error

* clang format

* remove unnecessary code

* fix review comments

* fix cppcheck error

* fix review comments
Co-authored-by: default avatarPaul Fultz II <pfultz2@yahoo.com>
parent 48ffbfa5
...@@ -39,6 +39,7 @@ add_library(migraphx ...@@ -39,6 +39,7 @@ add_library(migraphx
value.cpp value.cpp
verify_args.cpp verify_args.cpp
json.cpp json.cpp
convert_to_json.cpp
opt/memory_coloring.cpp opt/memory_coloring.cpp
opt/memory_coloring_impl.cpp opt/memory_coloring_impl.cpp
) )
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#include <migraphx/quantization.hpp> #include <migraphx/quantization.hpp>
#include <migraphx/cpu/target.hpp> #include <migraphx/cpu/target.hpp>
#include <migraphx/load_save.hpp> #include <migraphx/load_save.hpp>
#include <migraphx/make_op.hpp>
#include <migraphx/json.hpp>
#include <migraphx/convert_to_json.hpp>
#include <algorithm>
namespace migraphx { namespace migraphx {
...@@ -138,6 +142,18 @@ void quantize_int8_wrap(program& prog, const target& t, quantize_int8_options& o ...@@ -138,6 +142,18 @@ void quantize_int8_wrap(program& prog, const target& t, quantize_int8_options& o
migraphx::quantize_int8(prog, t, options.calibration, options.op_names); migraphx::quantize_int8(prog, t, options.calibration, options.op_names);
} }
operation create_op(const char* name, const char* attributes)
{
value v = value::object{};
if(attributes != nullptr)
{
v = from_json_string(convert_to_json(std::string(attributes)));
}
auto op = make_op(name, v);
return op;
}
template <class T> template <class T>
bool equal(const T& x, const T& y) bool equal(const T& x, const T& y)
{ {
...@@ -258,6 +274,16 @@ struct migraphx_program ...@@ -258,6 +274,16 @@ struct migraphx_program
migraphx::program object; migraphx::program object;
}; };
extern "C" struct migraphx_operation;
struct migraphx_operation
{
template <class... Ts>
migraphx_operation(Ts&&... xs) : object(std::forward<Ts>(xs)...)
{
}
migraphx::operation object;
};
extern "C" struct migraphx_onnx_options; extern "C" struct migraphx_onnx_options;
struct migraphx_onnx_options struct migraphx_onnx_options
{ {
...@@ -676,6 +702,34 @@ migraphx_program_equal(bool* out, const_migraphx_program_t program, const_migrap ...@@ -676,6 +702,34 @@ migraphx_program_equal(bool* out, const_migraphx_program_t program, const_migrap
}); });
} }
extern "C" migraphx_status migraphx_operation_destroy(migraphx_operation_t operation)
{
return migraphx::try_([&] { destroy((operation)); });
}
extern "C" migraphx_status
migraphx_operation_create(migraphx_operation_t* operation, const char* name, const char* attributes)
{
return migraphx::try_([&] {
*operation = object_cast<migraphx_operation_t>(
allocate<migraphx::operation>(migraphx::create_op((name), (attributes))));
});
}
extern "C" migraphx_status
migraphx_operation_name(char* out, size_t out_size, migraphx_operation_t operation)
{
return migraphx::try_([&] {
if(out == nullptr)
MIGRAPHX_THROW(migraphx_status_bad_param, "Bad parameter out: Null pointer");
if(operation == nullptr)
MIGRAPHX_THROW(migraphx_status_bad_param, "Bad parameter operation: Null pointer");
auto&& api_result = (operation->object).name();
auto* it = std::copy_n(api_result.begin(), std::min(api_result.size(), out_size - 1), out);
*it = '\0';
});
}
extern "C" migraphx_status extern "C" migraphx_status
migraphx_load(migraphx_program_t* out, const char* name, migraphx_file_options* options) migraphx_load(migraphx_program_t* out, const char* name, migraphx_file_options* options)
{ {
......
...@@ -74,6 +74,9 @@ typedef const struct migraphx_shapes* const_migraphx_shapes_t; ...@@ -74,6 +74,9 @@ typedef const struct migraphx_shapes* const_migraphx_shapes_t;
typedef struct migraphx_program* migraphx_program_t; typedef struct migraphx_program* migraphx_program_t;
typedef const struct migraphx_program* const_migraphx_program_t; typedef const struct migraphx_program* const_migraphx_program_t;
typedef struct migraphx_operation* migraphx_operation_t;
typedef const struct migraphx_operation* const_migraphx_operation_t;
typedef struct migraphx_onnx_options* migraphx_onnx_options_t; typedef struct migraphx_onnx_options* migraphx_onnx_options_t;
typedef const struct migraphx_onnx_options* const_migraphx_onnx_options_t; typedef const struct migraphx_onnx_options* const_migraphx_onnx_options_t;
...@@ -194,6 +197,14 @@ migraphx_status migraphx_program_run(migraphx_arguments_t* out, ...@@ -194,6 +197,14 @@ migraphx_status migraphx_program_run(migraphx_arguments_t* out,
migraphx_status migraphx_status
migraphx_program_equal(bool* out, const_migraphx_program_t program, const_migraphx_program_t x); migraphx_program_equal(bool* out, const_migraphx_program_t program, const_migraphx_program_t x);
migraphx_status migraphx_operation_destroy(migraphx_operation_t operation);
migraphx_status migraphx_operation_create(migraphx_operation_t* operation,
const char* name,
const char* attributes);
migraphx_status migraphx_operation_name(char* out, size_t out_size, migraphx_operation_t operation);
migraphx_status migraphx_status
migraphx_load(migraphx_program_t* out, const char* name, migraphx_file_options* options); migraphx_load(migraphx_program_t* out, const char* name, migraphx_file_options* options);
......
...@@ -517,6 +517,25 @@ struct program : MIGRAPHX_HANDLE_BASE(program) ...@@ -517,6 +517,25 @@ struct program : MIGRAPHX_HANDLE_BASE(program)
friend bool operator!=(const program& px, const program& py) { return !(px == py); } friend bool operator!=(const program& px, const program& py) { return !(px == py); }
}; };
struct operation : MIGRAPHX_HANDLE_BASE(operation)
{
operation(migraphx_operation* p, own) { this->set_handle(p, own{}); }
operation(migraphx_operation* p, borrow) { this->set_handle(p, borrow{}); }
operation(const char* name, const char* attributes = nullptr)
{
this->make_handle(&migraphx_operation_create, name, attributes);
}
std::string name()
{
std::array<char, 1024> out_name;
call(&migraphx_operation_name, out_name.data(), 1024, this->get_handle_ptr());
return std::string(out_name.data());
}
};
inline program load(const char* filename, migraphx_file_options options) inline program load(const char* filename, migraphx_file_options options)
{ {
return program(make<migraphx_program>(&migraphx_load, filename, &options), own{}); return program(make<migraphx_program>(&migraphx_load, filename, &options), own{});
......
...@@ -188,6 +188,14 @@ def program(h): ...@@ -188,6 +188,14 @@ def program(h):
const=True) const=True)
@auto_handle
def operation(h):
h.constructor('create',
api.params(name='const char*', attributes='const char*'),
fname='migraphx::create_op')
h.method('name', returns='std::string')
api.add_function('migraphx_load', api.add_function('migraphx_load',
api.params(name='const char*', api.params(name='const char*',
options='migraphx::file_options'), options='migraphx::file_options'),
......
#include <algorithm>
#include <string>
#include <vector>
#include <functional>
#include <sstream>
#include <migraphx/errors.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/convert_to_json.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
using token = std::pair<const char*, const char*>;
using lexer = std::function<const char*(const char* start, const char* end)>;
template <class P>
auto lex_while(P p)
{
return [=](const char* start, const char* end) {
return std::find_if(start, end, [&](char c) { return not p(c); });
};
}
template <class P>
auto lex_if(P p)
{
return [=](const char* start, const char*) {
if(p(*start))
return start + 1;
return start;
};
}
std::vector<token> tokenize(const char* start, const char* end, const std::vector<lexer>& lexers)
{
std::vector<token> result;
while(start != end)
{
bool error = true;
for(const auto& l : lexers)
{
const auto* next = l(start, end);
if(next != start)
{
result.emplace_back(start, next);
start = next;
error = false;
break;
}
}
if(error)
{
MIGRAPHX_THROW("TOKENIZE: no token found!");
}
}
return result;
}
std::vector<token> json_tokenize(const std::string& s)
{
std::vector<lexer> lexers;
// Quote
lexers.push_back([](const char* start, const char* end) {
if(*start != '\"')
return start;
++start;
while((start != end) and (*start != '\"'))
{
if(*start == '\\')
start++;
start++;
}
return ++start;
});
lexers.push_back(lex_while(&isspace));
// Punctation
lexers.push_back(lex_if(&ispunct));
// Identifier/number
lexers.push_back(lex_while([](char c) {
return (isalnum(c) != 0 or contains({'_', '.', '+'}, c));
}));
return tokenize(s.data(), s.data() + s.length(), lexers);
}
std::string convert_to_json(const std::string& str)
{
auto tokens = json_tokenize(str);
std::stringstream ss;
for(auto& token : tokens)
{
std::string s(token.first, token.second);
if(std::isalpha(s.front()) != 0 and
not contains({"null", "nan", "true", "false", "inf"}, s))
{
ss << "\"" << s << "\"";
}
else
{
ss << s;
}
}
return ss.str();
}
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#ifndef MIGRAPHX_GUARD_MIGRAPHLIB_CONVERT_TO_JSON_HPP
#define MIGRAPHX_GUARD_MIGRAPHLIB_CONVERT_TO_JSON_HPP
#include <string>
#include <migraphx/config.hpp>
namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
std::string convert_to_json(const std::string& str);
} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
#endif
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <migraphx/type_name.hpp> #include <migraphx/type_name.hpp>
#include <migraphx/load_save.hpp> #include <migraphx/load_save.hpp>
#include <migraphx/register_target.hpp> #include <migraphx/register_target.hpp>
#include <migraphx/json.hpp>
#include <migraphx/make_op.hpp>
#ifdef HAVE_GPU #ifdef HAVE_GPU
#include <migraphx/gpu/hip.hpp> #include <migraphx/gpu/hip.hpp>
...@@ -20,6 +22,70 @@ ...@@ -20,6 +22,70 @@
using half = half_float::half; using half = half_float::half;
namespace py = pybind11; namespace py = pybind11;
namespace migraphx {
migraphx::value to_value(py::kwargs kwargs);
migraphx::value to_value(py::list lst);
template <class T, class F>
void visit_py(T x, F f)
{
if(py::isinstance<py::kwargs>(x))
{
f(to_value(x.template cast<py::kwargs>()));
}
else if(py::isinstance<py::list>(x))
{
f(to_value(x.template cast<py::list>()));
}
else if(py::isinstance<py::bool_>(x))
{
f(x.template cast<bool>());
}
else if(py::isinstance<py::int_>(x))
{
f(x.template cast<int>());
}
else if(py::isinstance<py::float_>(x))
{
f(x.template cast<float>());
}
else if(py::isinstance<py::str>(x))
{
f(x.template cast<std::string>());
}
else
{
MIGRAPHX_THROW("VISIT_PY: Unsupported data type!");
}
}
migraphx::value to_value(py::list lst)
{
migraphx::value v = migraphx::value::array{};
for(auto val : lst)
{
visit_py(val, [&](auto py_val) { v.push_back(py_val); });
}
return v;
}
migraphx::value to_value(py::kwargs kwargs)
{
migraphx::value v = migraphx::value::object{};
for(auto arg : kwargs)
{
auto&& key = py::str(arg.first);
auto&& val = arg.second;
visit_py(val, [&](auto py_val) { v[key] = py_val; });
}
return v;
}
} // namespace migraphx
namespace pybind11 { namespace pybind11 {
namespace detail { namespace detail {
...@@ -200,6 +266,18 @@ PYBIND11_MODULE(migraphx, m) ...@@ -200,6 +266,18 @@ PYBIND11_MODULE(migraphx, m)
.def("__ne__", std::not_equal_to<migraphx::program>{}) .def("__ne__", std::not_equal_to<migraphx::program>{})
.def("__repr__", [](const migraphx::program& p) { return migraphx::to_string(p); }); .def("__repr__", [](const migraphx::program& p) { return migraphx::to_string(p); });
py::class_<migraphx::operation>(m, "op")
.def(py::init([](const std::string& name, py::kwargs kwargs) {
migraphx::value v = migraphx::value::object{};
if(kwargs)
{
v = migraphx::to_value(kwargs);
}
return migraphx::make_op(name, v);
}))
.def("name", &migraphx::operation::name);
m.def("parse_tf", m.def("parse_tf",
[](const std::string& filename, bool is_nhwc, unsigned int batch_size) { [](const std::string& filename, bool is_nhwc, unsigned int batch_size) {
return migraphx::parse_tf(filename, migraphx::tf_options{is_nhwc, batch_size}); return migraphx::parse_tf(filename, migraphx::tf_options{is_nhwc, batch_size});
......
...@@ -13,6 +13,7 @@ endfunction() ...@@ -13,6 +13,7 @@ endfunction()
add_api_test(cpu test_cpu.cpp) add_api_test(cpu test_cpu.cpp)
add_api_test(save_load test_save_load.cpp) add_api_test(save_load test_save_load.cpp)
add_api_test(op test_op_construct.cpp)
if(MIGRAPHX_ENABLE_GPU) if(MIGRAPHX_ENABLE_GPU)
add_api_test(gpu test_gpu.cpp) add_api_test(gpu test_gpu.cpp)
# GPU-based tests # GPU-based tests
......
#include <migraphx/migraphx.h>
#include <migraphx/migraphx.hpp>
#include "test.hpp"
TEST_CASE(add_op)
{
auto add_op = migraphx::operation("add");
EXPECT(add_op.name() == "add");
}
TEST_CASE(reduce_mean)
{
auto rm = migraphx::operation("reduce_mean", "{axes : [1, 2, 3, 4]}");
EXPECT(rm.name() == "reduce_mean");
}
TEST_CASE(reduce_mean1)
{
auto rm = migraphx::operation("reduce_mean", "{\"axes\" : [1, 2, 3, 4]}");
EXPECT(rm.name() == "reduce_mean");
}
int main(int argc, const char* argv[]) { test::run(argc, argv); }
#include <migraphx/convert_to_json.hpp>
#include <test.hpp>
TEST_CASE(key_int)
{
std::string str = "{abc:{key:1}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":1}}");
}
TEST_CASE(key_negative_int)
{
std::string str = "{abc:{key:-1}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":-1}}");
}
TEST_CASE(key_float)
{
std::string str = "{abc:{key:1.0}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":1.0}}");
}
TEST_CASE(key_negative_float)
{
std::string str = "{abc:{key:-1.0}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":-1.0}}");
}
TEST_CASE(key_exp)
{
std::string str = "{abc:{key:1e+10}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":1e+10}}");
}
TEST_CASE(key_exp_1)
{
std::string str = "{abc:{key:1E-10}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":1E-10}}");
}
TEST_CASE(key_null)
{
std::string str = "{abc:{key:null}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":null}}");
}
TEST_CASE(key_inf)
{
std::string str = "{abc:{key:inf}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":inf}}");
}
TEST_CASE(key_neg_inf)
{
std::string str = "{abc:{key:-inf}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":-inf}}");
}
TEST_CASE(key_true)
{
std::string str = "{abc:{key:true}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":true}}");
}
TEST_CASE(key_false)
{
std::string str = "{abc:{key:false}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":false}}");
}
TEST_CASE(key_nan)
{
std::string str = "{abc:{key:nan}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":nan}}");
}
TEST_CASE(quote_key_num)
{
std::string str = R"({"abc":{"key":1}})";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\":{\"key\":1}}");
}
TEST_CASE(quote_with_space_key_num)
{
std::string str = R"({"abc key":{"key":1}})";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc key\":{\"key\":1}}");
}
TEST_CASE(key_value_num_space)
{
std::string str = "{abc : { key : 1}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\" : { \"key\" : 1}}");
}
TEST_CASE(key_value_str)
{
std::string str = "{abc : {key : value}}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\" : {\"key\" : \"value\"}}");
}
TEST_CASE(key_space_value)
{
std::string str = "{abc : [key, value]}";
auto jstr = migraphx::convert_to_json(str);
EXPECT(jstr == "{\"abc\" : [\"key\", \"value\"]}");
}
int main(int argc, const char* argv[]) { test::run(argc, argv); }
...@@ -24,4 +24,5 @@ add_py_test(gpu_offload test_gpu_offload.py WORKING_DIRECTORY ${TEST_ONNX_DIR}) ...@@ -24,4 +24,5 @@ add_py_test(gpu_offload test_gpu_offload.py WORKING_DIRECTORY ${TEST_ONNX_DIR})
add_py_test(gpu test_gpu.py WORKING_DIRECTORY ${TEST_ONNX_DIR}) add_py_test(gpu test_gpu.py WORKING_DIRECTORY ${TEST_ONNX_DIR})
add_py_test(array test_array.py WORKING_DIRECTORY ${TEST_ONNX_DIR}) add_py_test(array test_array.py WORKING_DIRECTORY ${TEST_ONNX_DIR})
add_py_test(backend onnx_backend_test.py WORKING_DIRECTORY ${TEST_ONNX_DIR}) add_py_test(backend onnx_backend_test.py WORKING_DIRECTORY ${TEST_ONNX_DIR})
add_py_test(op test_op.py WORKING_DIRECTORY ${TEST_ONNX_DIR})
endif() endif()
import migraphx, array, sys
def test_add_op():
add_op = migraphx.op("add")
name = add_op.name()
assert name == "add"
def test_reduce_mean():
reduce_mean_op = migraphx.op("reduce_mean", **{"axes": [1, 2, 3, 4]})
name = reduce_mean_op.name()
assert name == "reduce_mean"
test_add_op()
test_reduce_mean()
...@@ -725,6 +725,30 @@ def vector_c_wrap(p): ...@@ -725,6 +725,30 @@ def vector_c_wrap(p):
p.read = '${type}(${name}, ${name}+${size})' p.read = '${type}(${name}, ${name}+${size})'
@cwrap('std::string')
def string_c_wrap(p):
t = Type('char*')
if p.returns:
if p.type.is_reference():
p.add_param(t.add_pointer())
p.bad_param('${name} == nullptr', 'Null pointer')
p.cpp_write = '${type}(${name})'
p.write = ['*${name} = ${result}.c_str()']
else:
p.add_param(t)
p.add_param('size_t', p.name + '_size')
p.bad_param('${name} == nullptr', 'Null pointer')
p.cpp_write = '${type}(${name})'
p.write = [
'auto* it = std::copy_n(${result}.begin(), std::min(${result}.size(), ${name}_size - 1), ${name});'
'*it = \'\\0\''
]
else:
p.add_param(t)
p.bad_param('${name} == nullptr', 'Null pointer')
p.read = '${type}(${name})'
class Handle: class Handle:
def __init__(self, name, ctype, cpptype): def __init__(self, name, ctype, cpptype):
self.name = name self.name = name
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#include <migraphx/quantization.hpp> #include <migraphx/quantization.hpp>
#include <migraphx/cpu/target.hpp> #include <migraphx/cpu/target.hpp>
#include <migraphx/load_save.hpp> #include <migraphx/load_save.hpp>
#include <migraphx/make_op.hpp>
#include <migraphx/json.hpp>
#include <migraphx/convert_to_json.hpp>
#include <algorithm>
namespace migraphx { namespace migraphx {
...@@ -138,6 +142,18 @@ void quantize_int8_wrap(program& prog, const target& t, quantize_int8_options& o ...@@ -138,6 +142,18 @@ void quantize_int8_wrap(program& prog, const target& t, quantize_int8_options& o
migraphx::quantize_int8(prog, t, options.calibration, options.op_names); migraphx::quantize_int8(prog, t, options.calibration, options.op_names);
} }
operation create_op(const char* name, const char* attributes)
{
value v = value::object{};
if(attributes != nullptr)
{
v = from_json_string(convert_to_json(std::string(attributes)));
}
auto op = make_op(name, v);
return op;
}
template <class T> template <class T>
bool equal(const T& x, const T& y) bool equal(const T& x, const T& y)
{ {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment