Minuit2 minimization with FCNBase

Hi,

I’m attempting to pythonize the ROOT Minuit minimization approach.

From what I understand you need to write a class that inherits from the FCNBase class then override the FCNBase::operator()(const std::vector&) method.

In python I interpret this as overriding the call method? However my attempts end in segmentation violations. Below is some example code and the seg fault error, ROOT version is 5.30.04

any help will be appreciated

import ROOT

class MyFCN(ROOT.Minuit2.FCNBase):
    def __init__(self):
        pass
    def __call__(self, params):
        return 0.
    
params = ROOT.Minuit2.MnUserParameters()
params.Add("param0", 0., 0.)
params.Add('param1', 1., 1.)
params.Add('param2', 2., 2.)
my_fcn = MyFCN()
minimizer = ROOT.Minuit2.MnMigrad(my_fcn, params)
minimization = minimizer()
python test.py

 *** Break *** segmentation violation



===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================

Thread 2 (Thread 0x4299a940 (LWP 21228)):
#0  0x00002ab4ba0f5d21 in sem_wait () from /lib64/libpthread.so.0
#1  0x00000000004c39a8 in PyThread_acquire_lock (lock=0x195db5a0, waitflag=128) at Python/thread_pthread.h:349
#2  0x0000000000493834 in PyEval_RestoreThread (tstate=0x1aa7a650) at Python/ceval.c:353
#3  0x00002ab4c8e0c482 in floatsleep (self=<value optimized out>, args=<value optimized out>) at /build/agaspar/work/Python-2.6.5/Modules/timemodule.c:921
#4  time_sleep (self=<value optimized out>, args=<value optimized out>) at /build/agaspar/work/Python-2.6.5/Modules/timemodule.c:206
#5  0x000000000049888c in call_function (f=0x1aa81000, throwflag=<value optimized out>) at Python/ceval.c:3750
#6  PyEval_EvalFrameEx (f=0x1aa81000, throwflag=<value optimized out>) at Python/ceval.c:2412
#7  0x000000000049a2a7 in PyEval_EvalCodeEx (co=0x192c0f30, globals=<value optimized out>, locals=<value optimized out>, args=0x19b21568, argcount=1, kws=0x195e95f0, kwcount=0, 
    defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:3000
#8  0x00000000004f2244 in function_call (func=0x19953aa0, arg=0x19b21550, kw=0x1aa7f4a0) at Objects/funcobject.c:524
#9  0x00000000004197b8 in PyObject_Call (func=0x19953aa0, arg=0x19b21550, kw=0x1aa7f4a0) at Objects/abstract.c:2492
#10 0x000000000049590a in ext_do_call (f=0x1aa80e20, throwflag=<value optimized out>) at Python/ceval.c:4063
#11 PyEval_EvalFrameEx (f=0x1aa80e20, throwflag=<value optimized out>) at Python/ceval.c:2452
#12 0x0000000000499097 in fast_function (f=0x1aa80a00, throwflag=<value optimized out>) at Python/ceval.c:3836
#13 call_function (f=0x1aa80a00, throwflag=<value optimized out>) at Python/ceval.c:3771
#14 PyEval_EvalFrameEx (f=0x1aa80a00, throwflag=<value optimized out>) at Python/ceval.c:2412
#15 0x0000000000499097 in fast_function (f=0x1aa77e80, throwflag=<value optimized out>) at Python/ceval.c:3836
#16 call_function (f=0x1aa77e80, throwflag=<value optimized out>) at Python/ceval.c:3771
#17 PyEval_EvalFrameEx (f=0x1aa77e80, throwflag=<value optimized out>) at Python/ceval.c:2412
#18 0x000000000049a2a7 in PyEval_EvalCodeEx (co=0x19b1a8a0, globals=<value optimized out>, locals=<value optimized out>, args=0x19b21528, argcount=1, kws=0x0, kwcount=0, 
    defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:3000
