Skip to content

Commit 5097fbf

Browse files
authored
Merge pull request #1 from lava/master
Sync up
2 parents 41477fc + d612b52 commit 5097fbf

File tree

6 files changed

+142
-51
lines changed

6 files changed

+142
-51
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: minimal
2+
dist: trusty
3+
services:
4+
- docker
5+
script:
6+
- make -C contrib docker_build

Makefile

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@
22
CXXFLAGS += -std=c++11 -Wno-conversion
33

44
# Default to using system's default version of python
5-
PYTHON_BIN ?= python
5+
PYTHON_BIN ?= python3
66
PYTHON_CONFIG := $(PYTHON_BIN)-config
77
PYTHON_INCLUDE ?= $(shell $(PYTHON_CONFIG) --includes)
88
EXTRA_FLAGS := $(PYTHON_INCLUDE)
9-
LDFLAGS += $(shell $(PYTHON_CONFIG) --libs)
9+
# NOTE: Since python3.8, the correct invocation is `python3-config --libs --embed`.
10+
# So of course the proper way to get python libs for embedding now is to
11+
# invoke that, check if it crashes, and fall back to just `--libs` if it does.
12+
LDFLAGS += $(shell if $(PYTHON_CONFIG) --libs --embed >/dev/null; then $(PYTHON_CONFIG) --libs --embed; else $(PYTHON_CONFIG) --libs; fi)
1013

1114
# Either finds numpy or set -DWITHOUT_NUMPY
1215
EXTRA_FLAGS += $(shell $(PYTHON_BIN) $(CURDIR)/numpy_flags.py)
13-
WITHOUT_NUMPY := $(findstring $(CXXFLAGS), WITHOUT_NUMPY)
16+
WITHOUT_NUMPY := $(findstring $(EXTRA_FLAGS), WITHOUT_NUMPY)
1417

1518
# Examples requiring numpy support to compile
16-
EXAMPLES_NUMPY := surface
19+
EXAMPLES_NUMPY := surface colorbar
1720
EXAMPLES := minimal basic modern animation nonblock xkcd quiver bar \
18-
fill_inbetween fill update subplot2grid colorbar lines3d \
19-
$(if WITHOUT_NUMPY,,$(EXAMPLES_NUMPY))
21+
fill_inbetween fill update subplot2grid lines3d \
22+
$(if $(WITHOUT_NUMPY),,$(EXAMPLES_NUMPY))
2023

2124
# Prefix every example with 'examples/build/'
2225
EXAMPLE_TARGETS := $(patsubst %,examples/build/%,$(EXAMPLES))

README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ A more comprehensive example:
3030
3131
namespace plt = matplotlibcpp;
3232
33-
int main()
33+
int main()
3434
{
3535
// Prepare data.
3636
int n = 5000;
@@ -73,26 +73,26 @@ Alternatively, matplotlib-cpp also supports some C++11-powered syntactic sugar:
7373
using namespace std;
7474
namespace plt = matplotlibcpp;
7575

76-
int main()
77-
{
76+
int main()
77+
{
7878
// Prepare data.
7979
int n = 5000; // number of data points
80-
vector<double> x(n),y(n);
80+
vector<double> x(n),y(n);
8181
for(int i=0; i<n; ++i) {
8282
double t = 2*M_PI*i/n;
8383
x.at(i) = 16*sin(t)*sin(t)*sin(t);
8484
y.at(i) = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t);
8585
}
8686

87-
// plot() takes an arbitrary number of (x,y,format)-triples.
87+
// plot() takes an arbitrary number of (x,y,format)-triples.
8888
// x must be iterable (that is, anything providing begin(x) and end(x)),
89-
// y must either be callable (providing operator() const) or iterable.
89+
// y must either be callable (providing operator() const) or iterable.
9090
plt::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-");
9191

9292

9393
// show plots
9494
plt::show();
95-
}
95+
}
9696
```
9797
g++ modern.cpp -std=c++11 -I/usr/include/python2.7 -lpython
9898
@@ -199,13 +199,16 @@ On Ubuntu:
199199
sudo apt-get install python-matplotlib python-numpy python2.7-dev
200200

201201
If, for some reason, you're unable to get a working installation of numpy on your system,
202-
you can add the define `WITHOUT_NUMPY` to erase this dependency.
202+
you can define the macro `WITHOUT_NUMPY` before including the header file to erase this
203+
dependency.
203204

204205
The C++-part of the library consists of the single header file `matplotlibcpp.h` which can be placed
205206
anywhere.
206207

207-
Since a python interpreter is opened internally, it is necessary to link against `libpython2.7` in order to use
208-
matplotlib-cpp.
208+
Since a python interpreter is opened internally, it is necessary to link against `libpython` in order
209+
to user matplotlib-cpp. Most versions should work, although `libpython2.7` and `libpython3.6` are
210+
probably the most regularly testedr.
211+
209212

210213
# CMake
211214

@@ -232,6 +235,20 @@ target_include_directories(myproject PRIVATE ${PYTHON_INCLUDE_DIRS})
232235
target_link_libraries(myproject ${PYTHON_LIBRARIES})
233236
```
234237

