ATLAS Offline Software
Loading...
Searching...
No Matches
NanobindBindings.cxx File Reference
#include <ColumnarToolWrapperPython/PythonToolHandle.h>
#include <ColumnarCore/ColumnarDef.h>
#include <ColumnarCore/ColumnInfoHelpers.h>
#include <CxxUtils/crc64.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()
size_t size() const
Number of registered mappings.

Function Documentation

◆ get_type_name()

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

escape hatch for char

Definition at line 45 of file NanobindBindings.cxx.

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

◆ getAddressString()

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

Definition at line 38 of file NanobindBindings.cxx.

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

◆ getColumnVoid()

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

Definition at line 87 of file NanobindBindings.cxx.

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

◆ NB_MODULE()

NB_MODULE ( python_tool_handle ,
module  )

load in the ColumnAccessMode enum

Definition at line 195 of file NanobindBindings.cxx.

195 {
196 module.doc() = "Nanobind bindings for PythonToolHandle";
197
198 static_assert(std::is_same_v<columnar::ColumnarMode,columnar::ColumnarModeArray>, "this module is only defined for array mode");
199
200 module.attr("numberOfEventsName") = &columnar::eventRangeColumnName;
201 module.attr("eventRangeColumnName") = &columnar::eventRangeColumnName;
202
203 // the value used for an invalid (null/empty) element link in array mode
204 module.attr("invalid_link_value") = columnar::ColumnarModeArray::invalidLinkValue;
205
206 module.def("crc64",
207 [](const std::string& data) { return CxxUtils::crc64(data); },
208 "data"_a,
209 "CRC-64 of a string, using the Athena default polynomial "
210 "(CxxUtils::crc64).");
211
212 module.def("crc64addint", &CxxUtils::crc64addint,
213 "crc"_a, "x"_a,
214 "Extend a previously-calculated CRC-64 to include an integer "
215 "(CxxUtils::crc64addint).");
216
217 module.def("sg_key",
218 &columnar::computeSgKey,
219 "name"_a, "clid"_a = 0,
220 "StoreGate hash (sgkey) of a container name, optionally mixed with "
221 "its CLID. ElementLink m_persKey values in Athena-written files are "
222 "sg_key(container_name, container_clid).");
223
224 // Install a Python callable as the global C++ message printer.
225#ifdef XAOD_STANDALONE
226 // Overload 1: with callback function
227 module.def("set_python_printer", &set_printer_from_callable, nb::arg("callback"),
228 "Install a Python callable(level: int, name: str, text: str) as the global "
229 "C++ message printer.");
230
231 // Overload 2: reset (no argument)
232 module.def("set_python_printer", &clear_printer,
233 "Reset to the default stdout message printer.");
234#else
235 // In Athena/AthAnalysis builds IMessagePrinter does not exist; expose the
236 // function so Python code doesn't get AttributeError, but raise at call time.
237 module.def("set_python_printer", [](nb::args, nb::kwargs) {
238 throw std::runtime_error(
239 "set_python_printer is only available in standalone "
240 "(AnalysisBase/ColumnarAnalysis) builds, not in Athena/AthAnalysis.");
241 }, "Not available in Athena/AthAnalysis builds.");
242#endif // XAOD_STANDALONE
243
245 nb::enum_<columnar::ColumnAccessMode>(module, "ColumnAccessMode")
246 .value("input", columnar::ColumnAccessMode::input)
247 .value("output", columnar::ColumnAccessMode::output)
248 .value("update", columnar::ColumnAccessMode::update)
249 .def("__repr__", [](const columnar::ColumnAccessMode &mode) -> std::string {
250 switch (mode) {
252 return "<ColumnAccessMode input>";
254 return "<ColumnAccessMode output>";
256 return "<ColumnAccessMode update>";
257 default:
258 return "<ColumnAccessMode update value=" + std::to_string(static_cast<int>(mode)) + ">";
259 }
260 })
261 .export_values(); // Makes the enum values accessible without namespace in Python
262
263 nb::enum_<MSG::Level>(module, "MsgLevel", nb::is_arithmetic())
264 .value("NIL", MSG::NIL)
265 .value("VERBOSE", MSG::VERBOSE)
266 .value("DEBUG", MSG::DEBUG)
267 .value("INFO", MSG::INFO)
268 .value("WARNING", MSG::WARNING)
269 .value("ERROR", MSG::ERROR)
270 .value("FATAL", MSG::FATAL)
271 .export_values();
272
273 nb::class_<columnar::ColumnInfo>(module, "ColumnInfo")
274 .def(nb::init<>()) // Default constructor
275 .def_ro("name", &columnar::ColumnInfo::name)
276 .def_ro("index", &columnar::ColumnInfo::index)
277 .def_prop_ro("dtype", [](const columnar::ColumnInfo &self){
278 return get_type_name(*self.type);
279 })
280 .def_ro("access_mode", &columnar::ColumnInfo::accessMode)
281 .def_ro("offset_name", &columnar::ColumnInfo::offsetName)
282 .def_ro("fixed_dimensions", &columnar::ColumnInfo::fixedDimensions)
283 .def_ro("sole_link_target_name", &columnar::ColumnInfo::soleLinkTargetName)
284 .def_ro("sole_link_target_clid", &columnar::ColumnInfo::soleLinkTargetClid)
285 .def_ro("is_variant_link", &columnar::ColumnInfo::isVariantLink)
286 .def_ro("variant_link_target_names", &columnar::ColumnInfo::variantLinkTargetNames)
287 .def_ro("key_column_for_variant_link", &columnar::ColumnInfo::keyColumnForVariantLink)
288 .def_ro("is_offset", &columnar::ColumnInfo::isOffset)
289 .def_ro("replaces_column", &columnar::ColumnInfo::replacesColumn)
290 .def_ro("is_optional", &columnar::ColumnInfo::isOptional)
291 .def("__repr__", [](const columnar::ColumnInfo &self) {
292 std::string access_mode;
293 switch (self.accessMode) {
295 access_mode = "input";
296 break;
298 access_mode = "output";
299 break;
301 access_mode = "update";
302 break;
303 default:
304 // For unknown values, return the integer value
305 access_mode = "unknown";
306 }
307 return "<ColumnInfo name='" + self.name + "'" +
308 (self.isOffset ? "" : ", offset='" + self.offsetName + "'") +
309 ", access_mode='" + access_mode + "'" +
310 ", dtype='" + (self.type ? get_type_name(*self.type) : "" ) + "'" +
311 (self.isOptional ? ", optional": "") +
312 ">";
313 })
314 .def("to_dict", [](const columnar::ColumnInfo& self) {
315 nb::dict d;
316 d["name"] = self.name;
317 d["index"] = self.index;
318 d["dtype"] = self.type ? get_type_name(*self.type) : "";
319 d["access_mode"] = static_cast<int>(self.accessMode);
320 d["offset_name"] = self.offsetName;
321 d["fixed_dimensions"] = self.fixedDimensions;
322 d["sole_link_target_name"] = self.soleLinkTargetName;
323 d["sole_link_target_clid"] = self.soleLinkTargetClid;
324 d["is_variant_link"] = self.isVariantLink;
325 d["variant_link_target_names"] = self.variantLinkTargetNames;
326 d["key_column_for_variant_link"] = self.keyColumnForVariantLink;
327 d["is_offset"] = self.isOffset;
328 d["replaces_column"] = self.replacesColumn;
329 d["is_optional"] = self.isOptional;
330 return d;
331 });
332
333 nb::class_<columnar::PythonToolHandle>(module, "PythonToolHandle")
334 .def(nb::init())
335
336 // Properties
337 .def_prop_ro("type", [](const columnar::PythonToolHandle &self) {
338 const asg::AsgToolConfig& config = self.getConfig();
339 const std::string& type = config.type();
340 if (type.empty()) {
341 std::cerr << "Warning: PythonToolHandle.type is empty."
342 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
343 }
344 return type;
345 })
346
347 .def_prop_ro("name", [](const columnar::PythonToolHandle &self) {
348 const asg::AsgToolConfig& config = self.getConfig();
349 const std::string& name = config.name();
350 if (name.empty()) {
351 std::cerr << "Warning: PythonToolHandle.name is empty."
352 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
353 }
354 return name;
355 })
356
357 // Methods
358 .def("set_type_and_name",
359 [](columnar::PythonToolHandle &self, const std::string& type_and_name) {
360 self.setTypeAndName(type_and_name);
361 },
362 "type_and_name"_a,
363 "Set the type and name of the tool.")
364
365 .def("set_property", &setProperty,
366 "key"_a, "value"_a,
367 "Set a property on the tool.")
368
369 .def("__setattr__", &setProperty,
370 "key"_a, "value"_a,
371 "Set a property on the tool.")
372
373 .def("preinitialize",
375 "Preinitialize the tool.")
376
377 // rename_containers([("from", "to"), ...])
378 .def("rename_containers",
380 "renames"_a,
381 "Rename the columns the tool uses.")
382
383 // rename_containers({"from": "to"}, ...})
384 .def("rename_containers",
385 [](columnar::PythonToolHandle &self, const std::map<std::string,std::string>& renames){
386 std::vector<std::pair<std::string, std::string>> vectorized;
387 for (const auto& pair : renames)
388 vectorized.emplace_back(pair);
389
390 return self.renameContainers(vectorized);
391 },
392 "renames"_a,
393 "Rename the columns the tool uses.")
394
395 .def("initialize",
397 "Initialize the tool.")
398
399 .def("apply_systematic_variation",
400 [](columnar::PythonToolHandle &self, const std::string& sys_name) {
401 self.applySystematicVariation(sys_name);
402 },
403 "sys_name"_a,
404 "Apply a systematic variation to the tool.")
405
406 .def("set_column",
407 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<float> column) {
408 self.setColumn<float>(key, column.shape(0), column.data());
409 },
410 "key"_a, "column"_a,
411 "Set a float column pointer.")
412
413 .def("set_column",
414 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<char> column) {
415 self.setColumn<char>(key, column.shape(0), column.data());
416 },
417 "key"_a, "column"_a,
418 "Set a char column pointer.")
419
420 .def("set_column",
421 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<int> column) {
422 self.setColumn<int>(key, column.shape(0), column.data());
423 },
424 "key"_a, "column"_a,
425 "Set an int column pointer.")
426
427 .def("set_column",
428 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint8_t> column) {
429 self.setColumn<uint8_t>(key, column.shape(0), column.data());
430 },
431 "key"_a, "column"_a,
432 "Set a uint8_t column pointer.")
433
434 .def("set_column",
435 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint16_t> column) {
436 self.setColumn<uint16_t>(key, column.shape(0), column.data());
437 },
438 "key"_a, "column"_a,
439 "Set a uint16_t column pointer.")
440
441 .def("set_column",
442 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint32_t> column) {
443 self.setColumn<uint32_t>(key, column.shape(0), column.data());
444 },
445 "key"_a, "column"_a,
446 "Set a uint32_t column pointer.")
447
448 .def("set_column",
449 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint64_t> column) {
450 self.setColumn<uint64_t>(key, column.shape(0), column.data());
451 },
452 "key"_a, "column"_a,
453 "Set a uint64_t column pointer.")
454
455 .def("set_column_void", &setColumnVoid,
456 // cppcheck-suppress assignBoolToPointer
457 "key"_a, "column"_a, "is_const"_a = true,
458 "Set a void column pointer (nanobind version).")
459
460 .def("__setitem__", &setImmutableColumnVoid,
461 "key"_a, "column"_a,
462 "Set a void immutable column pointer (nanobind version).")
463
464 .def("__getitem__", &getColumnVoid,
465 "key"_a,
466 "Get a column as a numpy array (zero-copy view into the tool's buffer).")
467
468 .def("keys",
470 "Return the column names (enables dict(handle)).")
471
472 .def("call",
474 "Call the tool and reset the columns.")
475
476 .def_prop_ro(
477 "columns",
479 "Get the expected column information."
480 )
481
482 .def("get_recommended_systematics",
484 "Get the recommended systematics.")
485
486 // Make this more fancy in the future
487 // <PythonToolHandle(CP::MuonEfficiencyScaleFactors/unique0) object at 0x7f2943b07568>
488 .def("__repr__", [](const columnar::PythonToolHandle &self) {
489 const asg::AsgToolConfig& config = self.getConfig();
490 return "<PythonToolHandle(" + config.type() + "/" + config.name() + ") object at " + getAddressString(self) + ">";
491 });
492}
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.
ColumnAccessMode
an enum for the different access modes for a column
Definition ColumnInfo.h:20
@ update
an updateable column
Definition ColumnInfo.h:28
@ output
an output column
Definition ColumnInfo.h:25
@ input
an input column
Definition ColumnInfo.h:22
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:36
std::string offsetName
the name of the offset column used for this column (or empty string for none)
Definition ColumnInfo.h:75
std::string soleLinkTargetName
for simple link columns: the name of the target container
Definition ColumnInfo.h:132
std::string keyColumnForVariantLink
if this is a key column for a variant link, the name of the associated link column
Definition ColumnInfo.h:192
std::uint32_t soleLinkTargetClid
for simple link columns: the CLID of the target container
Definition ColumnInfo.h:146
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
Definition ColumnInfo.h:83
bool isOptional
whether this column is optional
Definition ColumnInfo.h:122
std::vector< std::string > variantLinkTargetNames
for variant link key columns: the names of the containers we can link to
Definition ColumnInfo.h:177
std::string name
the name of the column
Definition ColumnInfo.h:43
bool isOffset
whether this is an offset column
Definition ColumnInfo.h:93
ColumnAccessMode accessMode
the access mode for the column
Definition ColumnInfo.h:59
std::string replacesColumn
whether this replaces another column
Definition ColumnInfo.h:103
bool isVariantLink
whether this is a variant link column
Definition ColumnInfo.h:154
unsigned index
the index of the column in the data array
Definition ColumnInfo.h:47
const std::type_info * type
the type of the individual entries in the column
Definition ColumnInfo.h:55

◆ setColumnVoid()

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

Definition at line 108 of file NanobindBindings.cxx.

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

151 {
152 setColumnVoid(self, key, column, true);
153};

◆ setProperty()

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

Definition at line 75 of file NanobindBindings.cxx.

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