4646
4747from sbmlsim .sensitivity .parameters import SensitivityParameter
4848from sbmlsim .sensitivity .outputs import SensitivityOutput
49-
49+ import pandas as pd
5050
5151@dataclass
5252class SensitivitySimulation :
@@ -69,7 +69,7 @@ def __init__(self, model_path: Path, selections: list[str], changes_simulation:
6969 self .rr : roadrunner .RoadRunner = roadrunner .RoadRunner (str (model_path ))
7070 self .rr .selections = self .selections
7171 integrator : roadrunner .Integrator = self .rr .integrator
72- integrator .setSetting ("variable_step_size" , True )
72+ # integrator.setSetting("variable_step_size", True)
7373 # state = rr.saveStateS()
7474
7575 # store the simulation changes
@@ -148,6 +148,7 @@ def __init__(self, sensitivity_simulation: SensitivitySimulation,
148148 self .results : Optional [xr .DataArray ] = None
149149 # sensitivity matrix; shape: (num_parameters x num_outputs); could be multiple
150150 self .sensitivity : Optional [xr .DataArray ] = None
151+ self .sensitivity_normalized : Optional [xr .DataArray ] = None
151152
152153
153154 @property
@@ -192,25 +193,22 @@ def simulate_samples(self) -> None:
192193
193194 def calculate_sensitivity (self ):
194195 """Calculate the sensitivity matrix."""
196+ pass
195197
196- self .sensitivity = xr .DataArray (
197- np .full ((self .num_parameters , self .num_outputs ), np .nan ),
198- dims = ["parameter" , "output" ],
199- coords = {"parameter" : [p .uid for p in self .parameters ],
200- "output" : self .outputs },
201- name = "sensitivity"
202- )
203198
204199
205200@dataclass
206201class LocalSensitivityAnalysis (SensitivityAnalysis ):
207- """Local sensitivity analysis based on local differences."""
202+ """Local sensitivity analysis based on local differences.
203+
204+ param difference: change for calculation of local sensitivity (0.01 = 1% change)
205+ """
208206
209207 difference : float
210208 sensitivity : np .ndarray = None
211209
212210 def __init__ (self , sensitivity_simulation : SensitivitySimulation ,
213- parameters : list [SensitivityParameter ], difference : float = 0.1 ):
211+ parameters : list [SensitivityParameter ], difference : float = 0.01 ):
214212
215213 super ().__init__ (sensitivity_simulation , parameters )
216214 self .sensitivity = np .zeros (shape = (self .num_parameters , self .num_outputs ))
@@ -260,34 +258,62 @@ def calculate_sensitivity(self):
260258 """Calculate the two-sided local sensitivity matrix."""
261259
262260 # num_parameters x num_outputs
263- super ().calculate_sensitivity ()
261+ # empty sensitivity
262+ self .sensitivity = xr .DataArray (
263+ np .full ((self .num_parameters , self .num_outputs ), np .nan ),
264+ dims = ["parameter" , "output" ],
265+ coords = {"parameter" : [p .uid for p in self .parameters ],
266+ "output" : self .outputs },
267+ name = "sensitivity"
268+ )
269+ self .sensitivity_normalized = xr .DataArray (
270+ np .full ((self .num_parameters , self .num_outputs ), np .nan ),
271+ dims = ["parameter" , "output" ],
272+ coords = {"parameter" : [p .uid for p in self .parameters ],
273+ "output" : self .outputs },
274+ name = "sensitivity"
275+ )
264276
265277 for kp , p in enumerate (self .parameters ):
266278 pid = self .parameters [kp ].uid
279+ p_ref = self .samples [- 1 , kp ]
280+ p_up = self .samples [2 * kp , kp ]
281+ p_down = self .samples [2 * kp + 1 , kp ]
282+
267283 for ko , oid in enumerate (self .outputs ):
268284 # num_samples x num_outputs
269- value_ref = self .results [- 1 , ko ]
270- value_up = self .results [2 * kp , ko ]
271- value_down = self .results [2 * kp + 1 , ko ]
285+ q_ref = self .results [- 1 , ko ]
286+ q_up = self .results [2 * kp , ko ]
287+ q_down = self .results [2 * kp + 1 , ko ]
272288
273- # midpoint method, two-sided sensitivity
274- self .sensitivity [kp , ko ] = (value_up - value_down ) / (2.0 * value_ref )
289+ # two-sided sensitivity
290+ self .sensitivity [kp , ko ] = (q_up - q_down ) / (p_up - p_down )
291+ # normalized: relative change in output per relative change in parameter
292+ self .sensitivity_normalized [kp , ko ] = self .sensitivity [kp , ko ] * p_ref / q_ref
275293
276-
277- def plot_sensitivity (self ):
278- from sbmlsim .sensitivity .plots import heatmap
279- import pandas as pd
280-
281- df = pd .DataFrame (
282- self .sensitivity .values ,
294+ @property
295+ def sensitivity_df (self ) -> pd .DataFrame :
296+ """Convert sensitivity information to dataframe."""
297+ return pd .DataFrame (
298+ self .sensitivity_normalized .values ,
283299 columns = self .sensitivity .coords ["output" ],
284300 index = self .sensitivity .coords ["parameter" ]
285301 )
286- console .print (df )
287- heatmap (df , cutoff = 0.01 )
288302
303+ def plot_sensitivity (self ):
304+ df = self .sensitivity_df
305+ self .plot_sensitivity_df (df )
306+
307+ @staticmethod
308+ def plot_sensitivity_df (df : pd .DataFrame , cutoff = 0.1 , cluster_rows : bool = True ):
309+ from sbmlsim .sensitivity .plots import heatmap
310+ console .print (df )
289311
312+ # TODO: labels of parameters
313+ # TODO: labels of outputs
314+ # TODO: better position of colorbar
290315
316+ heatmap (df , cutoff = cutoff , cluster_rows = False )
291317
292318
293319
0 commit comments