Skip to content

Commit

Permalink
Merge branch 'fix-units-refactoring' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaskoenig committed Mar 3, 2021
2 parents 4fba172 + 350029d commit f300267
Show file tree
Hide file tree
Showing 25 changed files with 467 additions and 312 deletions.
45 changes: 21 additions & 24 deletions src/sbmlsim/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from sbmlsim.combine import mathml
from sbmlsim.result import XResult
from sbmlsim.units import DimensionalityError, Quantity, UnitRegistry
from sbmlsim.units import DimensionalityError, Quantity, UnitRegistry, UnitsInformation
from sbmlsim.utils import deprecated


Expand Down Expand Up @@ -162,22 +162,22 @@ def get_data(self, to_units: str = None):
logger.error(error_msg)
raise KeyError(error_msg)
try:
self.unit = dset.udict[uindex]
self.unit = dset.uinfo[uindex]
except KeyError as err:
logger.error(
f"Units missing for key '{uindex}' in dataset: "
f"'{self.dset_id}'. Add missing units to dataset."
)
raise err
x = dset[self.index].values * dset.ureg(dset.udict[uindex])
x = dset[self.index].values * dset.uinfo.ureg(dset.uinfo[uindex])

elif self.dtype == Data.Types.TASK:
# read results of task
xres = self.experiment.results[self.task_id] # type: XResult
xres: XResult = self.experiment.results[self.task_id]
if not isinstance(xres, XResult):
raise ValueError("Only Result objects supported in task data.")

self.unit = xres.udict[self.index]
self.unit = xres.uinfo[self.index]
# FIXME: complete data must be kept
# print(xres)
x = xres.dim_mean(self.index)
Expand Down Expand Up @@ -240,7 +240,7 @@ class DataSeries(pd.Series):
"""DataSet - a pd.Series with additional unit information."""

# additional properties
_metadata = ["udict", "ureg"]
_metadata = ["uinfo"]

@property
def _constructor(self):
Expand All @@ -255,13 +255,11 @@ class DataSet(pd.DataFrame):
"""DataSet.
pd.DataFrame with additional unit information in the form
of a unit dictionary 'udict' (Dict[str, str]) mapping column
keys to units. The UnitRegistry is the UnitRegistry conversions
are calculated on.
of UnitInformations.
"""

# additional properties
_metadata = ["udict", "ureg"]
_metadata = ["uinfo", "Q_"]

