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

Model from JSON loading issue #281

Closed
brandomr opened this issue Feb 15, 2024 · 4 comments · Fixed by #282
Closed

Model from JSON loading issue #281

brandomr opened this issue Feb 15, 2024 · 4 comments · Fixed by #282

Comments

@brandomr
Copy link

brandomr commented Feb 15, 2024

This model:

{'id': '9559871a-e685-4b1d-a399-5da01f992e31', 'createdOn': '2024-02-14T21:05:54.779+00:00', 'updatedOn': '2024-02-15T18:18:05.653+00:00', 'temporary': False, 'publicAsset': False, 'header': {'name': 'SIDARTHE from equations', 'description': 'This is a model from equations', 'schema': 'https://github.com/DARPA-ASKEM/Model-Representations/blob/main/petrinet/petrinet_schema.json', 'schema_name': 'PetriNet', 'model_version': '0.1'}, 'model': {'states': [{'id': 'A', 'name': 'A'}, {'id': 'D', 'name': 'D'}, {'id': 'E', 'name': 'E'}, {'id': 'H', 'name': 'H'}, {'id': 'I', 'name': 'I'}, {'id': 'R', 'name': 'R'}, {'id': 'S', 'name': 'S'}, {'id': 'T', 'name': 'T'}], 'transitions': [{'id': 't0', 'input': ['I', 'S'], 'output': ['I', 'I']}, {'id': 't1', 'input': ['D', 'S'], 'output': ['I', 'D']}, {'id': 't10', 'input': ['T'], 'output': ['H']}, {'id': 't11', 'input': ['D'], 'output': ['H']}, {'id': 't12', 'input': ['I'], 'output': ['H']}, {'id': 't13', 'input': ['T'], 'output': ['E']}, {'id': 't14', 'input': ['D'], 'output': ['R']}, {'id': 't15', 'input': ['A'], 'output': ['R']}, {'id': 't2', 'input': ['A', 'S'], 'output': ['I', 'A']}, {'id': 't3', 'input': ['R', 'S'], 'output': ['I', 'R']}, {'id': 't4', 'input': ['I'], 'output': ['D']}, {'id': 't5', 'input': ['I'], 'output': ['A']}, {'id': 't6', 'input': ['A'], 'output': ['T']}, {'id': 't7', 'input': ['R'], 'output': ['T']}, {'id': 't8', 'input': ['A'], 'output': ['H']}, {'id': 't9', 'input': ['R'], 'output': ['H']}]}, 'semantics': {'ode': {'rates': [{'target': 't0', 'expression': 'α*I*S', 'expression_mathml': '<apply><times/><ci>I</ci><ci>α</ci><ci>S</ci></apply>'}, {'target': 't1', 'expression': 'β*D*S', 'expression_mathml': '<apply><times/><ci>D</ci><ci>β</ci><ci>S</ci></apply>'}, {'target': 't2', 'expression': 'γ*A*S', 'expression_mathml': '<apply><times/><ci>A</ci><ci>γ</ci><ci>S</ci></apply>'}, {'target': 't3', 'expression': 'δ*R*S', 'expression_mathml': '<apply><times/><ci>R</ci><ci>δ</ci><ci>S</ci></apply>'}, {'target': 't4', 'expression': 'ϵ*I', 'expression_mathml': '<apply><times/><ci>ϵ</ci><ci>I</ci></apply>'}, {'target': 't5', 'expression': 'ζ*I', 'expression_mathml': '<apply><times/><ci>ζ</ci><ci>I</ci></apply>'}, {'target': 't6', 'expression': 'μ*A', 'expression_mathml': '<apply><times/><ci>μ</ci><ci>A</ci></apply>'}, {'target': 't7', 'expression': 'ν*R', 'expression_mathml': '<apply><times/><ci>ν</ci><ci>R</ci></apply>'}, {'target': 't8', 'expression': 'κ*A', 'expression_mathml': '<apply><times/><ci>κ</ci><ci>A</ci></apply>'}, {'target': 't9', 'expression': 'ξ*R', 'expression_mathml': '<apply><times/><ci>ξ</ci><ci>R</ci></apply>'}, {'target': 't10', 'expression': 'σ*T', 'expression_mathml': '<apply><times/><ci>σ</ci><ci>T</ci></apply>'}, {'target': 't11', 'expression': 'ρ*D', 'expression_mathml': '<apply><times/><ci>ρ</ci><ci>D</ci></apply>'}, {'target': 't12', 'expression': 'λ*I', 'expression_mathml': '<apply><times/><ci>λ</ci><ci>I</ci></apply>'}, {'target': 't13', 'expression': 'τ*T', 'expression_mathml': '<apply><times/><ci>τ</ci><ci>T</ci></apply>'}, {'target': 't14', 'expression': 'η*D', 'expression_mathml': '<apply><times/><ci>η</ci><ci>D</ci></apply>'}, {'target': 't15', 'expression': 'θ*A', 'expression_mathml': '<apply><times/><ci>θ</ci><ci>A</ci></apply>'}], 'initials': [{'target': 'S', 'expression': '1-I-D-A-R-T-H-E', 'expression_mathml': '<apply><plus/><apply><minus/><apply><minus/><apply><minus/><apply><minus/><apply><minus/><apply><minus/><apply><minus/><ci>A</ci></apply><ci>D</ci></apply><ci>E</ci></apply><ci>H</ci></apply><ci>I</ci></apply><ci>R</ci></apply><ci>T</ci></apply><cn>1</cn></apply>'}, {'target': 'I', 'expression': '200/60000000', 'expression_mathml': '<apply><divide/><cn>1</cn><cn>300000</cn></apply>'}, {'target': 'D', 'expression': '20/60000000', 'expression_mathml': '<apply><divide/><cn>1</cn><cn>3000000</cn></apply>'}, {'target': 'A', 'expression': '1/60000000', 'expression_mathml': '<apply><divide/><cn>1</cn><cn>60000000</cn></apply>'}, {'target': 'T', 'expression': '0', 'expression_mathml': '<cn>0</cn>'}, {'target': 'H', 'expression': '0', 'expression_mathml': '<cn>0</cn>'}, {'target': 'E', 'expression': '0', 'expression_mathml': '<cn>0</cn>'}, {'target': 'R', 'expression': '2/60000000', 'expression_mathml': '<apply><divide/><cn>1</cn><cn>30000000</cn></apply>'}], 'parameters': [{'id': 'α', 'name': 'α', 'value': 0.57}, {'id': 'β', 'name': 'β', 'value': 0.011}, {'id': 'γ', 'name': 'γ', 'value': 0.456}, {'id': 'δ', 'name': 'δ', 'value': 0.011}, {'id': 'ζ', 'name': 'ζ', 'value': 0.125}, {'id': 'η', 'name': 'η', 'value': 0.125}, {'id': 'θ', 'name': 'θ', 'value': 0.371}, {'id': 'κ', 'name': 'κ', 'value': 0.017}, {'id': 'λ', 'name': 'λ', 'value': 0.034}, {'id': 'μ', 'name': 'μ', 'value': 0.017}, {'id': 'ν', 'name': 'ν', 'value': 0.027}, {'id': 'ξ', 'name': 'ξ', 'value': 0.017}, {'id': 'ρ', 'name': 'ρ', 'value': 0.034}, {'id': 'σ', 'name': 'σ', 'value': 0.017}, {'id': 'τ', 'name': 'τ', 'value': 0.01}, {'id': 'ϵ', 'name': 'ϵ', 'value': 0.171}]}}, 'metadata': {}}

