File: FastJetPythonExtensions.hh

package info (click to toggle)
fastjet 3.4.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,552 kB
  • sloc: cpp: 78,628; python: 6,112; sh: 1,038; fortran: 673; makefile: 636; ansic: 161
file content (326 lines) | stat: -rw-r--r-- 13,327 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#ifndef __FASTJET_PYTHONUSERINFO_HH__
#include "fastjet/PseudoJet.hh"
#include "fastjet/JetDefinition.hh"
#include "fastjet/Selector.hh"
#include "fastjet/Error.hh"
#include "Python.h"

FASTJET_BEGIN_NAMESPACE      // defined in fastjet/internal/base.hh

//----------------------------------------------------------------------
/// \class UserInfoPython
/// Internal helper class for making user info usable within python
///
/// This is an internal class that makes possible the calls to
///   pseudojet->set_python_info(object)
///   object = pseudojet->python_info()
/// through the PseudoJet::UserInfoBase interface and for any object
/// class in python
class UserInfoPython : public fastjet::PseudoJet::UserInfoBase {
public:
  UserInfoPython(PyObject * pyobj) : _pyobj(pyobj) {
    Py_XINCREF(_pyobj);
  }

  PyObject * get_pyobj() const {
    // since there's going to be an extra reference to this object
    // one must increase the reference count; it seems that this
    // is _our_ responsibility
    Py_XINCREF(_pyobj);
    return _pyobj;
  }
  //const PyObject * get_pyobj() const {return _pyobj;}
  
  ~UserInfoPython() {
    Py_XDECREF(_pyobj);
  }
private:
  PyObject * _pyobj;
};

//----------------------------------------------------------------------
/// get a C++ string from a Python object that is assumed to be
/// of string type (unicode for Py3).
///
/// Later we might imagine moving to using something like
/// SWIG_AsPtr_std_string.
///
/// Implementation with Python calls is inspired from discussions
/// at
///   https://stackoverflow.com/questions/22487780/what-do-i-use-instead-of-pystring-asstring-when-loading-a-python-module-in-3-3
///   https://mail.python.org/pipermail/python-list/2009-March/527813.html
///
inline std::string cpp_string_from_py_str(PyObject *py_str) {
  const char *char_result;
#if PY_VERSION_HEX >= 0x03000000
  char_result = PyUnicode_AsUTF8(py_str);
#else
  char_result = PyString_AsString(py_str);
#endif
  return std::string(char_result);
}

//----------------------------------------------------------------------
/// Invokes the str call on a python object and returns the corresponding
/// C++ string
inline std::string cpp_string_from_str_py_obj(PyObject *py_obj) {
  PyObject* py_str = PyObject_Str(py_obj);
  std::string cpp_str = cpp_string_from_py_str(py_str);
  Py_XDECREF(py_str);
  return cpp_str;
}

//----------------------------------------------------------------------
/// Invokes the name call on a python object and returns the corresponding
/// C++ string
inline std::string cpp_string_from_name_py_obj(PyObject *py_obj) {
  PyObject* py_str = PyObject_GetAttrString(py_obj, "__name__");
  std::string cpp_str = cpp_string_from_py_str(py_str);
  Py_XDECREF(py_str);
  return cpp_str;
}


//----------------------------------------------------------------------
/// \class SelectorWorkerPython
/// Internal class for making python classes/functions usable as selectors
///
/// This is an internal class that makes possible the calls to
///   selector = SelectorPython(pyton_function)
/// where python_function will take a PseudoJet as argument and return
/// a bool
class SelectorWorkerPython : public SelectorWorker{
public:
  // ctor based on a PYObject which should be callable (typically a
  // class or a function)
  SelectorWorkerPython(PyObject *py_class_or_function) : _py_class_or_function(py_class_or_function){
    // increment ref count on the py object
    Py_XINCREF(_py_class_or_function);

    // we directly make sure that the function is callable
    if (!PyCallable_Check(_py_class_or_function)){
      PyErr_SetString(PyExc_TypeError,
          "SelectorWorkerPython::SelectorWorkerPython: the argument should be callable");
      // do we also throw a fastjet error?
    }
  }

  // dtor
  ~SelectorWorkerPython(){
    // decrement ref count on the py object
    Py_XDECREF(_py_class_or_function);
  }

