Skip to content

Conversation

@Hs293Go
Copy link

@Hs293Go Hs293Go commented Jun 27, 2021

This PR is motivated by the desire to plot data in generic data structures without the boilerplate of copying data into std::vectors before each plot. (I just had to plot a 101385-element Eigen::VectorXd and the copy was NOT fun).

The solution adopted is to follow the example of the STL and define the graph data by a pair of templated input iterators. For ease of use, the interface is designed to mimic the STL in the following manner.

std::transform(a.begin(), a.end(), b.begin(), binaryFunctor);
plt::stl::plot(a.begin(), a.end(), b.begin(), "ro");

only 2D lineplots and scatter plots are addressed for the time being.

In due diligence, the examples stl_containers.cpp and eigen_objects.cpp and a section reviewing this interface in the README are added.

I think the main sticking points regarding this PR are

  1. If the STL-like interface looks out of place among functions mimicking matplotlib's interface
  2. The mechanism handling Contiguous and Non-Contiguous containers is clunky.

I suppose C++20 <ranges> is the silver bullet to resolve point 1, and C++20 contiguous_iterator concept deals with point 2, but this is the most elegant solution I have for now. If there's anything I can to do better before this PR can be merged, let me know!

Hs293Go added 4 commits June 27, 2021 18:55
This commit implements a STL-algorithm-like interface to plot 2D line
plots and scatter plots from data in a range defined by a pair of
iterators. New interfaces are in the matplotlibcpp::stl namespace
Two new examples demonstrate that the stl-like interface is able to work
with raw buffers, std::vector|list|deque|etc and Eigen Vector objects
@amadeus84
Copy link

I came across the same problem, I have data in c-style arrays

doble* x, * y:
size_t n; // same for x and y

and to pass it to plt::plot() I'd have to copy it into a vector first. Ranges (C++20) work! I tried. But perhaps the simplest solution I came up with was

auto xx = std::span(x, n);
auto yy = std:;span(y, n);
plt::plot(xx, yy, "");

(of course, you can put the span directly into plot(), no need to have xx and yy). And, of course, this works also when y has length a multiple of lenght of x:

double* x, * y;
size_t nx=100, ny = 5*nx;

for (size_t offset = 0; offset < ny; offset += nx)
plt::plot(std::span(x, nx), std::span(y+offset, nx), "");

So far so good, and if this is available in C++20, I don't see why we shouldn't use it. In fact, the whole variadic template part of matplotlibcpp looks like was specifically done with ranges in mind - all that's needed is begin(), end(), etc. i.e all the things a range would have.

The problem though, is that for types other than vector, matplotlibcpp ends up calling plot_impl(), which then copies the inputs to PyObject arrays. It defeats the purpose of using span / ranges in the first place.

Perhaps we can have a template specialization of plot (or plot_impl) for contiguous ranges, that would not iterate and copy the input data to PyObject arrays, but rather call PyArray_SimpleNewFromData(), like plot(vector) does, via get_array(). According to https://numpy.org/devdocs/reference/c-api/array.html PyArray_SimpleNewFromData() does not copy the data.

I'm experimenting with this now.

@Hs293Go
Copy link
Author

Hs293Go commented Mar 12, 2022

@amadeus84 I like your ideas! A potential difficulty may arise from the fact that plotting is always with a pair of ranges, and some inspiration might be taken from zip_view (currently in range-v3 only) to design concepts that ensure compatibility between the pair of ranges.

Alas, matplotlibcpp seem to be unmaintained for almost a year already. I've switched to the technique of using pybind11 to embed the python interpreter in C++ code and plot using the native matplotlib API. I just made public a demo of the code I've been sharing around: https://github.com/Hs293Go/demo_pybind11_matplotlib

@amadeus84
Copy link

@amadeus84 I like your ideas! A potential difficulty may arise from the fact that plotting is always with a pair of ranges, and some inspiration might be taken from zip_view (currently in range-v3 only) to design concepts that ensure compatibility between the pair of ranges.

Alas, matplotlibcpp seem to be unmaintained for almost a year already. I've switched to the technique of using pybind11 to embed the python interpreter in C++ code and plot using the native matplotlib API. I just made public a demo of the code I've been sharing around: https://github.com/Hs293Go/demo_pybind11_matplotlib

I now have support for ranges in my repository: https://github.com/amadeus84/matplotlib-cpp

This pull request was closed.
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.

2 participants