@property
def _constructor(self):
Expand All @@ -276,10 +274,9 @@ def get_quantity(self, key: str):
Requires using the numpy data instead of the series.
"""
return self.ureg.Quantity(
# downcasting !
return self.uinfo.ureg.Quantity(
self[key].values,
self.udict[key],
self.uinfo[key],
)

def __repr__(self) -> str:
Expand All @@ -303,9 +300,9 @@ def from_df(
'mean', 'value', 'sd' and 'se' columns
:param df: pandas.DataFrame
:param udict: optional unit dictionary
:param ureg:
:return:
:param uinfo: optional units information
:return: dataset
"""
if not isinstance(ureg, UnitRegistry):
raise ValueError(
Expand All @@ -318,7 +315,7 @@ def from_df(
udict = {}

# all units from udict and DataFrame
all_udict = {}
all_udict: Dict[str, str] = {}

for key in df.columns:
# handle '*_unit columns'
Expand Down Expand Up @@ -381,11 +378,11 @@ def from_df(
setattr(df, f"{key}_unit", unit)

dset = DataSet(df)
dset.udict = all_udict
dset.ureg = ureg
dset.uinfo = UnitsInformation(all_udict, ureg=ureg)
dset.Q_ = dset.uinfo.ureg.Quantity
return dset

def unit_conversion(self, key, factor: Quantity, filter=None):
def unit_conversion(self, key, factor: Quantity):
"""Convert the units of the given key in the dataset.
The quantity in the dataset is multiplied with the conversion factor.
Expand All @@ -407,14 +404,14 @@ def unit_conversion(self, key, factor: Quantity, filter=None):
:return: None
"""
if key in self.columns:
if key not in self.udict:
if key not in self.uinfo:
raise ValueError(
f"Unit conversion only possible on keys which have units! "
f"No unit defined for key '{key}'"
)

# unit conversion and simplification
new_quantity = self.ureg.Quantity(self[key], self.udict[key]) * factor
new_quantity = self.uinfo.Q_(self[key], self.uinfo[key]) * factor
new_quantity = new_quantity.to_base_units().to_reduced_units()

# updated values
Expand All @@ -425,7 +422,7 @@ def unit_conversion(self, key, factor: Quantity, filter=None):
if err_key in self.columns:
# error keys not stored in udict, only the base quantity
new_err_quantity = (
self.ureg.Quantity(self[err_key], self.udict[key]) * factor
self.uinfo.Q_(self[err_key], self.uinfo[key]) * factor
)
new_err_quantity = (
new_err_quantity.to_base_units().to_reduced_units()
Expand All @@ -437,7 +434,7 @@ def unit_conversion(self, key, factor: Quantity, filter=None):
new_units_str = (
str(new_units).replace("**", "^").replace(" ", "")
) # '{:~}'.format(new_units)
self.udict[key] = new_units_str
self.uinfo[key] = new_units_str

if f"{key}_unit" in self.columns:
self[f"{key}_unit"] = new_units_str
Expand Down
10 changes: 5 additions & 5 deletions src/sbmlsim/examples/example_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
ray,
)
from sbmlsim.test import MODEL_GLCWB, MODEL_REPRESSILATOR
from sbmlsim.units import Units
from sbmlsim.units import Units, UnitsInformation


def example_single_actor():
Expand All @@ -31,7 +31,7 @@ def example_single_actor():
# create state file
r = roadrunner.RoadRunner(str(MODEL_REPRESSILATOR))
RoadrunnerSBMLModel.set_timecourse_selections(r)
udict, ureg = Units.get_units_from_sbml(str(MODEL_REPRESSILATOR))
uinfo = UnitsInformation.from_sbml_path(MODEL_REPRESSILATOR)

f_state = tempfile.NamedTemporaryFile(suffix=".dat")
r.saveState(f_state.name)
Expand All @@ -47,7 +47,7 @@ def example_single_actor():
]
)

tcsim.normalize(udict=udict, ureg=ureg)
tcsim.normalize(uinfo=uinfo)
tc_id = sa._timecourse.remote(tcsim)
results = ray.get(tc_id)
return results
Expand All @@ -61,7 +61,7 @@ def example_multiple_actors():
# create state file
r = roadrunner.RoadRunner(str(MODEL_REPRESSILATOR))
RoadrunnerSBMLModel.set_timecourse_selections(r)
udict, ureg = Units.get_units_from_sbml(str(MODEL_REPRESSILATOR))
uinfo = UnitsInformation.from_sbml_path(MODEL_REPRESSILATOR)

f_state = tempfile.NamedTemporaryFile(suffix=".dat")
r.saveState(f_state.name)
Expand All @@ -76,7 +76,7 @@ def example_multiple_actors():
Timecourse(start=0, end=100, steps=100, changes={"X": 10, "Y": 20}),
]
)
tcsim.normalize(udict=udict, ureg=ureg)
tcsim.normalize(uinfo=uinfo)

# run simulation on simulators
tc_ids = [s._timecourse.remote(tcsim) for s in simulators]
Expand Down
8 changes: 4 additions & 4 deletions src/sbmlsim/examples/example_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
def run_scan0d() -> XResult:
"""Perform a parameter 0D scan, i.e., simple simulation"""
simulator = SimulatorSerial(model=MODEL_REPRESSILATOR)
Q_ = simulator.ureg.Quantity
Q_ = simulator.Q_

