ESP  0.1
The Example-based Sensor Predictions (ESP) system tries to bring machine learning to the maker community.
matplotlibcpp.h
Go to the documentation of this file.
1 /* The MIT License (MIT) */
2 
3 /* Copyright (c) 2014 Benno Evers */
4 
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy */
6 /* of this software and associated documentation files (the "Software"), to deal */
7 /* in the Software without restriction, including without limitation the rights */
8 /* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
9 /* copies of the Software, and to permit persons to whom the Software is */
10 /* furnished to do so, subject to the following conditions: */
11 
12 /* The above copyright notice and this permission notice shall be included in all */
13 /* copies or substantial portions of the Software. */
14 
15 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
16 /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
17 /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
18 /* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
19 /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
20 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */
21 /* SOFTWARE. */
22 
23 #pragma once
24 
25 #include <vector>
26 #include <map>
27 #include <numeric>
28 #include <stdexcept>
29 #include <iostream>
30 
31 #if __cplusplus > 199711L
32 #include <functional>
33 #endif
34 
35 #include <python2.7/Python.h>
36 
37 namespace matplotlibcpp {
38 
39  namespace detail {
40  struct _interpreter {
54 
55  /* For now, _interpreter is implemented as a singleton since its currently not possible to have
56  multiple independent embedded python interpreters without patching the python source code
57  or starting a seperate process for each.
58 
59  http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program
60  */
61 
62  static _interpreter& get() {
63  static _interpreter ctx;
64  return ctx;
65  }
66 
67  private:
68  _interpreter() {
69  char name[] = "plotting"; // silence compiler warning abount const strings
70  Py_SetProgramName(name); // optional but recommended
71  Py_Initialize();
72 
73  PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
74  PyObject* pylabname = PyString_FromString("pylab");
75  if(!pyplotname || !pylabname) { throw std::runtime_error("couldnt create string"); }
76 
77  PyObject* pymod = PyImport_Import(pyplotname);
78  Py_DECREF(pyplotname);
79  if(!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); }
80 
81  PyObject* pylabmod = PyImport_Import(pylabname);
82  Py_DECREF(pylabname);
83  if(!pymod) { throw std::runtime_error("Error loading module pylab!"); }
84 
85  s_python_function_show = PyObject_GetAttrString(pymod, "show");
86  s_python_function_figure = PyObject_GetAttrString(pymod, "figure");
87  s_python_function_plot = PyObject_GetAttrString(pymod, "plot");
88  s_python_function_legend = PyObject_GetAttrString(pymod, "legend");
89  s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim");
90  s_python_function_title = PyObject_GetAttrString(pymod, "title");
91  s_python_function_axis = PyObject_GetAttrString(pymod, "axis");
92  s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel");
93  s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel");
94  s_python_function_grid = PyObject_GetAttrString(pymod, "grid");
95  s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim");
96 
97  s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig");
98 
99  if(!s_python_function_show
100  || !s_python_function_save
101  || !s_python_function_figure
102  || !s_python_function_plot
103  || !s_python_function_legend
104  || !s_python_function_xlim
105  || !s_python_function_ylim
106  || !s_python_function_title
107  || !s_python_function_axis
108  || !s_python_function_xlabel
109  || !s_python_function_ylabel
110  || !s_python_function_grid
111  )
112  { throw std::runtime_error("Couldnt find required function!"); }
113 
114  if(!PyFunction_Check(s_python_function_show)
115  || !PyFunction_Check(s_python_function_save)
116  || !PyFunction_Check(s_python_function_figure)
117  || !PyFunction_Check(s_python_function_plot)
118  || !PyFunction_Check(s_python_function_legend)
119  || !PyFunction_Check(s_python_function_xlim)
120  || !PyFunction_Check(s_python_function_ylim)
121  || !PyFunction_Check(s_python_function_title)
122  || !PyFunction_Check(s_python_function_axis)
123  || !PyFunction_Check(s_python_function_xlabel)
124  || !PyFunction_Check(s_python_function_ylabel)
125  || !PyFunction_Check(s_python_function_grid)
126  )
127  { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); }
128 
129  s_python_empty_tuple = PyTuple_New(0);
130  }
131 
132  ~_interpreter() {
133  Py_Finalize();
134  }
135  };
136  }
137 
138 
139 
140  template<typename Numeric>
141  bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
142  {
143  assert(x.size() == y.size());
144 
145  // using python lists
146  PyObject* xlist = PyList_New(x.size());
147  PyObject* ylist = PyList_New(y.size());
148 
149  for(size_t i = 0; i < x.size(); ++i) {
150  PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i)));
151  PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i)));
152  }
153 
154  // construct positional args
155  PyObject* args = PyTuple_New(2);
156  PyTuple_SetItem(args, 0, xlist);
157  PyTuple_SetItem(args, 1, ylist);
158 
159  Py_DECREF(xlist);
160  Py_DECREF(ylist);
161 
162  // construct keyword args
163  PyObject* kwargs = PyDict_New();
164  for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
165  {
166  PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
167  }
168 
169  PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
170 
171  Py_DECREF(args);
172  Py_DECREF(kwargs);
173  if(res) Py_DECREF(res);
174 
175  return res;
176  }
177 
178 
179  template<typename NumericX, typename NumericY>
180  bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
181  {
182  assert(x.size() == y.size());
183 
184  PyObject* xlist = PyList_New(x.size());
185  PyObject* ylist = PyList_New(y.size());
186  PyObject* pystring = PyString_FromString(s.c_str());
187 
188  for(size_t i = 0; i < x.size(); ++i) {
189  PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i)));
190  PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i)));
191  }
192 
193  PyObject* plot_args = PyTuple_New(3);
194  PyTuple_SetItem(plot_args, 0, xlist);
195  PyTuple_SetItem(plot_args, 1, ylist);
196  PyTuple_SetItem(plot_args, 2, pystring);
197 
198  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
199 
200  Py_DECREF(xlist);
201  Py_DECREF(ylist);
202  Py_DECREF(plot_args);
203  if(res) Py_DECREF(res);
204 
205  return res;
206  }
207 
208 
209  template<typename Numeric>
210  bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
211  PyObject* kwargs = PyDict_New();
212  PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
213 
214  PyObject* xlist = PyList_New(x.size());
215  PyObject* ylist = PyList_New(y.size());
216  PyObject* pystring = PyString_FromString(format.c_str());
217 
218  for(size_t i = 0; i < x.size(); ++i) {
219  PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i)));
220  PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i)));
221  }
222 
223  PyObject* plot_args = PyTuple_New(3);
224  PyTuple_SetItem(plot_args, 0, xlist);
225  PyTuple_SetItem(plot_args, 1, ylist);
226  PyTuple_SetItem(plot_args, 2, pystring);
227 
228  PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
229 
230  Py_DECREF(kwargs);
231  Py_DECREF(xlist);
232  Py_DECREF(ylist);
233  Py_DECREF(plot_args);
234  if(res) Py_DECREF(res);
235 
236  return res;
237  }
238 
239  template<typename Numeric>
240  bool plot(const std::vector<Numeric>& y, const std::string& format = "")
241  {
242  std::vector<Numeric> x(y.size());
243  for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
244  return plot(x,y,format);
245  }
246 
247 
248  inline void legend() {
250  if(!res) throw std::runtime_error("Call to legend() failed.");
251 
252  Py_DECREF(res);
253  }
254 
255  template<typename Numeric>
256  void ylim(Numeric left, Numeric right)
257  {
258  PyObject* list = PyList_New(2);
259  PyList_SetItem(list, 0, PyFloat_FromDouble(left));
260  PyList_SetItem(list, 1, PyFloat_FromDouble(right));
261 
262  PyObject* args = PyTuple_New(1);
263  PyTuple_SetItem(args, 0, list);
264 
265  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
266  if(!res) throw std::runtime_error("Call to ylim() failed.");
267 
268  Py_DECREF(list);
269  Py_DECREF(args);
270  Py_DECREF(res);
271  }
272 
273  template<typename Numeric>
274  void xlim(Numeric left, Numeric right)
275  {
276  PyObject* list = PyList_New(2);
277  PyList_SetItem(list, 0, PyFloat_FromDouble(left));
278  PyList_SetItem(list, 1, PyFloat_FromDouble(right));
279 
280  PyObject* args = PyTuple_New(1);
281  PyTuple_SetItem(args, 0, list);
282 
283  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
284  if(!res) throw std::runtime_error("Call to xlim() failed.");
285 
286  Py_DECREF(list);
287  Py_DECREF(args);
288  Py_DECREF(res);
289  }
290 
291 
292  inline void title(const std::string &titlestr)
293  {
294  PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
295  PyObject* args = PyTuple_New(1);
296  PyTuple_SetItem(args, 0, pytitlestr);
297 
298  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args);
299  if(!res) throw std::runtime_error("Call to title() failed.");
300 
301  // if PyDeCRFF, the function doesn't work on Mac OS
302  }
303 
304  inline void axis(const std::string &axisstr)
305  {
306  PyObject* str = PyString_FromString(axisstr.c_str());
307  PyObject* args = PyTuple_New(1);
308  PyTuple_SetItem(args, 0, str);
309 
310  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
311  if(!res) throw std::runtime_error("Call to title() failed.");
312 
313  // if PyDeCRFF, the function doesn't work on Mac OS
314  }
315 
316 
317 
318  inline void xlabel(const std::string &str)
319  {
320  PyObject* pystr = PyString_FromString(str.c_str());
321  PyObject* args = PyTuple_New(1);
322  PyTuple_SetItem(args, 0, pystr);
323 
324  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlabel, args);
325  if(!res) throw std::runtime_error("Call to xlabel() failed.");
326 
327  // if PyDeCRFF, the function doesn't work on Mac OS
328  }
329 
330  inline void ylabel(const std::string &str)
331  {
332  PyObject* pystr = PyString_FromString(str.c_str());
333  PyObject* args = PyTuple_New(1);
334  PyTuple_SetItem(args, 0, pystr);
335 
336  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylabel, args);
337  if(!res) throw std::runtime_error("Call to ylabel() failed.");
338 
339  // if PyDeCRFF, the function doesn't work on Mac OS
340  }
341 
342  inline void grid(bool flag)
343  {
344  PyObject* pyflag = flag ? Py_True : Py_False;
345 
346  PyObject* args = PyTuple_New(1);
347  PyTuple_SetItem(args, 0, pyflag);
348 
349  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
350  if(!res) throw std::runtime_error("Call to grid() failed.");
351 
352  // if PyDeCRFF, the function doesn't work on Mac OS
353  }
354 
355 
356 
357  inline void show()
358  {
360  if(!res) throw std::runtime_error("Call to show() failed.");
361 
362  Py_DECREF(res);
363  }
364 
365  inline void save(const std::string& filename)
366  {
367  PyObject* pyfilename = PyString_FromString(filename.c_str());
368 
369  PyObject* args = PyTuple_New(1);
370  PyTuple_SetItem(args, 0, pyfilename);
371 
372  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
373  if(!res) throw std::runtime_error("Call to save() failed.");
374 
375  Py_DECREF(pyfilename);
376  Py_DECREF(args);
377  Py_DECREF(res);
378  }
379 
380 #if __cplusplus > 199711L
381  // C++11-exclusive content starts here (variadic plot() and initializer list support)
382 
383  namespace detail {
384  template<typename T>
385  using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
386 
387  template<bool obj, typename T>
388  struct is_callable_impl;
389 
390  template<typename T>
391  struct is_callable_impl<false, T>
392  {
393  typedef is_function<T> type;
394  }; // a non-object is callable iff it is a function
395 
396  template<typename T>
397  struct is_callable_impl<true, T>
398  {
399  struct Fallback { void operator()(); };
400  struct Derived : T, Fallback { };
401 
402  template<typename U, U> struct Check;
403 
404  template<typename U>
405  static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
406 
407  template<typename U>
408  static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
409 
410  public:
411  typedef decltype(test<Derived>(nullptr)) type;
412  typedef decltype(&Fallback::operator()) dtype;
413  static constexpr bool value = type::value;
414  }; // an object is callable iff it defines operator()
415 
416  template<typename T>
417  struct is_callable
418  {
419  // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
420  typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
421  };
422 
423  template<typename IsYDataCallable>
424  struct plot_impl { };
425 
426  template<>
427  struct plot_impl<std::false_type>
428  {
429  template<typename IterableX, typename IterableY>
430  bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
431  {
432  // 2-phase lookup for distance, begin, end
433  using std::distance;
434  using std::begin;
435  using std::end;
436 
437  auto xs = distance(begin(x), end(x));
438  auto ys = distance(begin(y), end(y));
439  assert(xs == ys && "x and y data must have the same number of elements!");
440 
441  PyObject* xlist = PyList_New(xs);
442  PyObject* ylist = PyList_New(ys);
443  PyObject* pystring = PyString_FromString(format.c_str());
444 
445  auto itx = begin(x), ity = begin(y);
446  for(size_t i = 0; i < xs; ++i) {
447  PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
448  PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
449  }
450 
451  PyObject* plot_args = PyTuple_New(3);
452  PyTuple_SetItem(plot_args, 0, xlist);
453  PyTuple_SetItem(plot_args, 1, ylist);
454  PyTuple_SetItem(plot_args, 2, pystring);
455 
456  PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
457 
458  Py_DECREF(xlist);
459  Py_DECREF(ylist);
460  Py_DECREF(plot_args);
461  if(res) Py_DECREF(res);
462 
463  return res;
464  }
465  };
466 
467  template<>
468  struct plot_impl<std::true_type>
469  {
470  template<typename Iterable, typename Callable>
471  bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
472  {
473  //std::cout << "Callable impl called" << std::endl;
474 
475  if(begin(ticks) == end(ticks)) return true;
476 
477  // We could use additional meta-programming to deduce the correct element type of y,
478  // but all values have to be convertible to double anyways
479  std::vector<double> y;
480  for(auto x : ticks) y.push_back(f(x));
481  return plot_impl<std::false_type>()(ticks,y,format);
482  }
483  };
484  }
485 
486  // recursion stop for the above
487  template<typename... Args>
488  bool plot() { return true; }
489 
490  template<typename A, typename B, typename... Args>
491  bool plot(const A& a, const B& b, const std::string& format, Args... args)
492  {
493  return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
494  }
495 
496  /*
497  * This group of plot() functions is needed to support initializer lists, i.e. calling
498  * plot( {1,2,3,4} )
499  */
500  bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
501  return plot<double,double>(x,y,format);
502  }
503 
504  bool plot(const std::vector<double>& y, const std::string& format = "") {
505  return plot<double>(y,format);
506  }
507 
508  bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
509  return plot<double>(x,y,keywords);
510  }
511 
512  bool named_plot(const std::string& name, const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
513  return named_plot<double>(name,x,y,format);
514  }
515 
516 #endif
517 
518 
519 
520 
521 }
void ylim(Numeric left, Numeric right)
Definition: matplotlibcpp.h:256
PyObject * s_python_function_show
Definition: matplotlibcpp.h:41
PyObject * s_python_function_ylim
Definition: matplotlibcpp.h:47
void title(const std::string &titlestr)
Definition: matplotlibcpp.h:292
void grid(bool flag)
Definition: matplotlibcpp.h:342
PyObject * s_python_function_xlabel
Definition: matplotlibcpp.h:50
PyObject * s_python_function_grid
Definition: matplotlibcpp.h:52
PyObject * s_python_function_xlim
Definition: matplotlibcpp.h:46
PyObject * s_python_function_title
Definition: matplotlibcpp.h:48
bool plot(const std::vector< Numeric > &x, const std::vector< Numeric > &y, const std::map< std::string, std::string > &keywords)
Definition: matplotlibcpp.h:141
void legend()
Definition: matplotlibcpp.h:248
static _interpreter & get()
Definition: matplotlibcpp.h:62
void ylabel(const std::string &str)
Definition: matplotlibcpp.h:330
PyObject * s_python_function_plot
Definition: matplotlibcpp.h:44
PyObject * s_python_function_save
Definition: matplotlibcpp.h:42
PyObject * s_python_function_figure
Definition: matplotlibcpp.h:43
Definition: matplotlibcpp.h:37
Definition: matplotlibcpp.h:40
PyObject * s_python_function_legend
Definition: matplotlibcpp.h:45
PyObject * s_python_function_axis
Definition: matplotlibcpp.h:49
void xlim(Numeric left, Numeric right)
Definition: matplotlibcpp.h:274
void xlabel(const std::string &str)
Definition: matplotlibcpp.h:318
PyObject * s_python_function_ylabel
Definition: matplotlibcpp.h:51
void save(const std::string &filename)
Definition: matplotlibcpp.h:365
void axis(const std::string &axisstr)
Definition: matplotlibcpp.h:304
bool named_plot(const std::string &name, const std::vector< Numeric > &x, const std::vector< Numeric > &y, const std::string &format="")
Definition: matplotlibcpp.h:210
void show()
Definition: matplotlibcpp.h:357
PyObject * s_python_empty_tuple
Definition: matplotlibcpp.h:53