238+
239+
# Vcpkg
240+
241+
You can download and install matplotlib-cpp using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
242+
243+
git clone https://github.com/Microsoft/vcpkg.git
244+
cd vcpkg
245+
./bootstrap-vcpkg.sh
246+
./vcpkg integrate install
247+
vcpkg install matplotlib-cpp
248+
249+
The matplotlib-cpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
250+
251+
235252
# C++11
236253

237254
Currently, c++11 is required to build matplotlib-cpp. The last working commit that did
@@ -256,10 +273,10 @@ The same technique can be used for linking against a custom build of python
256273

257274
Why?
258275
----
259-
I initially started this library during my diploma thesis. The usual approach of
276+
I initially started this library during my diploma thesis. The usual approach of
260277
writing data from the c++ algorithm to a file and afterwards parsing and plotting
261278
it in python using matplotlib proved insufficient: Keeping the algorithm
262-
and plotting code in sync requires a lot of effort when the C++ code frequently and substantially
279+
and plotting code in sync requires a lot of effort when the C++ code frequently and substantially
263280
changes. Additionally, the python yaml parser was not able to cope with files that
264281
exceed a few hundred megabytes in size.
265282

@@ -290,4 +307,4 @@ Todo/Issues/Wishlist
290307
in "".'
291308