#19 0x00000000004f213d in function_call (func=0x19b2acf8, arg=0x19b21510, kw=0x0) at Objects/funcobject.c:524
#20 0x00000000004197b8 in PyObject_Call (func=0x19b2acf8, arg=0x19b21510, kw=0x0) at Objects/abstract.c:2492
#21 0x0000000000420b00 in instancemethod_call (func=0x19b2acf8, arg=0x19b21510, kw=0x0) at Objects/classobject.c:2579
#22 0x00000000004197b8 in PyObject_Call (func=0x198193c0, arg=0x2ab4b9ce9050, kw=0x0) at Objects/abstract.c:2492
#23 0x0000000000492df6 in PyEval_CallObjectWithKeywords (func=0x198193c0, arg=0x2ab4b9ce9050, kw=0x0) at Python/ceval.c:3619
#24 0x00000000004c865d in t_bootstrap (boot_raw=0x195de160) at ./Modules/threadmodule.c:425
#25 0x00002ab4ba0ef73d in start_thread () from /lib64/libpthread.so.0
#26 0x00002ab4baa644bd in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x2ab4bace8af0 (LWP 21221)):
#0  0x00002ab4baa2a14f in waitpid () from /lib64/libc.so.6
#1  0x00002ab4ba9cc481 in do_system () from /lib64/libc.so.6
#2  0x00002ab4ba9cc7d7 in system () from /lib64/libc.so.6
#3  0x00002ab4bf258ff6 in TUnixSystem::StackTrace() () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libCore.so
#4  0x00002ab4bf2588cc in TUnixSystem::DispatchSignals(ESignals) () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libCore.so
#5  <signal handler called>
#6  0x00002aaaaab1afa6 in ROOT::Minuit2::MnUserFcn::operator()(ROOT::Minuit2::LAVector const&) const ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#7  0x00002aaaaab180f8 in ROOT::Minuit2::MnSeedGenerator::operator()(ROOT::Minuit2::MnFcn const&, ROOT::Minuit2::GradientCalculator const&, ROOT::Minuit2::MnUserParameterState const&, ROOT::Minuit2::MnStrategy const&) const () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#8  0x00002aaaaab259ba in ROOT::Minuit2::ModularFunctionMinimizer::Minimize(ROOT::Minuit2::FCNBase const&, ROOT::Minuit2::MnUserParameterState const&, ROOT::Minuit2::MnStrategy const&, unsigned int, double) const () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#9  0x00002aaaaaaf8bea in ROOT::Minuit2::MnApplication::operator()(unsigned int, double) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#10 0x00002aaaaab77af0 in G__G__Minuit2_315_0_3(G__value*, char const*, G__param*, int) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#11 0x00002ab4bfa3d7dc in Cint::G__CallFunc::Execute(void*) () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libCint.so
#12 0x00002ab4bed8e5e6 in PyROOT::TRootObjectByValueExecutor::Execute(Cint::G__CallFunc*, void*) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libPyROOT.so
#13 0x00002ab4bed94b06 in PyROOT::TMethodHolder<PyROOT::TScopeAdapter, PyROOT::TMemberAdapter>::CallSafe(void*) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libPyROOT.so
#14 0x00002ab4bed94ccc in PyROOT::TMethodHolder<PyROOT::TScopeAdapter, PyROOT::TMemberAdapter>::Execute(void*) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libPyROOT.so
#15 0x00002ab4bed935da in PyROOT::TMethodHolder<PyROOT::TScopeAdapter, PyROOT::TMemberAdapter>::operator()(PyROOT::ObjectProxy*, _object*, _object*, long) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libPyROOT.so
#16 0x00002ab4bed9a476 in PyROOT::(anonymous namespace)::mp_call(PyROOT::MethodProxy*, _object*, _object*) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libPyROOT.so
#17 0x00000000004197b8 in PyObject_Call (func=0x19b2ec10, arg=0x2ab4b9ce9050, kw=0x0) at Objects/abstract.c:2492
#18 0x000000000046e591 in slot_tp_call (self=0x19b26910, args=0x2ab4b9ce9050, kwds=0x0) at Objects/typeobject.c:5373
#19 0x00000000004197b8 in PyObject_Call (func=0x19b26910, arg=0x2ab4b9ce9050, kw=0x0) at Objects/abstract.c:2492
#20 0x0000000000496742 in do_call (f=0x192fb560, throwflag=<value optimized out>) at Python/ceval.c:3968
#21 call_function (f=0x192fb560, throwflag=<value optimized out>) at Python/ceval.c:3773
#22 PyEval_EvalFrameEx (f=0x192fb560, throwflag=<value optimized out>) at Python/ceval.c:2412
#23 0x000000000049a2a7 in PyEval_EvalCodeEx (co=0x192b1120, globals=<value optimized out>, locals=<value optimized out>, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, 
    defcount=0, closure=0x0) at Python/ceval.c:3000