throws this error when loading from JSON with mira.sources.amr.model_from_json

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/mira/sources/amr/__init__.py:67, in model_from_json(model_json)
     63     raise ValueError(f"No schema defined in the AMR header. The schema "
     64                      f"has to be a URL pointing to a JSON schema "
     65                      f"against which the AMR is validated.")
     66 if 'petrinet' in header['schema']:
---> 67     return petrinet.template_model_from_amr_json(model_json)
     68 elif 'regnet' in header['schema']:
     69     return regnet.template_model_from_amr_json(model_json)

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/mira/sources/amr/petrinet.py:204, in template_model_from_amr_json(model_json)
    201     controller_concepts = [concepts[i].copy(deep=True) for i in controllers]
    202     transition_id = transition['id']
--> 204     rate_law = get_sympy(rate_obj, local_dict=symbols)
    205     templates.extend(transition_to_templates(input_concepts,
    206                                              output_concepts,
    207                                              controller_concepts,
    208                                              rate_law,
    209                                              transition_id))
    210 # Handle static states

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/mira/sources/util.py:178, in get_sympy(expr_data, local_dict)
    176 # Sympy
    177 if expr_data.get("expression"):
--> 178     expr = safe_parse_expr(expr_data["expression"], local_dict=local_dict)
    179 # MathML
    180 elif expr_data.get("expression_mathml"):

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/mira/metamodel/utils.py:19, in safe_parse_expr(s, local_dict)
     17 def safe_parse_expr(s: str, local_dict=None) -> sympy.Expr:
     18     """Parse an expression that may contain lambda functions."""
