|
3 | 3 |
|
4 | 4 | import os |
5 | 5 | import tempfile |
6 | | -from collections.abc import Iterable |
| 6 | +from collections.abc import Iterable, Sequence |
7 | 7 | from math import nan |
| 8 | +from numbers import Number |
8 | 9 | from pathlib import Path, PurePosixPath |
9 | 10 | from typing import TYPE_CHECKING |
10 | 11 | from warnings import warn |
@@ -1005,3 +1006,177 @@ def n_priors(self) -> int: |
1005 | 1006 | return 0 |
1006 | 1007 |
|
1007 | 1008 | return self.parameter_df[OBJECTIVE_PRIOR_PARAMETERS].notna().sum() |
| 1009 | + |
| 1010 | + def add_condition(self, id_: str, name: str = None, **kwargs): |
| 1011 | + """Add a simulation condition to the problem. |
| 1012 | +
|
| 1013 | + Arguments: |
| 1014 | + id_: The condition id |
| 1015 | + name: The condition name |
| 1016 | + kwargs: Parameter, value pairs to add to the condition table. |
| 1017 | + """ |
| 1018 | + record = {CONDITION_ID: [id_], **kwargs} |
| 1019 | + if name is not None: |
| 1020 | + record[CONDITION_NAME] = name |
| 1021 | + tmp_df = pd.DataFrame(record).set_index([CONDITION_ID]) |
| 1022 | + if self.condition_df is None: |
| 1023 | + self.condition_df = tmp_df |
| 1024 | + else: |
| 1025 | + self.condition_df = pd.concat([self.condition_df, tmp_df]) |
| 1026 | + |
| 1027 | + def add_observable( |
| 1028 | + self, |
| 1029 | + id_: str, |
| 1030 | + formula: str | float | int, |
| 1031 | + noise_formula: str | float | int = None, |
| 1032 | + noise_distribution: str = None, |
| 1033 | + transform: str = None, |
| 1034 | + name: str = None, |
| 1035 | + **kwargs, |
| 1036 | + ): |
| 1037 | + """Add an observable to the problem. |
| 1038 | +
|
| 1039 | + Arguments: |
| 1040 | + id_: The observable id |
| 1041 | + formula: The observable formula |
| 1042 | + noise_formula: The noise formula |
| 1043 | + noise_distribution: The noise distribution |
| 1044 | + transform: The observable transformation |
| 1045 | + name: The observable name |
| 1046 | + kwargs: additional columns/values to add to the observable table |
| 1047 | +
|
| 1048 | + """ |
| 1049 | + record = { |
| 1050 | + OBSERVABLE_ID: [id_], |
| 1051 | + OBSERVABLE_FORMULA: [formula], |
| 1052 | + } |
| 1053 | + if name is not None: |
| 1054 | + record[OBSERVABLE_NAME] = [name] |
| 1055 | + if noise_formula is not None: |
| 1056 | + record[NOISE_FORMULA] = [noise_formula] |
| 1057 | + if noise_distribution is not None: |
| 1058 | + record[NOISE_DISTRIBUTION] = [noise_distribution] |
| 1059 | + if transform is not None: |
| 1060 | + record[OBSERVABLE_TRANSFORMATION] = [transform] |
| 1061 | + record.update(kwargs) |
| 1062 | + |
| 1063 | + tmp_df = pd.DataFrame(record).set_index([OBSERVABLE_ID]) |
| 1064 | + if self.observable_df is None: |
| 1065 | + self.observable_df = tmp_df |
| 1066 | + else: |
| 1067 | + self.observable_df = pd.concat([self.observable_df, tmp_df]) |
| 1068 | + |
| 1069 | + def add_parameter( |
| 1070 | + self, |
| 1071 | + id_: str, |
| 1072 | + estimated: bool | str | int = True, |
| 1073 | + nominal_value=None, |
| 1074 | + scale: str = None, |
| 1075 | + lb: Number = None, |
| 1076 | + ub: Number = None, |
| 1077 | + init_prior_type: str = None, |
| 1078 | + init_prior_pars: str | Sequence = None, |
| 1079 | + obj_prior_type: str = None, |
| 1080 | + obj_prior_pars: str | Sequence = None, |
| 1081 | + **kwargs, |
| 1082 | + ): |
| 1083 | + """Add a parameter to the problem. |
| 1084 | +
|
| 1085 | + Arguments: |
| 1086 | + id_: The parameter id |
| 1087 | + estimated: Whether the parameter is estimated |
| 1088 | + nominal_value: The nominal value of the parameter |
| 1089 | + scale: The parameter scale |
| 1090 | + lb: The lower bound of the parameter |
| 1091 | + ub: The upper bound of the parameter |
| 1092 | + init_prior_type: The type of the initialization prior distribution |
| 1093 | + init_prior_pars: The parameters of the initialization prior |
| 1094 | + distribution |
| 1095 | + obj_prior_type: The type of the objective prior distribution |
| 1096 | + obj_prior_pars: The parameters of the objective prior distribution |
| 1097 | + kwargs: additional columns/values to add to the parameter table |
| 1098 | + """ |
| 1099 | + record = { |
| 1100 | + PARAMETER_ID: [id_], |
| 1101 | + } |
| 1102 | + if estimated is not None: |
| 1103 | + record[ESTIMATE] = [ |
| 1104 | + int(estimated) |
| 1105 | + if isinstance(estimated, bool | int) |
| 1106 | + else estimated |
| 1107 | + ] |
| 1108 | + if nominal_value is not None: |
| 1109 | + record[NOMINAL_VALUE] = [nominal_value] |
| 1110 | + if scale is not None: |
| 1111 | + record[PARAMETER_SCALE] = [scale] |
| 1112 | + if lb is not None: |
| 1113 | + record[LOWER_BOUND] = [lb] |
| 1114 | + if ub is not None: |
| 1115 | + record[UPPER_BOUND] = [ub] |
| 1116 | + if init_prior_type is not None: |
| 1117 | + record[INITIALIZATION_PRIOR_TYPE] = [init_prior_type] |
| 1118 | + if init_prior_pars is not None: |
| 1119 | + if not isinstance(init_prior_pars, str): |
| 1120 | + init_prior_pars = PARAMETER_SEPARATOR.join( |
| 1121 | + map(str, init_prior_pars) |
| 1122 | + ) |
| 1123 | + record[INITIALIZATION_PRIOR_PARAMETERS] = [init_prior_pars] |
| 1124 | + if obj_prior_type is not None: |
| 1125 | + record[OBJECTIVE_PRIOR_TYPE] = [obj_prior_type] |
| 1126 | + if obj_prior_pars is not None: |
| 1127 | + if not isinstance(obj_prior_pars, str): |
| 1128 | + obj_prior_pars = PARAMETER_SEPARATOR.join( |
| 1129 | + map(str, obj_prior_pars) |
| 1130 | + ) |
| 1131 | + record[OBJECTIVE_PRIOR_PARAMETERS] = [obj_prior_pars] |
| 1132 | + record.update(kwargs) |
| 1133 | + |
| 1134 | + tmp_df = pd.DataFrame(record).set_index([PARAMETER_ID]) |
| 1135 | + if self.parameter_df is None: |
| 1136 | + self.parameter_df = tmp_df |
| 1137 | + else: |
| 1138 | + self.parameter_df = pd.concat([self.parameter_df, tmp_df]) |
| 1139 | + |
| 1140 | + def add_measurement( |
| 1141 | + self, |
| 1142 | + obs_id: str, |
| 1143 | + sim_cond_id: str, |
| 1144 | + time: float, |
| 1145 | + measurement: float, |
| 1146 | + observable_parameters: Sequence[str] = None, |
| 1147 | + noise_parameters: Sequence[str] = None, |
| 1148 | + preeq_cond_id: str = None, |
| 1149 | + ): |
| 1150 | + """Add a measurement to the problem. |
| 1151 | +
|
| 1152 | + Arguments: |
| 1153 | + obs_id: The observable ID |
| 1154 | + sim_cond_id: The simulation condition ID |
| 1155 | + time: The measurement time |
| 1156 | + measurement: The measurement value |
| 1157 | + observable_parameters: The observable parameters |
| 1158 | + noise_parameters: The noise parameters |
| 1159 | + preeq_cond_id: The pre-equilibration condition ID |
| 1160 | + """ |
| 1161 | + record = { |
| 1162 | + OBSERVABLE_ID: [obs_id], |
| 1163 | + SIMULATION_CONDITION_ID: [sim_cond_id], |
| 1164 | + TIME: [time], |
| 1165 | + MEASUREMENT: [measurement], |
| 1166 | + } |
| 1167 | + if observable_parameters is not None: |
| 1168 | + record[OBSERVABLE_PARAMETERS] = [ |
| 1169 | + PARAMETER_SEPARATOR.join(observable_parameters) |
| 1170 | + ] |
| 1171 | + if noise_parameters is not None: |
| 1172 | + record[NOISE_PARAMETERS] = [ |
| 1173 | + PARAMETER_SEPARATOR.join(noise_parameters) |
| 1174 | + ] |
| 1175 | + if preeq_cond_id is not None: |
| 1176 | + record[PREEQUILIBRATION_CONDITION_ID] = [preeq_cond_id] |
| 1177 | + |
| 1178 | + tmp_df = pd.DataFrame(record) |
| 1179 | + if self.measurement_df is None: |
| 1180 | + self.measurement_df = tmp_df |
| 1181 | + else: |
| 1182 | + self.measurement_df = pd.concat([self.measurement_df, tmp_df]) |
0 commit comments