#24 0x000000000049a3a2 in PyEval_EvalCode (co=0x0, globals=0x7fff59ebab50, locals=0x1a4211a0) at Python/ceval.c:541
#25 0x00000000004ba9aa in run_mod (fp=0x192e9a80, filename=0x7fff59ebc988 "test.py", start=<value optimized out>, globals=0x19238b20, locals=0x19238b20, closeit=1, 
    flags=0x7fff59ebb670) at Python/pythonrun.c:1339
#26 PyRun_FileExFlags (fp=0x192e9a80, filename=0x7fff59ebc988 "test.py", start=<value optimized out>, globals=0x19238b20, locals=0x19238b20, closeit=1, flags=0x7fff59ebb670)
    at Python/pythonrun.c:1325
#27 0x00000000004bac9d in PyRun_SimpleFileExFlags (fp=0x192e9a80, filename=0x7fff59ebc988 "test.py", closeit=1, flags=0x7fff59ebb670) at Python/pythonrun.c:935
#28 0x00000000004150a3 in Py_Main (argc=1, argv=0x7fff59ebb798) at Modules/main.c:572
#29 0x00002ab4ba9ad994 in __libc_start_main () from /lib64/libc.so.6
#30 0x00000000004141d9 in _start ()
===========================================================


The lines below might hint at the cause of the crash.
If they do not help you then please submit a bug report at
http://root.cern.ch/bugs. Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#6  0x00002aaaaab1afa6 in ROOT::Minuit2::MnUserFcn::operator()(ROOT::Minuit2::LAVector const&) const ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#7  0x00002aaaaab180f8 in ROOT::Minuit2::MnSeedGenerator::operator()(ROOT::Minuit2::MnFcn const&, ROOT::Minuit2::GradientCalculator const&, ROOT::Minuit2::MnUserParameterState const&, ROOT::Minuit2::MnStrategy const&) const () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#8  0x00002aaaaab259ba in ROOT::Minuit2::ModularFunctionMinimizer::Minimize(ROOT::Minuit2::FCNBase const&, ROOT::Minuit2::MnUserParameterState const&, ROOT::Minuit2::MnStrategy const&, unsigned int, double) const () from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
#9  0x00002aaaaaaf8bea in ROOT::Minuit2::MnApplication::operator()(unsigned int, double) ()
   from /afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/lib/libMinuit2.so
===========================================================


Traceback (most recent call last):
  File "test.py", line 15, in <module>
    minimization = minimizer()
SystemError: problem in C++; program state has been reset

Hi,

deriving a python class from a C++ one does not make the python methods visible on the C++ side. For this, and intermediate class (in C++) is necessary to forward calls coming in through the C++ vtable to python. This is done for a few fitter classes (see e.g. [url=https://root-forum.cern.ch/t/how-to-pass-fcn-func-defined-on-python-side-to-root-fitter/11168/1 topic[/url]), but the specific case of TMinuit is pythonized differently: SetFCN will take a callable python object.

For an example of the latter, please find Func5MinuitTestCase in python roottest.

Cheers,
Wim