diff --git a/cmake/functional.cmake b/cmake/functional.cmake new file mode 100644 index 0000000000000000000000000000000000000000..2590e408c8e4d5eddcbd29496cf3a9bb6f0129b8 --- /dev/null +++ b/cmake/functional.cmake @@ -0,0 +1,32 @@ +function(GENERATE_FUNCTIONAL_API_AND_PYBIND11_CPP SRCS HDRS PYBIND_SRCS ROOT_DIR) + set(YAML_FILE ${PROJECT_SOURCE_DIR}/oneflow/core/functional/functional_api.yaml) + set(GENERATED_API_DIR oneflow/core/functional) + set(GENERATED_PYBIND_DIR oneflow/api/python/functional) + + add_custom_target(create_functional_api_dir + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENERATED_API_DIR} + VERBATIM) + add_custom_target(create_functional_pybind_dir + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENERATED_PYBIND_DIR} + VERBATIM) + + list(APPEND SRCS ${PROJECT_BINARY_DIR}/${GENERATED_API_DIR}/functional_api.yaml.cpp) + list(APPEND HDRS ${PROJECT_BINARY_DIR}/${GENERATED_API_DIR}/functional_api.yaml.h) + list(APPEND PYBIND_SRCS ${PROJECT_BINARY_DIR}/${GENERATED_PYBIND_DIR}/functional_api.yaml.pybind.cpp) + + add_custom_command( + OUTPUT "${PROJECT_BINARY_DIR}/${GENERATED_API_DIR}/functional_api.yaml.cpp" + "${PROJECT_BINARY_DIR}/${GENERATED_API_DIR}/functional_api.yaml.h" + "${PROJECT_BINARY_DIR}/${GENERATED_PYBIND_DIR}/functional_api.yaml.pybind.cpp" + COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_functional_api.py + --yaml_file_path ${YAML_FILE} --generate_pybind + DEPENDS ${Python_EXECUTABLE} create_functional_api_dir create_functional_pybind_dir + ${PROJECT_SOURCE_DIR}/tools/generate_functional_api.py ${YAML_FILE} + VERBATIM) + + set_source_files_properties(${${SRCS}} ${${HDRS}} ${${PYBIND_SRCS}} PROPERTIES GENERATED TRUE) + set(${SRCS} ${${SRCS}} PARENT_SCOPE) + set(${HDRS} ${${HDRS}} PARENT_SCOPE) + set(${PYBIND_SRCS} ${${PYBIND_SRCS}} PARENT_SCOPE) + +endfunction() diff --git a/cmake/oneflow.cmake b/cmake/oneflow.cmake index 512c51551e7468ddec42f1ca5e2f0277f69e1e48..9e2400e87b1dad99e13bf07353b60f5b704a1954 100644 --- a/cmake/oneflow.cmake +++ b/cmake/oneflow.cmake @@ -215,7 +215,7 @@ add_dependencies(of_protoobj make_pyproto_dir ${PROTOBUF_COPY_TARGETS}) # cfg obj lib include(cfg) -GENERATE_CFG_AND_PYBIND11_CPP(CFG_SRCS CFG_HRCS PYBIND11_SRCS ${PROJECT_SOURCE_DIR}) +GENERATE_CFG_AND_PYBIND11_CPP(CFG_SRCS CFG_HRCS CFG_PYBIND11_SRCS ${PROJECT_SOURCE_DIR}) oneflow_add_library(of_cfgobj ${CFG_SRCS} ${CFG_HRCS}) add_dependencies(of_cfgobj of_protoobj generate_cfg) if (BUILD_SHARED_LIBS) @@ -228,6 +228,13 @@ else() target_link_libraries(of_cfgobj ${oneflow_third_party_libs}) endif() +include(functional) +GENERATE_FUNCTIONAL_API_AND_PYBIND11_CPP( + FUNCTIONAL_GENERATED_SRCS FUNCTIONAL_GENERATED_HRCS FUNCTIONAL_PYBIND11_SRCS ${PROJECT_SOURCE_DIR}) +list(APPEND of_all_obj_cc ${FUNCTIONAL_GENERATED_SRCS}) + +set(PYBIND11_SRCS ${CFG_PYBIND11_SRCS} ${FUNCTIONAL_PYBIND11_SRCS}) + include_directories(${PROJECT_SOURCE_DIR}) # TO FIND: third_party/eigen3/.. include_directories(${PROJECT_BINARY_DIR}) @@ -300,6 +307,8 @@ add_custom_target(of_pyscript_copy ALL COMMAND ${CMAKE_COMMAND} -E create_symlink "${PROJECT_SOURCE_DIR}/oneflow/python" "${of_pyscript_dir}/oneflow/python" COMMAND ${CMAKE_COMMAND} -E copy_directory "${of_proto_python_dir}/oneflow/core" "${of_pyscript_dir}/oneflow/core" COMMAND ${CMAKE_COMMAND} -E touch "${of_pyscript_dir}/oneflow/core/__init__.py" + COMMAND ${CMAKE_COMMAND} -E make_directory "${of_pyscript_dir}/oneflow/F" + COMMAND ${CMAKE_COMMAND} -E touch "${of_pyscript_dir}/oneflow/F/__init__.py" COMMAND ${CMAKE_COMMAND} -E make_directory "${of_pyscript_dir}/oneflow/python_gen" COMMAND ${CMAKE_COMMAND} -E touch "${of_pyscript_dir}/oneflow/python_gen/__init__.py" COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_pip_version.py ${gen_pip_args} --src=${PROJECT_SOURCE_DIR} diff --git a/dev-requirements.txt b/dev-requirements.txt index c5d610989fbe5681244d4beac62b64755f976ffa..0dcb33fe1201511dd8d09e552f757388a226229b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,3 +7,4 @@ requests onnx; sys_platform != 'darwin' jinja2 opencv-python==4.2.0.34; sys_platform != 'darwin' +PyYAML diff --git a/oneflow/api/python/framework/throw.h b/oneflow/api/python/framework/throw.h new file mode 100644 index 0000000000000000000000000000000000000000..7d8a82aa3397b8c363a891d33fa8418ad38dbbbc --- /dev/null +++ b/oneflow/api/python/framework/throw.h @@ -0,0 +1,72 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_API_PYTHON_FRAMEWORK_THROW_H_ +#define ONEFLOW_API_PYTHON_FRAMEWORK_THROW_H_ + +#include "oneflow/core/common/error.h" + +namespace oneflow { + +class Throw final { + public: + Throw(const Error& error) : error_(error) {} + ~Throw() { ThrowError(error_.error_proto()); } + + Error& error() { return error_; } + + private: + Error error_; +}; + +} // namespace oneflow + +#define CHECK_OR_THROW(expr) \ + if (!(expr)) \ + Throw(oneflow::Error::CheckFailedError().AddStackFrame(MAYBE_FAILED_LOC, __FUNCTION__)).error() \ + << " Check failed: " << OF_PP_STRINGIZE(expr) << "\t" + +#define CHECK_EQ_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) == (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_GE_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) >= (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_GT_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) > (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_LE_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) <= (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_LT_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) < (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_NE_OR_THROW(lhs, rhs) \ + CHECK_OR_THROW((lhs) != (rhs)) << "(" << (lhs) << " vs " << (rhs) << ") " + +#define CHECK_STREQ_OR_THROW(lhs, rhs) CHECK_EQ_OR_THROW(std::string(lhs), std::string(rhs)) + +#define CHECK_STRNE_OR_THROW(lhs, rhs) CHECK_NE_OR_THROW(std::string(lhs), std::string(rhs)) + +#define CHECK_NOTNULL_OR_THROW(ptr) CHECK_OR_THROW(ptr != nullptr) + +#define CHECK_ISNULL_OR_THROW(ptr) CHECK_OR_THROW(ptr == nullptr) + +#define TODO_THEN_THROW() \ + oneflow::Throw(oneflow::Error::Todo().AddStackFrame(MAYBE_FAILED_LOC, __FUNCTION__)).error() +#define UNIMPLEMENTED_THEN_THROW() \ + Throw(oneflow::Error::Unimplemented().AddStackFrame(MAYBE_FAILED_LOC, __FUNCTION__)).error() + +#endif // ONEFLOW_API_PYTHON_FRAMEWORK_THROW_H_ diff --git a/oneflow/api/python/functional/common.h b/oneflow/api/python/functional/common.h new file mode 100644 index 0000000000000000000000000000000000000000..164986148b3aa24d94e758861cb3a13193e2aedb --- /dev/null +++ b/oneflow/api/python/functional/common.h @@ -0,0 +1,139 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_API_PYTHON_FUNCTIONAL_COMMON_H_ +#define ONEFLOW_API_PYTHON_FUNCTIONAL_COMMON_H_ + +#include <string> +#include <vector> +#include <pybind11/pybind11.h> + +#include "oneflow/core/common/maybe.h" +#include "oneflow/core/common/preprocessor.h" +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/framework/attr_map.h" + +namespace py = pybind11; + +namespace oneflow { +namespace one { +namespace functional { + +namespace detail { + +#define ARITHMETIC_TYPE_SEQ \ + OF_PP_MAKE_TUPLE_SEQ(int32_t) \ + OF_PP_MAKE_TUPLE_SEQ(uint32_t) \ + OF_PP_MAKE_TUPLE_SEQ(int64_t) \ + OF_PP_MAKE_TUPLE_SEQ(uint64_t) \ + OF_PP_MAKE_TUPLE_SEQ(float) \ + OF_PP_MAKE_TUPLE_SEQ(double) \ + OF_PP_MAKE_TUPLE_SEQ(bool) + +#define MAKE_LIST_TUPLE_SEQ(T) (MAKE_LIST_TUPLE(T)) +#define MAKE_LIST_TUPLE(T) (std::vector<T>) +#define ARITHMETIC_LIST_TYPE_SEQ OF_PP_FOR_EACH_TUPLE(MAKE_LIST_TUPLE_SEQ, ARITHMETIC_TYPE_SEQ) + +template<typename T> +inline bool isinstance(py::handle obj) { + return py::isinstance<T>(obj); +} + +#define SPECIALIZE_IS_INSTANCE(T) \ + template<> \ + inline bool isinstance<T>(py::handle obj) { \ + static py::object dummy = py::cast(T()); \ + CHECK_NOTNULL_OR_THROW(dummy.ptr()) << "Pybind has no internal type " << #T; \ + return py::isinstance(obj, py::type::of(dummy)); \ + } + +OF_PP_FOR_EACH_TUPLE(SPECIALIZE_IS_INSTANCE, ARITHMETIC_TYPE_SEQ OF_PP_MAKE_TUPLE_SEQ(std::string)); +OF_PP_FOR_EACH_TUPLE(SPECIALIZE_IS_INSTANCE, + ARITHMETIC_LIST_TYPE_SEQ OF_PP_MAKE_TUPLE_SEQ(std::vector<std::string>)); + +#undef SPECIALIZE_IS_INSTANCE + +template<typename T> +struct type_caster { + static Maybe<T> cast(py::handle src) { return py::cast<T>(src); } +}; + +template<typename T> +inline Maybe<T> cast(py::handle obj) { + return type_caster<T>::cast(obj); +} + +template<typename T> +struct type_caster<std::vector<T>> { + static Maybe<std::vector<T>> cast(py::handle src); +}; + +#define SPECIALIZE_INTERNAL_IS_INSTANCE_ADN_CAST(T) \ + template<> \ + inline bool isinstance<std::shared_ptr<T>>(py::handle obj) { \ + return detail::isinstance<T>(obj); \ + } \ + \ + template<> \ + struct type_caster<std::shared_ptr<T>> { \ + static Maybe<std::shared_ptr<T>> cast(py::handle src) { \ + CHECK_OR_RETURN(detail::isinstance<T>(src)) \ + << "Can not cast to " << #T << " from python object whose type is " \ + << *JUST(detail::cast<std::string>(py::str(py::type::of(src)))); \ + return py::cast<std::shared_ptr<T>>(src); \ + } \ + }; + +SPECIALIZE_INTERNAL_IS_INSTANCE_ADN_CAST(one::Tensor); +SPECIALIZE_INTERNAL_IS_INSTANCE_ADN_CAST(one::TensorTuple); +SPECIALIZE_INTERNAL_IS_INSTANCE_ADN_CAST(MutableCfgAttrMap); + +#undef SPECIALIZE_INTERNAL_IS_INSTANCE_ADN_CAST + +template<typename T> +T&& dereference(T&& val) { + return std::move(val); +} + +template<typename T> +T&& dereference(std::shared_ptr<T>&& val) { + return std::move(*val); +} + +template<typename T> +/*static*/ Maybe<std::vector<T>> type_caster<std::vector<T>>::cast(py::handle src) { + PyObject* obj = src.ptr(); + bool is_tuple = PyTuple_Check(obj); + CHECK_OR_RETURN(is_tuple || PyList_Check(obj)) + << "The python object is not list or tuple, but is " + << *JUST(detail::cast<std::string>(py::str(py::type::of(src)))); + size_t size = is_tuple ? PyTuple_GET_SIZE(obj) : PyList_GET_SIZE(obj); + + auto values = std::make_shared<std::vector<T>>(size); + for (int i = 0; i < size; ++i) { + values->at(i) = detail::dereference<T>(JUST(detail::cast<T>( + is_tuple ? py::handle(PyTuple_GET_ITEM(obj, i)) : py::handle(PyList_GET_ITEM(obj, i))))); + } + return values; +} + +} // namespace detail + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_API_PYTHON_FUNCTIONAL_COMMON_H_ diff --git a/oneflow/api/python/functional/function_def.h b/oneflow/api/python/functional/function_def.h new file mode 100644 index 0000000000000000000000000000000000000000..69e9e648af6a9221fc4e3bb4245fda05b64a368f --- /dev/null +++ b/oneflow/api/python/functional/function_def.h @@ -0,0 +1,67 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_API_PYTHON_FUNCTIONAL_FUNCTION_DEF_H_ +#define ONEFLOW_API_PYTHON_FUNCTIONAL_FUNCTION_DEF_H_ + +#include <memory> +#include <string> +#include <vector> +#include <pybind11/pybind11.h> + +#include "oneflow/api/python/functional/python_arg.h" +#include "oneflow/core/functional/value_types.h" + +namespace py = pybind11; + +namespace oneflow { +namespace one { +namespace functional { + +struct ReturnDef { + ReturnDef() : type(kINVALID) {} + ReturnDef(const ValueType& t) : type(t) {} + ValueType type; +}; + +struct ArgumentDef { + ArgumentDef() : name(""), type(kINVALID), has_default_value(false) {} + ArgumentDef(const std::string& n, const ValueType& t) + : name(n), type(t), has_default_value(false) {} + + template<typename T> + ArgumentDef(const std::string& n, const T& v) + : name(n), type(ValueTypeOf<T>()), has_default_value(true) { + default_value = std::make_shared<detail::AnyData<T>>(v); + } + + std::string name; + ValueType type; + + bool has_default_value; + std::shared_ptr<const detail::AnyDataBase> default_value; +}; + +struct FunctionDef { + std::string name; + ReturnDef return_def; + std::vector<ArgumentDef> argument_def; +}; + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_API_PYTHON_FUNCTIONAL_FUNCTION_DEF_H_ diff --git a/oneflow/api/python/functional/py_function.h b/oneflow/api/python/functional/py_function.h new file mode 100644 index 0000000000000000000000000000000000000000..3d2b6b07c10014f82ad92873f3b273136c1396b5 --- /dev/null +++ b/oneflow/api/python/functional/py_function.h @@ -0,0 +1,56 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include <pybind11/pybind11.h> + +#include "oneflow/api/python/functional/python_arg.h" +#include "oneflow/api/python/functional/unpack_call.h" + +namespace py = pybind11; + +namespace oneflow { +namespace one { +namespace functional { + +template<typename SchemaT> +inline py::object PyFunction(py::args args, py::kwargs kwargs) { + // TODO(): Support multiple function signatures. + CHECK_LE(args.size(), SchemaT::max_positionals) + << "The maximum count of positional arguments is " << SchemaT::max_positionals; + CHECK_LE(kwargs.size(), SchemaT::max_keywords) + << "The maximum count of keyword arguments is " << SchemaT::max_keywords; + + // TODO(): Check argument types. + std::vector<PythonArg> _args(SchemaT::max_args); + for (int i = 0; i < args.size(); ++i) { _args[i] = PythonArg(args[i]); } + for (int i = args.size(); i < SchemaT::max_args; ++i) { + const auto& arg = SchemaT::argument_def.at(i); + if (kwargs.contains(arg.name.c_str())) { + _args[i] = PythonArg(kwargs[arg.name.c_str()]); + } else { + CHECK(arg.has_default_value) + << "Argument " << arg.name << " is required, and the function def is \"" + << SchemaT::signature << "\"."; + _args[i] = PythonArg(arg.default_value); + } + } + using FType = typename SchemaT::FType; + using R = typename SchemaT::R; + return py::cast(detail::unpack_call<FType, R, PythonArg>::apply(*SchemaT::func, _args)); +} + +} // namespace functional +} // namespace one +} // namespace oneflow diff --git a/oneflow/api/python/functional/python_arg.cpp b/oneflow/api/python/functional/python_arg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c27719f96a966a39058e721c2c083d3e1602705 --- /dev/null +++ b/oneflow/api/python/functional/python_arg.cpp @@ -0,0 +1,139 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "oneflow/api/python/functional/python_arg.h" + +#include "oneflow/api/python/functional/common.h" +#include "oneflow/core/common/data_type.cfg.h" +#include "oneflow/core/framework/attr_map.h" +#include "oneflow/core/framework/dtype.h" +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/framework/user_op_attr.cfg.h" +#include "oneflow/core/functional/scalar.h" + +namespace py = pybind11; + +namespace oneflow { +namespace one { +namespace functional { + +#define INSTANCE_CAST_OBJECT_AS(T) \ + template<> \ + Maybe<T> PythonArg::ObjectAs<T>() const { \ + return detail::cast<T>(Borrow()); \ + } \ + template<> \ + Maybe<std::vector<T>> PythonArg::ObjectAs<std::vector<T>>() const { \ + return detail::cast<std::vector<T>>(Borrow()); \ + } + +OF_PP_FOR_EACH_TUPLE(INSTANCE_CAST_OBJECT_AS, + ARITHMETIC_TYPE_SEQ OF_PP_MAKE_TUPLE_SEQ(std::string)); + +#undef INSTANCE_CAST_OBJECT_AS + +template<> +Maybe<Scalar> PythonArg::ObjectAs<Scalar>() const { + py::object obj = Borrow(); + if (detail::isinstance<int32_t>(obj)) { + return Scalar(JUST(detail::cast<int32_t>(obj))); + } else if (detail::isinstance<int64_t>(obj)) { + return Scalar(JUST(detail::cast<int64_t>(obj))); + } else if (detail::isinstance<float>(obj)) { + return Scalar(JUST(detail::cast<float>(obj))); + } else if (detail::isinstance<double>(obj)) { + return Scalar(JUST(detail::cast<double>(obj))); + } else if (detail::isinstance<bool>(obj)) { + return Scalar(JUST(detail::cast<bool>(obj))); + } else { + UNIMPLEMENTED_THEN_RETURN() << "Can not convert to scalar from python object whose type is " + << *JUST(detail::cast<std::string>(py::str(py::type::of(obj)))); + } +} + +template<> +Maybe<std::shared_ptr<one::Tensor>> PythonArg::ObjectAs<std::shared_ptr<one::Tensor>>() const { + return detail::cast<std::shared_ptr<one::Tensor>>(Borrow()); +} + +template<> +Maybe<std::shared_ptr<one::TensorTuple>> PythonArg::ObjectAs<std::shared_ptr<one::TensorTuple>>() + const { + py::object obj = Borrow(); + if (detail::isinstance<one::TensorTuple>(obj)) { + return detail::cast<std::shared_ptr<one::TensorTuple>>(obj); + } + + const auto& v = JUST(detail::cast<std::vector<std::shared_ptr<one::Tensor>>>(obj)); + auto values = std::make_shared<one::TensorTuple>(v->size()); + for (int i = 0; i < v->size(); ++i) { values->at(i) = v->at(i); } + return values; +} + +template<> +Maybe<one::TensorTuple> PythonArg::ObjectAs<one::TensorTuple>() const { + return *JUST(ObjectAs<std::shared_ptr<one::TensorTuple>>()); +} + +template<> +Maybe<std::shared_ptr<cfg::AttrValue>> PythonArg::ObjectAs<std::shared_ptr<cfg::AttrValue>>() + const { + py::object obj = Borrow(); + if (detail::isinstance<cfg::AttrValue>(obj)) { + return detail::cast<std::shared_ptr<cfg::AttrValue>>(obj); + } + auto attr_value = std::make_shared<cfg::AttrValue>(); + if (detail::isinstance<int32_t>(obj)) { + attr_value->set_at_int32(JUST(detail::cast<int32_t>(obj))); + } else if (detail::isinstance<double>(obj)) { + attr_value->set_at_double(JUST(detail::cast<double>(obj))); + } else { + UNIMPLEMENTED_THEN_RETURN() << "The attribute type was not supported which is " + << *JUST(detail::cast<std::string>(py::str(py::type::of(obj)))); + } + return attr_value; +} + +template<> +Maybe<AttrMap> PythonArg::ObjectAs<AttrMap>() const { + const auto& attrs = *(JUST(detail::cast<std::shared_ptr<MutableCfgAttrMap>>(Borrow()))); + return std::make_shared<AttrMap>(*attrs); + ; +} + +template<> +Maybe<DataType> PythonArg::ObjectAs<DataType>() const { + py::object obj = Borrow(); + if (detail::isinstance<cfg::DataType>(obj)) { + const auto& dtype = *JUST(detail::cast<std::shared_ptr<cfg::DataType>>(obj)); + return static_cast<DataType>(*dtype); + } else if (detail::isinstance<DType>(obj)) { + return JUST(detail::cast<DType&>(obj)).data_type(); + } else if (detail::isinstance<int32_t>(obj)) { + return static_cast<DataType>(JUST(detail::cast<int32_t>(obj))); + } else if (detail::isinstance<int64_t>(obj)) { + return static_cast<DataType>(JUST(detail::cast<int64_t>(obj))); + } else { + UNIMPLEMENTED_THEN_RETURN() << "Can not convert object to DataType from " + << *JUST(detail::cast<std::string>(py::str(py::type::of(obj)))); + } + return kInvalidDataType; +} + +} // namespace functional +} // namespace one +} // namespace oneflow diff --git a/oneflow/api/python/functional/python_arg.h b/oneflow/api/python/functional/python_arg.h new file mode 100644 index 0000000000000000000000000000000000000000..519f07e07a7eb20150bf863af83cc856f834d2fc --- /dev/null +++ b/oneflow/api/python/functional/python_arg.h @@ -0,0 +1,91 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef ONEFLOW_API_PYTHON_FUNCTIONAL_PYTHON_ARG_H_ +#define ONEFLOW_API_PYTHON_FUNCTIONAL_PYTHON_ARG_H_ + +#include <pybind11/pybind11.h> + +#include "oneflow/api/python/framework/throw.h" +#include "oneflow/core/common/maybe.h" +#include "oneflow/core/functional/value_types.h" + +namespace py = pybind11; + +namespace oneflow { +namespace one { +namespace functional { + +namespace detail { + +struct AnyDataBase { + virtual ValueType value_type() const = 0; + virtual const void* Ptr() const = 0; +}; + +template<typename T> +struct AnyData : public AnyDataBase { + T content; + explicit AnyData(const T& v) : content(v) {} + + ValueType value_type() const override { return ValueTypeOf<T>(); } + const void* Ptr() const override { return &content; } +}; + +} // namespace detail + +class PythonArg { + public: + PythonArg() = default; + PythonArg(py::object object) : object_(object.ptr()), active_tag_(HAS_OBJECT) {} + + PythonArg(const std::shared_ptr<const detail::AnyDataBase>& value) + : immediate_(value), active_tag_(HAS_IMMEDIATE) {} + + template<typename T, typename std::enable_if<!py::detail::is_pyobject<T>::value, int>::type = 0> + PythonArg(const T& value) + : immediate_(std::make_shared<detail::AnyData<T>>(value)), active_tag_(HAS_IMMEDIATE) {} + + virtual ~PythonArg() = default; + + template<typename T> + operator T() const { + if (active_tag_ == HAS_IMMEDIATE) { + CHECK_EQ_OR_THROW(ValueTypeOf<T>(), immediate_->value_type()) + << "Could not convert immediate value from type " << immediate_->value_type() + << " to type " << ValueTypeOf<T>(); + return *reinterpret_cast<const T*>(immediate_->Ptr()); + } + CHECK_EQ_OR_THROW(active_tag_, HAS_OBJECT); + return this->ObjectAs<oneflow::detail::remove_cvref_t<T>>().GetOrThrow(); + } + + private: + template<typename T> + Maybe<T> ObjectAs() const; + py::object Borrow() const { return py::reinterpret_borrow<py::object>(object_); } + + PyObject* object_; + std::shared_ptr<const detail::AnyDataBase> immediate_; + + enum { HAS_OBJECT, HAS_IMMEDIATE, HAS_NONE } active_tag_; +}; + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_API_PYTHON_FUNCTIONAL_PYTHON_ARG_H_ diff --git a/oneflow/api/python/functional/unpack_call.h b/oneflow/api/python/functional/unpack_call.h new file mode 100644 index 0000000000000000000000000000000000000000..f564d5b7cc48314c9eb64670b338f5dc389d500c --- /dev/null +++ b/oneflow/api/python/functional/unpack_call.h @@ -0,0 +1,83 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_API_PYTHON_FUNCTIONAL_UNPACK_CALL_H_ +#define ONEFLOW_API_PYTHON_FUNCTIONAL_UNPACK_CALL_H_ + +#include "oneflow/api/python/functional/python_arg.h" +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/common/function_traits.h" + +namespace oneflow { +namespace one { +namespace functional { + +namespace detail { + +template<typename F, typename R, typename T, int nleft, int index> +struct unpack_call_dispatcher { + template<typename... Args> + static R apply(const F& f, const std::vector<T>& args, Args&&... unpacked_args) { + return unpack_call_dispatcher<F, R, T, nleft - 1, index + 1>::apply( + f, args, std::forward<Args>(unpacked_args)..., args[index]); + } +}; + +template<typename F, typename R, typename T, int index> +struct unpack_call_dispatcher<F, R, T, 0, index> { + template<typename... Args> + static R apply(const F& f, const std::vector<T>& args, Args&&... unpacked_args) { + return f(std::forward<Args>(unpacked_args)...); + } +}; + +template<typename F, typename R, typename T> +struct unpack_call { + static R apply(const F& f, const std::vector<T>& args) { + constexpr size_t nargs = function_traits<F>::nargs; + CHECK_EQ(nargs, args.size()) << "Requires " << nargs << " arguments, but " << args.size() + << " is given."; + return unpack_call_dispatcher<F, R, T, nargs, 0>::apply(f, args); + } +}; + +#define INSTANCE_MAYBE_UNPACK_CALL(K, return_fn) \ + template<typename F, typename T> \ + struct unpack_call<F, K, T> { \ + static constexpr auto return_fn_ = (return_fn); \ + using R = typename function_traits<decltype(return_fn_)>::return_type; \ + static R apply(const F& f, const std::vector<T>& args) { \ + constexpr size_t nargs = function_traits<F>::nargs; \ + CHECK_EQ(nargs, args.size()) \ + << "Requires " << nargs << " arguments, but " << args.size() << " is given."; \ + return (return_fn)(unpack_call_dispatcher<F, K, T, nargs, 0>::apply(f, args)); \ + } \ + }; + +INSTANCE_MAYBE_UNPACK_CALL(Maybe<one::Tensor>, + ([](const Maybe<one::Tensor>& t) { return t.GetPtrOrThrow(); })); +INSTANCE_MAYBE_UNPACK_CALL(Maybe<one::TensorTuple>, + ([](const Maybe<one::TensorTuple>& t) { return t.GetPtrOrThrow(); })); + +#undef INSTANCE_MAYBE_UNPACK_CALL + +} // namespace detail + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_API_PYTHON_FUNCTIONAL_UNPACK_CALL_H_ diff --git a/oneflow/core/autograd/gradient_funcs/scalar_add.cpp b/oneflow/core/autograd/gradient_funcs/scalar_add.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b8db59221a96034645ed16750207348c1af11eeb --- /dev/null +++ b/oneflow/core/autograd/gradient_funcs/scalar_add.cpp @@ -0,0 +1,49 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "oneflow/core/framework/op_expr_grad_function.h" + +namespace oneflow { +namespace one { + +struct ScalarAddInterpState : public OpExprInterpState { + bool requires_grad; +}; + +class ScalarAdd : public OpExprGradFunction<ScalarAddInterpState> { + public: + Maybe<void> Init(const OpExpr& op) override { return Maybe<void>::Ok(); } + + Maybe<void> Capture(ScalarAddInterpState* ctx, const TensorTuple& inputs, + const TensorTuple& outputs, const AttrMap& attrs) const override { + CHECK_EQ_OR_RETURN(inputs.size(), 1); + ctx->requires_grad = inputs.at(0)->requires_grad(); + return Maybe<void>::Ok(); + } + + Maybe<void> Apply(const ScalarAddInterpState* ctx, const TensorTuple& out_grads, + TensorTuple* in_grads) const override { + CHECK_EQ_OR_RETURN(out_grads.size(), 1); + in_grads->resize(1); + if (ctx->requires_grad) { in_grads->at(0) = out_grads.at(0); } + return Maybe<void>::Ok(); + } +}; + +REGISTER_OP_EXPR_GRAD_FUNCTION("scalar_add", ScalarAdd); + +} // namespace one +} // namespace oneflow diff --git a/oneflow/core/common/error.h b/oneflow/core/common/error.h index 9eaa0641433377f5d6320388ec630513e4d2ecd3..93e4470c4e8450a59442c637e8f9c38d2acb6f0e 100644 --- a/oneflow/core/common/error.h +++ b/oneflow/core/common/error.h @@ -90,9 +90,8 @@ class Error final { void ThrowError(const std::shared_ptr<cfg::ErrorProto>& error); const std::shared_ptr<cfg::ErrorProto>& ThreadLocalError(); -// r-value reference is used to supporting expressions like `Error() << "invalid value"` template<typename T> -Error&& operator<<(Error&& error, const T& x) { +Error& operator<<(Error& error, const T& x) { std::ostringstream ss; ss << x; if (error->stack_frame().empty()) { @@ -101,6 +100,13 @@ Error&& operator<<(Error&& error, const T& x) { auto* stack_frame_top = error->mutable_stack_frame(error->stack_frame_size() - 1); stack_frame_top->set_error_msg(stack_frame_top->error_msg() + ss.str()); } + return error; +} + +// r-value reference is used to supporting expressions like `Error() << "invalid value"` +template<typename T> +Error&& operator<<(Error&& error, const T& x) { + error << x; return std::move(error); } diff --git a/oneflow/core/common/function_traits.h b/oneflow/core/common/function_traits.h index fd1d72651e563154f03f74bac858986608945ac2..4b8636601cffdec2dec5e32029e22023f1e07f40 100644 --- a/oneflow/core/common/function_traits.h +++ b/oneflow/core/common/function_traits.h @@ -24,22 +24,40 @@ using void_t = void; template<typename T, typename = void> struct function_traits; +template<typename Ret, typename... Args> +struct function_traits<Ret(Args...)> { + using func_type = Ret(Args...); + using return_type = Ret; + using args_type = std::tuple<Args...>; + + static constexpr size_t nargs = sizeof...(Args); +}; + template<typename Ret, typename... Args> struct function_traits<Ret (*)(Args...)> { + using func_type = Ret(Args...); using return_type = Ret; using args_type = std::tuple<Args...>; + + static constexpr size_t nargs = sizeof...(Args); }; template<typename Ret, typename C, typename... Args> struct function_traits<Ret (C::*)(Args...)> { + using func_type = Ret(Args...); using return_type = Ret; using args_type = std::tuple<Args...>; + + static constexpr size_t nargs = sizeof...(Args); }; template<typename Ret, typename C, typename... Args> struct function_traits<Ret (C::*)(Args...) const> { + using func_type = Ret(Args...); using return_type = Ret; using args_type = std::tuple<Args...>; + + static constexpr size_t nargs = sizeof...(Args); }; template<typename F> diff --git a/oneflow/core/common/type_traits.h b/oneflow/core/common/type_traits.h index 3d53189aae56aebc3f46c0d5f684d61a1ba1130e..e8d23682026e22c5aae88f297dd7e79ae96d4bc5 100644 --- a/oneflow/core/common/type_traits.h +++ b/oneflow/core/common/type_traits.h @@ -105,6 +105,10 @@ struct IsScalarType final { }; namespace detail { + +template<typename T> +using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; + template<typename T, typename Enabled = void> struct ScalarOrConstRef; diff --git a/oneflow/core/framework/tensor_tuple.h b/oneflow/core/framework/tensor_tuple.h index acda3f13b81daaad124bd411ea1476ce7a5e1755..b996aa5b080e899ed1cef1aa617960bc4665f8f5 100644 --- a/oneflow/core/framework/tensor_tuple.h +++ b/oneflow/core/framework/tensor_tuple.h @@ -28,8 +28,8 @@ class Tensor; class TensorTuple final : public std::vector<std::shared_ptr<Tensor>>, public std::enable_shared_from_this<TensorTuple> { public: - TensorTuple(const TensorTuple&) = delete; - TensorTuple(TensorTuple&) = delete; + // TensorTuple(const TensorTuple&) = delete; + // TensorTuple(TensorTuple&) = delete; TensorTuple() = default; TensorTuple(std::vector<std::shared_ptr<Tensor>>::size_type size); TensorTuple(std::initializer_list<std::shared_ptr<Tensor>> init_list); diff --git a/oneflow/core/functional/function_library.h b/oneflow/core/functional/function_library.h new file mode 100644 index 0000000000000000000000000000000000000000..e00168bef5dff9d95aeca09a95a3aeb7fdf59084 --- /dev/null +++ b/oneflow/core/functional/function_library.h @@ -0,0 +1,77 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_CORE_FUNCTIONAL_FUNCTION_LIBRARY_H_ +#define ONEFLOW_CORE_FUNCTIONAL_FUNCTION_LIBRARY_H_ + +#include "oneflow/core/common/util.h" +#include "oneflow/core/functional/packed_functor.h" + +namespace oneflow { +namespace one { +namespace functional { + +class FunctionLibrary { + public: + virtual ~FunctionLibrary() = default; + + template<typename Func> + void add_functor(const std::string& func_name) { + Func func; + add_functor(func_name, std::move(func)); + } + + template<typename Func> + void add_functor(const std::string& func_name, const Func& func) { + auto packed_func = PackedFunctor::Make(func_name, func); + functors_.emplace(func_name, packed_func); + } + + Maybe<PackedFunctor> find(const std::string& func_name) { + const auto& it = functors_.find(func_name); + CHECK_OR_RETURN(it != functors_.end()) + << "Functor was not found for op " << func_name + << ", please check whether the functor has been registered correctly or not."; + return std::make_shared<PackedFunctor>(it->second); + } + + static FunctionLibrary* Global() { + static FunctionLibrary global_function_library; + return &global_function_library; + } + + private: + FunctionLibrary() = default; + + // The reason for not using `std::shared_ptr<PackedFunctor>` is that + // the functor maybe stateful. + HashMap<std::string, PackedFunctor> functors_; +}; + +#define ONEFLOW_FUNCTION_LIBRARY(m) ONEFLOW_FUNCTION_LIBRARY_IMPL(m, __COUNTER__) +#define ONEFLOW_FUNCTION_LIBRARY_IMPL(m, uuid) \ + static void OF_PP_CAT(_oneflow_function_library_, uuid)(FunctionLibrary & m); \ + static int OF_PP_CAT(_oneflow_function_library_dummy_, uuid) = []() { \ + FunctionLibrary* library = FunctionLibrary::Global(); \ + OF_PP_CAT(_oneflow_function_library_, uuid)(*library); \ + return 0; \ + }(); \ + void OF_PP_CAT(_oneflow_function_library_, uuid)(FunctionLibrary & m) + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_FUNCTION_LIBRARY_H_ diff --git a/oneflow/core/functional/function_signature.h b/oneflow/core/functional/function_signature.h new file mode 100644 index 0000000000000000000000000000000000000000..1fdd6b82daa9141a04cbdd87d9eb4382382664ba --- /dev/null +++ b/oneflow/core/functional/function_signature.h @@ -0,0 +1,112 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef ONEFLOW_CORE_FUNCTIONAL_FUNCTION_SIGNATURE_H_ +#define ONEFLOW_CORE_FUNCTIONAL_FUNCTION_SIGNATURE_H_ + +#include <memory> + +#include "oneflow/core/functional/value_types.h" + +namespace oneflow { +namespace one { +namespace functional { + +struct FunctionSignature { + ValueType return_type; + std::vector<ValueType> argument_types; +}; + +namespace detail { + +template<typename T> +inline ValueType PackType() { + return ValueTypeOf<typename std::decay<T>::type>(); +} + +template<typename... Args> +struct PackTypeListImpl; + +template<> +struct PackTypeListImpl<> { + static void pack(std::vector<ValueType>* packed_types) {} +}; + +template<typename T, typename... Args> +struct PackTypeListImpl<T, Args...> { + static void pack(std::vector<ValueType>* packed_types) { + packed_types->emplace_back(PackType<T>()); + PackTypeListImpl<Args...>::pack(packed_types); + } +}; + +template<typename... Args> +inline std::vector<ValueType> PackTypeList() { + std::vector<ValueType> packed_types; + detail::PackTypeListImpl<Args...>::pack(&packed_types); + return packed_types; +} + +template<typename R, typename... Args> +inline FunctionSignature PackFunctionSignatureImpl() { + FunctionSignature signature; + signature.return_type = PackType<R>(); + signature.argument_types = detail::PackTypeList<Args...>(); + return signature; +} + +template<typename T> +struct PackFunctionSignature; + +template<typename R, typename... Args> +struct PackFunctionSignature<R(Args...)> { + static FunctionSignature pack() { + static auto signature = PackFunctionSignatureImpl<R, Args...>(); + return signature; + } +}; + +template<typename T> +class CheckSignature; + +template<typename R, typename... Args> +class CheckSignature<R(Args...)> { + public: + CheckSignature(const FunctionSignature& signature) { status_ = CheckSignatureImpl(signature); } + + bool Ok() { return status_; } + + private: + bool CheckSignatureImpl(const FunctionSignature& signature); + bool status_; +}; + +template<typename R, typename... Args> +bool CheckSignature<R(Args...)>::CheckSignatureImpl(const FunctionSignature& signature) { + static ValueType return_type = detail::PackType<R>(); + if (signature.return_type != return_type) { return false; } + static std::vector<ValueType> argument_types = detail::PackTypeList<Args...>(); + if (argument_types != signature.argument_types) { return false; } + return true; +} + +} // namespace detail + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_FUNCTION_SIGNATURE_H_ diff --git a/oneflow/core/functional/functional.h b/oneflow/core/functional/functional.h new file mode 100644 index 0000000000000000000000000000000000000000..6b330a2fda07762a81e0d305ff49cd8697f79e5b --- /dev/null +++ b/oneflow/core/functional/functional.h @@ -0,0 +1,21 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#ifndef ONEFLOW_CORE_FUNCTIONAL_FUNCTIONAL_H_ +#define ONEFLOW_CORE_FUNCTIONAL_FUNCTIONAL_H_ + +#include "oneflow/core/functional/functional_api.yaml.h" + +#endif // ONEFLOW_CORE_FUNCTIONAL_FUNCTIONAL_H_ diff --git a/oneflow/core/functional/functional_api.yaml b/oneflow/core/functional/functional_api.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3e2dca8c370bebbca870ebb30339c39ec39f5f62 --- /dev/null +++ b/oneflow/core/functional/functional_api.yaml @@ -0,0 +1,37 @@ +# Copyright 2020 The OneFlow Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The following data types are allowed: +# { +# "Tensor", "TensorTuple", "Scalar", "Int", "Int32", "Int64", "Float", "Double", "String", "Bool", +# "ScalarList", "IntList", "Int32List", "Int64List", "FloatList", "DoubleList", "StringList", "BoolList" +# } + +- name: "add" + signature: "Tensor Add(Tensor x, Tensor y)" + bind_python: True + +- name: "add_n" + signature: "Tensor AddN(TensorTuple inputs)" + bind_python: True + +- name: "add_scalar" + signature: "Tensor AddScalar(Tensor x, *, Scalar alpha)" + bind_python: True + +- name: "normalization" + signature: "Tensor Normalization(Tensor x, Tensor moving_mean, Tensor moving_variance, + Tensor gamma, Tensor beta, *, Int32 axis=1, Float epsilon=1e-5, + Float momentum=0.9, Bool is_training=False)" + bind_python: True diff --git a/oneflow/core/functional/impl/add_functor.cpp b/oneflow/core/functional/impl/add_functor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..522d25dd85d67ba3200cc0ba2f8df03347d660ed --- /dev/null +++ b/oneflow/core/functional/impl/add_functor.cpp @@ -0,0 +1,101 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "oneflow/core/framework/attr_map.h" +#include "oneflow/core/framework/op_builder.h" +#include "oneflow/core/framework/op_expr.h" +#include "oneflow/core/framework/op_interpreter/op_interpreter_util.h" +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/functional/function_library.h" +#include "oneflow/core/functional/scalar.h" + +namespace oneflow { +namespace one { +namespace functional { + +namespace impl { + +class AddFunctor { + public: + AddFunctor() { + add_op_ = CHECK_JUST(one::OpBuilder("add_n").Input("in", 2).Output("out").Build()); + } + Maybe<Tensor> operator()(const std::shared_ptr<one::Tensor>& x, + const std::shared_ptr<one::Tensor>& y) const { + return OpInterpUtil::Dispatch<Tensor>(*add_op_, {x, y}); + } + + private: + std::shared_ptr<OpExpr> add_op_; +}; + +class AddNFunctor { + public: + AddNFunctor() { + add_n_op_.resize(128 /*the maximum number of inputs*/); + for (int n = 2; n < add_n_op_.size(); ++n) { + add_n_op_[n] = CHECK_JUST(one::OpBuilder("add_n").Input("in", n).Output("out").Build()); + } + } + Maybe<Tensor> operator()(const TensorTuple& inputs) const { + CHECK_GE_OR_RETURN(inputs.size(), 2); + CHECK_LT_OR_RETURN(inputs.size(), add_n_op_.size()) + << "The maximum number supported of inputs is " << add_n_op_.size(); + return OpInterpUtil::Dispatch<Tensor>(*add_n_op_.at(inputs.size()), inputs); + } + + private: + std::vector<std::shared_ptr<OpExpr>> add_n_op_; +}; + +class AddScalarFunctor { + public: + AddScalarFunctor() { + add_scalar_op_ = CHECK_JUST(one::OpBuilder("scalar_add").Input("in").Output("out").Build()); + } + Maybe<Tensor> operator()(const std::shared_ptr<one::Tensor>& x, const Scalar& scalar) const { + MutableAttrMap attrs; + if (scalar.IsFloatingPoint()) { + JUST(attrs.SetAttr<double>("float_operand", JUST(scalar.As<double>()))); + JUST(attrs.SetAttr<bool>("has_float_operand", true)); + JUST(attrs.SetAttr<bool>("has_int_operand", false)); + return OpInterpUtil::Dispatch<Tensor>(*add_scalar_op_, {x}, attrs); + } else if (scalar.IsIntegral()) { + JUST(attrs.SetAttr<int64_t>("int_operand", JUST(scalar.As<int64_t>()))); + JUST(attrs.SetAttr<bool>("has_float_operand", false)); + JUST(attrs.SetAttr<bool>("has_int_operand", true)); + return OpInterpUtil::Dispatch<Tensor>(*add_scalar_op_, {x}, attrs); + } else { + UNIMPLEMENTED_THEN_RETURN(); + } + } + + private: + std::shared_ptr<OpExpr> add_scalar_op_; +}; + +} // namespace impl + +ONEFLOW_FUNCTION_LIBRARY(m) { + m.add_functor<impl::AddFunctor>("Add"); + m.add_functor<impl::AddNFunctor>("AddN"); + m.add_functor<impl::AddScalarFunctor>("AddScalar"); +}; + +} // namespace functional +} // namespace one +} // namespace oneflow diff --git a/oneflow/core/functional/impl/normalization_functor.cpp b/oneflow/core/functional/impl/normalization_functor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec3c302a9b781d881f999abc27cb833669a303a1 --- /dev/null +++ b/oneflow/core/functional/impl/normalization_functor.cpp @@ -0,0 +1,88 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "oneflow/core/framework/attr_map.h" +#include "oneflow/core/framework/op_builder.h" +#include "oneflow/core/framework/op_expr.h" +#include "oneflow/core/framework/op_interpreter/op_interpreter_util.h" +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/functional/function_library.h" +#include "oneflow/core/functional/scalar.h" + +namespace oneflow { +namespace one { +namespace functional { + +namespace impl { + +class NormalizationFunctor { + public: + NormalizationFunctor() { + norm_eval_op_ = CHECK_JUST(one::OpBuilder("normalization") + .Input("x") + .Input("moving_mean") + .Input("moving_variance") + .Input("gamma") + .Input("beta") + .Output("y") + .Attr("training", false) + .Build()); + norm_training_op_ = CHECK_JUST(one::OpBuilder("normalization") + .Input("x") + .Input("moving_mean") + .Input("moving_variance") + .Input("gamma") + .Input("beta") + .Output("y") + .Output("mean") + .Output("inv_variance") + .Attr("training", true) + .Build()); + } + Maybe<Tensor> operator()(const std::shared_ptr<one::Tensor>& x, + const std::shared_ptr<one::Tensor>& moving_mean, + const std::shared_ptr<one::Tensor>& moving_variance, + const std::shared_ptr<one::Tensor>& gamma, + const std::shared_ptr<one::Tensor>& beta, const int32_t& axis, + const float& epsilon, const float& momentum, + const bool& is_training) const { + MutableAttrMap attrs; + JUST(attrs.SetAttr<int32_t>("axis", axis)); + JUST(attrs.SetAttr<float>("epsilon", epsilon)); + JUST(attrs.SetAttr<float>("momentum", momentum)); + std::shared_ptr<OpExpr> op; + if (is_training) { + op = norm_training_op_; + } else { + op = norm_eval_op_; + } + return OpInterpUtil::Dispatch<one::Tensor>(*op, {x, moving_mean, moving_variance, gamma, beta}, + attrs); + } + + private: + std::shared_ptr<OpExpr> norm_eval_op_; + std::shared_ptr<OpExpr> norm_training_op_; +}; + +} // namespace impl + +ONEFLOW_FUNCTION_LIBRARY(m) { m.add_functor<impl::NormalizationFunctor>("Normalization"); }; + +} // namespace functional +} // namespace one +} // namespace oneflow diff --git a/oneflow/core/functional/packed_functor.h b/oneflow/core/functional/packed_functor.h new file mode 100644 index 0000000000000000000000000000000000000000..206153d3e431786ebfc1592f23b35b2689ae9b33 --- /dev/null +++ b/oneflow/core/functional/packed_functor.h @@ -0,0 +1,113 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef ONEFLOW_CORE_FUNCTIONAL_FUNCTOR_H_ +#define ONEFLOW_CORE_FUNCTIONAL_FUNCTOR_H_ + +#include <memory> + +#include "oneflow/core/common/function_traits.h" +#include "oneflow/core/common/type_traits.h" +#include "oneflow/core/functional/value_types.h" +#include "oneflow/core/functional/function_signature.h" + +namespace oneflow { +namespace one { +namespace functional { + +template<typename T> +using remove_cvref_t = oneflow::detail::remove_cvref_t<T>; + +struct FunctionBody { + virtual operator void*() = 0; +}; + +template<typename T> +class FunctionBodyImpl; + +template<typename R, typename... Args> +class FunctionBodyImpl<R(Args...)> : public FunctionBody { + public: + template<typename Func, + typename std::enable_if< + std::is_same<typename function_traits<Func>::func_type, R(Args...)>::value, + int>::type = 0> + FunctionBodyImpl(const Func& func) { + func_ = [func](const remove_cvref_t<Args>&... args) { + return func(std::forward<const remove_cvref_t<Args>&>(args)...); + }; + } + + operator void*() override { return &func_; } + + private: + std::function<R(const remove_cvref_t<Args>&...)> func_; +}; + +class Functor { + public: + Functor(const std::shared_ptr<FunctionBody>& body, const FunctionSignature& signatrue) + : signatrue_(signatrue), body_(body) {} + + template<typename R, typename... Args> + R call(const remove_cvref_t<Args>&... args) const { + if (!detail::CheckSignature<R(Args...)>(signatrue_).Ok()) { + LOG(FATAL) << "The function was called with wrong arguments."; + } + using FuncType = std::function<R(const remove_cvref_t<Args>&...)>; + auto* func = reinterpret_cast<FuncType*>(body_->operator void*()); + return (*func)(std::forward<const remove_cvref_t<Args>&>(args)...); + } + + private: + FunctionSignature signatrue_; + std::shared_ptr<FunctionBody> body_; +}; + +class PackedFunctor { + public: + virtual ~PackedFunctor() = default; + + template<typename Func> + static PackedFunctor Make(const std::string& func_name, const Func& func); + + template<typename R, typename... Args> + R call(Args... args) const { + return functor_.call<R, Args...>(std::forward<Args>(args)...); + } + + private: + PackedFunctor(const std::string& func_name, const Functor& functor) + : func_name_(func_name), functor_(functor) {} + + std::string func_name_; + Functor functor_; +}; + +template<typename Func> +PackedFunctor PackedFunctor::Make(const std::string& func_name, const Func& func) { + using func_type = typename function_traits<Func>::func_type; + auto body = std::make_shared<FunctionBodyImpl<func_type>>(func); + FunctionSignature signatute = detail::PackFunctionSignature<func_type>::pack(); + Functor functor(body, signatute); + return PackedFunctor(func_name, std::move(functor)); +} + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_FUNCTOR_H_ diff --git a/oneflow/core/functional/scalar.h b/oneflow/core/functional/scalar.h new file mode 100644 index 0000000000000000000000000000000000000000..89e508926e7f856d364f8febd4a53decbd844cc9 --- /dev/null +++ b/oneflow/core/functional/scalar.h @@ -0,0 +1,78 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef ONEFLOW_CORE_FUNCTIONAL_SCALAR_H_ +#define ONEFLOW_CORE_FUNCTIONAL_SCALAR_H_ + +#include <type_traits> + +#include "oneflow/core/common/maybe.h" +#include "oneflow/core/functional/value_types.h" + +namespace oneflow { +namespace one { +namespace functional { + +class Scalar { + public: + Scalar() : Scalar(int32_t(0)) {} + + template<typename T, typename std::enable_if< + std::is_integral<T>::value && std::is_signed<T>::value, int>::type = 0> + explicit Scalar(const T& value) + : value_type_(ValueTypeOf<T>()), value_{.s = value}, active_tag_(HAS_S) {} + + template<typename T, typename std::enable_if< + std::is_integral<T>::value && std::is_unsigned<T>::value, int>::type = 0> + explicit Scalar(const T& value) + : value_type_(ValueTypeOf<T>()), value_{.u = value}, active_tag_(HAS_U) {} + + template<typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> + explicit Scalar(const T& value) + : value_type_(ValueTypeOf<T>()), value_{.d = value}, active_tag_(HAS_D) {} + + const ValueType& type() const { return value_type_; } + + template<typename T, typename std::enable_if<std::is_scalar<T>::value, int>::type = 0> + Maybe<T> As() const { + switch (active_tag_) { + case HAS_S: return static_cast<T>(value_.s); + case HAS_U: return static_cast<T>(value_.u); + case HAS_D: return static_cast<T>(value_.d); + default: UNIMPLEMENTED_THEN_RETURN() << "The scalar has not been initialized."; + } + } + + bool IsIntegral() const { return active_tag_ == HAS_S || active_tag_ == HAS_U; } + bool IsFloatingPoint() const { return active_tag_ == HAS_D; } + bool IsSigned() const { return active_tag_ == HAS_S || active_tag_ == HAS_D; } + bool IsUnsigned() const { return active_tag_ == HAS_U; } + + private: + ValueType value_type_; + union Value { + int64_t s; + uint64_t u; + double d; + } value_; + enum { HAS_S, HAS_U, HAS_D, HAS_NONE } active_tag_; +}; + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_SCALAR_H_ diff --git a/oneflow/core/functional/value_types.h b/oneflow/core/functional/value_types.h new file mode 100644 index 0000000000000000000000000000000000000000..4d0f1b0ad21ab52009e861d43d90ee2d18f3986b --- /dev/null +++ b/oneflow/core/functional/value_types.h @@ -0,0 +1,121 @@ +/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef ONEFLOW_CORE_FUNCTIONAL_VALUE_TYPES_H_ +#define ONEFLOW_CORE_FUNCTIONAL_VALUE_TYPES_H_ + +#include <memory> + +#include "oneflow/core/common/data_type.pb.h" +#include "oneflow/core/common/maybe.h" + +namespace oneflow { +class Shape; +class AttrMap; + +namespace cfg { +class AttrValue; +} // namespace cfg + +namespace one { +class Tensor; +class TensorTuple; + +namespace functional { +class Scalar; +} // namespace functional +} // namespace one + +namespace one { +namespace functional { + +enum ValueType { + kINVALID = 0, + kVOID, + kINT32, + kUINT32, + kINT64, + kUINT64, + kFLOAT, + kDOUBLE, + kBOOL, + kSTRING, + kINT32_LIST, + kUINT32_LIST, + kINT64_LIST, + kUINT64_LIST, + kFLOAT_LIST, + kDOUBLE_LIST, + kBOOL_LIST, + kSTRING_LIST, + kSCALAR, + kTENSOR, + kTENSOR_REF, + kTENSOR_MAYBE, + kTENSOR_TUPLE, + kTENSOR_TUPLE_REF, + kTENSOR_TUPLE_MAYBE, + kATTR, + kATTR_REF, + kATTR_MAP, + kDTYPE, + kSHAPE, +}; + +#define VALUE_TYPE_OF_IMPL(cpp_type, value_type) \ + template<typename T, typename std::enable_if<std::is_same<T, cpp_type>::value, int>::type = 0> \ + inline ValueType ValueTypeOf() { \ + return value_type; \ + } + +VALUE_TYPE_OF_IMPL(void, kVOID); +VALUE_TYPE_OF_IMPL(int32_t, kINT32); +VALUE_TYPE_OF_IMPL(uint32_t, kUINT32); +VALUE_TYPE_OF_IMPL(int64_t, kINT64); +VALUE_TYPE_OF_IMPL(uint64_t, kUINT64); +VALUE_TYPE_OF_IMPL(float, kFLOAT); +VALUE_TYPE_OF_IMPL(double, kDOUBLE); +VALUE_TYPE_OF_IMPL(bool, kBOOL); +VALUE_TYPE_OF_IMPL(std::string, kSTRING); +VALUE_TYPE_OF_IMPL(std::vector<int32_t>, kINT32_LIST); +VALUE_TYPE_OF_IMPL(std::vector<uint32_t>, kUINT32_LIST); +VALUE_TYPE_OF_IMPL(std::vector<int64_t>, kINT64_LIST); +VALUE_TYPE_OF_IMPL(std::vector<uint64_t>, kUINT64_LIST); +VALUE_TYPE_OF_IMPL(std::vector<float>, kFLOAT_LIST); +VALUE_TYPE_OF_IMPL(std::vector<double>, kDOUBLE_LIST); +VALUE_TYPE_OF_IMPL(std::vector<bool>, kBOOL_LIST); +VALUE_TYPE_OF_IMPL(std::vector<std::string>, kSTRING_LIST); + +VALUE_TYPE_OF_IMPL(Scalar, kSCALAR); +VALUE_TYPE_OF_IMPL(one::Tensor, kTENSOR); +VALUE_TYPE_OF_IMPL(std::shared_ptr<one::Tensor>, kTENSOR_REF); +VALUE_TYPE_OF_IMPL(Maybe<one::Tensor>, kTENSOR_MAYBE); +VALUE_TYPE_OF_IMPL(one::TensorTuple, kTENSOR_TUPLE); +VALUE_TYPE_OF_IMPL(std::shared_ptr<one::TensorTuple>, kTENSOR_TUPLE_REF); +VALUE_TYPE_OF_IMPL(Maybe<one::TensorTuple>, kTENSOR_TUPLE_MAYBE); +VALUE_TYPE_OF_IMPL(cfg::AttrValue, kATTR); +VALUE_TYPE_OF_IMPL(std::shared_ptr<cfg::AttrValue>, kATTR_REF); +VALUE_TYPE_OF_IMPL(AttrMap, kATTR_MAP); +VALUE_TYPE_OF_IMPL(DataType, kDTYPE); +VALUE_TYPE_OF_IMPL(Shape, kSHAPE); + +#undef VALUE_TYPE_OF_IMPL + +} // namespace functional +} // namespace one +} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_VALUE_TYPES_H_ diff --git a/oneflow/python/framework/functional.py b/oneflow/python/framework/functional.py new file mode 100644 index 0000000000000000000000000000000000000000..af726befbca345249583d82f7bd427de36f04835 --- /dev/null +++ b/oneflow/python/framework/functional.py @@ -0,0 +1,69 @@ +""" +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import oneflow as flow +import oneflow._oneflow_internal + + +def RecursveDetermine(arg): + if isinstance(arg, flow.Tensor): + if not arg.is_determined: + arg.determine() + return arg._local_or_consistent_tensor + elif isinstance(arg, list) or isinstance(arg, tuple): + arg = list(arg) + for i in range(len(arg)): + arg[i] = RecursveDetermine(arg[i]) + return arg + elif isinstance(arg, dict): + for k, v in arg.items(): + arg[k] = RecursveDetermine(v) + else: + return arg + + +class Function: + def __init__(self, func_name, handle): + self.func_name = func_name + self.handle = handle + + def __call__(self, *args, **kwargs): + args = list(args) + for i in range(len(args)): + args[i] = RecursveDetermine(args[i]) + for k, v in kwargs.items(): + kwargs[k] = RecursveDetermine(v) + return self.handle(*args, **kwargs) + + +def RegisterFunctionalApis(): + import inspect + import oneflow.F + + for s in dir(oneflow._oneflow_internal.F): + f = getattr(oneflow._oneflow_internal.F, s) + if inspect.isbuiltin(f): + func_name = s + if s in _function_name_aliases: + func_name = _function_name_aliases[s] + setattr(oneflow.F, func_name, Function(func_name, f)) + setattr(oneflow.F, s, Function(func_name, f)) + + del inspect + + +_function_name_aliases = { + "add_scalar": "scalar_add", +} diff --git a/oneflow/python/framework/register_class_method_util.py b/oneflow/python/framework/register_class_method_util.py index 94774f0872369a966a63d366211f96e55f510eaf..93e2fcd8224c38fdf218c53cf7639580fb48eb88 100644 --- a/oneflow/python/framework/register_class_method_util.py +++ b/oneflow/python/framework/register_class_method_util.py @@ -15,6 +15,7 @@ limitations under the License. """ import oneflow.python.eager.eager_blob_util as eager_blob_util import oneflow.python.framework.op_expr_util as op_expr_util +import oneflow.python.framework.functional as functional import oneflow.python.framework.remote_blob as remote_blob_util import oneflow.python.framework.blob_trait as blob_trait import oneflow._oneflow_internal @@ -22,6 +23,7 @@ import oneflow._oneflow_internal def RegisterMethod4Class(): op_expr_util.RegisterMethod4UserOpExpr() + functional.RegisterFunctionalApis() eager_blob_util.RegisterMethod4EagerPhysicalBlob() diff --git a/oneflow/python/nn/modules/batchnorm.py b/oneflow/python/nn/modules/batchnorm.py index 07657a098831e4c470ba6c4f392d3c10b3f98649..5f4613442b6bcc59387e9713831843158fd8be05 100644 --- a/oneflow/python/nn/modules/batchnorm.py +++ b/oneflow/python/nn/modules/batchnorm.py @@ -101,36 +101,6 @@ class _BatchNorm(_NormBase): track_running_stats=True, ): super().__init__(num_features, eps, momentum, affine, track_running_stats) - self._training_op = ( - flow.builtin_op("normalization") - .Input("x") - .Input("moving_mean") - .Input("moving_variance") - .Input("gamma") - .Input("beta") - .Attr("axis", 1) - .Attr("epsilon", eps) - .Attr("momentum", momentum) - .Output("y") - .Output("mean") - .Output("inv_variance") - .Attr("training", True) - .Build() - ) - self._testing_op = ( - flow.builtin_op("normalization") - .Input("x") - .Input("moving_mean") - .Input("moving_variance") - .Input("gamma") - .Input("beta") - .Attr("axis", 1) - .Attr("epsilon", eps) - .Attr("momentum", momentum) - .Output("y") - .Attr("training", False) - .Build() - ) def forward(self, x): self._check_input_dim(x) @@ -191,15 +161,17 @@ class _BatchNorm(_NormBase): return affined else: - if self.training: - res = self._training_op( - x, self.running_mean, self.running_var, self.weight, self.bias - )[0] - else: - res = self._testing_op( - x, self.running_mean, self.running_var, self.weight, self.bias - )[0] - return res + return flow.F.normalization( + x, + self.running_mean, + self.running_var, + self.weight, + self.bias, + axis=1, + epsilon=self.eps, + momentum=self.momentum, + is_training=self.training, + ) @oneflow_export("nn.BatchNorm1d") diff --git a/oneflow/python/nn/modules/math_ops.py b/oneflow/python/nn/modules/math_ops.py index c335359a2c1d2f17767412718517626c6f9f60ff..9a720c555aaf56a4a7d60d2de2a4fca35ec2a400 100644 --- a/oneflow/python/nn/modules/math_ops.py +++ b/oneflow/python/nn/modules/math_ops.py @@ -356,31 +356,14 @@ class BroadcastSub(Module): class ScalarAdd(Module): - def __init__(self, operand) -> None: + def __init__(self, alpha) -> None: super().__init__() - self._op = flow.builtin_op("scalar_add").Input("in").Output("out") - - if isinstance(operand, int): - self._op = ( - self._op.Attr("has_int_operand", True) - .Attr("has_float_operand", False) - .Attr("int_operand", operand) - .Attr("float_operand", 0.0) - .Build() - ) - elif isinstance(operand, float): - self._op = ( - self._op.Attr("has_int_operand", False) - .Attr("has_float_operand", True) - .Attr("int_operand", 0) - .Attr("float_operand", operand) - .Build() - ) - else: - raise ValueError("operand type can only be int or float") + if not isinstance(alpha, int) and not isinstance(alpha, float): + raise ValueError("scalar type can only be int or float") + self.alpha = alpha def forward(self, x): - return self._op(x)[0] + return flow.F.add_scalar(x, self.alpha) @oneflow_export("sub") @@ -576,10 +559,9 @@ class ScalarAddByTensor(Module): class ElementwiseAdd(Module): def __init__(self) -> None: super().__init__() - self._op = flow.builtin_op("add_n").Input("in", 2).Output("out").Build() def forward(self, x, y): - return self._op(x, y)[0] + return flow.F.add(x, y) class BroadcastAdd(Module): diff --git a/tools/generate_functional_api.py b/tools/generate_functional_api.py new file mode 100644 index 0000000000000000000000000000000000000000..630f5e3eada517bf976ea79a1dacb82d10e0bd58 --- /dev/null +++ b/tools/generate_functional_api.py @@ -0,0 +1,465 @@ +""" +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import os +import re +import argparse +import yaml + +parser = argparse.ArgumentParser() +parser.add_argument( + "--yaml_file_path", + type=str, + default="oneflow/core/functional/functional_api.yaml", + help="The yaml format file that helps to generate the functional api or pybind cpp.", +) +parser.add_argument( + "--generate_pybind", + action="store_true", + default=False, + help="Enable to generate functional pybind cpp files.", +) +args = parser.parse_args() + +api_generate_dir = "oneflow/core/functional" +pybind_generate_dir = "oneflow/api/python/functional" + +license = """/* +Copyright 2020 The OneFlow Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the \"License\"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an \"AS IS\" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Generated from oneflow/core/functional/functional_api.yaml. DO NOT EDIT!""" + +header_fmt = ( + license + + """ + +#ifndef ONEFLOW_CORE_FUNCTIONAL_GENERATED_FUNCTIONAL_API_H_ +#define ONEFLOW_CORE_FUNCTIONAL_GENERATED_FUNCTIONAL_API_H_ + +#include "oneflow/core/framework/tensor.h" +#include "oneflow/core/framework/tensor_tuple.h" +#include "oneflow/core/functional/scalar.h" + +namespace oneflow {{ +namespace one {{ +namespace functional {{ +{0} +}} // namespace functional +}} // namespace one +}} // namespace oneflow + +#endif // ONEFLOW_CORE_FUNCTIONAL_GENERATED_FUNCTIONAL_API_H_""" +) + +source_fmt = ( + license + + """ + +#include "{0}/functional_api.yaml.h" +#include "oneflow/core/functional/function_library.h" + +namespace oneflow {{ +namespace one {{ +namespace functional {{ +{1} +}} // namespace functional +}} // namespace one +}} // namespace oneflow +""" +) + +pybind_fmt = ( + license + + """ + +#include <vector> +#include <pybind11/pybind11.h> + +#include "oneflow/api/python/of_api_registry.h" +#include "oneflow/api/python/functional/function_def.h" +#include "oneflow/api/python/functional/py_function.h" +#include "oneflow/core/common/maybe.h" +#include "oneflow/core/functional/functional.h" + +namespace oneflow {{ +namespace one {{ +namespace functional {{ +{0} +}} // namespace functional +}} // namespace one + +namespace functional = one::functional; + +ONEFLOW_API_PYBIND11_MODULE("F", m) {{ +{1} +}} + +}} // namespace oneflow +""" +) + +types_allowed = { + "Tensor", + "TensorTuple", + "Scalar", + "Int", + "Int32", + "Int64", + "Float", + "Double", + "String", + "Bool", + "ScalarList", + "IntList", + "Int32List", + "Int64List", + "FloatList", + "DoubleList", + "StringList", + "BoolList", + "DataType", + "Shape", +} + +generic_type_aliases = { + "Int": "int32_t", + "Int32": "int32_t", + "Int64": "int64_t", + "Float": "float", + "Double": "double", + "Bool": "bool", +} + +argument_type_aliases = { + "Tensor": "const std::shared_ptr<one::Tensor>&", + "TensorTuple": "const TensorTuple&", + "Scalar": "const Scalar&", + "ScalarList": "const std::vector<Scalar>&", + "IntList": "const std::vector<int32_t>&", + "Int32List": "const std::vector<int32_t>&", + "Int64List": "const std::vector<int64_t>&", + "FloatList": "const std::vector<float>&", + "DoubleList": "const std::vector<double>&", + "String": "const std::string&", + "StringList": "const std::vector<std::string>&", + "BoolList": "const std::vector<bool>&", + "DataType": "const DataType&", + "Shape": "const Shape&", + **generic_type_aliases, +} + +return_type_aliases = { + "Tensor": "Maybe<one::Tensor>", + "TensorTuple": "Maybe<one::TensorTuple>", + "String": "std::string", + **generic_type_aliases, +} + +value_aliases = { + "True": "true", + "False": "false", +} + + +def _escape_quote(fmt): + return re.sub(r"\"|\'", '\\"', fmt) + + +def _normalize(fmt): + fmt = fmt.strip() + return re.sub(r"\s+", " ", fmt) + + +def _std_decay(fmt): + fmt = fmt.strip() + fmt = re.sub(r"(const|&)", "", fmt) + return _normalize(fmt) + + +def parse_function_params(fmt): + params = [] + fmt = _normalize(fmt) + if not fmt.endswith(")"): + raise ValueError('Function def should end with ")": ' + fmt) + open_paren = fmt.find("(") + if open_paren == -1: + raise ValueError('Missing "(" in function def: ' + fmt) + + header = fmt[0:open_paren] + items = header.split(" ") + if (len(items)) != 2: + raise ValueError("Missing return type or function name in function def: " + fmt) + params.append(items[0]) + function_name = items[1] + + tail = fmt[open_paren + 1 : -1] + # TODO(): Parse the parameter list more comprehensively. + items = tail.split(",") + for param in items: + params.append(_normalize(param)) + return function_name, params + + +class Argument: + def __init__(self, fmt, keyword_allowed=False): + self._keyword_allowed = keyword_allowed + self._type = None + self._name = None + self._default_value = None + + fmt = _normalize(fmt) + sp = fmt.rfind(" ") + if sp == -1: + raise ValueError("Missing argument type or name for argument def: " + fmt) + self._type = _normalize(fmt[0:sp]) + assert self._type in types_allowed, "Unknow type: " + self._type + + if self._type in argument_type_aliases: + self._cpp_type = argument_type_aliases[self._type] + else: + self._cpp_type = self._type + self._name = _normalize(fmt[sp + 1 :]) + sp = self._name.find("=") + if sp != -1: + self._default_value = _normalize(self._name[sp + 1 :]) + if self._default_value in value_aliases: + self._default_cpp_value = value_aliases[self._default_value] + else: + self._default_cpp_value = self._default_value + self._name = _normalize(self._name[0:sp]) + + @property + def has_default_value(self): + return self._default_value is not None + + def to_string(self, to_cpp=False): + fmt = "{0} {1}".format(self._cpp_type if to_cpp else self._type, self._name) + if not to_cpp and self.has_default_value: + fmt += "={0}".format(self._default_value) + return fmt + + +class Return: + def __init__(self, fmt): + self._type = _normalize(fmt) + assert self._type in types_allowed, "Unknow type: " + self._type + + if self._type in return_type_aliases: + self._cpp_type = return_type_aliases[self._type] + else: + self._cpp_type = self._type + + @property + def type(self): + return self._type + + def to_string(self, to_cpp=False): + return self._cpp_type if to_cpp else self._type + + +class FunctionSignature: + def __init__(self, fmt): + self._fmt = fmt + self._name, self._params = parse_function_params(fmt) + self._ret = Return(self._params[0]) + keyword_allowed = False + self._args = [] + for arg in self._params[1:]: + if arg == "*": + keyword_allowed = True + continue + self._args.append(Argument(arg, keyword_allowed=keyword_allowed)) + + self._max_args_count = len(self._args) + self._max_positional_args_count = self._max_args_count + count = 0 + for arg in self._args: + if arg._keyword_allowed: + count += 1 + self._max_keyword_args_count = count + + @property + def num_of_args(): + return len(self._args) + + def to_string(self, to_cpp=False): + fmt = "{0} {1}(".format(self._ret.to_string(to_cpp=to_cpp), self._name) + keyword_start = False + for i, arg in enumerate(self._args): + if i > 0 and i < len(self._args): + fmt += ", " + if not keyword_start and arg._keyword_allowed: + keyword_start = True + if not to_cpp: + fmt += "*, " + fmt += arg.to_string(to_cpp=to_cpp) + fmt += ")" + return fmt + + +class Block: + def __init__(self, name, signature, bind_python): + self._name = name + self._signature = signature + self._bind_python = bind_python + + +class FunctionalGenerator: + def __init__(self, input_file): + self._blocks = {} + with open(input_file) as f: + doc = yaml.load(f, Loader=yaml.FullLoader) + for block in doc: + assert "name" in block + assert "signature" in block + name = block["name"] + signature = block["signature"] + bind_python = False + if "bind_python" in block: + bind_python = block["bind_python"] + self._blocks[name] = Block( + name, FunctionSignature(signature), bind_python + ) + + def generate_cpp_header_file(self, target_header_file): + fmt = "" + for name, block in self._blocks.items(): + fmt += "\n" + fmt += block._signature.to_string(to_cpp=True) + fmt += ";\n" + + with open(target_header_file, "w") as f: + f.write(header_fmt.format(fmt)) + + def generate_cpp_source_file(self, target_source_file): + fmt = "" + for name, block in self._blocks.items(): + signature = block._signature + fmt += "\n" + fmt += signature.to_string(to_cpp=True) + fmt += " {\n" + fmt += ' static thread_local const auto& op = CHECK_JUST(FunctionLibrary::Global()->find("{0}"));\n'.format( + signature._name + ) + fmt += " return op->call<{0}, {1}>({2});\n".format( + signature._ret._cpp_type, + ", ".join([arg._cpp_type for arg in signature._args]), + ", ".join([arg._name for arg in signature._args]), + ) + fmt += "}\n" + + with open(target_source_file, "w") as f: + f.write(source_fmt.format(api_generate_dir, fmt)) + + def generate_pybind_for_python(self, target_pybind_source_file): + schema_fmt = "" + module_fmt = "" + for name, block in self._blocks.items(): + if not block._bind_python: + continue + signature = block._signature + return_type = signature._ret._cpp_type + schema_fmt += "\n" + schema_fmt += "struct {0}Schema {{\n".format(signature._name) + schema_fmt += " using FType = decltype(functional::{0});\n".format( + signature._name + ) + schema_fmt += " using R = {0};\n".format(return_type) + schema_fmt += "\n" + schema_fmt += " static constexpr FType* func = &functional::{0};\n".format( + signature._name + ) + schema_fmt += " static constexpr size_t max_args = {0};\n".format( + signature._max_args_count + ) + schema_fmt += " static constexpr size_t max_positionals = {0};\n".format( + signature._max_positional_args_count + ) + schema_fmt += " static constexpr size_t max_keywords = {0};\n".format( + signature._max_keyword_args_count + ) + schema_fmt += ' static constexpr char const* signature = "{0}";\n'.format( + _escape_quote(signature.to_string()) + ) + schema_fmt += " static ReturnDef return_def;\n" + schema_fmt += " static std::vector<ArgumentDef> argument_def;\n" + schema_fmt += "};\n" + schema_fmt += "\n" + schema_fmt += "ReturnDef {0}Schema::return_def = ReturnDef(ValueTypeOf<{1}>());\n".format( + signature._name, return_type, + ) + + argument_def = [] + for arg in signature._args: + if arg.has_default_value: + argument_def.append( + 'ArgumentDef("{0}", {1}({2}))'.format( + arg._name, _std_decay(arg._cpp_type), arg._default_cpp_value + ) + ) + else: + argument_def.append( + 'ArgumentDef("{0}", ValueTypeOf<{1}>())'.format( + arg._name, _std_decay(arg._cpp_type) + ) + ) + + schema_fmt += "std::vector<ArgumentDef> {0}Schema::argument_def = {{{1}}};\n".format( + signature._name, ", ".join(argument_def) + ) + module_fmt += ' m.def("{0}", &functional::PyFunction<functional::{1}Schema>);\n'.format( + name, signature._name + ) + + with open(target_pybind_source_file, "w") as f: + f.write(pybind_fmt.format(schema_fmt, module_fmt)) + + +if __name__ == "__main__": + assert os.path.isfile(args.yaml_file_path), ( + "It is not a regular file for the yaml file which is " + args.yaml_file_path + ) + g = FunctionalGenerator(args.yaml_file_path) + + assert os.path.isdir(api_generate_dir), ( + "Could not locate the api generate directory which is " + api_generate_dir + ) + target_header_file = os.path.join(api_generate_dir, "functional_api.yaml.h") + g.generate_cpp_header_file(target_header_file) + target_source_file = os.path.join(api_generate_dir, "functional_api.yaml.cpp") + g.generate_cpp_source_file(target_source_file) + + if args.generate_pybind: + assert os.path.isdir(pybind_generate_dir), ( + "Could not locate the pybind generate directory which is " + + pybind_generate_dir + ) + target_pybind_source_file = os.path.join( + pybind_generate_dir, "functional_api.yaml.pybind.cpp" + ) + g.generate_pybind_for_python(target_pybind_source_file)