---> 19     return sympy.parse_expr(get_parseable_expression(s),
     20                             local_dict={get_parseable_expression(k): v
     21                                         for k, v in local_dict.items()}
     22                                         if local_dict else None)

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/sympy/parsing/sympy_parser.py:1087, in parse_expr(s, local_dict, transformations, global_dict, evaluate)
   1085 for i in local_dict.pop(null, ()):
   1086     local_dict[i] = null
-> 1087 raise e from ValueError(f"Error from parse_expr with transformed code: {code!r}")

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/sympy/parsing/sympy_parser.py:1078, in parse_expr(s, local_dict, transformations, global_dict, evaluate)
   1075     code = compile(evaluateFalse(code), '<string>', 'eval') # type: ignore
   1077 try:
-> 1078     rv = eval_expr(code, local_dict, global_dict)
   1079     # restore neutral definitions for names
   1080     for i in local_dict.pop(null, ()):

File ~/opt/anaconda3/envs/py311/lib/python3.11/site-packages/sympy/parsing/sympy_parser.py:906, in eval_expr(code, local_dict, global_dict)
    900 def eval_expr(code, local_dict: DICT, global_dict: DICT):
    901     """
    902     Evaluate Python code generated by ``stringify_expr``.
    903
    904     Generally, ``parse_expr`` should be used.
    905     """
--> 906     expr = eval(
    907         code, global_dict, local_dict)  # take local objects in preference
    908     return expr

File <string>:1

NameError: name 'ε' is not defined

I am not sure if this is expected (e.g. the symbols will break things) or if perhaps the model is broken in some way I can't tell. It came from @mwdchang so I'm not sure what to think ;)

Anything stand out as obviously wrong with this that would make it incompatible with Mira?

@bgyori
Copy link
Member

bgyori commented Feb 15, 2024

I'm looking into it - it's pretty mysterious, and I am almost wondering if it's a bug in sympy. Will report back once I know more.

@bgyori
Copy link
Member

bgyori commented Feb 16, 2024

Submitted an issue to sympy: sympy/sympy#26234

@bgyori
Copy link
Member

bgyori commented Feb 16, 2024

The issue actually goes pretty deep. In the model, ϵ is used as a parameter. When parsing strings into expressions, sympy uses Python's eval under the hood. It turns out that while Python allows unicode variable names, there is some normalization going on where certain unicode variants are mapped. And ϵ seems to be one of these cases. This is demonstrated by the following minimal code:

>>> import ast
>>> ast.parse('ϵ').body[0].value.id
'ε'

As you can see, ϵ is mapped to ε, a different unicode character. How was this model made? If it's something manually constructed, just use ε as the parameter!

@brandomr
Copy link
Author

Got it, I think it was made graphically through Terarium so that might make sense. Thanks for diving into this.

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

Successfully merging a pull request may close this issue.

2 participants