Belle II Software development
PythonExpert Class Reference

Expert for the Python MVA method. More...

#include <Python.h>

Inheritance diagram for PythonExpert:
Expert

Public Member Functions

 PythonExpert ()
 Constructs a new Python Expert.
 
virtual void load (Weightfile &weightfile) override
 Load the expert from a Weightfile.
 
virtual std::vector< float > apply (Dataset &test_data) const override
 Apply this expert onto a dataset.
 
virtual std::vector< std::vector< float > > applyMulticlass (Dataset &test_data) const override
 Apply this expert onto a dataset for multiclass problem.
 

Protected Attributes

PythonOptions m_specific_options
 Method specific options.
 
boost::python::object m_unique_mva_module
 python module containing the mva methods
 
boost::python::object m_state
 current state object of method
 
std::vector< float > m_means
 Means of all features for normalization.
 
std::vector< float > m_stds
 Stds of all features for normalization.
 
GeneralOptions m_general_options
 General options loaded from the weightfile.
 

Detailed Description

Expert for the Python MVA method.

Definition at line 111 of file Python.h.

Constructor & Destructor Documentation

◆ PythonExpert()

Constructs a new Python Expert.

Definition at line 402 of file Python.cc.

403 {
404 PythonInitializerSingleton::GetInstance();
405 }

Member Function Documentation

◆ apply()

std::vector< float > apply ( Dataset & test_data) const
overridevirtual

Apply this expert onto a dataset.

Parameters
test_datadataset

Implements Expert.

Definition at line 462 of file Python.cc.

463 {
464
465 uint64_t numberOfFeatures = test_data.getNumberOfFeatures();
466 uint64_t numberOfEvents = test_data.getNumberOfEvents();
467
468 auto X = std::unique_ptr<float[]>(new float[numberOfEvents * numberOfFeatures]);
469 npy_intp dimensions_X[2] = {static_cast<npy_intp>(numberOfEvents), static_cast<npy_intp>(numberOfFeatures)};
470
471 for (uint64_t iEvent = 0; iEvent < numberOfEvents; ++iEvent) {
472 test_data.loadEvent(iEvent);
473 if (m_specific_options.m_normalize) {
474 for (uint64_t iFeature = 0; iFeature < numberOfFeatures; ++iFeature)
475 X[iEvent * numberOfFeatures + iFeature] = (test_data.m_input[iFeature] - m_means[iFeature]) / m_stds[iFeature];
476 } else {
477 for (uint64_t iFeature = 0; iFeature < numberOfFeatures; ++iFeature)
478 X[iEvent * numberOfFeatures + iFeature] = test_data.m_input[iFeature];
479 }
480 }
481
482 std::vector<float> probabilities(test_data.getNumberOfEvents(), std::numeric_limits<float>::quiet_NaN());
483
484 try {
485 auto ndarray_X = boost::python::handle<>(PyArray_SimpleNewFromData(2, dimensions_X, NPY_FLOAT32, X.get()));
486 auto result = m_unique_mva_module.attr("apply")(m_state, ndarray_X);
487 for (uint64_t iEvent = 0; iEvent < numberOfEvents; ++iEvent) {
488 // We have to do some nasty casting here, because the Python C-Api uses structs which are binary compatible
489 // to a PyObject but do not inherit from it!
490 probabilities[iEvent] = static_cast<float>(*static_cast<float*>(PyArray_GETPTR1(reinterpret_cast<PyArrayObject*>(result.ptr()),
491 iEvent)));
492 }
493 } catch (...) {
494 PyErr_Print();
495 PyErr_Clear();
496 B2ERROR("Failed calling applying PythonExpert");
497 throw std::runtime_error("Failed calling applying PythonExpert");
498 }
499
500 return probabilities;
501 }

◆ applyMulticlass()

std::vector< std::vector< float > > applyMulticlass ( Dataset & test_data) const
overridevirtual

Apply this expert onto a dataset for multiclass problem.

Parameters
test_datadataset

Reimplemented from Expert.

Definition at line 503 of file Python.cc.

