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

Implement handling static concepts in equations #441

Merged
merged 1 commit into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions mira/sources/sympy_ode/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,23 @@ def template_model_from_sympy_odes(odes, concept_data=None, param_data=None):
raise ValueError("Multiple time variables in the ODEs")
time_variable = time_variables.pop()

# Step 2: determine LHS variables
# Step 2: determine LHS variables and handle static concepts
is_static = set()
for ode in odes:
lhs_fun = ode.lhs.args[0]
variable_name = lhs_fun.name
variables.append(variable_name)
if ode.rhs == 0:
is_static.add(variable_name)

# Step 3: Interpret RHS equations and build a hypergraph
parameters = set()
all_terms = []
G = Hypergraph()
for lhs_variable, eq in zip(variables, odes):
# No terms to add for static variables
if lhs_variable in is_static:
continue
# Break up the RHS into a sum of terms
terms = eq.rhs.as_ordered_terms()
for term_idx, term in enumerate(terms):
Expand Down Expand Up @@ -205,9 +211,15 @@ def template_model_from_sympy_odes(odes, concept_data=None, param_data=None):
# Remove ambiguous edges
G.remove_ambiguous_edges()

# We first look at unconnected nodes of the graph and construct
# production or degradation templates
templates = []

# We first handle static concepts
for variable in is_static:
concept = make_concept(variable, concept_data)
templates.append(StaticConcept(subject=concept))

# We next look at unconnected nodes of the graph and construct
# production or degradation templates
for node in G.get_unconnected_nodes():
data = G.nodes[node]
term = data['term']
Expand Down
22 changes: 22 additions & 0 deletions tests/test_sympy_odes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sympy
from sympy import Function, symbols, Eq, Symbol

from mira.metamodel import StaticConcept
from mira.sources.sympy_ode import template_model_from_sympy_odes


Expand Down Expand Up @@ -279,3 +280,24 @@ def test_large_model():
tm = template_model_from_sympy_odes(equations)

assert len(tm.templates) == 67


def test_extract_static():
# Define time variable
t = sympy.symbols("t")

# Define variables with time derivative to be time-dependent functions
DP, TE, EV, PS, ASI = sympy.symbols("DP TE EV PS ASI", cls=sympy.Function)

# Define the equations without time-derivative on the left hand side
equation_output = [
sympy.Eq(DP(t).diff(t), 0),
sympy.Eq(TE(t).diff(t), 0),
sympy.Eq(EV(t).diff(t), 0),
sympy.Eq(PS(t).diff(t), 0),
sympy.Eq(ASI(t).diff(t), 0),
]

tm = template_model_from_sympy_odes(equation_output)

assert all(isinstance(t, StaticConcept) for t in tm.templates)