Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support numpy scalar parameters #7935

Merged
merged 26 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
76ce1a3
feat(functional): support numpy scalar parameters
wyg1997 Mar 31, 2022
d05101d
rename inferface
wyg1997 Mar 31, 2022
87a707c
Merge branch 'master' into feat-param_support_np_scalar
wyg1997 Mar 31, 2022
ce6dd6a
feat(*): TensorIndex support numpy scalar
wyg1997 Mar 31, 2022
551e434
feat(TensorIndex): support advance indexing
wyg1997 Mar 31, 2022
aaeb387
Merge branch 'master' into feat-param_support_np_scalar
marigoold Apr 1, 2022
f94b8de
Merge branch 'master' into feat-param_support_np_scalar
marigoold Apr 2, 2022
e3e44f3
add unittest and int32 support for branch feat-param_support_np_scala…
marigoold Apr 2, 2022
77da008
Merge branch 'master' into feat-param_support_np_scalar
wyg1997 Apr 2, 2022
f1d7094
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 5, 2022
bb9815e
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 5, 2022
d79d35f
auto format by CI
oneflow-ci-bot Apr 6, 2022
7e5d0a7
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 6, 2022
a4a2bee
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 6, 2022
0b8ef72
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 6, 2022
cae1bc2
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 7, 2022
518ddb4
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 7, 2022
b1ac66b
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 7, 2022
dba4847
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 7, 2022
c47c881
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 7, 2022
60aa0ac
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 8, 2022
c8bfccf
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 8, 2022
5c0d1cc
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 8, 2022
8456845
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 8, 2022
4e82982
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 9, 2022
3b594e7
Merge branch 'master' into feat-param_support_np_scalar
mergify[bot] Apr 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions oneflow/api/python/functional/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ limitations under the License.
#include <string>