292309
* MacOS: `Unable to import matplotlib.pyplot`. Cause: In mac os image rendering back end of matplotlib (what-is-a-backend to render using the API of Cocoa by default). There is Qt4Agg and GTKAgg and as a back-end is not the default. Set the back end of macosx that is differ compare with other windows or linux os.
293-
Solution is discribed [here](https://stackoverflow.com/questions/21784641/installation-issue-with-matplotlib-python?noredirect=1&lq=1), additional information can be found there too(see links in answers).
310+
Solution is described [here](https://stackoverflow.com/questions/21784641/installation-issue-with-matplotlib-python?noredirect=1&lq=1), additional information can be found there too(see links in answers).

contrib/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM debian:10 AS builder
2+
RUN apt-get update \
3+
&& apt-get install --yes --no-install-recommends \
4+
g++ \
5+
libpython3-dev \
6+
make \
7+
python3 \
8+
python3-dev \
9+
python3-numpy
10+
11+
ADD Makefile matplotlibcpp.h numpy_flags.py /opt/
12+
ADD examples/*.cpp /opt/examples/
13+
RUN cd /opt \
14+
&& make PYTHON_BIN=python3 \
15+
&& ls examples/build
16+
17+
FROM debian:10
18+
RUN apt-get update \
19+
&& apt-get install --yes --no-install-recommends \
20+
libpython3-dev \
21+
python3-matplotlib \
22+
python3-numpy
23+
24+
COPY --from=builder /opt/examples/build /opt/
25+
RUN cd /opt \
26+
&& ls \
27+
&& ./basic

contrib/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
all: docker_build
2+
3+
docker_build:
4+
cd .. && \
5+
docker build . -f contrib/Dockerfile -t matplotlibcpp && \
6+
cd contrib

matplotlibcpp.h

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#pragma once
22

3+
// Python headers must be included before any system headers, since
4+
// they define _POSIX_C_SOURCE
5+
#include <Python.h>
6+
37
#include <vector>
48
#include <map>
59
#include <array>
@@ -9,9 +13,6 @@
913
#include <iostream>
1014
#include <cstdint> // <cstdint> requires c++11 support
1115
#include <functional>
12-
#include <unordered_map>
13-
14-
#include <Python.h>
1516

1617
#ifndef WITHOUT_NUMPY
1718
# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
@@ -297,25 +298,35 @@ template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY
297298
template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
298299
template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
299300

301+
// Sanity checks; comment them out or change the numpy type below if you're compiling on
302+
// a platform where they don't apply
303+
static_assert(sizeof(long long) == 8);
304+
template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
305+
static_assert(sizeof(unsigned long long) == 8);
306+
template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
307+
// TODO: add int, long, etc.
308+
300309
template<typename Numeric>
301310
PyObject* get_array(const std::vector<Numeric>& v)
302311
{
303-
detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
312+
detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
313+
npy_intp vsize = v.size();
304314
NPY_TYPES type = select_npy_type<Numeric>::type;
305-
if (type == NPY_NOTYPE)
306-
{
307-
std::vector<double> vd(v.size());
308-
npy_intp vsize = v.size();
309-
std::copy(v.begin(),v.end(),vd.begin());
310-
PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data()));
315+
if (type == NPY_NOTYPE) {
316+
size_t memsize = v.size()*sizeof(double);
317+
double* dp = static_cast<double*>(::malloc(memsize));
318+
for (size_t i=0; i<v.size(); ++i)
319+
dp[i] = v[i];
320+
PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, dp);
321+
PyArray_UpdateFlags(reinterpret_cast<PyArrayObject*>(varray), NPY_ARRAY_OWNDATA);
311322
return varray;
312323
}
313-
314-
npy_intp vsize = v.size();
324+
315325
PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
316326
return varray;
317327
}
318328

329+
319330
template<typename Numeric>
320331
PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
321332
{
@@ -340,8 +351,22 @@ PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
340351
return reinterpret_cast<PyObject *>(varray);
341352
}
342353

354+
#else // fallback if we don't have numpy: copy every element of the given vector
355+
356+
template<typename Numeric>
357+
PyObject* get_array(const std::vector<Numeric>& v)
358+
{
359+
PyObject* list = PyList_New(v.size());
360+
for(size_t i = 0; i < v.size(); ++i) {
361+
PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
362+
}
363+
return list;
364+
}
365+
366+
#endif // WITHOUT_NUMPY
367+
343368
// sometimes, for labels and such, we need string arrays
344-
PyObject * get_array(const std::vector<std::string>& strings)
369+
inline PyObject * get_array(const std::vector<std::string>& strings)
345370
{
346371
PyObject* list = PyList_New(strings.size());
347372
for (std::size_t i = 0; i < strings.size(); ++i) {
@@ -361,20 +386,6 @@ PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
361386
return listlist;
362387
}
363388

364-
#else // fallback if we don't have numpy: copy every element of the given vector
365-
366-
template<typename Numeric>
367-
PyObject* get_array(const std::vector<Numeric>& v)
368-
{
369-
PyObject* list = PyList_New(v.size());
370-
for(size_t i = 0; i < v.size(); ++i) {
371-
PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
372-
}
373-
return list;
374-
}
375-
376-
#endif // WITHOUT_NUMPY
377-
378389
} // namespace detail
379390

380391
/// Plot a line through the given x and y data points..
@@ -793,7 +804,7 @@ template<typename NumericX, typename NumericY>
793804
bool scatter(const std::vector<NumericX>& x,
794805
const std::vector<NumericY>& y,
795806
const double s=1.0, // The marker size in points**2
796-
const std::unordered_map<std::string, std::string> & keywords = {})
807+
const std::map<std::string, std::string> & keywords = {})
797808
{
798809
assert(x.size() == y.size());
799810

@@ -823,7 +834,7 @@ bool scatter(const std::vector<NumericX>& x,
823834
template<typename Numeric>
824835
bool boxplot(const std::vector<std::vector<Numeric>>& data,
825836
const std::vector<std::string>& labels = {},
826-
const std::unordered_map<std::string, std::string> & keywords = {})
837+
const std::map<std::string, std::string> & keywords = {})
827838
{
828839
PyObject* listlist = detail::get_listlist(data);
829840
PyObject* args = PyTuple_New(1);
@@ -854,7 +865,7 @@ bool boxplot(const std::vector<std::vector<Numeric>>& data,
854865

855866
template<typename Numeric>
856867
bool boxplot(const std::vector<Numeric>& data,
857-
const std::unordered_map<std::string, std::string> & keywords = {})
868+
const std::map<std::string, std::string> & keywords = {})
858869
{
859870
PyObject* vector = detail::get_array(data);
860871
PyObject* args = PyTuple_New(1);
@@ -1163,6 +1174,9 @@ bool errorbar(const std::vector<NumericX> &x, const std::vector<NumericY> &y, co
11631174
template<typename Numeric>
11641175
bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
11651176
{
1177+
// Make sure python is initialized.
1178+
detail::_interpreter::get();
1179+
11661180
PyObject* kwargs = PyDict_New();
11671181
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
11681182

@@ -1187,6 +1201,9 @@ bool named_plot(const std::string& name, const std::vector<Numeric>& y, const st
11871201
template<typename Numeric>
11881202
bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
11891203
{
1204+
// Make sure python is initialized.
1205+
detail::_interpreter::get();
1206+
11901207
PyObject* kwargs = PyDict_New();
11911208
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
11921209

@@ -1212,6 +1229,9 @@ bool named_plot(const std::string& name, const std::vector<Numeric>& x, const st
12121229
template<typename Numeric>
12131230
bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
12141231
{
1232+
// Make sure python is initialized.
1233+
detail::_interpreter::get();
1234+
12151235
PyObject* kwargs = PyDict_New();
12161236
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
12171237

@@ -1237,6 +1257,9 @@ bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, cons
12371257
template<typename Numeric>
12381258
bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
12391259
{
1260+
// Make sure python is initialized.
1261+
detail::_interpreter::get();
1262+
12401263
PyObject* kwargs = PyDict_New();
12411264
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
12421265

@@ -1262,6 +1285,9 @@ bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, cons
12621285
template<typename Numeric>
12631286
bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
12641287
{
1288+
// Make sure python is initialized.
1289+
detail::_interpreter::get();
1290+
12651291
PyObject* kwargs = PyDict_New();
12661292
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
12671293

@@ -1322,7 +1348,7 @@ void text(Numeric x, Numeric y, const std::string& s = "")
13221348
Py_DECREF(res);
13231349
}
13241350

1325-
void colorbar(PyObject* mappable = NULL, const std::map<std::string, float>& keywords = {})
1351+
inline void colorbar(PyObject* mappable = NULL, const std::map<std::string, float>& keywords = {})
13261352
{
13271353
if (mappable == NULL)
13281354
throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc.");
@@ -1611,6 +1637,9 @@ inline void tick_params(const std::map<std::string, std::string>& keywords, cons
16111637

16121638
inline void subplot(long nrows, long ncols, long plot_number)
16131639
{
1640+
// Make sure interpreter is initialized
1641+
detail::_interpreter::get();
1642+
16141643
// construct positional args
16151644
PyObject* args = PyTuple_New(3);
16161645
PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows));
@@ -1670,6 +1699,9 @@ inline void title(const std::string &titlestr, const std::map<std::string, std::
16701699

16711700
inline void suptitle(const std::string &suptitlestr, const std::map<std::string, std::string> &keywords = {})
16721701
{
1702+
// Make sure interpreter is initialized
1703+
detail::_interpreter::get();
1704+
16731705
PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str());
16741706
PyObject* args = PyTuple_New(1);
16751707
PyTuple_SetItem(args, 0, pysuptitlestr);
@@ -1700,7 +1732,7 @@ inline void axis(const std::string &axisstr)
17001732
Py_DECREF(res);
17011733
}
17021734

1703-
void axvline(double x, double ymin = 0., double ymax = 1., const std::map<std::string, std::string>& keywords = std::map<std::string, std::string>())
1735+
inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map<std::string, std::string>& keywords = std::map<std::string, std::string>())
17041736
{
17051737
// construct positional args
17061738
PyObject* args = PyTuple_New(3);

0 commit comments

Comments
 (0)