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

[BUG]: In-place modification of ndarray does not work #711

Closed
ivan-tkatchev opened this issue Sep 9, 2024 · 4 comments
Closed

[BUG]: In-place modification of ndarray does not work #711

ivan-tkatchev opened this issue Sep 9, 2024 · 4 comments

Comments

@ivan-tkatchev
Copy link

Problem description

A C++ function accepts an ndarray and modifies it in place, but changes aren't reflected on the Python side.

>>> import numethods
>>> import numpy as np
>>> arr = np.array([[1,1],[1,2],[1,3],[2,4],[2,5],[2,6]])
>>> numethods.emva(arr)
>>> arr
array([[1, 1],
       [1, 2],
       [1, 3],
       [2, 4],
       [2, 5],
       [2, 6]])

If the C++ code is modified to return the input array then the function works as expected:

array([[1.        , 0.3       ],
       [1.        , 0.81000006],
       [1.        , 1.467     ],
       [2.        , 1.2       ],
       [2.        , 2.3400002 ],
       [2.        , 3.4380002 ]], dtype=float32)

Reproducible example code

#include <cmath>

#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>

using array_of_pairs_t = nanobind::ndarray<
    float,
    nanobind::shape<-1, 2>,
    nanobind::c_contig,
    nanobind::device::cpu>;

void emva(array_of_pairs_t&& data_) {
    constexpr float a = 0.3;

    auto data = data_.view();
    float prev_key = NAN;
    float emva = 0;

    for (size_t i = 0; i < data.shape(0); ++i) {
        float key = data(i, 0);
        float val = data(i, 1);

        if (key != prev_key) {
            emva = 0;
            prev_key = key;
        }

        emva = emva * (1 - a)  + val * a;
        data(i, 1) = emva;
    }
}

NB_MODULE(numethods, m) {
    m.def("emva", &emva);
}
@ivan-tkatchev
Copy link
Author

Obviously the array is being copied when passed into the C++ function, but there is no indication of why or how.
Documentation says nothing about possible hidden magic copies of ndarray on the Python/C++ boundary.

@ivan-tkatchev
Copy link
Author

If dtype is specified explicitly during array creation then the function works as expected:

>>> import numethods
>>> import numpy as np
>>> arr = np.array([[1,1],[1,2],[1,3],[2,4],[2,5],[2,6]], dtype='float32')
>>> arr
array([[1., 1.],
       [1., 2.],
       [1., 3.],
       [2., 4.],
       [2., 5.],
       [2., 6.]], dtype=float32)
>>> numethods.emva(arr)
>>> arr
array([[1.        , 0.3       ],
       [1.        , 0.81000006],
       [1.        , 1.467     ],
       [2.        , 1.2       ],
       [2.        , 2.3400002 ],
       [2.        , 3.4380002 ]], dtype=float32)

@wjakob
Copy link
Owner

wjakob commented Sep 9, 2024

If your input array on the Python side has a different dtype, then nanobind must make an implicit conversion to be able to do the call. You can disable implicit conversions via nb::arg("arg_name").noconvert(), in which case you will get an error message instead.

@wjakob wjakob closed this as completed Sep 9, 2024
@wjakob
Copy link
Owner

wjakob commented Sep 10, 2024

This is documented, by the way: please review the ndarray part of the documentation here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants