ATLAS Offline Software
Loading...
Searching...
No Matches
NanobindBindings.cxx File Reference
#include <ColumnarToolWrapperPython/PythonToolHandle.h>
#include <ColumnarCore/ColumnarDef.h>
#include <AsgMessaging/MsgLevel.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/operators.h>
#include <nanobind/stl/list.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/pair.h>
#include <nanobind/stl/map.h>
#include <cstdio>
#include <string>

Go to the source code of this file.

Macros

#define MAKE_NDARRAY(T)

Functions

std::string getAddressString (const columnar::PythonToolHandle &obj)
std::string get_type_name (const std::type_info &type_info)
void setProperty (columnar::PythonToolHandle &self, const std::string &key, nb::object value)
nb::object getColumnVoid (columnar::PythonToolHandle &self, const std::string &key)
void setColumnVoid (columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column, bool is_const=true)
void setImmutableColumnVoid (columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column)
 NB_MODULE (python_tool_handle, module)

Macro Definition Documentation

◆ MAKE_NDARRAY

#define MAKE_NDARRAY ( T)
Value:
nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()

Function Documentation

◆ get_type_name()

std::string get_type_name ( const std::type_info & type_info)

escape hatch for char

Definition at line 43 of file NanobindBindings.cxx.

43 {
44 if (type_info == typeid(int)) {
45 return "int32";
46 } else if (type_info == typeid(unsigned int)) {
47 return "uint32";
48 } else if (type_info == typeid(short)) {
49 return "int16";
50 } else if (type_info == typeid(unsigned short)) {
51 return "uint16";
52 } else if (type_info == typeid(char)) {
54 return "int8";
55 } else if (type_info == typeid(unsigned char)) {
56 return "uint8";
57 } else if (type_info == typeid(float)) {
58 return "float32";
59 } else if (type_info == typeid(double)) {
60 return "float64";
61 } else if (type_info == typeid(long)) {
62 return "int64";
63 } else if (type_info == typeid(unsigned long)) {
64 return "uint64";
65 } else if (type_info == typeid(bool)) {
66 return "bool";
67 } else {
68 // If the type is unknown, you can return the mangled name or a default message
69 return std::string("unknown ('") + type_info.name() + "')";
70 }
71}

◆ getAddressString()

std::string getAddressString ( const columnar::PythonToolHandle & obj)

Definition at line 36 of file NanobindBindings.cxx.

36 {
37 char buffer[32]; // Ensure the buffer is large enough for the hex representation
38 std::snprintf(buffer, sizeof(buffer), "0x%llx", reinterpret_cast<unsigned long long>(&obj));
39 return std::string(buffer);
40}

◆ getColumnVoid()

nb::object getColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key )

Definition at line 85 of file NanobindBindings.cxx.

85 {
86 auto [size, ptr, type] = self.getColumnVoid(key);
87 // Wrap the raw pointer in a numpy array without copying. The caller is
88 // responsible for keeping the PythonToolHandle alive while using the result.
89#define MAKE_NDARRAY(T) \
90 nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()
91 if (*type == typeid(float)) return MAKE_NDARRAY(float);
92 if (*type == typeid(double)) return MAKE_NDARRAY(double);
93 if (*type == typeid(char)) return MAKE_NDARRAY(char);
94 if (*type == typeid(int)) return MAKE_NDARRAY(int);
95 if (*type == typeid(std::uint8_t)) return MAKE_NDARRAY(std::uint8_t);
96 if (*type == typeid(std::uint16_t)) return MAKE_NDARRAY(std::uint16_t);
97 if (*type == typeid(std::uint32_t)) return MAKE_NDARRAY(std::uint32_t);
98 if (*type == typeid(std::uint64_t)) return MAKE_NDARRAY(std::uint64_t);
99 if (*type == typeid(std::int16_t)) return MAKE_NDARRAY(std::int16_t);
100 if (*type == typeid(std::int32_t)) return MAKE_NDARRAY(std::int32_t);
101 if (*type == typeid(std::int64_t)) return MAKE_NDARRAY(std::int64_t);
102#undef MAKE_NDARRAY
103 throw std::runtime_error("getColumnVoid: unsupported column type: " + std::string(type->name()));
104}
#define MAKE_NDARRAY(T)
std::tuple< std::size_t, const void *, const std::type_info * > getColumnVoid(const std::string &name) const
get a column pointer by name; returns {size, ptr, type_info}
void * ptr(T *p)
Definition SGImplSvc.cxx:74