504 {
505
506 uint64_t numberOfFeatures = test_data.getNumberOfFeatures();
507 uint64_t numberOfEvents = test_data.getNumberOfEvents();
508
509 auto X = std::unique_ptr<float[]>(new float[numberOfEvents * numberOfFeatures]);
510 npy_intp dimensions_X[2] = {static_cast<npy_intp>(numberOfEvents), static_cast<npy_intp>(numberOfFeatures)};
511
512 for (uint64_t iEvent = 0; iEvent < numberOfEvents; ++iEvent) {
513 test_data.loadEvent(iEvent);
514 if (m_specific_options.m_normalize) {
515 for (uint64_t iFeature = 0; iFeature < numberOfFeatures; ++iFeature)
516 X[iEvent * numberOfFeatures + iFeature] = (test_data.m_input[iFeature] - m_means[iFeature]) / m_stds[iFeature];
517 } else {
518 for (uint64_t iFeature = 0; iFeature < numberOfFeatures; ++iFeature)
519 X[iEvent * numberOfFeatures + iFeature] = test_data.m_input[iFeature];
520 }
521 }
522
523 unsigned int nClasses = m_general_options.m_nClasses;
524 std::vector<std::vector<float>> probabilities(test_data.getNumberOfEvents(), std::vector<float>(nClasses,
525 std::numeric_limits<float>::quiet_NaN()));
526
527 try {
528 auto ndarray_X = boost::python::handle<>(PyArray_SimpleNewFromData(2, dimensions_X, NPY_FLOAT32, X.get()));
529 auto result = m_unique_mva_module.attr("apply")(m_state, ndarray_X);
530 for (uint64_t iEvent = 0; iEvent < numberOfEvents; ++iEvent) {
531 // We have to do some nasty casting here, because the Python C-Api uses structs which are binary compatible
532 // to a PyObject but do not inherit from it!
533 for (uint64_t iClass = 0; iClass < nClasses; ++iClass) {
534 probabilities[iEvent][iClass] = static_cast<float>(*static_cast<float*>(PyArray_GETPTR2(reinterpret_cast<PyArrayObject*>
535 (result.ptr()),
536 iEvent, iClass)));
537 }
538 }
539 } catch (...) {
540 PyErr_Print();
541 PyErr_Clear();
542 B2ERROR("Failed calling applying PythonExpert");
543 throw std::runtime_error("Failed calling applying PythonExpert");
544 }
545
546 return probabilities;
547 }

◆ load()

void load ( Weightfile & weightfile)
overridevirtual

Load the expert from a Weightfile.

Parameters
weightfilecontaining all information necessary to build the expert

Implements Expert.

Definition at line 408 of file Python.cc.

409 {
410
411 std::string custom_weightfile = weightfile.generateFileName();
412 weightfile.getFile("Python_Weightfile", custom_weightfile);
413 weightfile.getOptions(m_general_options);
414 weightfile.getOptions(m_specific_options);
415
416 if (m_specific_options.m_normalize) {
417 m_means = weightfile.getVector<float>("Python_Means");
418 m_stds = weightfile.getVector<float>("Python_Stds");
419 }
420
421 try {
422 auto pickle = boost::python::import("pickle");
423 auto builtins = boost::python::import("builtins");
424
425 // Create a new empty module with a unique name.
426 // This way we dont end up with multiple mvas trying to implement
427 // the same apply method with the last one being used by all.
428 boost::uuids::random_generator uuid_gen;
429 std::string unique_mva_module_name = "custom_framework_" + boost::uuids::to_string(uuid_gen());
430 boost::python::object type = boost::python::import("types");
431 m_unique_mva_module = type.attr("ModuleType")(unique_mva_module_name.c_str());
432
433 // Find the framework file. Then execute it in the scope of the new module
434 auto framework = boost::python::import((std::string("basf2_mva_python_interface.") + m_specific_options.m_framework).c_str());
435 auto framework_file = framework.attr("__file__");
436 boost::python::extract<std::string> extractor(framework_file);
437 std::string framework_filename = extractor();
438 auto framework_file_source_code = loadPythonFileAsString(framework_filename);
439 builtins.attr("exec")(framework_file_source_code.c_str(), boost::python::object(m_unique_mva_module.attr("__dict__")));
440
441 // Overwrite framework with user-defined code from the steering file if defined
442 if (weightfile.containsElement("Python_Steeringfile")) {
443 std::string custom_steeringfile = weightfile.generateFileName();
444 weightfile.getFile("Python_Steeringfile", custom_steeringfile);
445 auto steeringfile = builtins.attr("open")(custom_steeringfile.c_str(), "rb");
446 auto source_code = pickle.attr("load")(steeringfile);
447 builtins.attr("exec")(boost::python::object(source_code), boost::python::object(m_unique_mva_module.attr("__dict__")));
448 }
449
450 auto file = builtins.attr("open")(custom_weightfile.c_str(), "rb");
451 auto unpickled_fit_object = pickle.attr("load")(file);
452 m_state = m_unique_mva_module.attr("load")(unpickled_fit_object);
453 } catch (...) {
454 PyErr_Print();
455 PyErr_Clear();
456 B2ERROR("Failed calling load in PythonExpert");
457 throw std::runtime_error("Failed calling load in PythonExpert");
458 }
459
460 }

Member Data Documentation

◆ m_general_options

GeneralOptions m_general_options
protectedinherited

General options loaded from the weightfile.

Definition at line 70 of file Expert.h.

◆ m_means

std::vector<float> m_means
protected

Means of all features for normalization.

Definition at line 141 of file Python.h.

◆ m_specific_options

PythonOptions m_specific_options
protected

Method specific options.

Definition at line 138 of file Python.h.

◆ m_state

boost::python::object m_state
protected

current state object of method

Definition at line 140 of file Python.h.

◆ m_stds

std::vector<float> m_stds
protected

Stds of all features for normalization.

Definition at line 142 of file Python.h.

◆ m_unique_mva_module

boost::python::object m_unique_mva_module
protected

python module containing the mva methods

Definition at line 139 of file Python.h.


The documentation for this class was generated from the following files: