1 #pragma once
2 
3 #include <pybind11/pybind11.h>
4 #include "py_ex.hh"
5 #include "py_helpers.hh"
6 #include "py_kernel.hh"
7 #include "py_progress.hh"
8 
9 namespace cadabra {
10 
11 	/// \ingroup pythoncore
12 	///
13 	/// Generic internal entry point for the Python side to execute a
14 	/// C++ algorithm. This gets called by the various apply_algo
15 	/// functions below, which in turn get called by the def_algo
16 	/// functions.
17 	template <class Algo>
apply_algo_base(Algo & algo,Ex_ptr ex,bool deep,bool repeat,unsigned int depth,bool pre_order=false)18 	Ex_ptr apply_algo_base(Algo& algo, Ex_ptr ex, bool deep, bool repeat, unsigned int depth, bool pre_order=false)
19 		{
20 		Ex::iterator it = ex->begin();
21 		if (ex->is_valid(it)) {
22 			ProgressMonitor* pm = get_progress_monitor();
23 			algo.set_progress_monitor(pm);
24 			if(pre_order)
25 				ex->update_state(algo.apply_pre_order(repeat));
26 			else
27 				ex->update_state(algo.apply_generic(it, deep, repeat, depth));
28 			call_post_process(*get_kernel_from_scope(), ex);
29 			}
30 
31 		return ex;
32 		}
33 
34 	template <class Algo>
apply_algo(Ex_ptr ex,bool deep,bool repeat,unsigned int depth)35 	Ex_ptr apply_algo(Ex_ptr ex, bool deep, bool repeat, unsigned int depth)
36 		{
37 		Algo algo(*get_kernel_from_scope(), *ex);
38 		return apply_algo_base(algo, ex, deep, repeat, depth, false);
39 		}
40 
41 	template <class Algo, typename Arg1>
apply_algo(Ex_ptr ex,Arg1 arg1,bool deep,bool repeat,unsigned int depth)42 	Ex_ptr apply_algo(Ex_ptr ex, Arg1 arg1, bool deep, bool repeat, unsigned int depth)
43 		{
44 		Algo algo(*get_kernel_from_scope(), *ex, arg1);
45 		return apply_algo_base(algo, ex, deep, repeat, depth, false);
46 		}
47 
48 	template <class Algo, typename Arg1, typename Arg2>
apply_algo(Ex_ptr ex,Arg1 arg1,Arg2 arg2,bool deep,bool repeat,unsigned int depth)49 	Ex_ptr apply_algo(Ex_ptr ex, Arg1 arg1, Arg2 arg2, bool deep, bool repeat, unsigned int depth)
50 		{
51 		Algo algo(*get_kernel_from_scope(), *ex, arg1, arg2);
52 		return apply_algo_base(algo, ex, deep, repeat, depth, false);
53 		}
54 
55 	template <class Algo, typename Arg1, typename Arg2, typename Arg3>
apply_algo(Ex_ptr ex,Arg1 arg1,Arg2 arg2,Arg3 arg3,bool deep,bool repeat,unsigned int depth)56 	Ex_ptr apply_algo(Ex_ptr ex, Arg1 arg1, Arg2 arg2, Arg3 arg3, bool deep, bool repeat, unsigned int depth)
57 		{
58 		Algo algo(*get_kernel_from_scope(), *ex, arg1, arg2, arg3);
59 		return apply_algo_base(algo, ex, deep, repeat, depth, false);
60 		}
61 
62 
63 	/// \ingroup pythoncore
64 	///
65 	/// Method to declare a Python function with variable number of
66 	/// arguments, and make that call a C++ algorithm as specified in
67 	/// the Algo template parameter.  This will make the algorithm
68 	/// traverse post-order, that is to say, first on the innermost
69 	/// child (or leaf) of an expression tree, and then, if that fails,
70 	/// on parent nodes, and so on.
71 	template<class Algo, typename... Args, typename... PyArgs>
def_algo(pybind11::module & m,const char * name,bool deep,bool repeat,unsigned int depth,PyArgs...pyargs)72 	void def_algo(pybind11::module& m, const char* name, bool deep, bool repeat, unsigned int depth, PyArgs... pyargs)
73 		{
74 		m.def(name,
75 		      &apply_algo<Algo, Args...>,
76 		      pybind11::arg("ex"),
77 		      std::forward<PyArgs>(pyargs)...,
78 		      pybind11::arg("deep") = deep,
79 		      pybind11::arg("repeat") = repeat,
80 		      pybind11::arg("depth") = depth,
81 		      pybind11::doc(read_manual("algorithms", name).c_str()),
82 		      pybind11::return_value_policy::reference_internal);
83 		}
84 
85 	template <class Algo>
apply_algo_preorder(Ex_ptr ex,bool deep,bool repeat,unsigned int depth)86 	Ex_ptr apply_algo_preorder(Ex_ptr ex, bool deep, bool repeat, unsigned int depth)
87 		{
88 		Algo algo(*get_kernel_from_scope(), *ex);
89 		return apply_algo_base(algo, ex, deep, repeat, depth, true);
90 		}
91 
92 	template <class Algo, typename Arg1>
apply_algo_preorder(Ex_ptr ex,Arg1 arg1,bool deep,bool repeat,unsigned int depth)93 	Ex_ptr apply_algo_preorder(Ex_ptr ex, Arg1 arg1, bool deep, bool repeat, unsigned int depth)
94 		{
95 		Algo algo(*get_kernel_from_scope(), *ex, arg1);
96 		return apply_algo_base(algo, ex, deep, repeat, depth, true);
97 		}
98 
99 	template <class Algo, typename Arg1, typename Arg2>
apply_algo_preorder(Ex_ptr ex,Arg1 arg1,Arg2 arg2,bool deep,bool repeat,unsigned int depth)100 	Ex_ptr apply_algo_preorder(Ex_ptr ex, Arg1 arg1, Arg2 arg2, bool deep, bool repeat, unsigned int depth)
101 		{
102 		Algo algo(*get_kernel_from_scope(), *ex, arg1, arg2);
103 		return apply_algo_base(algo, ex, deep, repeat, depth, true);
104 		}
105 
106 	/// \ingroup pythoncore
107 	///
108 	/// Method to declare a Python function with variable number of arguments, and
109 	/// make that call a C++ algorithm as specified in the Algo template parameter.
110 	/// In contrast to def_algo, this one will apply the algorithm in pre-order
111 	/// traversal style, that is, it will first attempt to apply on a node itself
112 	/// before traversing further down the child nodes and attempting there.
113 	template<class Algo, typename... Args, typename... PyArgs>
def_algo_preorder(pybind11::module & m,const char * name,bool deep,bool repeat,unsigned int depth,PyArgs...pyargs)114 	void def_algo_preorder(pybind11::module& m, const char* name, bool deep, bool repeat, unsigned int depth, PyArgs... pyargs)
115 		{
116 		m.def(name,
117 		      &apply_algo_preorder<Algo, Args...>,
118 		      pybind11::arg("ex"),
119 		      std::forward<PyArgs>(pyargs)...,
120 		      pybind11::arg("deep") = deep,
121 		      pybind11::arg("repeat") = repeat,
122 		      pybind11::arg("depth") = depth,
123 		      pybind11::doc(read_manual("algorithms", name).c_str()),
124 		      pybind11::return_value_policy::reference_internal);
125 		}
126 
127 
128 	void init_algorithms(pybind11::module& m);
129 	}
130