scan0d = ScanSim(
simulation=TimecourseSim(
Expand All @@ -37,7 +37,7 @@ def run_scan1d() -> XResult:
Scanning a single parameter.
"""
simulator = SimulatorSerial(model=MODEL_REPRESSILATOR)
Q_ = simulator.ureg.Quantity
Q_ = simulator.Q_

scan1d = ScanSim(
simulation=TimecourseSim(
Expand Down Expand Up @@ -67,7 +67,7 @@ def run_scan1d() -> XResult:
def run_scan2d() -> XResult:
"""Perform a parameter scan"""
simulator = SimulatorSerial(model=MODEL_REPRESSILATOR)
Q_ = simulator.ureg.Quantity
Q_ = simulator.Q_

scan2d = ScanSim(
simulation=TimecourseSim(
Expand Down Expand Up @@ -102,7 +102,7 @@ def run_scan2d() -> XResult:
def run_scan1d_distribution() -> XResult:
"""Perform a parameter scan by sampling from a distribution"""
simulator = SimulatorSerial(model=MODEL_REPRESSILATOR)
Q_ = simulator.ureg.Quantity
Q_ = simulator.Q_

scan1d = ScanSim(
simulation=TimecourseSim(
Expand Down
3 changes: 1 addition & 2 deletions src/sbmlsim/examples/example_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ def run_demo_example():
""" Run various timecourses. """
simulator = Simulator(MODEL_DEMO)
# build quantities using the unit registry for the model
Q_ = simulator.ureg.Quantity
pprint(simulator.udict)
Q_ = simulator.Q_

# 1. simple timecourse simulation
print("*** setting concentrations and amounts ***")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from sbmlsim.result import XResult
from sbmlsim.simulation import Dimension, ScanSim, Timecourse, TimecourseSim
from sbmlsim.task import Task
from sbmlsim.units import UnitsInformation
from sbmlsim.utils import timeit


Expand Down Expand Up @@ -98,7 +99,7 @@ def datasets(self) -> Dict[str, DataSet]:
"mean": df["unit"].unique()[0],
}
dsets[hormone_key.lower()] = DataSet.from_df(
df, udict=udict, ureg=self.ureg
df, ureg=self.ureg, udict=udict
)

return dsets
Expand Down Expand Up @@ -160,8 +161,7 @@ def figures(self) -> Dict[str, Figure]:

dose_response["[glc_ext]"] = glc_vec
df = pd.DataFrame(dose_response)
dset = DataSet.from_df(df, udict=model.udict, ureg=self.ureg)
print(dset)
dset = DataSet.from_df(df, udict=model.uinfo.udict, ureg=self.ureg)

# plot scan results
kwargs = {"linewidth": 2, "linestyle": "-", "marker": "None", "color": "black"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ <h2>Code</h2>
from sbmlsim.result import XResult
from sbmlsim.simulation import Dimension, ScanSim, Timecourse, TimecourseSim
from sbmlsim.task import Task
from sbmlsim.units import UnitsInformation
from sbmlsim.utils import timeit


Expand Down Expand Up @@ -162,7 +163,7 @@ <h2>Code</h2>
"mean": df["unit"].unique()[0],
}
dsets[hormone_key.lower()] = DataSet.from_df(
df, udict=udict, ureg=self.ureg
df, ureg=self.ureg, udict=udict
)

return dsets
Expand Down Expand Up @@ -224,8 +225,7 @@ <h2>Code</h2>

dose_response["[glc_ext]"] = glc_vec
df = pd.DataFrame(dose_response)
dset = DataSet.from_df(df, udict=model.udict, ureg=self.ureg)
print(dset)
dset = DataSet.from_df(df, udict=model.uinfo.udict, ureg=self.ureg)

# plot scan results
kwargs = {"linewidth": 2, "linestyle": "-", "marker": "None", "color": "black"}
Expand Down
8 changes: 4 additions & 4 deletions src/sbmlsim/experiment/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,17 +398,17 @@ def _run_tasks(self, simulator, reduced_selections: bool = True):
simulator.set_timecourse_selections(selections=None)

# normalize model changes (these must be set in simulation!)
model.normalize(udict=simulator.udict, ureg=simulator.ureg)
model.normalize(uinfo=model.uinfo)

for task_key in task_keys: # type: str
task = self._tasks[task_key]

sim = self._simulations[
sim: Union[ScanSim, TimecourseSim] = self._simulations[
task.simulation_id
] # type: Union[ScanSim, TimecourseSim]
]

# normalization before running to ensure correct serialization
sim.normalize(udict=simulator.udict, ureg=simulator.ureg)
sim.normalize(uinfo=simulator.uinfo)

# inject model changes (copy to create independent)
sim = deepcopy(sim)
Expand Down
6 changes: 3 additions & 3 deletions src/sbmlsim/experiment/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sbmlsim.experiment import ExperimentResult, SimulationExperiment
from sbmlsim.model import RoadrunnerSBMLModel
from sbmlsim.simulator import SimulatorSerial
from sbmlsim.units import UnitRegistry, Units
from sbmlsim.units import UnitRegistry, Units, UnitsInformation
from sbmlsim.utils import timeit


Expand All @@ -31,13 +31,13 @@ def __init__(
base_path: Path,
data_path: Path,
simulator: SimulatorSerial = None,
ureg: UnitRegistry = None,
ureg: UnitRegistry = None, # FIXME: is this needed on ExperimentRunner?
**kwargs,
):

# single UnitRegistry per runner
if not ureg:
ureg = Units.default_ureg()
ureg = UnitsInformation._default_ureg()
self.ureg = ureg
self.Q_ = ureg.Quantity

Expand Down
12 changes: 5 additions & 7 deletions src/sbmlsim/fit/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,7 @@ def initialize(

task_id = mapping.observable.task_id
task = sim_experiment._tasks[task_id]
model = sim_experiment._models[
task.model_id
] # type: RoadrunnerSBMLModel
model: RoadrunnerSBMLModel = sim_experiment._models[task.model_id]
simulation = sim_experiment._simulations[task.simulation_id]

if not isinstance(simulation, TimecourseSim):
Expand All @@ -323,8 +321,8 @@ def initialize(
# observable units
obs_xid = mapping.observable.x.index
obs_yid = mapping.observable.y.index
obs_x_unit = model.udict[obs_xid]
obs_y_unit = model.udict[obs_yid]
obs_x_unit = model.uinfo[obs_xid]
obs_y_unit = model.uinfo[obs_yid]

# prepare data
data_ref = mapping.reference.get_data()
Expand Down Expand Up @@ -683,8 +681,8 @@ def residuals(self, xlog: np.ndarray, complete_data=False):
simulator.set_timecourse_selections(selections=self.selections[k])

# FIXME: normalize simulations and parameters once outside of loop
simulation = self.simulations[k] # type: TimecourseSim
simulation.normalize(udict=simulator.udict, ureg=simulator.ureg)
simulation: TimecourseSim = self.simulations[k]
simulation.normalize(uinfo=simulator.uinfo)

# run simulation
try:
Expand Down
6 changes: 3 additions & 3 deletions src/sbmlsim/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Dict, List, Optional, Tuple, Union

from sbmlsim.model.model_resources import Source
from sbmlsim.units import Units
from sbmlsim.units import Units, UnitsInformation


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -83,9 +83,9 @@ def __init__(

# normalize parameters at end of initialization

def normalize(self, udict, ureg):
def normalize(self, uinfo: UnitsInformation):
"""Normalize values to model units for all changes."""
self.changes = Units.normalize_changes(self.changes, udict=udict, ureg=ureg)
self.changes = UnitsInformation.normalize_changes(self.changes, uinfo=uinfo)

def to_dict(self):
"""Convert to dictionary."""
Expand Down

0 comments on commit f300267

Please sign in to comment.