◆ NB_MODULE()

NB_MODULE ( python_tool_handle ,
module  )

load in the ColumnAccessMode enum

Definition at line 193 of file NanobindBindings.cxx.

193 {
194 module.doc() = "Nanobind bindings for PythonToolHandle";
195
197 throw nb::import_error("This module can only be used in columnar access mode. Try setting up a ColumnarAnalysis release instead.");
198
199 module.attr("numberOfEventsName") = &columnar::eventRangeColumnName;
200 module.attr("eventRangeColumnName") = &columnar::eventRangeColumnName;
201
202 // Install a Python callable as the global C++ message printer.
203#ifdef XAOD_STANDALONE
204 // Overload 1: with callback function
205 module.def("set_python_printer", &set_printer_from_callable, nb::arg("callback"),
206 "Install a Python callable(level: int, name: str, text: str) as the global "
207 "C++ message printer.");
208
209 // Overload 2: reset (no argument)
210 module.def("set_python_printer", &clear_printer,
211 "Reset to the default stdout message printer.");
212#else
213 // In Athena/AthAnalysis builds IMessagePrinter does not exist; expose the
214 // function so Python code doesn't get AttributeError, but raise at call time.
215 module.def("set_python_printer", [](nb::args, nb::kwargs) {
216 throw std::runtime_error(
217 "set_python_printer is only available in standalone "
218 "(AnalysisBase/ColumnarAnalysis) builds, not in Athena/AthAnalysis.");
219 }, "Not available in Athena/AthAnalysis builds.");
220#endif // XAOD_STANDALONE
221
223 nb::enum_<columnar::ColumnAccessMode>(module, "ColumnAccessMode")
224 .value("input", columnar::ColumnAccessMode::input)
225 .value("output", columnar::ColumnAccessMode::output)
226 .value("update", columnar::ColumnAccessMode::update)
227 .def("__repr__", [](const columnar::ColumnAccessMode &mode) -> std::string {
228 switch (mode) {
230 return "<ColumnAccessMode input>";
232 return "<ColumnAccessMode output>";
234 return "<ColumnAccessMode update>";
235 default:
236 return "<ColumnAccessMode update value=" + std::to_string(static_cast<int>(mode)) + ">";
237 }
238 })
239 .export_values(); // Makes the enum values accessible without namespace in Python
240
241 nb::enum_<MSG::Level>(module, "MsgLevel", nb::is_arithmetic())
242 .value("NIL", MSG::NIL)
243 .value("VERBOSE", MSG::VERBOSE)
244 .value("DEBUG", MSG::DEBUG)
245 .value("INFO", MSG::INFO)
246 .value("WARNING", MSG::WARNING)
247 .value("ERROR", MSG::ERROR)
248 .value("FATAL", MSG::FATAL)
249 .export_values();
250
251 nb::class_<columnar::ColumnInfo>(module, "ColumnInfo")
252 .def(nb::init<>()) // Default constructor
253 .def_ro("name", &columnar::ColumnInfo::name)
254 .def_ro("index", &columnar::ColumnInfo::index)
255 .def_prop_ro("dtype", [](const columnar::ColumnInfo &self){
256 return get_type_name(*self.type);
257 })
258 .def_ro("access_mode", &columnar::ColumnInfo::accessMode)
259 .def_ro("offset_name", &columnar::ColumnInfo::offsetName)
260 .def_ro("fixed_dimensions", &columnar::ColumnInfo::fixedDimensions)
261 .def_ro("link_target_names", &columnar::ColumnInfo::linkTargetNames)
262 .def_ro("variant_link_key_column", &columnar::ColumnInfo::variantLinkKeyColumn)
263 .def_ro("is_offset", &columnar::ColumnInfo::isOffset)
264 .def_ro("replaces_column", &columnar::ColumnInfo::replacesColumn)
265 .def_ro("is_optional", &columnar::ColumnInfo::isOptional)
266 .def("__repr__", [](const columnar::ColumnInfo &self) {
267 std::string access_mode;
268 switch (self.accessMode) {
270 access_mode = "input";
271 break;
273 access_mode = "output";
274 break;
276 access_mode = "update";
277 break;
278 default:
279 // For unknown values, return the integer value
280 access_mode = "unknown";
281 }
282 return "<ColumnInfo name='" + self.name + "'" +
283 (self.isOffset ? "" : ", offset='" + self.offsetName + "'") +
284 ", access_mode='" + access_mode + "'" +
285 ", dtype='" + (self.type ? get_type_name(*self.type) : "" ) + "'" +
286 (self.isOptional ? ", optional": "") +
287 ">";
288 })
289 .def("to_dict", [](const columnar::ColumnInfo& self) {
290 nb::dict d;
291 d["name"] = self.name;
292 d["index"] = self.index;
293 d["dtype"] = self.type ? get_type_name(*self.type) : "";
294 d["access_mode"] = static_cast<int>(self.accessMode);
295 d["offset_name"] = self.offsetName;
296 d["fixed_dimensions"] = self.fixedDimensions;
297 d["link_target_names"] = self.linkTargetNames;
298 d["variant_link_key_column"] = self.variantLinkKeyColumn;
299 d["is_offset"] = self.isOffset;
300 d["replaces_column"] = self.replacesColumn;
301 d["is_optional"] = self.isOptional;
302 return d;
303 });
304
305 nb::class_<columnar::PythonToolHandle>(module, "PythonToolHandle")
306 .def(nb::init())
307
308 // Properties
309 .def_prop_ro("type", [](const columnar::PythonToolHandle &self) {
310 const asg::AsgToolConfig& config = self.getConfig();
311 const std::string& type = config.type();
312 if (type.empty()) {
313 std::cerr << "Warning: PythonToolHandle.type is empty."
314 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
315 }
316 return type;
317 })
318
319 .def_prop_ro("name", [](const columnar::PythonToolHandle &self) {
320 const asg::AsgToolConfig& config = self.getConfig();
321 const std::string& name = config.name();
322 if (name.empty()) {
323 std::cerr << "Warning: PythonToolHandle.name is empty."
324 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
325 }
326 return name;
327 })
328
329 // Methods
330 .def("set_type_and_name",
331 [](columnar::PythonToolHandle &self, const std::string& type_and_name) {
332 self.setTypeAndName(type_and_name);
333 },
334 "type_and_name"_a,
335 "Set the type and name of the tool.")
336
337 .def("set_property", &setProperty,
338 "key"_a, "value"_a,
339 "Set a property on the tool.")
340
341 .def("__setattr__", &setProperty,
342 "key"_a, "value"_a,
343 "Set a property on the tool.")
344
345 .def("preinitialize",
347 "Preinitialize the tool.")
348
349 // rename_containers([("from", "to"), ...])
350 .def("rename_containers",
352 "renames"_a,
353 "Rename the columns the tool uses.")
354
355 // rename_containers({"from": "to"}, ...})
356 .def("rename_containers",
357 [](columnar::PythonToolHandle &self, const std::map<std::string,std::string>& renames){
358 std::vector<std::pair<std::string, std::string>> vectorized;
359 for (const auto& pair : renames)
360 vectorized.emplace_back(pair);
361
362 return self.renameContainers(vectorized);
363 },
364 "renames"_a,
365 "Rename the columns the tool uses.")
366
367 .def("initialize",
369 "Initialize the tool.")
370
371 .def("apply_systematic_variation",
372 [](columnar::PythonToolHandle &self, const std::string& sys_name) {
373 self.applySystematicVariation(sys_name);
374 },
375 "sys_name"_a,
376 "Apply a systematic variation to the tool.")
377
378 .def("set_column",
379 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<float> column) {
380 self.setColumn<float>(key, column.shape(0), column.data());
381 },
382 "key"_a, "column"_a,
383 "Set a float column pointer.")
384
385 .def("set_column",
386 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<char> column) {
387 self.setColumn<char>(key, column.shape(0), column.data());
388 },
389 "key"_a, "column"_a,
390 "Set a char column pointer.")
391
392 .def("set_column",
393 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<int> column) {
394 self.setColumn<int>(key, column.shape(0), column.data());
395 },
396 "key"_a, "column"_a,
397 "Set an int column pointer.")
398
399 .def("set_column",
400 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint8_t> column) {
401 self.setColumn<uint8_t>(key, column.shape(0), column.data());
402 },
403 "key"_a, "column"_a,
404 "Set a uint8_t column pointer.")
405
406 .def("set_column",
407 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint16_t> column) {
408 self.setColumn<uint16_t>(key, column.shape(0), column.data());
409 },
410 "key"_a, "column"_a,
411 "Set a uint16_t column pointer.")
412
413 .def("set_column",
414 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint32_t> column) {
415 self.setColumn<uint32_t>(key, column.shape(0), column.data());
416 },
417 "key"_a, "column"_a,
418 "Set a uint32_t column pointer.")
419
420 .def("set_column",
421 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint64_t> column) {
422 self.setColumn<uint64_t>(key, column.shape(0), column.data());
423 },
424 "key"_a, "column"_a,
425 "Set a uint64_t column pointer.")
426
427 .def("set_column_void", &setColumnVoid,
428 // cppcheck-suppress assignBoolToPointer
429 "key"_a, "column"_a, "is_const"_a = true,
430 "Set a void column pointer (nanobind version).")
431
432 .def("__setitem__", &setImmutableColumnVoid,
433 "key"_a, "column"_a,
434 "Set a void immutable column pointer (nanobind version).")
435
436 .def("__getitem__", &getColumnVoid,
437 "key"_a,
438 "Get a column as a numpy array (zero-copy view into the tool's buffer).")
439
440 .def("keys",
442 "Return the column names (enables dict(handle)).")
443
444 .def("call",
446 "Call the tool and reset the columns.")
447
448 .def_prop_ro(
449 "columns",
451 "Get the expected column information."
452 )
453
454 .def("get_recommended_systematics",
456 "Get the recommended systematics.")
457
458 // Make this more fancy in the future
459 // <PythonToolHandle(CP::MuonEfficiencyScaleFactors/unique0) object at 0x7f2943b07568>
460 .def("__repr__", [](const columnar::PythonToolHandle &self) {
461 const asg::AsgToolConfig& config = self.getConfig();
462 return "<PythonToolHandle(" + config.type() + "/" + config.name() + ") object at " + getAddressString(self) + ">";
463 });
464}
nb::object getColumnVoid(columnar::PythonToolHandle &self, const std::string &key)
std::string get_type_name(const std::type_info &type_info)
void setColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column, bool is_const=true)
void setProperty(columnar::PythonToolHandle &self, const std::string &key, nb::object value)
void setImmutableColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column)
std::string getAddressString(const columnar::PythonToolHandle &obj)
an object that can create a AsgTool
a handle to a python tool for use via nanobind
void setColumn(const std::string &key, std::size_t size, CT *dataPtr)
set a column pointer (raw pointer version)
void renameContainers(const std::vector< std::pair< std::string, std::string > > &renames)
rename the columns the tool uses
void preinitialize()
preinitialize the tool
void applySystematicVariation(const std::string &sysName)
set the tool to apply the given systematic variation
void setTypeAndName(const std::string &typeAndName)
set the type and name for the tool
std::vector< std::string > getRecommendedSystematics() const
get the recommended systematics
const asg::AsgToolConfig & getConfig() const
get the AsgToolConfig
std::vector< ColumnInfo > getColumnInfo() const
get the expected column info
std::vector< std::string > getColumnNames() const
get the expected column names
void call()
call the tool and reset the columns
void initialize()
initialize the tool
STL class.
constexpr unsigned columnarAccessMode
ColumnAccessMode
an enum for the different access modes for a column
Definition ColumnInfo.h:19
@ update
an updateable column
Definition ColumnInfo.h:27
@ output
an output column
Definition ColumnInfo.h:24
@ input
an input column
Definition ColumnInfo.h:21
setWord1 uint16_t
setEventNumber uint32_t
a struct that contains meta-information about each column that's needed to interface the column with ...
Definition ColumnInfo.h:35
std::string offsetName
the name of the offset column used for this column (or empty string for none)
Definition ColumnInfo.h:74
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
Definition ColumnInfo.h:82
bool isOptional
whether this column is optional
Definition ColumnInfo.h:121
std::string variantLinkKeyColumn
if this is a variant link column, this is the name of the column with the container keys
Definition ColumnInfo.h:169
std::string name
the name of the column
Definition ColumnInfo.h:42
std::vector< std::string > linkTargetNames
for link columns: the name(s) of the container(s) we link to
Definition ColumnInfo.h:154
bool isOffset
whether this is an offset column
Definition ColumnInfo.h:92
ColumnAccessMode accessMode
the access mode for the column
Definition ColumnInfo.h:58
std::string replacesColumn
whether this replaces another column
Definition ColumnInfo.h:102
unsigned index
the index of the column in the data array
Definition ColumnInfo.h:46
const std::type_info * type
the type of the individual entries in the column
Definition ColumnInfo.h:54

