1 #pragma once
2 
3 #include <pybind11/pybind11.h>
4 
5 #include "../Kernel.hh"
6 
7 namespace cadabra {
8 
9 	/// \ingroup pythoncore
10 	///
11 	/// Setup of kernels in current scope, callable from Python.
12 	/// When the decision was made to graft Cadabra onto Python, a choice had
13 	/// to be made about how Python variable scope would influence the
14 	/// visibility of Cadabra properties. It clearly makes sense to be able to
15 	/// declare properties which only hold inside a particular
16 	/// function. However Cadabra expressions and properties do not directly
17 	/// correspond to Python objects. Rather, declaring a property is more
18 	/// like a function call into the Cadabra module, which leaves its imprint
19 	/// on the state of the C++ part but does not change anything on the
20 	/// Python side, as you typically do not assign the created property to a
21 	/// Python symbol. Therefore, properties do not naturally inherit Python's
22 	/// scoping rules (this is different from e.g.~SymPy, in which
23 	///   mathematical objects are always in one-to-one correspondence with a
24 	///   Python object). A more fundamental problem is that properties can be
25 	/// attached to patterns, and those patterns can involve more than just
26 	/// the symbols which one passes into a function.
27 	///
28 	/// In order to not burden the user, properties are therefore by default
29 	/// global variables, stored in a single global Cadabra object
30 	/// `__cdbkernel__` which is initialised at import of the Cadabra module.
31 	/// If you add new properties inside a function scope, these will go
32 	/// into this already existing *global* property list by default.
33 	/// If you want to create a local scope for your computations, create a
34 	/// new `__cdbkernel__` as in
35 	/// ```
36 	/// def fun():
37 	///    __cdbkernel__ = cadabra.create_scope();
38 	///    [your code here]
39 	/// ```
40 	/// Now computations will not see the global properties at all.
41 	/// If you want to import the global properties, use instead
42 	/// ```
43 	/// def fun():
44 	///    __cdbkernel__ = cadabra.create_scope_from_global()
45 	///    [your code here]
46 	/// ```
47 	/// It is crucial that the
48 	/// `__cdbkernel__` symbol is referenced from within Python and visible to the bytecompiler, because
49 	/// it is not possible to create new variables on the local stack at runtime.
50 	/// Internally, the second version above fetches, at runtime, the
51 	/// `__cdbkernel__` from the globals stack, copies all properties in there
52 	/// into a new kernel, and returns the latter.
53 	///
54 	/// Both versions above do populate the newly created kernel with
55 	/// Cadabra's default properties. If you want a completely clean slate
56 	/// (for e.g.~testing purposes, or because you really do not want default
57 	/// rules for sums and products), use
58 	/// ```
59 	/// def fun():
60 	///    __cdbkernel__ = cadabra.create_empty_scope()
61 	///    [your code here]
62 	/// ```
63 	/// Note that in all these cases, changes to properties remain local and
64 	/// do not leak into the global property list.
65 	///
66 	/// All Cadabra algorithms, when called from Python, will first look for a
67 	/// kernel on the locals stack (i.e.~what `locals()` produces). If
68 	/// there is no kernel available locally, they will then revert to using
69 	/// the global kernel.
70 	Kernel *create_scope();
71 	Kernel *create_scope_from_global();
72 	Kernel *create_empty_scope();
73 
74 	/// \ingroup pythoncore
75 	///
76 	/// Get a pointer to the currently visible kernel.
77 	Kernel *get_kernel_from_scope();
78 
79 
80 	void init_kernel(pybind11::module& m);
81 
82 	}
83