  // description of the Selector
  virtual std::string description() const{
    // Functions define __name__ which gives a more readable output
    // that __str__. So we'll use it for the description
    if (PyObject_HasAttrString(_py_class_or_function, "__name__")){
      //Py_XINCREF(_py_class_or_function); // GPS: not needed?
      std::string cpp_str = cpp_string_from_name_py_obj(_py_class_or_function);
      //Py_XDECREF(_py_class_or_function); // GPS: not needed?
      return std::string("Selector based on python function ")+cpp_str;
    }
    
    // reuse the python string if it is available
    //
    // Note: this will take the __str__ method in classes but it would
    // also be available for functions, producing a rather inelegant
    // output of the form
    //   Selector based on python condition <function is_pileup at 0x...>
    // Not sure how to avoid this?
    if (PyObject_HasAttrString(_py_class_or_function, "__str__")){
      //Py_XINCREF(_py_class_or_function); // GPS: not needed?
      std::string cpp_str = cpp_string_from_str_py_obj(_py_class_or_function);
      //Py_XDECREF(_py_class_or_function); // GPS: not needed?
      return std::string("Selector based on python condition ")+cpp_str;
    }
    return "Selector based on python function";
  }

  // implement the check whether the given PseudoJet passes the
  // selection or not
  virtual bool pass(const PseudoJet &jet) const{
    // first make a copy of the jet in a PyObject* managed by swig
    PyObject *py_jet = 0;
    py_jet = SWIG_NewPointerObj((new fastjet::PseudoJet(static_cast< const fastjet::PseudoJet& >(jet))), SWIGTYPE_p_fastjet__PseudoJet, SWIG_POINTER_OWN |  0 );

    // now call the user-defined selection function (in python)
    // GPS: are the INCREF and DECREF really needed here?
    // GS:  I don't think so but I am unsure so I preferred to play it safe.
    Py_XINCREF(_py_class_or_function);
    PyObject * args = Py_BuildValue("(O)", py_jet);
    PyObject *py_result = PyObject_CallObject(_py_class_or_function, args);
    Py_XDECREF(_py_class_or_function);

    // and interpret the result as a bool
    //
    // Note that somehow the conversion from bool via SWIG_AsVal_bool
    // is not available at this stage so we cannot simply do:
    //   bool result;
    //   int conversion_result = SWIG_AsVal_bool(py_result, &result);
    //   if (!SWIG_IsOK(conversion_result)){
    //     throw Error("SelectorWorkerPython::pass(): the value returned by the python function could not be casted to a bool");
    //   }
    if (py_result == NULL)
      throw Error("SelectorWorkerPython::pass(): call to python function returned a NULL result.");

    if (!PyBool_Check(py_result))
      throw Error("SelectorWorkerPython::pass(): the value returned by the python function could not be cast to a bool");
    int result = PyObject_IsTrue(py_result);
    if (result == -1)
      throw Error("SelectorWorkerPython::pass(): the value returned by the python function could not be cast to a bool");
    Py_XDECREF(py_result); ///???

    return result;
  }
  
private:
  PyObject *_py_class_or_function;
};

// effectively create a Selector for python
Selector SelectorPython(PyObject *py_function_or_class) {
  return Selector(new SelectorWorkerPython(py_function_or_class));
}

//----------------------------------------------------------------------
/// \class RecombinerPython
/// Class allowing user-defined Recombiners in python
///
/// If a (python) user implements a (python) class providing the 
///   __str__()
///   PseudoJet recombine(PseudoJet pa, PseudoJe pb)
///   PseudoJet preprocess(PseudoJet pa)
/// methods
///
/// Note that compared to the C++ implementation, the result is
/// returned by the method rather than being passed as a reference.
class RecombinerPython : public JetDefinition::Recombiner{
public:
  /// ctor with the python recombier class as an argument
  RecombinerPython(PyObject *py_class) : _py_class(py_class){
    Py_XINCREF(_py_class);
    
    // here we could add some tests that the class has the required
    // methods
  }
  
  /// dtor
  virtual ~RecombinerPython(){
    Py_XDECREF(_py_class);
  }

  /// return a textual description of the recombiner
  virtual std::string description() const{
    if (! PyObject_HasAttrString(_py_class, "__str__")){
      throw Error("RecombinerPython: the provided class should implement the __str__ method (for description");
    }
    
    //Py_XINCREF(_py_class); // GPS not needed
    std::string cpp_str = cpp_string_from_str_py_obj(_py_class);
    //Py_XDECREF(_py_class); // GPS not needed
    return std::string("User-defined recombiner based on python recombiner ")+cpp_str;
  }
  