◆ setColumnVoid()

void setColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key,
nb::ndarray<> column,
bool is_const = true )

Definition at line 106 of file NanobindBindings.cxx.

106 {
107 // TODO: figure out how to get type_info from handle instead...
108 // nb::handle handle = column.handle();
109
110 const std::type_info* type_info = nullptr;
111 const nb::dlpack::dtype dtype = column.dtype();
112 switch ((nb::dlpack::dtype_code) dtype.code) {
113 case nb::dlpack::dtype_code::Int:
114 switch (dtype.bits) {
115 // escape hatch to handle char for now
116 // we should rely on signed/unsigned and nbits, instead of std::type_info
117 // case 8: type_info = &typeid(std::int8_t); break;
118 case 8: type_info = &typeid(char); break;
119 case 16: type_info = &typeid(std::int16_t); break;
120 case 32: type_info = &typeid(std::int32_t); break;
121 case 64: type_info = &typeid(std::int64_t); break;
122 }
123 break;
124
125 case nb::dlpack::dtype_code::UInt:
126 switch (dtype.bits) {
127 case 8: type_info = &typeid(std::uint8_t); break;
128 case 16: type_info = &typeid(std::uint16_t); break;
129 case 32: type_info = &typeid(std::uint32_t); break;
130 case 64: type_info = &typeid(std::uint64_t); break;
131 }
132 break;
133
134 case nb::dlpack::dtype_code::Float:
135 switch (dtype.bits) {
136 case 32: type_info = &typeid(float); break;
137 case 64: type_info = &typeid(double); break;
138 }
139 break;
140
141 default:
142 break;
143 }
144
145 if (type_info == nullptr) throw std::runtime_error ("unsupported column type passed in");
146 self.setColumnVoid(key, column.shape(0), column.data(), *type_info, is_const);
147}
void setColumnVoid(const std::string &name, std::size_t size, const void *dataPtr, const std::type_info &type, bool isConst)
set a column pointer

◆ setImmutableColumnVoid()

void setImmutableColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key,
nb::ndarray<> column )

Definition at line 149 of file NanobindBindings.cxx.

149 {
150 setColumnVoid(self, key, column, true);
151};

◆ setProperty()

void setProperty ( columnar::PythonToolHandle & self,
const std::string & key,
nb::object value )

Definition at line 73 of file NanobindBindings.cxx.

73 {
74 if (nb::isinstance<nb::str>(value)) {
75 self.setProperty(key, nb::cast<std::string>(value));
76 } else if (nb::isinstance<nb::int_>(value)) {
77 self.setProperty(key, nb::cast<int>(value));
78 } else if (nb::isinstance<nb::float_>(value)) {
79 self.setProperty(key, nb::cast<double>(value));
80 } else {
81 throw std::runtime_error("Unsupported property type. Must be str, int, or float.");
82 }
83}
void setProperty(const std::string &key, T &&value)
set a property on the tool