Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getMath() is swapping objects somehow #9

Open
Karrenbelt opened this issue Jan 18, 2020 · 3 comments
Open

getMath() is swapping objects somehow #9

Karrenbelt opened this issue Jan 18, 2020 · 3 comments

Comments

@Karrenbelt
Copy link

Karrenbelt commented Jan 18, 2020

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']
@fbergmann
Copy link
Member

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

@Karrenbelt
Copy link
Author

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.

@fbergmann
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants