Skip to content

Change writeable flag after moving array from C++ #2629

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

Closed
wants to merge 6 commits into from

Conversation

jorgensd
Copy link
Member

Resolves #2625.
Not the prettiest of fixes, but I cannot see a nice workaround for this.

@garth-wells
Copy link
Member

See pybind/pybind11#1466.

@jorgensd
Copy link
Member Author

See pybind/pybind11#1466.

I cannot get that to work with a py::array_t structure.
One could use the C++ equivalent of the current solution, suggested in:
pybind/pybind11#481 (comment)
It seems like the buffer solution never made its way up to array_t:
pybind/pybind11#338 (comment)

@jorgensd
Copy link
Member Author

See pybind/pybind11#1466.

I cannot get that to work with a py::array_t structure. One could use the C++ equivalent of the current solution, suggested in: pybind/pybind11#481 (comment) It seems like the buffer solution never made its way up to array_t: pybind/pybind11#338 (comment)

@michalhabera suggested the new approach, which is influenced by the C++ assign flag comment

@jorgensd jorgensd mentioned this pull request Apr 21, 2023
@garth-wells
Copy link
Member

This should be wrapped up in a helper function, otherwise we'll have low-level code everywhere.

@michalhabera
Copy link
Contributor

This should be wrapped up in a helper function, otherwise we'll have low-level code everywhere.

@jorgensd I think Garth wrote wrappers/array.h for this purpose. My suggested approach might be still flawed wrt. lifetime management, py::cast(self) passed as a base also made lifetime of returned numpy array coupled with the MeshTags cpp object.

@jorgensd
Copy link
Member Author

This should be wrapped up in a helper function, otherwise we'll have low-level code everywhere.

@jorgensd I think Garth wrote wrappers/array.h for this purpose. My suggested approach might be still flawed wrt. lifetime management, py::cast(self) passed as a base also made lifetime of returned numpy array coupled with the MeshTags cpp object.

How about:

        py::array_t<T> array(self.values().size(), self.values().data(),
                                 py::cast(self));
            reinterpret_cast<py::detail::PyArray_Proxy*>(array.ptr())->flags
                &= ~py::detail::npy_api::NPY_ARRAY_WRITEABLE_;
            return array;

I guess we could add this to the as_pyarray function. Just need to deduce if the arguments should be readonly or not

@garth-wells
Copy link
Member

I think there is a mis-understanding of def_property_readonly. One should be able to change the values of a MeshTags object.

def_property_readonly means something is accessible as a property, but that there is no setter. I.e., one can access the mesh tag data and change the values, but one cannot associate a new array with the MeshTags object.

@jorgensd
Copy link
Member Author

Not if you look at the C++ signature, which returns a const span https://github.com/FEniCS/dolfinx/blob/main/cpp/dolfinx/mesh/MeshTags.h#L98

@garth-wells
Copy link
Member

Not if you look at the C++ signature, which returns a const span https://github.com/FEniCS/dolfinx/blob/main/cpp/dolfinx/mesh/MeshTags.h#L98

For the indices yes, but not for the values. The PR prevents users from changing the values.

Python doesn't generally have the concept of const, despite NumPy arrays having a 'writeable' flag. The philosophy is to trust users. What is important here is that that the owndata flag is correctly set since changing size would cause a corruption of the MeshTags object.

@jorgensd
Copy link
Member Author

jorgensd commented Apr 21, 2023

Not if you look at the C++ signature, which returns a const span https://github.com/FEniCS/dolfinx/blob/main/cpp/dolfinx/mesh/MeshTags.h#L98

For the indices yes, but not for the values. The PR prevents users from changing the values.

Python doesn't generally have the concept of const, despite NumPy arrays having a 'writeable' flag. The philosophy is to trust users. What is important here is that that the owndata flag is correctly set since changing size would cause a corruption of the MeshTags object.

The values are const span: https://github.com/FEniCS/dolfinx/blob/main/cpp/dolfinx/mesh/MeshTags.h#L101
and with making the modifiable, one makes the form domain Construction unclear, as that is done with the meshtags at initialization.

@garth-wells
Copy link
Member

Not if you look at the C++ signature, which returns a const span https://github.com/FEniCS/dolfinx/blob/main/cpp/dolfinx/mesh/MeshTags.h#L98

Indeed, my mistake.

Still, I don't think it's unreasonable to be able change the values from Python, even if it risks breaking something else.

@jorgensd
Copy link
Member Author

Im in favor of avoiding alot of unexpected behavior with the interaction with dolfinx::fem::Form, which has major assumptions on the const-ness of integration entities specified in MeshTags. As I mentioned in an earlier commit, we could also force this purely on the Python side (inside the property functions).

@jhale
Copy link
Member

jhale commented May 12, 2023

This should really be fixed at the pybind11 level.

Having said that, I fall on the side that the numpy array should be set as non-writeable, reflecting the underlying const specification in the C++ span. I don't have a strong view on whether the flag should be set at the C++/pybind11 level, or in the pure Python wrappers.

@jorgensd
Copy link
Member Author

I agree with that, and I am happy with setting it in the pybinding, as done in this PR currently.

@garth-wells
Copy link
Member

I'm not too fussed either way. In any case, a helper function should be added in array.h rather than adding the low-level code to each wrapper function.

Some lifetime/reference counting tests are need too since we're not 100% sure what is happing behind the scenes with pybind11 and what this PR does.

@garth-wells
Copy link
Member

garth-wells commented Sep 28, 2023

Note: nanobind handles const and NumPy arrays transparently.

@garth-wells
Copy link
Member

This will be addressed when we switch to nanobind, see #2820.

@garth-wells garth-wells deleted the dokken/writable-false branch January 18, 2024 06:23
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

Successfully merging this pull request may close these issues.

[BUG]: AttributeError: Can't set attribute -- except I can
5 participants