  /// recombine pa and pb and put result into pab
  virtual void recombine(const PseudoJet & pa, const PseudoJet & pb, 
                         PseudoJet & pab) const{
    // first make a copy of the "input" arguments as PyObject* managed by swig
    //PseudoJet pa_copy = pa;  // not sure this is needed
    PyObject *py_pa = 0;
    py_pa = SWIG_NewPointerObj((new fastjet::PseudoJet(static_cast< const fastjet::PseudoJet& >(pa))), SWIGTYPE_p_fastjet__PseudoJet, SWIG_POINTER_OWN |  0 );

    //PseudoJet pb_copy = pb;  // not sure this is needed
    PyObject *py_pb = 0;
    py_pb = SWIG_NewPointerObj((new fastjet::PseudoJet(static_cast< const fastjet::PseudoJet& >(pb))), SWIGTYPE_p_fastjet__PseudoJet, SWIG_POINTER_OWN |  0 );

    // now call the recombiner
    Py_XINCREF(_py_class);
    PyObject *py_result = PyObject_CallMethod(_py_class, (char *) "recombine",
                                              (char *) "(OO)", py_pa, py_pb);
    Py_XDECREF(_py_class);
    if (py_result == NULL)
      throw Error("RecombinerPython::recombine(): call to python function returned a NULL result.");

    // put the result in pab
    void *pab_void_ptr = 0;
    PseudoJet *pab_ptr = 0;
    int res1 = SWIG_ConvertPtr(py_result, &pab_void_ptr, SWIGTYPE_p_fastjet__PseudoJet, 0 );
    if (!SWIG_IsOK(res1)) {
      throw Error("RecombinerPython::recombine(): cannot reinterpret the last argument as a fastjet::PseudoJet.");
    }
    pab_ptr = reinterpret_cast< fastjet::PseudoJet * >(pab_void_ptr);
    pab = *pab_ptr;
    Py_XDECREF(py_result);  ///needed???
  }

  /// routine called to preprocess each input jet (to make all input
  /// jets compatible with the scheme requirements (e.g. massless).
  virtual void preprocess(PseudoJet & pa) const {
    // first make a copy of the arguments as PyObject* managed by swig
    //PseudoJet pa_copy = pa;  // not sure this is needed
    PyObject *py_pa = 0;
    py_pa = SWIG_NewPointerObj((new fastjet::PseudoJet(static_cast< fastjet::PseudoJet& >(pa))), SWIGTYPE_p_fastjet__PseudoJet, SWIG_POINTER_OWN |  0 );

    // then call the user-defined python function
    Py_XINCREF(_py_class);
    PyObject *py_result = PyObject_CallMethod(_py_class, (char *) "preprocess",
                                              (char *) "(O)", py_pa);
    Py_XDECREF(_py_class);
    if (py_result == NULL)
      throw Error("RecombinerPython::preprocess(): call to python function returned a NULL result.");

    // copy the result back in pa
    void *pa_void_ptr = 0;
    PseudoJet *pa_ptr = 0;
    int res1 = SWIG_ConvertPtr(py_pa, &pa_void_ptr, SWIGTYPE_p_fastjet__PseudoJet, 0);
    if (!SWIG_IsOK(res1)) {
      throw Error("RecombinerPython::preprocess(): cannot reinterpret the last argument as a fastjet::PseudoJet.");
    }
    pa_ptr = reinterpret_cast< fastjet::PseudoJet * >(pa_void_ptr);
    pa = *pa_ptr;
    Py_XDECREF(py_result);  ///needed???
  }

private:
   PyObject *_py_class;  
};

//----------------------------------------------------------------------
// Since Python handles enum types as int, there can be some confusion
// between different JetDefinition ctors, where a int param (intended
// as a double, like using R=1 or p=-1 for the genkt algorithm) is
// actually interpreted an te enum (for the recombination scheme).
//
// We therefore provide a few helpers to force the construction of a
// Jet Definition with a fied number of parameters (+recombiner+strategy)
//
// JetDefinition0Param(algorithm, recomb_scheme, strategy)
JetDefinition JetDefinition0Param(JetAlgorithm jet_algorithm, 
                                  RecombinationScheme recomb_scheme = E_scheme,
                                  Strategy strategy = Best){
  return JetDefinition(jet_algorithm, recomb_scheme, strategy);
}

// JetDefinition1Param(algorithm, R, recomb_scheme, strategy)
JetDefinition JetDefinition1Param(JetAlgorithm jet_algorithm, 
                                  double R_in, 
                                  RecombinationScheme recomb_scheme = E_scheme,
                                  Strategy strategy = Best){
  return JetDefinition(jet_algorithm, R_in, recomb_scheme, strategy);
}

// JetDefinition2Param(algorithm, R, extrarecomb_scheme, strategy)
JetDefinition JetDefinition2Param(JetAlgorithm jet_algorithm, 
                                  double R_in, 
                                  double xtra_param,
                                  RecombinationScheme recomb_scheme = E_scheme,
                                  Strategy strategy = Best){
  return JetDefinition(jet_algorithm, R_in, xtra_param, recomb_scheme, strategy);
}


FASTJET_END_NAMESPACE      // defined in fastjet/internal/base.hh

#endif // __FASTJET_PYTHONUSERINFO_HH__