#include "oneflow/api/python/functional/indexing.h"
#include "oneflow/extension/python/numpy.h"
#include "oneflow/core/common/just.h"
#include "oneflow/core/common/scalar.h"
#include "oneflow/core/framework/dtype.h"
Expand Down Expand Up @@ -195,18 +196,20 @@ Maybe<std::vector<Symbol<SbpParallel>>> PyUnpackSbpParallelSequence(PyObject* ob

// Tensor index
bool PyTensorIndexCheck(PyObject* obj) {
return PySlice_Check(obj) || PyLong_Check(obj) || obj == Py_Ellipsis || obj == Py_None
|| PyTensorCheck(obj) || PySequence_Check(obj) || PyUnicode_Check(obj);
return PySlice_Check(obj) || PyLong_Check(obj) || numpy::PyArrayCheckLongScalar(obj)
|| obj == Py_Ellipsis || obj == Py_None || PyTensorCheck(obj) || PySequence_Check(obj)
|| PyUnicode_Check(obj);
}
Maybe<TensorIndex> PyUnpackTensorIndex(PyObject* obj) {
auto tensor_index = std::make_shared<TensorIndex>();
// Obvious single-entry cases.
if (PySlice_Check(obj) // NOLINT
|| PyLong_Check(obj) // NOLINT
|| obj == Py_Ellipsis // NOLINT
|| obj == Py_None // NOLINT
|| PyTensorCheck(obj) // NOLINT
|| !PySequence_Check(obj) // NOLINT
if (PySlice_Check(obj) // NOLINT
|| PyLong_Check(obj) // NOLINT
|| numpy::PyArrayCheckLongScalar(obj) // NOLINT
|| obj == Py_Ellipsis // NOLINT
|| obj == Py_None // NOLINT
|| PyTensorCheck(obj) // NOLINT
|| !PySequence_Check(obj) // NOLINT
|| PyUnicode_Check(obj)) {
tensor_index->emplace_back(*JUST(detail::UnpackIndexItem(obj)));
return tensor_index;
Expand Down
17 changes: 14 additions & 3 deletions oneflow/api/python/functional/indexing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Maybe<DataType> InferScalarType(PyObject* object) {
return DataType::kInt64;
} else if (PyArray_Check(object)) {
return numpy::GetOFDataTypeFromNpArray(reinterpret_cast<PyArrayObject*>(object));
} else if (PyArray_CheckScalar(object)) {
return numpy::NumpyTypeToOFDataType(PyArray_DescrFromScalar(object)->type_num);
} else if (PySequence_Check(object)) {
int64_t length = PySequence_Length(object);
CHECK_GT_OR_RETURN(length, 0) << "Index should not be empty.";
Expand All @@ -86,13 +88,20 @@ Maybe<DataType> InferScalarType(PyObject* object) {

Maybe<void> ParseScalar(PyObject* object, char* data, const DataType& dtype) {
if (dtype == DataType::kInt64) {
CHECK_OR_RETURN(PyLong_Check(object)) << "Expected a long value.";
CHECK_OR_RETURN(PyLong_Check(object) || numpy::PyArrayCheckLongScalar(object))
<< "Expected a long value.";
*(reinterpret_cast<int64_t*>(data)) = PyLong_AsLongLong(object);
return Maybe<void>::Ok();
} else if (dtype == DataType::kInt32) {
CHECK_OR_RETURN(PyLong_Check(object) || numpy::PyArrayCheckLongScalar(object))
<< "Expected a long value.";
*(reinterpret_cast<int32_t*>(data)) = PyLong_AsLongLong(object);
return Maybe<void>::Ok();
} else if (dtype == DataType::kUInt8 || dtype == DataType::kBool) {
CHECK_OR_RETURN(PyBool_Check(object) || PyLong_Check(object))
CHECK_OR_RETURN(PyBool_Check(object) || PyLong_Check(object)
|| numpy::PyArrayCheckLongScalar(object))
<< "Expected a boolean or long value.";
if (PyBool_Check(object)) {
if (PyBool_Check(object) || numpy::PyArrayCheckBoolScalar(object)) {
*(reinterpret_cast<bool*>(data)) = (object == Py_True);
} else {
int64_t value = PyLong_AsLongLong(object);
Expand Down Expand Up @@ -193,6 +202,8 @@ Maybe<IndexItem> UnpackIndexItem(PyObject* object) {
return std::make_shared<IndexItem>(start, end, step);
} else if (PyLong_Check(object) && object != Py_False && object != Py_True) {
return std::make_shared<IndexItem>(static_cast<int64_t>(PyLong_AsLongLong(object)));
} else if (numpy::PyArrayCheckLongScalar(object)) {
return std::make_shared<IndexItem>(static_cast<int64_t>(PyLong_AsLongLong(object)));
} else if (object == Py_False || object == Py_True) {
return std::make_shared<IndexItem>(object == Py_True);
} else if (object == Py_None) {
Expand Down
11 changes: 8 additions & 3 deletions oneflow/api/python/functional/python_arg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
#include "oneflow/api/python/functional/python_arg.h"
#include "oneflow/api/python/functional/common.h"
#include "oneflow/api/python/functional/indexing.h"
#include "oneflow/extension/python/numpy.h"
#include "oneflow/core/common/scalar.h"
#include "oneflow/core/framework/dtype.h"
#include "oneflow/core/framework/device.h"
Expand Down Expand Up @@ -198,21 +199,25 @@ Maybe<bool> PythonArg::TypeCheck(ValueType type) const {
case kUINT32:
case kINT64:
case kUINT64:
case kBOOL: return PyLong_Check(object_);
case kBOOL: return PyLong_Check(object_) || numpy::PyArrayCheckLongScalar(object_);
case kINT32_LIST:
case kUINT32_LIST:
case kINT64_LIST:
case kUINT64_LIST:
case kBOOL_LIST: return PyLongSequenceCheck(object_) || (size_ > 0 && PyLong_Check(object_));
case kFLOAT:
case kDOUBLE: return PyFloat_Check(object_) || PyLong_Check(object_);
case kDOUBLE:
return PyFloat_Check(object_) || PyLong_Check(object_)
|| numpy::PyArrayCheckFloatScalar(object_) || numpy::PyArrayCheckLongScalar(object_);
case kFLOAT_LIST:
case kDOUBLE_LIST:
return PyFloatSquenceCheck(object_)
|| (size_ > 0 && (PyFloat_Check(object_) || PyLong_Check(object_)));
case kSTRING: return PyStringCheck(object_);
case kSTRING_LIST: return PyStringSequenceCheck(object_);
case kSCALAR: return PyScalarCheck(object_);
case kSCALAR:
return PyScalarCheck(object_) || numpy::PyArrayCheckLongScalar(object_)
|| numpy::PyArrayCheckFloatScalar(object_);
case kTENSOR:
case kTENSOR_REF: return PyTensorCheck(object_);
case kTENSOR_TUPLE: return PyTensorTupleCheck(object_) || PyTensorSequenceCheck(object_);
Expand Down
1 change: 0 additions & 1 deletion oneflow/api/python/utils/tensor_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ limitations under the License.
#include "oneflow/core/register/ofblob.h"
#include "oneflow/core/common/blocking_then_busy.h"
#include "oneflow/core/vm/virtual_machine.h"
#include "oneflow/extension/python/numpy.h"
#include "oneflow/core/common/foreign_lock_helper.h"

namespace py = pybind11;
Expand Down
12 changes: 12 additions & 0 deletions oneflow/extension/python/numpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ std::vector<size_t> OFStrideToNumpyStride(const StrideVector& fixed_vec, const D
return result;
}

bool PyArrayCheckLongScalar(PyObject* obj) {
return PyArray_CheckScalar(obj) && PyDataType_ISINTEGER(PyArray_DescrFromScalar(obj));
}

bool PyArrayCheckFloatScalar(PyObject* obj) {
return PyArray_CheckScalar(obj) && PyDataType_ISFLOAT(PyArray_DescrFromScalar(obj));
}

bool PyArrayCheckBoolScalar(PyObject* obj) {
return PyArray_CheckScalar(obj) && PyDataType_ISBOOL(PyArray_DescrFromScalar(obj));
}

// Executing any numpy c api before _import_array() results in segfault
// NOTE: this InitNumpyCAPI() works because of `PY_ARRAY_UNIQUE_SYMBOL`
// defined in numpy_internal.h
Expand Down
6 changes: 6 additions & 0 deletions oneflow/extension/python/numpy_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ std::vector<size_t> OFShapeToNumpyShape(const DimVector& fixed_vec);

std::vector<size_t> OFStrideToNumpyStride(const StrideVector& fixed_vec, const DataType data_type);

bool PyArrayCheckLongScalar(PyObject* obj);

bool PyArrayCheckFloatScalar(PyObject* obj);

bool PyArrayCheckBoolScalar(PyObject* obj);

Maybe<void> InitNumpyCAPI();

} // namespace numpy
Expand Down
83 changes: 83 additions & 0 deletions python/oneflow/test/tensor/test_tensor_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,67 @@
import oneflow.unittest


def _test_numpy_scalar_indexing(test_case, numpy_x, np_scalar):
x = flow.Tensor(numpy_x)

# basic_slice
test_case.assertTrue(np.allclose(numpy_x[np_scalar(1)], x[np_scalar(1)].numpy()))
test_case.assertTrue(np.allclose(numpy_x[np_scalar(-2)], x[np_scalar(-2)].numpy()))
test_case.assertTrue(
np.allclose(
numpy_x[np_scalar(0), np_scalar(1)], x[np_scalar(0), np_scalar(1)].numpy()
)
)
test_case.assertTrue(
np.allclose(
numpy_x[(np_scalar(0), np_scalar(1))],
x[(np_scalar(0), np_scalar(1))].numpy(),
)
)
test_case.assertTrue(
np.allclose(
numpy_x[((np_scalar(0), np_scalar(1)))],
x[((np_scalar(0), np_scalar(1)))].numpy(),
)
)


def _test_numpy_scalar_advance_indexing(test_case, numpy_x, np_scalar):
x = flow.Tensor(numpy_x)

# advance indexing
test_case.assertTrue(
np.allclose(
numpy_x[[np_scalar(0), np_scalar(1)]],
x[[np_scalar(0), np_scalar(1)]].numpy(),
)
)
test_case.assertTrue(
np.allclose(
numpy_x[[np_scalar(0), np_scalar(1)], [np_scalar(1), np_scalar(0)]],
x[[np_scalar(0), np_scalar(1)], [np_scalar(1), np_scalar(0)]].numpy(),
)
)
test_case.assertTrue(
np.allclose(
numpy_x[
[
[np_scalar(0), np_scalar(1)],
[np_scalar(0), np_scalar(1)],
[np_scalar(1), np_scalar(0)],
]
],
x[
[
[np_scalar(0), np_scalar(1)],
[np_scalar(0), np_scalar(1)],
[np_scalar(1), np_scalar(0)],
]
].numpy(),
)
)


def _test_basic_slice(test_case, numpy_x):
x = flow.tensor(numpy_x)

Expand Down Expand Up @@ -278,6 +339,28 @@ def test_combining_indexing(test_case):
numpy_x = np.arange(0, 720, 1).reshape([8, 9, 10]).astype(np.float32)
_test_combining_indexing(test_case, numpy_x)

def test_numpy_scalar_indexing(test_case):
for np_scalar in [np.int8, np.int16, np.int32, np.int64]:
numpy_x = np.arange(0, 60, 1).reshape([3, 4, 5]).astype(np.float32)
_test_numpy_scalar_indexing(test_case, numpy_x, np_scalar)

numpy_x = np.arange(0, 360, 1).reshape([3, 4, 5, 6]).astype(np.float32)
_test_numpy_scalar_indexing(test_case, numpy_x, np_scalar)

numpy_x = np.arange(0, 720, 1).reshape([8, 9, 10]).astype(np.float32)
_test_numpy_scalar_indexing(test_case, numpy_x, np_scalar)

# TODO: add np.int16 when advance indexing supports np.int16 mapping
for np_scalar in [np.int32, np.int64]:
numpy_x = np.arange(0, 60, 1).reshape([3, 4, 5]).astype(np.float32)
_test_numpy_scalar_advance_indexing(test_case, numpy_x, np_scalar)

numpy_x = np.arange(0, 360, 1).reshape([3, 4, 5, 6]).astype(np.float32)
_test_numpy_scalar_advance_indexing(test_case, numpy_x, np_scalar)

numpy_x = np.arange(0, 720, 1).reshape([8, 9, 10]).astype(np.float32)
_test_numpy_scalar_advance_indexing(test_case, numpy_x, np_scalar)

def test_mask_getitem(test_case):
numpy_x = np.arange(0, 60, 1).reshape([3, 4, 5]).astype(np.float32)
_test_mask_getitem(test_case, numpy_x)
Expand Down