Python deepcopy and RooFit

I use a RooChebychev pdf. I transfer it to another function and can also make a copy of that.
I wrote a demo based on that suggestion by @jonas to take care of ownership (so that variables of RooAbsPdf do not get destroyed):

import copy

import ROOT


def demo_init():
    x = ROOT.RooRealVar("x", "x", -1, 1)
    x.setBins(10)

    a1 = ROOT.RooRealVar("a1", "a1", 0.5, 0., 1.)
    a2 = ROOT.RooRealVar("a2", "a2", 0.0, 0., 1.)

    cheb = ROOT.RooChebychev("cheb", "Background", x, ROOT.RooArgSet(a1, a2))
    ROOT.SetOwnership(x, False)
    ROOT.SetOwnership(a1, False)
    ROOT.SetOwnership(a2, False)
    cheb.addOwnedComponents([x, a1, a2])

    ## Choose a copy method here ##
    # cheb2 = cheb
    cheb2 = copy.deepcopy(cheb)
    return (cheb, cheb2) 


def demo():
    cheb, cheb2 = demo_init()
    cheb.Print()
    del cheb
    cheb2.Print()


demo()

The results depend on whether I use a simple copy or a widely used copy.deepcopy:

A simple copy (everything works fine):

RooChebychev::cheb[ x=x coefficients=(a1,a2) ] = 1
RooChebychev::cheb[ x=x coefficients=(a1,a2) ] = 1

A deep copy (the second function fails with after the first one is destroyed) (the same holds for copy.copy):

RooChebychev::cheb[ x=x coefficients=(a1,a2) ] = 1
RooChebychev::cheb = 1

I believe that in the worst case copy.deepcopy for a PyROOT object should resort to a simple copy. I tried to store parameters of the pdf in an unrelated RooArgList, but surprisingly it had no effect on the cloned function (its parameters got destroyed with the destruction of cheb).

I have to use ROOT 6.30/06 with Python 2 because of some unknown bugs. Feel free to check that with a recent version.

An interesting note: it looks like pickling does not work properly with copied functions. I think it uses some kind of deepcopy internally.

Let’s see what @jonas can tell about this

Hi! Please don’t use copy.deepcopy for RooFit objects. This is unsupported. If you want to do a deep or shallow copy in RooFit, these are your options:

    # A deep copy, cloning the object iself and all the input recursively:
    cheb2 = cheb.cloneTree()

    # A shallow copy of only "cheb", taking the same inputs as the original:
    # The original name would not have to be passed as of this PR:
    # https://github.com/root-project/root/pull/18283
    cheb2 = cheb.clone(cheb.GetName())

Note that cheb2 = cheb doesn’t do any copy at all. It simply creates a new Python reference with a different name that points to the same object.

Hi @jonas, thanks for the reply!

Is there any reference to that? Are you going to add it somewhere into the documentation?

How are you going to deal with deep copies? I believe that many Python programmers use it automatically (also for heaps of heterogeneous data). It would be error-prone to leave that only in the documentation. Do you consider implementing some logic for deep copy, raising an error or at least a warning?
I can’t see it explicitly from your commit (and also how RooAbsArg::clone() is connected with deep copying).

Yes, at the moment I don’t do any deep copies of RooFit objects.

Thanks for your good work again.

Cheers,
Yaroslav