python-libsbml icon indicating copy to clipboard operation
python-libsbml copied to clipboard

getMath() is swapping objects somehow

Open Karrenbelt opened this issue 5 years ago • 3 comments

This is not the kind of behaviour that is expected from a getter; is it returning the original and keeping a copy?

Somehow the memory handling is not working correctly

import libsbml 
assert libsbml.__version__ == '5.18.0'
import string

def unique(sequence):
    seen = set()
    return [x for x in sequence if not (x in seen or seen.add(x))]

def simplify(li):
    d = dict(zip(unique(li), string.ascii_uppercase))
    return [d[el] for el in li]

level = 3
version = 2
f_def = libsbml.FunctionDefinition(level, version)
i_ass = libsbml.InitialAssignment(level, version)
k_law = libsbml.KineticLaw(level, version)

# same for libsbml.parseL3Formula('x'), thus not because it is empty or type is unknown 
a = libsbml.ASTNode() 
assert a is not None and a.isWellFormedASTNode()

ast_node_ids = []
for obj in [f_def, i_ass, k_law]:
    ids = []
    ids.append(id(a))             # 0. 'A'
    assert obj.setMath(a) == 0
    ids.append(id(a))             # 1. 'A'
    ids.append(id(obj.getMath())) # 2. 'B'
    ids.append(id(obj.getMath())) # 3. 'B'
    ids.append(id(obj.getMath())) # 4. 'B'

    # assigning to variables
    b = obj.getMath()
    ids.append(id(b))             # 5. 'B'
    ids.append(id(obj.getMath())) # 6. 'C'
    c = obj.getMath()
    ids.append(id(b))             # 7. 'B'
    ids.append(id(obj.getMath())) # 8. 'D'

    ## assigning to same variable as before: c
    c = obj.getMath()
    ids.append(id(obj.getMath())) # 9. 'C'

    ast_node_ids.append(ids)

# generate simpler representation
simpler = list(map(simplify, ast_node_ids))

for li in simpler:
    print(li)

output:

['A', 'A', 'B', 'B', 'B', 'B', 'C', 'B', 'D', 'C']
['A', 'A', 'B', 'B', 'B', 'B', 'C', 'B', 'D', 'C']
['A', 'A', 'B', 'B', 'B', 'B', 'C', 'B', 'D', 'C']

expected:

['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']

Karrenbelt avatar Jan 18 '20 11:01 Karrenbelt

Uhm ... just to make it easier for us to look at ... can you let us know what you would expect.

fbergmann avatar Jan 18 '20 11:01 fbergmann

I would hope to have the same reference returned. This is the case when I call it multiple times, but somehow when I assign it to a variable, the next time I call the getter it returns a different reference. When I assign it twice to the same variable however, the reference is the same again. Updated the post.

Karrenbelt avatar Jan 18 '20 12:01 Karrenbelt

thanks .. that explains it ... unfortunately, this is not how it is implemented in libSBML. the call to setMath takes a constant ASTNode element, and assigns a clone of it to the element. (Otherwise multiple object would own the same node, which might result in memory corruption at a later point).

So that would explain why you get a different result after the setMath call. The original node is untouched, and you would get one additional ASTNode for each setMath call. Or so it would be in C++ in which libsbml is written in.

We are using SWIG to generate wrapper classes, so that the library can be used easily from for example python. And it does seem that the generated code creates a new wrapper object. So each call to getMath will give you a different wrapper object (at least it does so here).

whenever you hold a reference to a wrapper object (as you do in your code with the variables a, b, c you already have a python object, and so no new one needs to be constructed for it.

what surprises me, is that you get the same id for 2-4. As that is not the case if i just run things with a one liner like:

In [62]: print(repr(obj.getMath()));
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x05695A28> >

In [63]: print(repr(obj.getMath()));
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x05695B18> >

In [64]: print(repr(obj.getMath()));
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x056959E0> >

In [65]: print(repr(obj.getMath()));
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x05695C08> >

but when i run them all together i get:

In [59]: print(repr(obj.getMath())); print(repr(obj.getMath()));print(repr(obj.getMath()));print(repr(obj.getMath()));
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x056956E0> >
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x056956E0> >
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x056956E0> >
<libsbml.ASTNode; proxy of <Swig Object of type 'ASTNode_t *' at 0x056956E0> >

so all that goes along is to say, you can't count on the python id of things. I'm not quite sure there is anything i can do about this behavior.

fbergmann avatar Jan 20 '20 07:01 fbergmann