def split_raw_record_name(line): """Splits the raw record name of the first line of a record from the rest of the record""" m = re.match(r'(\s*\$[A-za-z]+)(.*)', line, flags=re.MULTILINE | re.DOTALL) if m: return m.group(1, 2) else: raise ModelSyntaxError(f'Bad record name in: {line}')
def validate(self): in_problem = False for record in self.records: if in_problem and record.name == 'SIZES': raise ModelSyntaxError('The SIZES record must come before the first PROBLEM record') elif record.name == 'PROBLEM': in_problem = True if hasattr(record, 'validate'): record.validate()
def get_pred_pk_record(self): pred = self.control_stream.get_records('PRED') if not pred: pk = self.control_stream.get_records('PK') if not pk: raise ModelSyntaxError('Model has no $PK or $PRED') return pk[0] else: return pred[0]
def estimation_steps(self): try: return self._estimation_steps except AttributeError: pass steps = [] records = self.control_stream.get_records('ESTIMATION') covrec = self.control_stream.get_records('COVARIANCE') for record in records: value = record.get_option('METHOD') if value is None or value == '0' or value == 'ZERO': name = 'fo' elif value == '1' or value == 'CONDITIONAL' or value == 'COND': name = 'foce' else: if value in list_supported_est(): name = value else: raise ModelSyntaxError( f'Non-recognized estimation method in: {str(record.root)}' ) if record.has_option('INTERACTION') or record.has_option('INTER'): interaction = True else: interaction = False if covrec: cov = True else: cov = False options_stored = ['METHOD', 'METH', 'INTERACTION', 'INTER'] options_left = [ option for option in record.all_options if option.key not in options_stored ] meth = EstimationMethod(name, interaction=interaction, cov=cov, options=options_left) steps.append(meth) self._estimation_steps = steps self._old_estimation_steps = copy.deepcopy(steps) return steps
def _block_flags(self): """Get a tuple of all interesting flags for block""" fix = bool(self.root.find('FIX')) var = bool(self.root.find('VAR')) sd = bool(self.root.find('SD')) cov = bool(self.root.find('COV')) corr = bool(self.root.find('CORR')) cholesky = bool(self.root.find('CHOLESKY')) for node in self.root.all('omega'): if node.find('FIX'): if fix: raise ModelSyntaxError( 'Cannot specify option FIX more than once') else: fix = True if node.find('VAR'): if var or sd or cholesky: raise ModelSyntaxError( 'Cannot specify either option VARIANCE, SD or ' 'CHOLESKY more than once') else: var = True if node.find('SD'): if sd or var or cholesky: raise ModelSyntaxError( 'Cannot specify either option VARIANCE, SD or ' 'CHOLESKY more than once') else: sd = True if node.find('COV'): if cov or corr: raise ModelSyntaxError( 'Cannot specify either option COVARIANCE or ' 'CORRELATION more than once') else: cov = True if node.find('CORR'): if corr or cov: raise ModelSyntaxError( 'Cannot specify either option COVARIANCE or ' 'CORRELATION more than once') else: corr = True if node.find('CHOLESKY'): if cholesky or var or sd: raise ModelSyntaxError( 'Cannot specify either option VARIANCE, SD or ' 'CHOLESKY more than once') else: cholesky = True return fix, sd, corr, cholesky
def _find_rates(model, ncomps): pkrec = model.control_stream.get_records('PK')[0] for stat in pkrec.statements: if hasattr(stat, 'symbol'): name = stat.symbol.name m = re.match(r'^K(\d+)(T\d+)?$', name) if m: if m.group(2): from_n = int(m.group(1)) to_n = int(m.group(2)[1:]) else: n = m.group(1) if len(n) == 2: from_n = int(n[0]) to_n = int(n[1]) elif len(n) == 3: f1 = int(n[0]) t1 = int(n[1:]) f2 = int(n[0:2]) t2 = int(n[2:]) q1 = f1 <= ncomps and t1 <= ncomps and t1 != 0 q2 = f2 <= ncomps and t2 <= ncomps if q1 and q2: raise ModelSyntaxError( f'Rate parameter {n} is ambiguous. ' f'Use the KiTj notation.' ) if q1: from_n = f1 to_n = t1 elif q2: from_n = f2 to_n = t2 else: # Too large to or from compartment index. What would NONMEM do? # Could also be too large later continue elif len(n) == 4: from_n = int(n[0:2]) to_n = int(n[2:]) if to_n == 0: to_n = ncomps yield from_n, to_n, symbol(name)
def compartmental_model(model, advan, trans, des=None): if advan == 'ADVAN1': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') cm.add_flow(central, output, _advan1and2_trans(trans)) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'OUTPUT': 2} elif advan == 'ADVAN2': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') cm.add_flow(central, output, _advan1and2_trans(trans)) cm.add_flow(depot, central, symbol('KA')) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'OUTPUT': 3} elif advan == 'ADVAN3': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') peripheral = cm.add_compartment('PERIPHERAL') output = cm.add_compartment('OUTPUT') k, k12, k21 = _advan3_trans(trans) cm.add_flow(central, output, k) cm.add_flow(central, peripheral, k12) cm.add_flow(peripheral, central, k21) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) peripheral.lag_time = get_alag(model, 2) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'PERIPHERAL': 2, 'OUTPUT': 3} elif advan == 'ADVAN4': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') peripheral = cm.add_compartment('PERIPHERAL') output = cm.add_compartment('OUTPUT') k, k23, k32, ka = _advan4_trans(trans) cm.add_flow(depot, central, ka) cm.add_flow(central, output, k) cm.add_flow(central, peripheral, k23) cm.add_flow(peripheral, central, k32) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) peripheral.lag_time = get_alag(model, 3) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'PERIPHERAL': 3, 'OUTPUT': 4} elif advan == 'ADVAN5' or advan == 'ADVAN7': cm = CompartmentalSystem() modrec = model.control_stream.get_records('MODEL')[0] defobs = None defdose = None central = None depot = None first_dose = None compartments = [] for i, (name, opts) in enumerate(modrec.compartments()): comp = cm.add_compartment(name) if 'DEFOBSERVATION' in opts: defobs = comp if 'DEFDOSE' in opts: defdose = comp dose_no = i + 1 if name == 'CENTRAL': central = comp elif name == 'DEPOT': depot = comp depot_no = i + 1 if first_dose is None and 'NODOSE' not in opts: first_dose = comp first_dose_no = i + 1 compartments.append(comp) output = cm.add_compartment('OUTPUT') compartments.append(output) comp_map = {comp.name: i + 1 for i, comp in enumerate(compartments)} ncomp = i + 2 if not defobs: if central: defobs = central else: defobs = compartments[0] if not defdose: if depot: defdose = depot dose_no = depot_no elif first_dose is not None: defdose = first_dose dose_no = first_dose_no else: raise ModelSyntaxError('Dosing compartment is unknown') for from_n, to_n, rate in _find_rates(model, ncomp): cm.add_flow(compartments[from_n - 1], compartments[to_n - 1], rate) dose = _dosing(model, dose_no) defdose.dose = dose for i, comp in enumerate(compartments): if i == len(compartments) - 1: break comp.lag_time = get_alag(model, i) ass = _f_link_assignment(model, defobs) elif advan == 'ADVAN10': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') vm = symbol('VM') km = symbol('KM') dose = _dosing(model, 1) central.dose = dose t = symbol('t') cm.add_flow(central, output, vm / (km + sympy.Function(central.amount.name)(t))) central.lag_time = get_alag(model, 1) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'OUTPUT': 2} elif advan == 'ADVAN11': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') per1 = cm.add_compartment('PERIPHERAL1') per2 = cm.add_compartment('PERIPHERAL2') output = cm.add_compartment('OUTPUT') k, k12, k21, k13, k31 = _advan11_trans(trans) cm.add_flow(central, output, k) cm.add_flow(central, per1, k12) cm.add_flow(per1, central, k21) cm.add_flow(central, per2, k13) cm.add_flow(per2, central, k31) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) per1.lag_time = get_alag(model, 2) per2.lag_time = get_alag(model, 3) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'PERIPHERAL1': 2, 'PERIPHERAL2': 3, 'OUTPUT': 4} elif advan == 'ADVAN12': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') per1 = cm.add_compartment('PERIPHERAL1') per2 = cm.add_compartment('PERIPHERAL2') output = cm.add_compartment('OUTPUT') k, k23, k32, k24, k42, ka = _advan12_trans(trans) cm.add_flow(depot, central, ka) cm.add_flow(central, output, k) cm.add_flow(central, per1, k23) cm.add_flow(per1, central, k32) cm.add_flow(central, per2, k24) cm.add_flow(per2, central, k42) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) per1.lag_time = get_alag(model, 3) per2.lag_time = get_alag(model, 4) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'PERIPHERAL1': 3, 'PERIPHERAL2': 4, 'OUTPUT': 5} elif des and all(str(s.symbol).startswith('DADT') for s in des.statements): rec_model = model.control_stream.get_records('MODEL')[0] subs_dict, comp_names = dict(), dict() comps = [c for c, _ in rec_model.compartments()] t = symbol('t') for i, c in enumerate(comps, 1): a = Function(f'A_{c}') subs_dict[f'DADT({i})'] = Derivative(a(t)) subs_dict[f'A({i})'] = a(t) comp_names[f'A({i})'] = a sset = des.statements sset.subs(subs_dict) a_out = Function('A_OUTPUT') dose = _dosing(model, 1) ics = {v(0): sympy.Integer(0) for v in comp_names.values()} ics[a_out(0)] = sympy.Integer(0) ics[comp_names['A(1)'](0)] = dose.amount dadt_dose = sset.find_assignment(str(subs_dict['DADT(1)'])) if len(comps) > 1: dadt_rest = [Eq(s.symbol, s.expression) for s in sset if s != dadt_dose] lhs_sum = dadt_dose.expression for eq in dadt_rest: lhs_sum += eq.rhs dadt_out = Eq(Derivative(a_out(t)), -lhs_sum) dadt_rest.append(sympy.simplify(dadt_out)) else: dadt_rest = [Eq(Derivative(a_out(t)), dadt_dose.expression * -1)] if isinstance(dose, Infusion): dadt_dose.expression += Piecewise( (dose.amount / dose.duration, dose.duration > t), (0, True) ) ics[comp_names['A(1)'](0)] = sympy.Integer(0) eqs = [Eq(dadt_dose.symbol, dadt_dose.expression)] + dadt_rest ode = ExplicitODESystem(eqs, ics) ass = _f_link_assignment(model, symbol('A_CENTRAL')) return ode, ass else: return None model._compartment_map = comp_map return cm, ass
def validate(self): """ Syntax validation of this data record Assumes only on $DATA exists in this $PROBLEM. """ if len(self.root.all('ignchar')) > 1: raise ModelSyntaxError('More than one IGNORE=c')
def compartmental_model(model, advan, trans): if advan == 'ADVAN1': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') cm.add_flow(central, output, _advan1and2_trans(trans)) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'OUTPUT': 2} elif advan == 'ADVAN2': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') cm.add_flow(central, output, _advan1and2_trans(trans)) cm.add_flow(depot, central, symbol('KA')) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'OUTPUT': 3} elif advan == 'ADVAN3': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') peripheral = cm.add_compartment('PERIPHERAL') output = cm.add_compartment('OUTPUT') k, k12, k21 = _advan3_trans(trans) cm.add_flow(central, output, k) cm.add_flow(central, peripheral, k12) cm.add_flow(peripheral, central, k21) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) peripheral.lag_time = get_alag(model, 2) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'PERIPHERAL': 2, 'OUTPUT': 3} elif advan == 'ADVAN4': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') peripheral = cm.add_compartment('PERIPHERAL') output = cm.add_compartment('OUTPUT') k, k23, k32, ka = _advan4_trans(trans) cm.add_flow(depot, central, ka) cm.add_flow(central, output, k) cm.add_flow(central, peripheral, k23) cm.add_flow(peripheral, central, k32) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) peripheral.lag_time = get_alag(model, 3) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'PERIPHERAL': 3, 'OUTPUT': 4} elif advan == 'ADVAN5' or advan == 'ADVAN7': cm = CompartmentalSystem() modrec = model.control_stream.get_records('MODEL')[0] defobs = None defdose = None central = None depot = None first_dose = None compartments = [] for i, (name, opts) in enumerate(modrec.compartments()): comp = cm.add_compartment(name) if 'DEFOBSERVATION' in opts: defobs = comp if 'DEFDOSE' in opts: defdose = comp dose_no = i + 1 if name == 'CENTRAL': central = comp elif name == 'DEPOT': depot = comp depot_no = i + 1 if first_dose is None and 'NODOSE' not in opts: first_dose = comp first_dose_no = i + 1 compartments.append(comp) output = cm.add_compartment('OUTPUT') compartments.append(output) comp_map = {comp.name: i + 1 for i, comp in enumerate(compartments)} ncomp = i + 2 if not defobs: if central: defobs = central else: defobs = compartments[0] if not defdose: if depot: defdose = depot dose_no = depot_no elif first_dose is not None: defdose = first_dose dose_no = first_dose_no else: raise ModelSyntaxError('Dosing compartment is unknown') for from_n, to_n, rate in _find_rates(model, ncomp): cm.add_flow(compartments[from_n - 1], compartments[to_n - 1], rate) dose = _dosing(model, dose_no) defdose.dose = dose for i, comp in enumerate(compartments): if i == len(compartments) - 1: break comp.lag_time = get_alag(model, i) ass = _f_link_assignment(model, defobs) elif advan == 'ADVAN10': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') output = cm.add_compartment('OUTPUT') vm = symbol('VM') km = symbol('KM') dose = _dosing(model, 1) central.dose = dose t = symbol('t') cm.add_flow(central, output, vm / (km + sympy.Function(central.amount.name)(t))) central.lag_time = get_alag(model, 1) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'OUTPUT': 2} elif advan == 'ADVAN11': cm = CompartmentalSystem() central = cm.add_compartment('CENTRAL') per1 = cm.add_compartment('PERIPHERAL1') per2 = cm.add_compartment('PERIPHERAL2') output = cm.add_compartment('OUTPUT') k, k12, k21, k13, k31 = _advan11_trans(trans) cm.add_flow(central, output, k) cm.add_flow(central, per1, k12) cm.add_flow(per1, central, k21) cm.add_flow(central, per2, k13) cm.add_flow(per2, central, k31) dose = _dosing(model, 1) central.dose = dose central.lag_time = get_alag(model, 1) per1.lag_time = get_alag(model, 2) per2.lag_time = get_alag(model, 3) ass = _f_link_assignment(model, central) comp_map = {'CENTRAL': 1, 'PERIPHERAL1': 2, 'PERIPHERAL2': 3, 'OUTPUT': 4} elif advan == 'ADVAN12': cm = CompartmentalSystem() depot = cm.add_compartment('DEPOT') central = cm.add_compartment('CENTRAL') per1 = cm.add_compartment('PERIPHERAL1') per2 = cm.add_compartment('PERIPHERAL2') output = cm.add_compartment('OUTPUT') k, k23, k32, k24, k42, ka = _advan12_trans(trans) cm.add_flow(depot, central, ka) cm.add_flow(central, output, k) cm.add_flow(central, per1, k23) cm.add_flow(per1, central, k32) cm.add_flow(central, per2, k24) cm.add_flow(per2, central, k42) dose = _dosing(model, 1) depot.dose = dose depot.lag_time = get_alag(model, 1) central.lag_time = get_alag(model, 2) per1.lag_time = get_alag(model, 3) per2.lag_time = get_alag(model, 4) ass = _f_link_assignment(model, central) comp_map = {'DEPOT': 1, 'CENTRAL': 2, 'PERIPHERAL1': 3, 'PERIPHERAL2': 4, 'OUTPUT': 5} else: return None model._compartment_map = comp_map return cm, ass
def parameters(self, start_omega, previous_size): """Get a ParameterSet for this omega record """ row = start_omega block = self.root.find('block') bare_block = self.root.find('bare_block') same = bool(self.root.find('same')) parameters = ParameterSet() if not (block or bare_block): for node in self.root.all('diag_item'): init = node.init.NUMERIC fixed = bool(node.find('FIX')) sd = bool(node.find('SD')) var = bool(node.find('VAR')) n = node.n.INT if node.find('n') else 1 if sd and var: raise ModelSyntaxError(f'Initial estimate for {self.name.upper} cannot be both' f' on SD and VAR scale\n{self.root}') if init == 0 and not fixed: raise ModelSyntaxError(f'If initial estimate for {self.name.upper} is 0 it' f' must be set to FIX') if sd: init = init ** 2 for _ in range(n): name = f'{self.name}({row},{row})' param = Parameter(name, init, lower=0, fix=fixed) parameters.add(param) row += 1 size = 1 next_omega = row else: inits = [] if bare_block: size = previous_size else: size = self.root.block.size.INT fix, sd, corr, cholesky = self._block_flags() for node in self.root.all('omega'): init = node.init.NUMERIC n = node.n.INT if node.find('n') else 1 inits += [init] * n if not same: if size != pharmpy.math.triangular_root(len(inits)): raise ModelSyntaxError('Wrong number of inits in BLOCK') if not cholesky: A = pharmpy.math.flattened_to_symmetric(inits) if corr: for i in range(size): for j in range(size): if i != j: if sd: A[i, j] = A[i, i] * A[j, j] * A[i, j] else: A[i, j] = math.sqrt(A[i, i]) * math.sqrt(A[j, j]) * A[i, j] if sd: np.fill_diagonal(A, A.diagonal()**2) else: L = np.zeros((size, size)) inds = np.tril_indices_from(L) L[inds] = inits A = L @ L.T for i in range(size): for j in range(0, i + 1): name = f'{self.name}({i + start_omega},{j + start_omega})' init = A[i, j] lower = None if i != j else 0 param = Parameter(name, init, lower=lower, fix=fix) parameters.add(param) next_omega = start_omega + size return parameters, next_omega, size
def parameters(self, start_omega, previous_size, seen_labels=None): """Get a Parameters for this omega record""" row = start_omega block = self.root.find('block') bare_block = self.root.find('bare_block') same = bool(self.root.find('same')) parameters = Parameters() coords = [] try: self.comment_map except AttributeError: self.comment_map = dict() if seen_labels is None: seen_labels = set() if not (block or bare_block): for node in self.root.all('diag_item'): init = node.init.NUMERIC fixed = bool(node.find('FIX')) sd = bool(node.find('SD')) var = bool(node.find('VAR')) n = node.n.INT if node.find('n') else 1 if sd and var: raise ModelSyntaxError( f'Initial estimate for {self.name.upper()} cannot be both' f' on SD and VAR scale\n{self.root}' ) if init == 0 and not fixed: raise ModelSyntaxError( f'If initial estimate for {self.name.upper()} is 0 it' f' must be set to FIX' ) if sd: init = init ** 2 for _ in range(n): name = self._find_label(node, seen_labels) comment = self._get_name(node) if not name: name = f'{self.name}({row},{row})' if comment: self.comment_map[name] = comment seen_labels.add(name) coords.append((row, row)) param = Parameter(name, init, lower=0, fix=fixed) parameters.append(param) row += 1 size = 1 next_omega = row else: inits = [] if bare_block: size = previous_size else: size = self.root.block.size.INT fix, sd, corr, cholesky = self._block_flags() labels = [] for node in self.root.all('omega'): init = node.init.NUMERIC n = node.n.INT if node.find('n') else 1 inits += [init] * n name = self._find_label(node, seen_labels) if name is not None: seen_labels.add(name) labels.append(name) if n > 1: labels.extend([None] * (n - 1)) if not same: if size != pharmpy.math.triangular_root(len(inits)): raise ModelSyntaxError('Wrong number of inits in BLOCK') if not cholesky: A = pharmpy.math.flattened_to_symmetric(inits) if corr: for i in range(size): for j in range(size): if i != j: if sd: A[i, j] = A[i, i] * A[j, j] * A[i, j] else: A[i, j] = math.sqrt(A[i, i]) * math.sqrt(A[j, j]) * A[i, j] if sd: np.fill_diagonal(A, A.diagonal() ** 2) else: L = np.zeros((size, size)) inds = np.tril_indices_from(L) L[inds] = inits A = L @ L.T label_index = 0 for i in range(size): for j in range(0, i + 1): name = labels[label_index] if name is None: name = f'{self.name}({i + start_omega},{j + start_omega})' coords.append((i + start_omega, j + start_omega)) init = A[i, j] lower = None if i != j else 0 param = Parameter(name, init, lower=lower, fix=fix) parameters.append(param) label_index += 1 next_omega = start_omega + size try: self.name_map except AttributeError: self.name_map = {name: c for i, (name, c) in enumerate(zip(parameters.names, coords))} return parameters, next_omega, size