From 9f1ce838c0438d0b21c0d52b022316d28892d763 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 6 Mar 2023 18:27:53 +0100 Subject: [PATCH 01/43] Moving to PYMC for Gaussian only --- build/lib/pcntoolkit/configs.py | 9 ++ pcntoolkit/model/SHASH.py | 27 +++--- pcntoolkit/model/hbr.py | 110 ++++++++++--------------- pcntoolkit/normative_model/norm_hbr.py | 71 ++++++++-------- pcntoolkit/util/hbr_utils.py | 2 +- pcntoolkit/util/utils.py | 16 ++-- setup.py | 8 +- 7 files changed, 116 insertions(+), 127 deletions(-) create mode 100644 build/lib/pcntoolkit/configs.py diff --git a/build/lib/pcntoolkit/configs.py b/build/lib/pcntoolkit/configs.py new file mode 100644 index 00000000..98b56f17 --- /dev/null +++ b/build/lib/pcntoolkit/configs.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Mon Dec 7 12:51:07 2020 + +@author: seykia +""" + +PICKLE_PROTOCOL = 4 diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index 5ab91e51..9b3aaf59 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -1,14 +1,13 @@ -import theano.tensor -from pymc3.distributions import Continuous, draw_values, generate_samples -import theano.tensor as tt +import aesara.tensor +from pymc.distributions import Continuous, draw_values, generate_samples +import aesara.tensor as tt import numpy as np -from pymc3.distributions.dist_math import bound +from pymc.distributions.dist_math import bound import scipy.special as spp -from theano import as_op -from theano.gof.fg import NullType -from theano.gof.op import Op -from theano.gof.graph import Apply -from theano.gradient import grad_not_implemented + +from aesara.graph.op import Op +from aesara.graph import Apply +from aesara.gradient import grad_not_implemented """ @author: Stijn de Boer (AuguB) @@ -19,11 +18,11 @@ class K(Op): """ - Modified Bessel function of the second kind, theano implementation + Modified Bessel function of the second kind, aesara implementation """ def make_node(self, p, x): - p = theano.tensor.as_tensor_variable(p, 'floatX') - x = theano.tensor.as_tensor_variable(x, 'floatX') + p = aesara.tensor.as_tensor_variable(p, 'floatX') + x = aesara.tensor.as_tensor_variable(x, 'floatX') return Apply(self, [p,x], [p.type()]) def perform(self, node, inputs, output_storage, params=None): @@ -124,8 +123,8 @@ def m(self, r): combs = spp.comb(r, i) flip = np.power(-1, i) ex = np.exp((r - 2 * i) * self.epsilon / self.delta) - # This is the reason we can not sample delta/kurtosis using NUTS; the gradient of P is unknown to pymc3 - # TODO write a class that inherits theano.Op and do the gradient in there :) + # This is the reason we can not sample delta/kurtosis using NUTS; the gradient of P is unknown to pymc + # TODO write a class that inherits aesara.Op and do the gradient in there :) p = self.P((r - 2 * i) / self.delta) acc += combs * flip * ex * p return frac1 * acc diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 98c71037..4b191e0f 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -13,24 +13,21 @@ from tkinter.font import names import numpy as np -import pymc3 as pm -import theano +import pymc as pm +import aesara + from itertools import product from functools import reduce -import theano -from pymc3 import Metropolis, NUTS, Slice, HamiltonianMC +from pymc import Metropolis, NUTS, Slice, HamiltonianMC from scipy import stats import bspline from bspline import splinelab -from model.SHASH import SHASHo2, SHASHb, SHASHo from util.utils import create_poly_basis from util.utils import expand_all from pcntoolkit.util.utils import cartesian_product -from theano import printing, function - def bspline_fit(X, order, nknots): feature_num = X.shape[1] bsp_basis = [] @@ -161,11 +158,10 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ - X = theano.shared(X) - X = theano.tensor.cast(X,'floatX') - y = theano.shared(y) - y = theano.tensor.cast(y,'floatX') - + X = aesara.shared(X) + X = aesara.tensor.cast(X,'floatX') + y = aesara.shared(y) + y = aesara.tensor.cast(y,'floatX') with pm.Model() as model: @@ -182,27 +178,6 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_plus = pm.math.log(1+pm.math.exp(sigma)) y_like = pm.Normal('y_like',mu=mu, sigma=sigma_plus, observed=y) - elif configs['likelihood'] in ['SHASHb','SHASHo','SHASHo2']: - """ - Comment 1 - The current parameterizations are tuned towards standardized in- and output data. - It is possible to adjust the priors through the XXX_dist and XXX_params kwargs, like here we do with epsilon_params. - Supported distributions are listed in the Prior class. - - Comment 2 - Any mapping that is applied here after sampling should also be applied in util.hbr_utils.forward in order for the functions there to properly work. - For example, the softplus applied to sigma here is also applied in util.hbr_utils.forward - """ - SHASH_map = {'SHASHb':SHASHb,'SHASHo':SHASHo,'SHASHo2':SHASHo2} - - mu = pb.make_param("mu", slope_mu_params = (0.,3.), mu_intercept_mu_params=(0.,1.), sigma_intercept_mu_params = (1.,)).get_samples(pb) - sigma = pb.make_param("sigma", sigma_params = (1.,2.), slope_sigma_params=(0.,1.), intercept_sigma_params = (1., 1.)).get_samples(pb) - sigma_plus = pm.math.log(1+pm.math.exp(sigma)) - epsilon = pb.make_param("epsilon", epsilon_params = (0.,1.), slope_epsilon_params=(0.,1.), intercept_epsilon_params=(0.,1)).get_samples(pb) - delta = pb.make_param("delta", delta_params=(1.5,2.), slope_delta_params=(0.,1), intercept_delta_params=(2., 1)).get_samples(pb) - delta_plus = pm.math.log(1+pm.math.exp(delta)) + 0.3 - y_like = SHASH_map[configs['likelihood']]('y_like', mu=mu, sigma=sigma_plus, epsilon=epsilon, delta=delta_plus, observed = y) - return model @@ -243,7 +218,7 @@ def get_step_methods(self, m): Slice, CategoricalGibbsMetropolis, ) - :param m: a PyMC3 model + :param m: a PyMC model :return: """ samplermap = {'NUTS': NUTS, 'MH': Metropolis, 'Slice': Slice, 'HMC': HamiltonianMC} @@ -436,7 +411,7 @@ def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): class Prior: """ - A wrapper class for a PyMC3 distribution. + A wrapper class for a PyMC distribution. - creates a fitted distribution from the trace, if one is present - overloads the __getitem__ function with something that switches between indexing or not, based on the shape """ @@ -445,6 +420,8 @@ def __init__(self, name, dist, params, pb, shape=(1,)) -> None: self.name = name self.shape = shape self.has_random_effect = True if len(shape)>1 else False + # TODO + print(self.has_random_effect) self.distmap = {'normal': pm.Normal, 'hnormal': pm.HalfNormal, 'gamma': pm.Gamma, @@ -454,7 +431,7 @@ def __init__(self, name, dist, params, pb, shape=(1,)) -> None: self.make_dist(dist, params, pb) def make_dist(self, dist, params, pb): - """This creates a pymc3 distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" + """This creates a pymc distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" with pb.model as m: if (pb.trace is not None) and (not self.has_random_effect): int_dist = from_posterior(param=self.name, @@ -463,18 +440,21 @@ def make_dist(self, dist, params, pb): freedom=pb.configs['freedom']) self.dist = int_dist.reshape(self.shape) else: - shape_prod = np.product(np.array(self.shape)) + # shape_prod = int(np.product(np.array(self.shape))) print(self.name) print(f"dist={dist}") - print(f"params={params}") - int_dist = self.distmap[dist](self.name, *params, shape=shape_prod) - self.dist = int_dist.reshape(self.shape) + print(params) + self.dist = self.distmap[dist](self.name, *params, shape=self.shape) + # self.dist = int_dist.reshape(self.shape) + print(type(self.dist)) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" assert self.dist is not None, "Distribution not initialized" if self.has_random_effect: - return self.dist[idx] + # TODO + # return self.dist[idx] + return self.dist else: return self.dist @@ -560,9 +540,9 @@ def __init__(self, name, dim): def get_samples(self, pb): with pb.model: - samples = theano.tensor.zeros([pb.n_ys, *self.dim]) + samples = aesara.tensor.zeros([pb.n_ys, *self.dim]) for be, idx in pb.be_idx_tups: - samples = theano.tensor.set_subtensor(samples[idx], self.dist[be]) + samples = aesara.tensor.set_subtensor(samples[idx], self.dist[be]) return samples @@ -635,11 +615,11 @@ def __init__(self, name, dim, slope_parameterization, intercept_parameterization def get_samples(self, pb:ParamBuilder): with pb.model: - samples = theano.tensor.zeros([pb.n_ys, *self.dim]) + samples = aesara.tensor.zeros([pb.n_ys, *self.dim]) for be, idx in pb.be_idx_tups: - dot = theano.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T + dot = aesara.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T intercept = self.intercept_parameterization.dist[be] - samples = theano.tensor.set_subtensor(samples[idx,:],dot+intercept) + samples = aesara.tensor.set_subtensor(samples[idx,:],dot+intercept) return samples @@ -768,7 +748,7 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): mu_prior_intercept * sigma_prior_intercept) # Build the neural network and estimate y_hat: - y_hat = theano.tensor.zeros(y.shape) + y_hat = aesara.tensor.zeros(y.shape) for be in be_idx: # Find the indices corresponding to 'group be': a = [] @@ -776,14 +756,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1 = pm.math.tanh(theano.tensor.dot(X[idx, :], weights_in_1[be])) + act_1 = pm.math.tanh(aesara.tensor.dot(X[idx, :], weights_in_1[be])) if n_layers == 2: - act_2 = pm.math.tanh(theano.tensor.dot(act_1, weights_1_2[be])) - y_hat = theano.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + theano.tensor.dot(act_2, weights_2_out[be])) + act_2 = pm.math.tanh(aesara.tensor.dot(act_1, weights_1_2[be])) + y_hat = aesara.tensor.set_subtensor(y_hat[idx, 0], + intercepts[be] + aesara.tensor.dot(act_2, weights_2_out[be])) else: - y_hat = theano.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + theano.tensor.dot(act_1, weights_2_out[be])) + y_hat = aesara.tensor.set_subtensor(y_hat[idx, 0], + intercepts[be] + aesara.tensor.dot(act_1, weights_2_out[be])) # If we want to estimate varying noise terms across groups: if configs['random_noise']: @@ -864,20 +844,20 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): # intercepts_offset_noise * sigma_prior_intercept_noise) # Build the neural network and estimate the sigma_y: - sigma_y = theano.tensor.zeros(y.shape) + sigma_y = aesara.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1_noise = pm.math.sigmoid(theano.tensor.dot(X[idx, :], weights_in_1_noise[be])) + act_1_noise = pm.math.sigmoid(aesara.tensor.dot(X[idx, :], weights_in_1_noise[be])) if n_layers == 2: - act_2_noise = pm.math.sigmoid(theano.tensor.dot(act_1_noise, weights_1_2_noise[be])) - temp = pm.math.log1pexp(theano.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 + act_2_noise = pm.math.sigmoid(aesara.tensor.dot(act_1_noise, weights_1_2_noise[be])) + temp = pm.math.log1pexp(aesara.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 else: - temp = pm.math.log1pexp(theano.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 - sigma_y = theano.tensor.set_subtensor(sigma_y[idx, 0], temp) + temp = pm.math.log1pexp(aesara.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 + sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: if trace is not None: # Used for transferring the priors @@ -886,14 +866,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) - sigma_y = theano.tensor.zeros(y.shape) + sigma_y = aesara.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = theano.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) + sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) else: # do not allow for random noise terms across groups: if trace is not None: # Used for transferring the priors @@ -901,25 +881,25 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) - sigma_y = theano.tensor.zeros(y.shape) + sigma_y = aesara.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = theano.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) + sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) if configs['skewed_likelihood']: skewness = pm.Uniform('skewness', lower=-10, upper=10, shape=(batch_effects_size)) - alpha = theano.tensor.zeros(y.shape) + alpha = aesara.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - alpha = theano.tensor.set_subtensor(alpha[idx, 0], skewness[be]) + alpha = aesara.tensor.set_subtensor(alpha[idx, 0], skewness[be]) else: alpha = 0 # symmetrical normal distribution diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index d769163e..16c9a139 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -122,36 +122,36 @@ def __init__(self, **kwargs): self.configs = dict() # inputs - self.configs['trbefile'] = kwargs.pop('trbefile', None) - self.configs['tsbefile'] = kwargs.pop('tsbefile', None) + self.configs['trbefile'] = kwargs.get('trbefile', None) + self.configs['tsbefile'] = kwargs.get('tsbefile', None) # Model settings - self.configs['type'] = kwargs.pop('model_type', 'linear') - self.configs['random_noise'] = kwargs.pop('random_noise', 'True') == 'True' - self.configs['likelihood'] = kwargs.pop('likelihood', 'Normal') + self.configs['type'] = kwargs.get('model_type', 'linear') + self.configs['random_noise'] = kwargs.get('random_noise', 'True') == 'True' + self.configs['likelihood'] = kwargs.get('likelihood', 'Normal') # sampler settings - self.configs['n_samples'] = int(kwargs.pop('n_samples', '1000')) - self.configs['n_tuning'] = int(kwargs.pop('n_tuning', '500')) - self.configs['n_chains'] = int(kwargs.pop('n_chains', '1')) - self.configs['sampler'] = kwargs.pop('sampler', 'NUTS') - self.configs['target_accept'] = float(kwargs.pop('target_accept', '0.8')) - self.configs['init'] = kwargs.pop('init', 'jitter+adapt_diag') - self.configs['cores'] = int(kwargs.pop('cores', '1')) + self.configs['n_samples'] = int(kwargs.get('n_samples', '1000')) + self.configs['n_tuning'] = int(kwargs.get('n_tuning', '500')) + self.configs['n_chains'] = int(kwargs.get('n_chains', '1')) + self.configs['sampler'] = kwargs.get('sampler', 'NUTS') + self.configs['target_accept'] = float(kwargs.get('target_accept', '0.8')) + self.configs['init'] = kwargs.get('init', 'jitter+adapt_diag') + self.configs['cores'] = int(kwargs.get('cores', '1')) # model transfer setting - self.configs['freedom'] = int(kwargs.pop('freedom', '1')) + self.configs['freedom'] = int(kwargs.get('freedom', '1')) self.configs['transferred'] = False # deprecated settings - self.configs['skewed_likelihood'] = kwargs.pop('skewed_likelihood', 'False') == 'True' + self.configs['skewed_likelihood'] = kwargs.get('skewed_likelihood', 'False') == 'True' # misc - self.configs['pred_type'] = kwargs.pop('pred_type', 'single') + self.configs['pred_type'] = kwargs.get('pred_type', 'single') if self.configs['type'] == 'bspline': - self.configs['order'] = int(kwargs.pop('order', '3')) - self.configs['nknots'] = int(kwargs.pop('nknots', '5')) + self.configs['order'] = int(kwargs.get('order', '3')) + self.configs['nknots'] = int(kwargs.get('nknots', '5')) elif self.configs['type'] == 'polynomial': - self.configs['order'] = int(kwargs.pop('order', '3')) + self.configs['order'] = int(kwargs.get('order', '3')) elif self.configs['type'] == 'nn': - self.configs['nn_hidden_neuron_num'] = int(kwargs.pop('nn_hidden_neuron_num', '2')) - self.configs['nn_hidden_layers_num'] = int(kwargs.pop('nn_hidden_layers_num', '2')) + self.configs['nn_hidden_neuron_num'] = int(kwargs.get('nn_hidden_neuron_num', '2')) + self.configs['nn_hidden_layers_num'] = int(kwargs.get('nn_hidden_layers_num', '2')) if self.configs['nn_hidden_layers_num'] > 2: raise ValueError("Using " + str(self.configs['nn_hidden_layers_num']) \ + " layers was not implemented. The number of " \ @@ -165,40 +165,41 @@ def __init__(self, **kwargs): if self.configs['type'] in ['bspline', 'polynomial', 'linear']: for p in ['mu', 'sigma', 'epsilon', 'delta']: - self.configs[f'linear_{p}'] = kwargs.pop(f'linear_{p}', 'False') == 'True' + self.configs[f'linear_{p}'] = kwargs.get(f'linear_{p}', 'False') == 'True' ######## Deprecations (remove in later version) if f'{p}_linear' in kwargs.keys(): print(f'The keyword \'{p}_linear\' is deprecated. It is now automatically replaced with \'linear_{p}\'') - self.configs[f'linear_{p}'] = kwargs.pop(f'{p}_linear', 'False') == 'True' + self.configs[f'linear_{p}'] = kwargs.get(f'{p}_linear', 'False') == 'True' ##### End Deprecations for c in ['centered','random']: - self.configs[f'{c}_{p}'] = kwargs.pop(f'{c}_{p}', 'False') == 'True' + self.configs[f'{c}_{p}'] = kwargs.get(f'{c}_{p}', 'False') == 'True' for sp in ['slope','intercept']: - self.configs[f'{c}_{sp}_{p}'] = kwargs.pop(f'{c}_{sp}_{p}', 'False') == 'True' + self.configs[f'{c}_{sp}_{p}'] = kwargs.get(f'{c}_{sp}_{p}', 'False') == 'True' ######## Deprecations (remove in later version) if self.configs['linear_sigma']: if 'random_noise' in kwargs.keys(): print("The keyword \'random_noise\' is deprecated. It is now automatically replaced with \'random_intercept_sigma\', because sigma is linear") - self.configs['random_intercept_sigma'] = kwargs.pop('random_noise','True') == 'True' + self.configs['random_intercept_sigma'] = kwargs.get('random_noise','True') == 'True' elif 'random_noise' in kwargs.keys(): print("The keyword \'random_noise\' is deprecated. It is now automatically replaced with \'random_sigma\', because sigma is fixed") - self.configs['random_sigma'] = kwargs.pop('random_noise','True') == 'True' + self.configs['random_sigma'] = kwargs.get('random_noise','True') == 'True' if 'random_slope' in kwargs.keys(): print("The keyword \'random_slope\' is deprecated. It is now automatically replaced with \'random_intercept_mu\'") - self.configs['random_slope_mu'] = kwargs.pop('random_slope','True') == 'True' + self.configs['random_slope_mu'] = kwargs.get('random_slope','True') == 'True' ##### End Deprecations ## Default parameters - self.configs['linear_mu'] = kwargs.pop('linear_mu','True') == 'True' - self.configs['random_mu'] = kwargs.pop('random_mu','True') == 'True' - self.configs['random_intercept_mu'] = kwargs.pop('random_intercept_mu','True') == 'True' - self.configs['random_slope_mu'] = kwargs.pop('random_slope_mu','True') == 'True' - self.configs['random_sigma'] = kwargs.pop('random_sigma','True') == 'True' - self.configs['centered_sigma'] = kwargs.pop('centered_sigma','True') == 'True' + self.configs['linear_mu'] = kwargs.get('linear_mu','True') == 'True' + print(self.configs['linear_mu']) + self.configs['random_mu'] = kwargs.get('random_mu','True') == 'True' + self.configs['random_intercept_mu'] = kwargs.get('random_intercept_mu','True') == 'True' + self.configs['random_slope_mu'] = kwargs.get('random_slope_mu','True') == 'True' + self.configs['random_sigma'] = kwargs.get('random_sigma','True') == 'True' + self.configs['centered_sigma'] = kwargs.get('centered_sigma','True') == 'True' ## End default parameters self.hbr = HBR(self.configs) @@ -213,7 +214,7 @@ def neg_log_lik(self): def estimate(self, X, y, **kwargs): - trbefile = kwargs.pop('trbefile', None) + trbefile = kwargs.get('trbefile', None) if trbefile is not None: batch_effects_train = fileio.load(trbefile) else: @@ -226,7 +227,7 @@ def estimate(self, X, y, **kwargs): def predict(self, Xs, X=None, Y=None, **kwargs): - tsbefile = kwargs.pop('tsbefile', None) + tsbefile = kwargs.get('tsbefile', None) if tsbefile is not None: batch_effects_test = fileio.load(tsbefile) else: diff --git a/pcntoolkit/util/hbr_utils.py b/pcntoolkit/util/hbr_utils.py index db7a5ccd..5c1619fc 100644 --- a/pcntoolkit/util/hbr_utils.py +++ b/pcntoolkit/util/hbr_utils.py @@ -7,7 +7,7 @@ import pickle import matplotlib.pyplot as plt import pandas as pd -import pymc3 as pm +import pymc as pm from pcntoolkit.model.SHASH import * from pcntoolkit.model.hbr import bspline_transform diff --git a/pcntoolkit/util/utils.py b/pcntoolkit/util/utils.py index e81c8158..e0aae42c 100644 --- a/pcntoolkit/util/utils.py +++ b/pcntoolkit/util/utils.py @@ -14,7 +14,7 @@ import bspline from bspline import splinelab from sklearn.datasets import make_regression -import pymc3 as pm +import pymc as pm from io import StringIO import subprocess import re @@ -1322,16 +1322,16 @@ def get_package_versions(): versions['Python'] = platform.python_version() try: - import theano - versions['Theano'] = theano.__version__ + import aesara + versions['Aesara'] = aesara.__version__ except: - versions['Theano'] = '' + versions['Aesara'] = '' try: - import pymc3 - versions['PyMC3'] = pymc3.__version__ + import pymc + versions['PyMC'] = pymc.__version__ except: - versions['PyMC3'] = '' + versions['PyMC'] = '' try: import pcntoolkit @@ -1450,7 +1450,7 @@ def yes_or_no(question): -#====== This is stuff used for the SHASH distributions, but using numpy (not pymc or theano) === +#====== This is stuff used for the SHASH distributions, but using numpy (not pymc or aesara) === def K(p, x): return np.array(spp.kv(p, x)) diff --git a/setup.py b/setup.py index 74ce24e3..4383cc5e 100644 --- a/setup.py +++ b/setup.py @@ -15,14 +15,14 @@ 'scikit-learn', 'bspline', 'matplotlib', - 'numpy>=1.19.5,<1.23', + 'numpy', 'scipy>=1.3.2', 'pandas>=0.25.3', 'torch>=1.1.0', 'sphinx-tabs', - 'pymc3>=3.8,<=3.9.3', - 'theano==1.0.5', - 'arviz==0.11.0' + 'pymc>=4', + 'aesara', + 'arviz==0.13.0' ], #python_requires='<3.10', zip_safe=False) From e573d3d6dfd308739d22a497232405b5b0d38fbf Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 6 Mar 2023 18:45:07 +0100 Subject: [PATCH 02/43] Commiting tutorials as well --- .gitignore | 5 + dist/pcntoolkit-0.27-py3.11.egg | Bin 0 -> 286786 bytes doc/build/html/_modules/utils.html | 365 ++++---- ...R_NormativeModel_FCONdata_Tutorial.rst.txt | 1 - .../normative_modelling_walkthrough.rst.txt | 1 - .../HBR_NormativeModel_FCONdata_Tutorial.html | 2 +- .../normative_modelling_walkthrough.html | 863 ++++++++++-------- doc/requirements.txt | 2 +- .../HBR_NormativeModel_FCONdata_Tutorial.rst | 1 - .../pages/normative_modelling_walkthrough.rst | 1 - pcntoolkit/model/SHASH.py | 270 ------ pcntoolkit/model/hbr.py | 1 - 12 files changed, 697 insertions(+), 815 deletions(-) create mode 100644 dist/pcntoolkit-0.27-py3.11.egg delete mode 100644 pcntoolkit/model/SHASH.py diff --git a/.gitignore b/.gitignore index 00ca225b..48fbca93 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,8 @@ Temporary Items # IDE .vscode +# build folder +build + +# egg +pcntoolkit.egg-info \ No newline at end of file diff --git a/dist/pcntoolkit-0.27-py3.11.egg b/dist/pcntoolkit-0.27-py3.11.egg new file mode 100644 index 0000000000000000000000000000000000000000..7ba241b2c7ffa993beb41a9c974a8a29c781b717 GIT binary patch literal 286786 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∈E0Cal;fI@ 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 z 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 - + + + - - - - - utils — Predictive Clinical Neuroscience Toolkit 0.20 documentation - - - - - - - - - - - - - - - - - - + + + + + utils — Predictive Clinical Neuroscience Toolkit 0.20 documentation + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - + - -

- - + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for utils

+
+
 from __future__ import print_function
 
 import os
@@ -191,7 +208,7 @@ 

Source code for utils

 import bspline
 from bspline import splinelab
 from sklearn.datasets import make_regression
-import pymc3 as pm
+import pymc as pm
 
 # -----------------
 # Utility functions
@@ -947,45 +964,49 @@ 

Source code for utils

     plt.xticks(rotation=90, fontsize=7)
     plt.tight_layout()
     plt.show()
-
+
+
-
- -
-
- +
-
+
+ +
+

+ © Copyright 2020, Andre F. Marquand -

-
+

+ + Built with Sphinx using a theme provided by Read the Docs. + + + + + + + + + + + + + - - - - - - - - + \ No newline at end of file diff --git a/doc/build/html/_sources/pages/HBR_NormativeModel_FCONdata_Tutorial.rst.txt b/doc/build/html/_sources/pages/HBR_NormativeModel_FCONdata_Tutorial.rst.txt index 4db24f06..d4933e38 100644 --- a/doc/build/html/_sources/pages/HBR_NormativeModel_FCONdata_Tutorial.rst.txt +++ b/doc/build/html/_sources/pages/HBR_NormativeModel_FCONdata_Tutorial.rst.txt @@ -24,7 +24,6 @@ Step 0: Install necessary libraries & grab data files .. code:: ipython3 - ! pip uninstall -y Theano-PyMC # conflicts with Theano on some environments ! pip install pcntoolkit==0.26 diff --git a/doc/build/html/_sources/pages/normative_modelling_walkthrough.rst.txt b/doc/build/html/_sources/pages/normative_modelling_walkthrough.rst.txt index 06eb5017..f63ec8fa 100644 --- a/doc/build/html/_sources/pages/normative_modelling_walkthrough.rst.txt +++ b/doc/build/html/_sources/pages/normative_modelling_walkthrough.rst.txt @@ -46,7 +46,6 @@ bad if you cannot complete everything during the tutorial. .. code:: ipython3 #install normative modeling - ! pip uninstall -y Theano-PyMC # conflicts with Theano on some environments ! pip install pcntoolkit==0.26 diff --git a/doc/build/html/pages/HBR_NormativeModel_FCONdata_Tutorial.html b/doc/build/html/pages/HBR_NormativeModel_FCONdata_Tutorial.html index ec9e668b..2e1a829a 100644 --- a/doc/build/html/pages/HBR_NormativeModel_FCONdata_Tutorial.html +++ b/doc/build/html/pages/HBR_NormativeModel_FCONdata_Tutorial.html @@ -207,7 +207,7 @@

Hierarchical Bayesian Regressionhttps://colab.research.google.com/assets/colab-badge.svg

Step 0: Install necessary libraries & grab data files¶

-
! pip uninstall -y Theano-PyMC  # conflicts with Theano on some environments
+
! 
 ! pip install pcntoolkit==0.26
 
diff --git a/doc/build/html/pages/normative_modelling_walkthrough.html b/doc/build/html/pages/normative_modelling_walkthrough.html index 0ed663c0..d2b3d562 100644 --- a/doc/build/html/pages/normative_modelling_walkthrough.html +++ b/doc/build/html/pages/normative_modelling_walkthrough.html @@ -1,39 +1,40 @@ - - - + + + - + GPR tutorial — Predictive Clinical Neuroscience Toolkit 0.20 documentation - - - - - - + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -43,118 +44,142 @@ - - - - + + + + - +
- +
- +
- +
- - @@ -170,74 +195,91 @@ -
- - -
-
+
+ + + + +
+
-
- -
-

Gaussian Process Regression¶

-

Created by

-

Mariam Zabihi @m_zabihi

-

Saige Rutherford @being_saige

-

Thomas Wolfers @ThomasWolfers -_______________________________________________________________________________

-https://colab.research.google.com/assets/colab-badge.svg -
-

Background Story¶

-

Morten and Ingrid are concerned about the health of their father, -Nordan. He recently turned 65 years. A few months ago he could not find -his way home. Together, they visit a neurologist/psychiatrist to conduct -a number of cognitive tests. However, those tests were inconclusive. -While Nordan has a relatively low IQ it could not explain his trouble -returning home.

-

Recently, the family heard about a new screening technique called -normative modeling with which one can place individuals in reference to -a population norm on for instance measures such as brain volume. Nordan -would like to undertake this procedure to better know what is going on -and to potentially find targets for treatment. Therefore, the family -booked an appointment with you, the normative modeling specialist. To -find out what is going on you compare Nordan’s hyppocampus to the norm -and to a group of persons with Dementia disorders, who have a similar -IQ, age as well as the same sex as Nordan.

-

Do your best to get as far as you can. However, you do not need to feel -bad if you cannot complete everything during the tutorial.

-
-
-

Task 0: Load data and install the pcntoolkit¶

-
#install normative modeling
-! pip uninstall -y Theano-PyMC  # conflicts with Theano on some environments
+            
+ +
+

Gaussian Process Regression¶

+

Created by

+

Mariam Zabihi @m_zabihi

+

Saige Rutherford @being_saige

+

Thomas Wolfers @ThomasWolfers + _______________________________________________________________________________

+ https://colab.research.google.com/assets/colab-badge.svg +
+

Background Story¶

+

Morten and Ingrid are concerned about the health of their father, + Nordan. He recently turned 65 years. A few months ago he could not find + his way home. Together, they visit a neurologist/psychiatrist to conduct + a number of cognitive tests. However, those tests were inconclusive. + While Nordan has a relatively low IQ it could not explain his trouble + returning home.

+

Recently, the family heard about a new screening technique called + normative modeling with which one can place individuals in reference to + a population norm on for instance measures such as brain volume. Nordan + would like to undertake this procedure to better know what is going on + and to potentially find targets for treatment. Therefore, the family + booked an appointment with you, the normative modeling specialist. To + find out what is going on you compare Nordan’s hyppocampus to the norm + and to a group of persons with Dementia disorders, who have a similar + IQ, age as well as the same sex as Nordan.

+

Do your best to get as far as you can. However, you do not need to feel + bad if you cannot complete everything during the tutorial.

+
+
+

Task 0: Load data and install the pcntoolkit¶

+
+
+
#install normative modeling
+!
 ! pip install pcntoolkit==0.26
-
-
-

Option 1: Connect your Google Drive account, and load data from -Google Drive. Having Google Drive connected will allow you to save any -files created back to your Drive folder. This step will require you to -download the csv files from -Github -to your computer, and then make a folder in your Google Drive account -and upload the csv files to this folder.

-
from google.colab import drive
+
+
+
+

Option 1: Connect your Google Drive account, and load data from + Google Drive. Having Google Drive connected will allow you to save any + files created back to your Drive folder. This step will require you to + download the csv files from + Github + to your computer, and then make a folder in your Google Drive account + and upload the csv files to this folder. +

+
+
+
from google.colab import drive
 drive.mount('/content/drive')
 
 #change dir to data on your google drive
@@ -245,19 +287,25 @@ 

Task 0: Load data and install the pcntoolkitos.chdir('drive/My Drive/name-of-folder-where-you-uploaded-csv-files-from-Github/') #Change this path to match the path to your data in Google Drive # code by T. Wolfers -

-
-

Option 2: Import the files directly from Github, and skip adding -them to Google Drive.

-
!wget -nc https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_demographics.csv
+
+
+
+

Option 2: Import the files directly from Github, and skip adding + them to Google Drive.

+
+
+
!wget -nc https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_demographics.csv
 !wget -nc https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_demographics_nordan.csv
 !wget -nc https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_features.csv
 !wget -nc https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_features_nordan.csv
 
 # code by S. Rutherford
-
-
-
--2022-02-17 15:03:58--  https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_demographics.csv
+
+
+
+
+
+
--2022-02-17 15:03:58--  https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/master/tutorials/CPC_2020/data/camcan_demographics.csv
 Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.108.133, 185.199.111.133, ...
 Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
 HTTP request sent, awaiting response... 200 OK
@@ -300,23 +348,29 @@ 

Task 0: Load data and install the pcntoolkit -

TASK 1: Format input data¶

-

You have four files. The features and demographics file for the -normsample and two files of the same name for Nordan your test sample. -As one of your coworkers has done the preporcessing and quality control -there are more subjects in the demographics file than in the features -file of the norm sample. Please select the overlap of participants -between those two files.

-

Question for your understanding:

-
    -
  1. Why do we have to select the overlap between participants in terms of -featrues and demographics?

  2. -
-
import pandas as pd
+
+
+
+

+
+

TASK 1: Format input data¶

+

You have four files. The features and demographics file for the + normsample and two files of the same name for Nordan your test sample. + As one of your coworkers has done the preporcessing and quality control + there are more subjects in the demographics file than in the features + file of the norm sample. Please select the overlap of participants + between those two files.

+

Question for your understanding:

+
    +
  1. +

    Why do we have to select the overlap between participants in terms of + featrues and demographics?

    +
  2. +
+
+
+
import pandas as pd
 
 # read in the files.
 norm_demographics = pd.read_csv('camcan_demographics.csv',
@@ -340,9 +394,12 @@ 

TASK 1: Format input dataprint(norm_demographics_features) # code by T. Wolfers -

-
-
             age sex_name  sex  IQ_random
+
+
+
+
+
+
             age sex_name  sex  IQ_random
 paricipants
 CC110033      24     MALE    1         73
 CC110037      18     MALE    1        103
@@ -386,34 +443,42 @@ 

TASK 1: Format input data -

TASK 2: Prepare the covariate_normsample and testresponse_normsample file.¶

-

As mentioned in the introductory presentation those files need a -specific format and the entries need to be seperated by spaces. Use -whatever method you know to prepare those files based on the data -provided in TASK 1. Save those files in .txt format in your drive. Also -get rid of the column names and participant IDs.

-

Given that we only have limited time in this practical we have to make a -selection for the features based on your prior knowledge. With the -information in mind that Nordan does not remember his way home, which -subfield of the hyppocampus is probably a good target for the -investigations? Select a maximum of four hyppocampal regions as -features.

-

NOTE: Normative modeling is a screening tool we just make this selection -due to time constraints, in reality we build these models on millions of -putative biomarkers that are not restricted to brain imaging.

-

Qestions for your understanding:

-
    -
  1. What is the requirement for the features in terms of variable -properties (e.g. dicotomous or continous)? 3) What is the requirement -for the covariates in terms of these properties? 4) What are the -requirements for both together? 5) How does this depent on the -algorithm used?

  2. -
-
# perpare covariate_normsample for sex and age
+
+
+
+

+
+

TASK 2: Prepare the covariate_normsample and testresponse_normsample file.¶

+

As mentioned in the introductory presentation those files need a + specific format and the entries need to be seperated by spaces. Use + whatever method you know to prepare those files based on the data + provided in TASK 1. Save those files in .txt format in your drive. Also + get rid of the column names and participant IDs.

+

Given that we only have limited time in this practical we have to make a + selection for the features based on your prior knowledge. With the + information in mind that Nordan does not remember his way home, which + subfield of the hyppocampus is probably a good target for the + investigations? Select a maximum of four hyppocampal regions as + features.

+

NOTE: Normative modeling is a screening tool we just make this selection + due to time constraints, in reality we build these models on millions of + putative biomarkers that are not restricted to brain imaging.

+

Qestions for your understanding:

+
    +
  1. +

    What is the requirement for the features in terms of variable + properties (e.g. dicotomous or continous)? 3) What is the requirement + for the covariates in terms of these properties? 4) What are the + requirements for both together? 5) How does this depent on the + algorithm used?

    +
  2. +
+
+
+
# perpare covariate_normsample for sex and age
 covariate_normsample = norm_demographics_features[['sex',
                                                    'age']]
 
@@ -434,24 +499,30 @@ 

TASK 2: Prepare the covariate_normsample and testresponse_n index = False) # code by T. Wolfers -

-
-
-
-

TASK 3: Estimate normative model¶

-

Once you have prepared and saved all the necessary files. Look at the -pcntoolkit for running normative modeling. Select an appropritate method -set up the toolkit and run your analyses using 2-fold cross validation -in the normsample. Change the output suffix from estimate to ’_2fold’.

-

HINT: You primarily need the estimate function.

-

SUGGESTION: While this process is running you can go to the next TASK 4, -you will have no doubt when it is correctly running.

-

Question for your understaning:

-
    -
  1. What does cvfolds mean and why do we use it? 7) What is the output of -the estimate function and what does it mean?

  2. -
-
import pcntoolkit as pcn
+
+
+
+
+
+

TASK 3: Estimate normative model¶

+

Once you have prepared and saved all the necessary files. Look at the + pcntoolkit for running normative modeling. Select an appropritate method + set up the toolkit and run your analyses using 2-fold cross validation + in the normsample. Change the output suffix from estimate to ’_2fold’.

+

HINT: You primarily need the estimate function.

+

SUGGESTION: While this process is running you can go to the next TASK 4, + you will have no doubt when it is correctly running.

+

Question for your understaning:

+
    +
  1. +

    What does cvfolds mean and why do we use it? 7) What is the output of + the estimate function and what does it mean?

    +
  2. +
+
+
+
import pcntoolkit as pcn
 
 # run normative modeling using 2-fold cross-validation
 
@@ -462,9 +533,12 @@ 

TASK 3: Estimate normative modeloutputsuffix = '_2fold') # code by T. Wolfers -

-
-
Processing data in features_normsample.txt
+
+
+
+
-
-
-

TASK 4: Estimate the forward model of the normative model¶

-

In order to visulize the normative trajectories you first need to run -the forward model. To this end you need to set up an appropriate -covariate_forwardmodel file that covers the age range appropriately for -both sexes. Save this file as .txt . Then you can input the files you -made in TASK 1 as well as the file you made now and run the forward -model using the appropriate specifications.

-

Question for your understaning:

-
    -
  1. What is yhat and ys2? 9) Why does the output of the forward model -does not inlcude the Z-scores?

  2. -
-
# create covariate_forwardmodel.txt file
+
+
+
+
+
+

TASK 4: Estimate the forward model of the normative model¶

+

In order to visulize the normative trajectories you first need to run + the forward model. To this end you need to set up an appropriate + covariate_forwardmodel file that covers the age range appropriately for + both sexes. Save this file as .txt . Then you can input the files you + made in TASK 1 as well as the file you made now and run the forward + model using the appropriate specifications.

+

Question for your understaning:

+
    +
  1. +

    What is yhat and ys2? 9) Why does the output of the forward model + does not inlcude the Z-scores?

    +
  2. +
+
+
+
# create covariate_forwardmodel.txt file
 covariate_forwardmodel = {'sex': [0, 0, 0, 0, 0, 0, 0,
                                   1, 1, 1, 1, 1, 1, 1],
                           'age': [20, 30, 40, 50, 60, 70, 80,
@@ -591,9 +672,12 @@ 

TASK 4: Estimate the forward model of the normative modeloutputsuffix = '_forward') # code by T. Wolfers -

-
-
Processing data in features_normsample.txt
+
+
+
+
-
-
-

TASK 5: Visualize forward model¶

-

Visualize the forward model of the normative model similar to the figure -below.

-
-1-s2.0-S245190221830329X-gr2.jpg -
-

1-s2.0-S245190221830329X-gr2.jpg¶

-
-
-

HINT: First create a function that calculates the confidence intervals -and then plot yhat, y2 of the forward model. Finally, plot the data of -individual participants.

-
import numpy as np
+
+
+
+
+
+

TASK 5: Visualize forward model¶

+

Visualize the forward model of the normative model similar to the figure + below.

+
+ 1-s2.0-S245190221830329X-gr2.jpg +
+

1-s2.0-S245190221830329X-gr2.jpg¶

+
+
+

HINT: First create a function that calculates the confidence intervals + and then plot yhat, y2 of the forward model. Finally, plot the data of + individual participants.

+
+
+
import numpy as np
 import matplotlib.pyplot as plt
 
 # confidence interval calculation at x_forward
@@ -726,20 +816,34 @@ 

TASK 5: Visualize forward modelplt.close() # code by M. Zabihi -

-
-../_images/normative_modelling_walkthrough_22_0.png -../_images/normative_modelling_walkthrough_22_1.png -../_images/normative_modelling_walkthrough_22_2.png -../_images/normative_modelling_walkthrough_22_3.png -../_images/normative_modelling_walkthrough_22_4.png -../_images/normative_modelling_walkthrough_22_5.png -../_images/normative_modelling_walkthrough_22_6.png -../_images/normative_modelling_walkthrough_22_7.png -
-
-

TASK 6: Apply the normative model to Nordan’s data and the dementia patients.¶

-
# read in Nordan's as well as the patient's demographics and features
+
+
+
+ ../_images/normative_modelling_walkthrough_22_0.png + ../_images/normative_modelling_walkthrough_22_1.png + ../_images/normative_modelling_walkthrough_22_2.png + ../_images/normative_modelling_walkthrough_22_3.png + ../_images/normative_modelling_walkthrough_22_4.png + ../_images/normative_modelling_walkthrough_22_5.png + ../_images/normative_modelling_walkthrough_22_6.png + ../_images/normative_modelling_walkthrough_22_7.png +
+
+

TASK 6: Apply the normative model to Nordan’s data and the dementia patients.¶

+
+
+
# read in Nordan's as well as the patient's demographics and features
 demographics_nordan = pd.read_csv('camcan_demographics_nordan.csv',
                                        sep= ",",
                                        index_col = 0)
@@ -776,9 +880,12 @@ 

TASK 6: Apply the normative model to Nordan’s data and th outputsuffix = '_nordan') # code by T. Wolfers -

-
-
Processing data in features_normsample.txt
+
+
+
+
+
+
Processing data in features_normsample.txt
 Estimating model  1 of 4
 Warning: Estimation of posterior distribution failed
 Warning: Estimation of posterior distribution failed
@@ -838,32 +945,45 @@ 

TASK 6: Apply the normative model to Nordan’s data and th Gradient evaluations: 104 Evaluating the model ... Writing outputs ... -

-
-
-
-

TASK 7: In which hyppocampal subfield(s) does Nordan deviate extremely?¶

-

No coding necessary just create a presentation which includes -recommendations to Nordan and his family. Use i) |Z| > 3.6 ii) |Z| > -1.96 as definitions for extreme normative deviations.

-
-
-

TASK 8 (OPTIONAL): Implement a function that calculates percentage change.¶

-

Percentage change = \frac{x1 - x2}{|x2|}*100

-
# function that calculates percentage change
+
+
+
+
+
+

TASK 7: In which hyppocampal subfield(s) does Nordan deviate extremely?¶

+

No coding necessary just create a presentation which includes + recommendations to Nordan and his family. Use i) |Z| > 3.6 ii) |Z| > + 1.96 as definitions for extreme normative deviations.

+
+
+

TASK 8 (OPTIONAL): Implement a function that calculates percentage change.¶

+

Percentage change = \frac{x1 - x2}{|x2|}*100

+
+
+
# function that calculates percentage change
 def calculate_percentage_change(x1, x2):
   percentage_change = ((x1 - x2) / abs(x2)) * 100
   return percentage_change
 
 # code by T. Wolfers
-
-
-
-
-

TASK 9 (OPTIONAL): Visualize percent change¶

-

Plot the prercentage change in Yhat of the forward model in reference to -age 20. Do that for both sexes seperately.

-
import matplotlib.pyplot as plt
+
+
+
+
+
+

TASK 9 (OPTIONAL): Visualize percent change¶

+

Plot the prercentage change in Yhat of the forward model in reference to + age 20. Do that for both sexes seperately.

+
+
+
import matplotlib.pyplot as plt
 
 forward_yhat = pd.read_csv('yhat_forward.txt', sep = ' ', header=None)
 
@@ -907,42 +1027,52 @@ 

TASK 9 (OPTIONAL): Visualize percent changeplt.ylim(-20, 2) # code by T. Wolfers -

-
-
(-20.0, 2.0)
-
-
-../_images/normative_modelling_walkthrough_32_1.png -
-
- - -
- +
+
+
+
+
+
(-20.0, 2.0)
+
+
+
+ ../_images/normative_modelling_walkthrough_32_1.png +
+
+ + +
+
+ + + +
+ +
+

+ © Copyright 2020, Andre F. Marquand + +

+
+ Built with Sphinx using a theme provided by Read the Docs. + +
@@ -950,19 +1080,20 @@

TASK 9 (OPTIONAL): Visualize percent change - jQuery(function () { - SphinxRtdTheme.Navigation.enable(true); - }); + jQuery(function () { + SphinxRtdTheme.Navigation.enable(true); + }); - - - - + + + + + \ No newline at end of file diff --git a/doc/requirements.txt b/doc/requirements.txt index 54e34c46..9f871205 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -12,4 +12,4 @@ numpy>=1.19.5 scipy>=1.3.2 pandas>=0.25.3 torch>=1.1.0 -pymc3>=3.11.2 +pymc>=4 diff --git a/doc/source/pages/HBR_NormativeModel_FCONdata_Tutorial.rst b/doc/source/pages/HBR_NormativeModel_FCONdata_Tutorial.rst index 4db24f06..d4933e38 100644 --- a/doc/source/pages/HBR_NormativeModel_FCONdata_Tutorial.rst +++ b/doc/source/pages/HBR_NormativeModel_FCONdata_Tutorial.rst @@ -24,7 +24,6 @@ Step 0: Install necessary libraries & grab data files .. code:: ipython3 - ! pip uninstall -y Theano-PyMC # conflicts with Theano on some environments ! pip install pcntoolkit==0.26 diff --git a/doc/source/pages/normative_modelling_walkthrough.rst b/doc/source/pages/normative_modelling_walkthrough.rst index 06eb5017..f63ec8fa 100644 --- a/doc/source/pages/normative_modelling_walkthrough.rst +++ b/doc/source/pages/normative_modelling_walkthrough.rst @@ -46,7 +46,6 @@ bad if you cannot complete everything during the tutorial. .. code:: ipython3 #install normative modeling - ! pip uninstall -y Theano-PyMC # conflicts with Theano on some environments ! pip install pcntoolkit==0.26 diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py deleted file mode 100644 index 9b3aaf59..00000000 --- a/pcntoolkit/model/SHASH.py +++ /dev/null @@ -1,270 +0,0 @@ -import aesara.tensor -from pymc.distributions import Continuous, draw_values, generate_samples -import aesara.tensor as tt -import numpy as np -from pymc.distributions.dist_math import bound -import scipy.special as spp - -from aesara.graph.op import Op -from aesara.graph import Apply -from aesara.gradient import grad_not_implemented - -""" -@author: Stijn de Boer (AuguB) - -See: Jones et al. (2009), Sinh-Arcsinh distributions. -""" - - -class K(Op): - """ - Modified Bessel function of the second kind, aesara implementation - """ - def make_node(self, p, x): - p = aesara.tensor.as_tensor_variable(p, 'floatX') - x = aesara.tensor.as_tensor_variable(x, 'floatX') - return Apply(self, [p,x], [p.type()]) - - def perform(self, node, inputs, output_storage, params=None): - # Doing this on the unique values avoids doing A LOT OF double work, apparently scipy doesn't do this by itself - unique_inputs, inverse_indices = np.unique(inputs[0], return_inverse=True) - unique_outputs = spp.kv(unique_inputs, inputs[1]) - outputs = unique_outputs[inverse_indices].reshape(inputs[0].shape) - output_storage[0][0] = outputs - - def grad(self, inputs, output_grads): - # Approximation of the derivative. This should suffice for using NUTS - dp = 1e-10 - p = inputs[0] - x = inputs[1] - grad = (self(p+dp,x) - self(p, x))/dp - return [output_grads[0]*grad, grad_not_implemented(0,1,2,3)] - -class SHASH(Continuous): - """ - SHASH described by Jones et al., based on a standard normal - All SHASH subclasses inherit from this - """ - def __init__(self, epsilon, delta, **kwargs): - super().__init__(**kwargs) - self.epsilon = tt.as_tensor_variable(epsilon) - self.delta = tt.as_tensor_variable(delta) - self.K = K() - - - def random(self, point=None, size=None): - epsilon, delta = draw_values([self.epsilon, self.delta], - point=point, size=size) - - def _random(epsilon, delta, size=None): - samples_transformed = np.sinh((np.arcsinh(np.random.randn(*size)) + epsilon) / delta) - return samples_transformed - - return generate_samples(_random, epsilon=epsilon, delta=delta, dist_shape=self.shape, size=size) - - def logp(self, value): - epsilon = self.epsilon - delta = self.delta + tt.np.finfo(np.float32).eps - - this_S = self.S(value) - this_S_sqr = tt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -tt.log(tt.constant(2 * tt.np.pi))/2 - frac2 = tt.log(delta) + tt.log(this_C_sqr)/2 - tt.log(1 + tt.sqr(value)) / 2 - exp = -this_S_sqr / 2 - - return bound(frac1 + frac2 + exp, delta > 0) - - def S(self, x): - """ - - :param epsilon: - :param delta: - :param x: - :return: The sinharcsinh transformation of x - """ - return tt.sinh(tt.arcsinh(x) * self.delta - self.epsilon) - - def S_inv(self, x): - return tt.sinh((tt.arcsinh(x) + self.epsilon) / self.delta) - - def C(self, x): - """ - :param epsilon: - :param delta: - :param x: - :return: the cosharcsinh transformation of x - Be aware that this is sqrt(1+S(x)^2), so you may save some compute if you can re-use the result from S. - """ - return tt.cosh(tt.arcsinh(x) * self.delta - self.epsilon) - - def P(self, q): - """ - The P function as given in Jones et al. - :param q: - :return: - """ - frac = np.exp(1 / 4) / np.power(8 * np.pi, 1 / 2) - K1 = self.K((q+1)/2,1/4) - K2 = self.K((q-1)/2,1/4) - a = (K1 + K2) * frac - return a - - def m(self, r): - """ - :param epsilon: - :param delta: - :param r: - :return: The r'th uncentered moment of the SHASH distribution parameterized by epsilon and delta. Given by Jones et al. - """ - frac1 = tt.as_tensor_variable(1 / np.power(2, r)) - acc = tt.as_tensor_variable(0) - for i in range(r + 1): - combs = spp.comb(r, i) - flip = np.power(-1, i) - ex = np.exp((r - 2 * i) * self.epsilon / self.delta) - # This is the reason we can not sample delta/kurtosis using NUTS; the gradient of P is unknown to pymc - # TODO write a class that inherits aesara.Op and do the gradient in there :) - p = self.P((r - 2 * i) / self.delta) - acc += combs * flip * ex * p - return frac1 * acc - -class SHASHo(SHASH): - """ - This is the shash where the location and scale parameters have simply been applied as an linear transformation - directly on the original shash. - """ - - def __init__(self, mu, sigma, epsilon, delta, **kwargs): - super().__init__(epsilon, delta, **kwargs) - self.mu = tt.as_tensor_variable(mu) - self.sigma = tt.as_tensor_variable(sigma) - - def random(self, point=None, size=None): - mu, sigma, epsilon, delta = draw_values([self.mu, self.sigma, self.epsilon, self.delta], - point=point, size=size) - - def _random(mu, sigma, epsilon, delta, size=None): - samples_transformed = np.sinh((np.arcsinh(np.random.randn(*size)) + epsilon) / delta) * sigma + mu - return samples_transformed - - return generate_samples(_random, mu=mu, sigma=sigma, epsilon=epsilon, delta=delta, - dist_shape=self.shape, - size=size) - - def logp(self, value): - mu = self.mu - sigma = self.sigma + tt.np.finfo(np.float32).eps - epsilon = self.epsilon - delta = self.delta + tt.np.finfo(np.float32).eps - - value_transformed = (value - mu) / sigma - - this_S = self.S( value_transformed) - this_S_sqr = tt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -tt.log(tt.constant(2 * tt.np.pi))/2 - frac2 = tt.log(delta) + tt.log(this_C_sqr)/2 - tt.log( - 1 + tt.sqr(value_transformed)) / 2 - exp = -this_S_sqr / 2 - change_of_variable = -tt.log(sigma) - - return bound(frac1 + frac2 + exp + change_of_variable, sigma > 0, delta > 0) - - -class SHASHo2(SHASH): - """ - This is the shash where we apply the reparameterization provided in section 4.3 in Jones et al. - """ - - def __init__(self, mu, sigma, epsilon, delta, **kwargs): - super().__init__(epsilon, delta, **kwargs) - self.mu = tt.as_tensor_variable(mu) - self.sigma = tt.as_tensor_variable(sigma) - - def random(self, point=None, size=None): - mu, sigma, epsilon, delta = draw_values( - [self.mu, self.sigma, self.epsilon, self.delta], - point=point, size=size) - sigma_d = sigma / delta - - def _random(mu, sigma, epsilon, delta, size=None): - samples_transformed = np.sinh( - (np.arcsinh(np.random.randn(*size)) + epsilon) / delta) * sigma_d + mu - return samples_transformed - - return generate_samples(_random, mu=mu, sigma=sigma_d, epsilon=epsilon, delta=delta, - dist_shape=self.shape, - size=size) - - def logp(self, value): - mu = self.mu - sigma = self.sigma + tt.np.finfo(np.float32).eps - epsilon = self.epsilon - delta = self.delta + tt.np.finfo(np.float32).eps - sigma_d = sigma / delta - - - # Here a double change of variables is applied - value_transformed = ((value - mu) / sigma_d) - - this_S = self.S(value_transformed) - this_S_sqr = tt.sqr(this_S) - this_C = tt.sqrt(1+this_S_sqr) - frac1 = -tt.log(tt.sqrt(tt.constant(2 * tt.np.pi))) - frac2 = tt.log(delta) + tt.log(this_C) - tt.log( - 1 + tt.sqr(value_transformed)) / 2 - exp = -this_S_sqr / 2 - change_of_variable = -tt.log(sigma_d) - - # the change of variables is accounted for in the density by division and multiplication (adding and subtracting for logp) - return bound(frac1 + frac2 + exp + change_of_variable, delta > 0, sigma > 0) - -class SHASHb(SHASH): - """ - This is the shash where the location and scale parameters been applied as an linear transformation on the shash - distribution which was corrected for mean and variance. - """ - - def __init__(self, mu, sigma, epsilon, delta, **kwargs): - super().__init__(epsilon, delta, **kwargs) - self.mu = tt.as_tensor_variable(mu) - self.sigma = tt.as_tensor_variable(sigma) - - def random(self, point=None, size=None): - mu, sigma, epsilon, delta = draw_values( - [self.mu, self.sigma, self.epsilon, self.delta], - point=point, size=size) - mean = (tt.sinh(epsilon/delta)*self.P(1/delta)).eval() - var = ((tt.cosh(2*epsilon/delta)*self.P(2/delta)-1)/2).eval() - mean**2 - - def _random(mean, var, mu, sigma, epsilon, delta, size=None): - samples_transformed = ((np.sinh( - (np.arcsinh(np.random.randn(*size)) + epsilon) / delta) - mean) / np.sqrt(var)) * sigma + mu - return samples_transformed - - return generate_samples(_random, mean=mean, var=var, mu=mu, sigma=sigma, epsilon=epsilon, delta=delta, - dist_shape=self.shape, - size=size) - - def logp(self, value): - mu = self.mu - sigma = self.sigma + tt.np.finfo(np.float32).eps - epsilon = self.epsilon - delta = self.delta + tt.np.finfo(np.float32).eps - mean = tt.sinh(epsilon/delta)*self.P(1/delta) - var = (tt.cosh(2*epsilon/delta)*self.P(2/delta)-1)/2 - tt.sqr(mean) - - # Here a double change of variables is applied - value_transformed = ((value - mu) / sigma) * tt.sqrt(var) + mean - - this_S = self.S(value_transformed) - this_S_sqr = tt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -tt.log(tt.constant(2 * tt.np.pi))/2 - frac2 = tt.log(delta) + tt.log(this_C_sqr)/2 - tt.log(1 + tt.sqr(value_transformed)) / 2 - exp = -this_S_sqr / 2 - change_of_variable = tt.log(var)/2 - tt.log(sigma) - - # the change of variables is accounted for in the density by division and multiplication (addition and subtraction in the log domain) - return bound(frac1 + frac2 + exp + change_of_variable, delta > 0, sigma > 0, var > 0) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 4b191e0f..4060304d 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -421,7 +421,6 @@ def __init__(self, name, dist, params, pb, shape=(1,)) -> None: self.shape = shape self.has_random_effect = True if len(shape)>1 else False # TODO - print(self.has_random_effect) self.distmap = {'normal': pm.Normal, 'hnormal': pm.HalfNormal, 'gamma': pm.Gamma, From caa3e998dcef8deaaea04145ce018de0a84ee235 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 6 Mar 2023 18:59:01 +0100 Subject: [PATCH 03/43] gitiitigigijgigigj --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 00ca225b..caae9a5f 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ Temporary Items # IDE .vscode +build +pcntoolkit.egg-info \ No newline at end of file From 034796ab457d89954f5b463e53a2128b0a9fc2aa Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 12 Mar 2023 10:27:53 +0100 Subject: [PATCH 04/43] Working on simple Gaussian model without error --- pcntoolkit/model/hbr.py | 89 ++++++++++++------------- pcntoolkit/normative_model/norm_hbr.py | 1 - pcntoolkit/util/utils.py | 8 +-- setup.py | 4 +- tests/test_hbr_pymc.py | 92 ++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 tests/test_hbr_pymc.py diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 4060304d..22a08226 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -14,7 +14,7 @@ import numpy as np import pymc as pm -import aesara +import pytensor from itertools import product from functools import reduce @@ -158,10 +158,10 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ - X = aesara.shared(X) - X = aesara.tensor.cast(X,'floatX') - y = aesara.shared(y) - y = aesara.tensor.cast(y,'floatX') + X = pytensor.shared(X) + X = pytensor.tensor.cast(X,'floatX') + y = pytensor.shared(y) + y = pytensor.tensor.cast(y,'floatX') with pm.Model() as model: @@ -297,9 +297,9 @@ def predict(self, X, batch_effects, pred='single'): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, samples=samples, progressbar=True) - pred_mean = ppc['y_like'].mean(axis=0) - pred_var = ppc['y_like'].var(axis=0) + ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -334,9 +334,9 @@ def predict_on_new_site(self, X, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, trace=self.trace): - ppc = pm.sample_posterior_predictive(self.trace, samples=samples, progressbar=True) - pred_mean = ppc['y_like'].mean(axis=0) - pred_var = ppc['y_like'].var(axis=0) + ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -349,9 +349,9 @@ def generate(self, X, batch_effects, samples): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, samples=samples, progressbar=True) + ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) - generated_samples = np.reshape(ppc['y_like'].squeeze().T, [X.shape[0] * samples, 1]) + generated_samples = np.reshape(ppc.posterior_predictive['y_like'].squeeze().T, [X.shape[0] * samples, 1]) X = np.repeat(X, samples) if len(X.shape) == 1: X = np.expand_dims(X, axis=1) @@ -420,7 +420,6 @@ def __init__(self, name, dist, params, pb, shape=(1,)) -> None: self.name = name self.shape = shape self.has_random_effect = True if len(shape)>1 else False - # TODO self.distmap = {'normal': pm.Normal, 'hnormal': pm.HalfNormal, 'gamma': pm.Gamma, @@ -439,21 +438,15 @@ def make_dist(self, dist, params, pb): freedom=pb.configs['freedom']) self.dist = int_dist.reshape(self.shape) else: - # shape_prod = int(np.product(np.array(self.shape))) - print(self.name) - print(f"dist={dist}") - print(params) - self.dist = self.distmap[dist](self.name, *params, shape=self.shape) - # self.dist = int_dist.reshape(self.shape) - print(type(self.dist)) + shape_prod = int(np.product(np.array(self.shape))) + print(f"{self.name} \tdist = {dist}") + self.dist = self.distmap[dist](self.name, *params, shape=shape_prod) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" assert self.dist is not None, "Distribution not initialized" if self.has_random_effect: - # TODO - # return self.dist[idx] - return self.dist + return self.dist[idx] else: return self.dist @@ -539,9 +532,9 @@ def __init__(self, name, dim): def get_samples(self, pb): with pb.model: - samples = aesara.tensor.zeros([pb.n_ys, *self.dim]) + samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) for be, idx in pb.be_idx_tups: - samples = aesara.tensor.set_subtensor(samples[idx], self.dist[be]) + samples = pytensor.tensor.set_subtensor(samples[idx], self.dist[be]) return samples @@ -614,11 +607,11 @@ def __init__(self, name, dim, slope_parameterization, intercept_parameterization def get_samples(self, pb:ParamBuilder): with pb.model: - samples = aesara.tensor.zeros([pb.n_ys, *self.dim]) + samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) for be, idx in pb.be_idx_tups: - dot = aesara.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T + dot = pytensor.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T intercept = self.intercept_parameterization.dist[be] - samples = aesara.tensor.set_subtensor(samples[idx,:],dot+intercept) + samples = pytensor.tensor.set_subtensor(samples[idx,:],dot+intercept) return samples @@ -747,7 +740,7 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): mu_prior_intercept * sigma_prior_intercept) # Build the neural network and estimate y_hat: - y_hat = aesara.tensor.zeros(y.shape) + y_hat = pytensor.tensor.zeros(y.shape) for be in be_idx: # Find the indices corresponding to 'group be': a = [] @@ -755,14 +748,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1 = pm.math.tanh(aesara.tensor.dot(X[idx, :], weights_in_1[be])) + act_1 = pm.math.tanh(pytensor.tensor.dot(X[idx, :], weights_in_1[be])) if n_layers == 2: - act_2 = pm.math.tanh(aesara.tensor.dot(act_1, weights_1_2[be])) - y_hat = aesara.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + aesara.tensor.dot(act_2, weights_2_out[be])) + act_2 = pm.math.tanh(pytensor.tensor.dot(act_1, weights_1_2[be])) + y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_2, weights_2_out[be])) else: - y_hat = aesara.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + aesara.tensor.dot(act_1, weights_2_out[be])) + y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_1, weights_2_out[be])) # If we want to estimate varying noise terms across groups: if configs['random_noise']: @@ -843,20 +836,20 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): # intercepts_offset_noise * sigma_prior_intercept_noise) # Build the neural network and estimate the sigma_y: - sigma_y = aesara.tensor.zeros(y.shape) + sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1_noise = pm.math.sigmoid(aesara.tensor.dot(X[idx, :], weights_in_1_noise[be])) + act_1_noise = pm.math.sigmoid(pytensor.tensor.dot(X[idx, :], weights_in_1_noise[be])) if n_layers == 2: - act_2_noise = pm.math.sigmoid(aesara.tensor.dot(act_1_noise, weights_1_2_noise[be])) - temp = pm.math.log1pexp(aesara.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 + act_2_noise = pm.math.sigmoid(pytensor.tensor.dot(act_1_noise, weights_1_2_noise[be])) + temp = pm.math.log1pexp(pytensor.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 else: - temp = pm.math.log1pexp(aesara.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 - sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], temp) + temp = pm.math.log1pexp(pytensor.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 + sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: if trace is not None: # Used for transferring the priors @@ -865,14 +858,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) - sigma_y = aesara.tensor.zeros(y.shape) + sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) + sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) else: # do not allow for random noise terms across groups: if trace is not None: # Used for transferring the priors @@ -880,25 +873,25 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) - sigma_y = aesara.tensor.zeros(y.shape) + sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = aesara.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) + sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) if configs['skewed_likelihood']: skewness = pm.Uniform('skewness', lower=-10, upper=10, shape=(batch_effects_size)) - alpha = aesara.tensor.zeros(y.shape) + alpha = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] for i, b in enumerate(be): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - alpha = aesara.tensor.set_subtensor(alpha[idx, 0], skewness[be]) + alpha = pytensor.tensor.set_subtensor(alpha[idx, 0], skewness[be]) else: alpha = 0 # symmetrical normal distribution diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index 16c9a139..65903ace 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -194,7 +194,6 @@ def __init__(self, **kwargs): ## Default parameters self.configs['linear_mu'] = kwargs.get('linear_mu','True') == 'True' - print(self.configs['linear_mu']) self.configs['random_mu'] = kwargs.get('random_mu','True') == 'True' self.configs['random_intercept_mu'] = kwargs.get('random_intercept_mu','True') == 'True' self.configs['random_slope_mu'] = kwargs.get('random_slope_mu','True') == 'True' diff --git a/pcntoolkit/util/utils.py b/pcntoolkit/util/utils.py index e0aae42c..3d22bb57 100644 --- a/pcntoolkit/util/utils.py +++ b/pcntoolkit/util/utils.py @@ -1322,10 +1322,10 @@ def get_package_versions(): versions['Python'] = platform.python_version() try: - import aesara - versions['Aesara'] = aesara.__version__ + import pytensor + versions['pytensor'] = pytensor.__version__ except: - versions['Aesara'] = '' + versions['pytensor'] = '' try: import pymc @@ -1450,7 +1450,7 @@ def yes_or_no(question): -#====== This is stuff used for the SHASH distributions, but using numpy (not pymc or aesara) === +#====== This is stuff used for the SHASH distributions, but using numpy (not pymc or pytensor) === def K(p, x): return np.array(spp.kv(p, x)) diff --git a/setup.py b/setup.py index 4383cc5e..0f4ce19a 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ 'pandas>=0.25.3', 'torch>=1.1.0', 'sphinx-tabs', - 'pymc>=4', - 'aesara', + 'pymc>=5.1.0', + 'pytensor', 'arviz==0.13.0' ], #python_requires='<3.10', diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py new file mode 100644 index 00000000..e6c96bc6 --- /dev/null +++ b/tests/test_hbr_pymc.py @@ -0,0 +1,92 @@ +import os +import pandas as pd +import pcntoolkit as ptk +import numpy as np +import pickle +from matplotlib import pyplot as plt +processing_dir = "HBR_demo/" # replace with a path to your working directory +if not os.path.isdir(processing_dir): + os.makedirs(processing_dir) +os.chdir(processing_dir) +processing_dir = os.getcwd() + + +def main(): + # Optional + fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_tr.csv') + fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_te.csv') + icbm_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_tr.csv') + icbm_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_te.csv') + idps = ['rh_MeanThickness_thickness'] + + X_train = (fcon_tr['age']/100).to_numpy(dtype=float) + Y_train = fcon_tr[idps].to_numpy(dtype=float) + + # configure batch effects for site and sex + # batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) + + # or only site + batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) + + with open('X_train.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(X_train), file) + with open('Y_train.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(Y_train), file) + with open('trbefile.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(batch_effects_train), file) + + + X_test = (fcon_te['age']/100).to_numpy(dtype=float) + Y_test = fcon_te[idps].to_numpy(dtype=float) + #batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) + batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) + + with open('X_test.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(X_test), file) + with open('Y_test.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(Y_test), file) + with open('tsbefile.pkl', 'wb') as file: + pickle.dump(pd.DataFrame(batch_effects_test), file) + + # a simple function to quickly load pickle files + def ldpkl(filename: str): + with open(filename, 'rb') as f: + return pickle.load(f) + + respfile = os.path.join(processing_dir, 'Y_train.pkl') # measurements (eg cortical thickness) of the training samples (columns: the various features/ROIs, rows: observations or subjects) + covfile = os.path.join(processing_dir, 'X_train.pkl') # covariates (eg age) the training samples (columns: covariates, rows: observations or subjects) + + testrespfile_path = os.path.join(processing_dir, 'Y_test.pkl') # measurements for the testing samples + testcovfile_path = os.path.join(processing_dir, 'X_test.pkl') # covariate file for the testing samples + + trbefile = os.path.join(processing_dir, 'trbefile.pkl') # training batch effects file (eg scanner_id, gender) (columns: the various batch effects, rows: observations or subjects) + tsbefile = os.path.join(processing_dir, 'tsbefile.pkl') # testing batch effects file + + output_path = os.path.join(processing_dir, 'Models/') # output path, where the models will be written + log_dir = os.path.join(processing_dir, 'log/') # + if not os.path.isdir(output_path): + os.mkdir(output_path) + if not os.path.isdir(log_dir): + os.mkdir(log_dir) + + outputsuffix = '_estimate' # a string to name the output files, of use only to you, so adapt it for your needs.` + ptk.normative.estimate(covfile=covfile, + respfile=respfile, + tsbefile=tsbefile, + trbefile=trbefile, + alg='hbr', + linear_mu='False', + random_mu='False', + random_intercept_mu='False', + random_slope_mu='False', + random_sigma='False', + log_path=log_dir, + binary=True, + output_path=output_path, + testcov= testcovfile_path, + testresp = testrespfile_path, + outputsuffix=outputsuffix, + savemodel=True) + +if __name__=="__main__": + main() \ No newline at end of file From 087d7b2cb7d527c00e6054b0d5047123c183fe88 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 12 Mar 2023 11:42:50 +0100 Subject: [PATCH 05/43] Fixed the Assertion error --- pcntoolkit/model/hbr.py | 22 ++++++++++++---------- tests/test_hbr_pymc.py | 32 ++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 22a08226..4be73560 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -432,15 +432,13 @@ def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" with pb.model as m: if (pb.trace is not None) and (not self.has_random_effect): - int_dist = from_posterior(param=self.name, + self.dist = from_posterior(param=self.name, samples=pb.trace[self.name], distribution=dist, freedom=pb.configs['freedom']) - self.dist = int_dist.reshape(self.shape) else: - shape_prod = int(np.product(np.array(self.shape))) print(f"{self.name} \tdist = {dist}") - self.dist = self.distmap[dist](self.name, *params, shape=shape_prod) + self.dist = self.distmap[dist](self.name, *params, shape=self.shape) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" @@ -448,7 +446,7 @@ def __getitem__(self, idx): if self.has_random_effect: return self.dist[idx] else: - return self.dist + return self.dist[tuple([0]*len(self.shape))] class ParamBuilder: @@ -464,14 +462,14 @@ def __init__(self, model, X, y, batch_effects, trace, configs): :param model: model to attach all the distributions to :param X: Covariates :param y: IDPs - :param batch_effects: I guess this speaks for itself + :param batch_effects: array of batch effects :param trace: idem :param configs: idem """ self.model = model self.X = X self.y = y - self.batch_effects = batch_effects + self.batch_effects = batch_effects.astype(np.int16) self.trace = trace self.configs = configs @@ -483,10 +481,14 @@ def __init__(self, model, X, y, batch_effects, trace, configs): self.batch_effects_size = [] self.all_idx = [] for i in range(self.batch_effects_num): - # Count the unique values for each batch effect - self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) + # # Count the unique values for each batch effect + # self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) + # # Store the unique values for each batch effect + # self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) + + self.batch_effects_size.append(self.batch_effects[:,i].max()+1) # Store the unique values for each batch effect - self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) + self.all_idx.append(np.arange(self.batch_effects_size[-1]+1)) # Make a cartesian product of all the unique values of each batch effect self.be_idx = list(product(*self.all_idx)) diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index e6c96bc6..a5f2419d 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -15,19 +15,27 @@ def main(): # Optional fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_tr.csv') fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_te.csv') - icbm_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_tr.csv') - icbm_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_te.csv') + # fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_tr.csv') + # fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_te.csv') idps = ['rh_MeanThickness_thickness'] X_train = (fcon_tr['age']/100).to_numpy(dtype=float) Y_train = fcon_tr[idps].to_numpy(dtype=float) + + # fcon_tr.loc[fcon_tr['sitenum'] == 21,'sitenum'] = 13 + # fcon_te.loc[fcon_te['sitenum'] == 21,'sitenum'] = 13 + # configure batch effects for site and sex - # batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) + batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) + # or only site - batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) - + # batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) + + print(np.unique(batch_effects_train,axis=0)) # Here we see there are missing sites + + with open('X_train.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_train), file) with open('Y_train.pkl', 'wb') as file: @@ -38,8 +46,8 @@ def main(): X_test = (fcon_te['age']/100).to_numpy(dtype=float) Y_test = fcon_te[idps].to_numpy(dtype=float) - #batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) - batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) + batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) + # batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) with open('X_test.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_test), file) @@ -75,11 +83,11 @@ def ldpkl(filename: str): tsbefile=tsbefile, trbefile=trbefile, alg='hbr', - linear_mu='False', - random_mu='False', - random_intercept_mu='False', - random_slope_mu='False', - random_sigma='False', + linear_mu='True', + random_mu='True', + random_intercept_mu='True', + random_slope_mu='True', + random_sigma='True', log_path=log_dir, binary=True, output_path=output_path, From 54c98cce6fbdd1b14602dac3e4fe5f5479d0f0e2 Mon Sep 17 00:00:00 2001 From: Andre Marquand Date: Thu, 30 Mar 2023 09:32:06 +0200 Subject: [PATCH 06/43] added kwargs for trendsurf (to change optimizer) --- pcntoolkit/trendsurf.py | 9 ++++++--- setup.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pcntoolkit/trendsurf.py b/pcntoolkit/trendsurf.py index d7e03732..f009ad29 100644 --- a/pcntoolkit/trendsurf.py +++ b/pcntoolkit/trendsurf.py @@ -134,7 +134,7 @@ def get_args(*args): def estimate(filename, maskfile, basis, ard=False, outputall=False, - saveoutput=True): + saveoutput=True, **kwargs): """ Estimate a trend surface model This will estimate a trend surface model, independently for each subject. @@ -166,7 +166,10 @@ def estimate(filename, maskfile, basis, ard=False, outputall=False, * explainedvar - explained variance * rmse - standardised mean squared error """ - + + # parse arguments + optim = kwargs.get('optimizer', 'powell') + # load data print("Processing data in", filename) Y, X, mask = load_data(filename, maskfile) @@ -204,7 +207,7 @@ def estimate(filename, maskfile, basis, ard=False, outputall=False, for i in range(0, N): print("Estimating model ", i+1, "of", N) breg = BLR() - hyp[i, :] = breg.estimate(hyp0, Phi, Yz[:, i]) + hyp[i, :] = breg.estimate(hyp0, Phi, Yz[:, i], optimizer=optim) m[i, :] = breg.m nlZ[i] = breg.nlZ diff --git a/setup.py b/setup.py index 74ce24e3..fe04a48c 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup(name='pcntoolkit', - version='0.27', + version='0.28', description='Predictive Clinical Neuroscience toolkit', url='http://github.com/amarquand/PCNtoolkit', author='Andre Marquand', From d4fa2212b5e20b16e13c4bf96e793b4f2b85bbd6 Mon Sep 17 00:00:00 2001 From: Andre Marquand Date: Tue, 2 May 2023 11:03:35 +0200 Subject: [PATCH 07/43] adjust file permissions for parallel execution --- pcntoolkit/normative_parallel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pcntoolkit/normative_parallel.py b/pcntoolkit/normative_parallel.py index ad9d7c35..268e9461 100755 --- a/pcntoolkit/normative_parallel.py +++ b/pcntoolkit/normative_parallel.py @@ -916,7 +916,7 @@ def bashwrap_nm(processing_dir, job_call + ["\n"]) # changes permissoins for bash.sh file - os.chmod(processing_dir + job_name, 0o700) + os.chmod(processing_dir + job_name, 0o770) def qsub_nm(job_path, @@ -1121,7 +1121,7 @@ def sbatchwrap_nm(processing_dir, job_call + ["\n"] + [sbatch_exit]) # changes permissoins for bash.sh file - os.chmod(processing_dir + job_name, 0o700) + os.chmod(processing_dir + job_name, 0o770) def sbatch_nm(job_path, log_path): From 9b553c59447e4c853eef0e9c3806647f8022907d Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sat, 13 May 2023 10:40:04 +0200 Subject: [PATCH 08/43] Removed SHASH from the utils file --- pcntoolkit/model/hbr.py | 12 ++---- pcntoolkit/util/hbr_utils.py | 76 ++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 4be73560..02d4d83f 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -275,7 +275,6 @@ def estimate(self, X, y, batch_effects): self.batch_effects_size = [] for i in range(self.batch_effects_num): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) - X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: @@ -481,14 +480,11 @@ def __init__(self, model, X, y, batch_effects, trace, configs): self.batch_effects_size = [] self.all_idx = [] for i in range(self.batch_effects_num): - # # Count the unique values for each batch effect - # self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) - # # Store the unique values for each batch effect - # self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) - - self.batch_effects_size.append(self.batch_effects[:,i].max()+1) + # Count the unique values for each batch effect + self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) # Store the unique values for each batch effect - self.all_idx.append(np.arange(self.batch_effects_size[-1]+1)) + self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) + # Make a cartesian product of all the unique values of each batch effect self.be_idx = list(product(*self.all_idx)) diff --git a/pcntoolkit/util/hbr_utils.py b/pcntoolkit/util/hbr_utils.py index 5c1619fc..0d93a8f8 100644 --- a/pcntoolkit/util/hbr_utils.py +++ b/pcntoolkit/util/hbr_utils.py @@ -8,7 +8,7 @@ import matplotlib.pyplot as plt import pandas as pd import pymc as pm -from pcntoolkit.model.SHASH import * +# from pcntoolkit.model.SHASH import * from pcntoolkit.model.hbr import bspline_transform """ @@ -41,25 +41,25 @@ def get_single_zscores(X, Y, Z, model, sample): def z_score(Y, params, likelihood = "Normal"): """Get the z-scores of Y, given likelihood parameters""" - if likelihood.startswith('SHASH'): - mu = params['mu'] - sigma = params['sigma'] - epsilon = params['epsilon'] - delta = params['delta'] - if likelihood == "SHASHo": - SHASH = (Y-mu)/sigma - Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) - elif likelihood == "SHASHo2": - sigma_d = sigma/delta - SHASH = (Y-mu)/sigma_d - Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) - elif likelihood == "SHASHb": - true_mu = m(epsilon, delta, 1) - true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) - SHASH_c = ((Y-mu)/sigma) - SHASH = SHASH_c * true_sigma + true_mu - Z = np.sinh(np.arcsinh(SHASH) * delta - epsilon) - elif likelihood == 'Normal': + # if likelihood.startswith('SHASH'): + # mu = params['mu'] + # sigma = params['sigma'] + # epsilon = params['epsilon'] + # delta = params['delta'] + # if likelihood == "SHASHo": + # SHASH = (Y-mu)/sigma + # Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + # elif likelihood == "SHASHo2": + # sigma_d = sigma/delta + # SHASH = (Y-mu)/sigma_d + # Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + # elif likelihood == "SHASHb": + # true_mu = m(epsilon, delta, 1) + # true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + # SHASH_c = ((Y-mu)/sigma) + # SHASH = SHASH_c * true_sigma + true_mu + # Z = np.sinh(np.arcsinh(SHASH) * delta - epsilon) + if likelihood == 'Normal': Z = (Y-params['mu'])/params['sigma'] else: exit("Unsupported likelihood") @@ -98,22 +98,22 @@ def get_single_quantiles(synthetic_X, z_scores, model, be, sample): def quantile(zs, params, likelihood = "Normal"): """Get the zs'th quantiles given likelihood parameters""" - if likelihood.startswith('SHASH'): - mu = params['mu'] - sigma = params['sigma'] - epsilon = params['epsilon'] - delta = params['delta'] - if likelihood == "SHASHo": - quantiles = S_inv(zs,epsilon,delta)*sigma + mu - elif likelihood == "SHASHo2": - sigma_d = sigma/delta - quantiles = S_inv(zs,epsilon,delta)*sigma_d + mu - elif likelihood == "SHASHb": - true_mu = m(epsilon, delta, 1) - true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) - SHASH_c = ((S_inv(zs,epsilon,delta)-true_mu)/true_sigma) - quantiles = SHASH_c *sigma + mu - elif likelihood == 'Normal': + # if likelihood.startswith('SHASH'): + # mu = params['mu'] + # sigma = params['sigma'] + # epsilon = params['epsilon'] + # delta = params['delta'] + # if likelihood == "SHASHo": + # quantiles = S_inv(zs,epsilon,delta)*sigma + mu + # elif likelihood == "SHASHo2": + # sigma_d = sigma/delta + # quantiles = S_inv(zs,epsilon,delta)*sigma_d + mu + # elif likelihood == "SHASHb": + # true_mu = m(epsilon, delta, 1) + # true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + # SHASH_c = ((S_inv(zs,epsilon,delta)-true_mu)/true_sigma) + # quantiles = SHASH_c *sigma + mu + if likelihood == 'Normal': quantiles = zs*params['sigma'] + params['mu'] else: exit("Unsupported likelihood") @@ -157,8 +157,8 @@ def forward(X, Z, model, sample): if likelihood == 'Normal': parameter_list = ['mu','sigma'] - elif likelihood in ['SHASHb','SHASHo','SHASHo2']: - parameter_list = ['mu','sigma','epsilon','delta'] + # elif likelihood in ['SHASHb','SHASHo','SHASHo2']: + # parameter_list = ['mu','sigma','epsilon','delta'] else: exit("Unsupported likelihood") From a7f31ce50e6d3251007e0a10e0a20f136acbe7bf Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 14 May 2023 14:53:46 +0200 Subject: [PATCH 09/43] Updated the gitignore --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 48fbca93..ff6f1fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,4 +67,10 @@ Temporary Items build # egg -pcntoolkit.egg-info \ No newline at end of file +pcntoolkit.egg-info + +# Demo folder +HBR_demo + + +ssh_error_log.txt From 7e20e1b4cdde3cacf50f87bdce7673ed59767be2 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 14 May 2023 15:22:37 +0200 Subject: [PATCH 10/43] Adapted path to my machine --- tests/testHBR.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testHBR.py b/tests/testHBR.py index 6b98ed9f..8984bd27 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -22,7 +22,7 @@ ########################### Experiment Settings ############################### -working_dir = '/home/preclineu/seykia/temp/tests/' # Specift a working directory +working_dir = '/home/stijn/temp/tests/' # Specift a working directory # to save data and results. simulation_method = 'linear' # 'non-linear' From b34d5794a96313e6fe98a4bbfc6d65c85ebb6cc9 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 14 May 2023 20:15:44 +0200 Subject: [PATCH 11/43] Refactored the parambuilder to use pymc coords --- pcntoolkit/model/hbr.py | 183 ++++++++++++++++++++-------------------- tests/test_hbr_pymc.py | 37 +++++--- 2 files changed, 116 insertions(+), 104 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 02d4d83f..c882ae4a 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -163,20 +163,16 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): y = pytensor.shared(y) y = pytensor.tensor.cast(y,'floatX') - with pm.Model() as model: + # Make a param builder that will make the correct calls + pb = ParamBuilder(X, y, batch_effects, trace, configs) - # Make a param builder that will make the correct calls - pb = ParamBuilder(model, X, y, batch_effects, trace, configs) + with pm.Model(coords=pb.coords) as model: + pb.model=model if configs['likelihood'] == 'Normal': - mu = pb.make_param("mu", mu_slope_mu_params = (0.,10.), - sigma_slope_mu_params = (5.,), - mu_intercept_mu_params=(0.,10.), - sigma_intercept_mu_params = (5.,)).get_samples(pb) - sigma = pb.make_param("sigma", mu_sigma_params = (10., 5.), - sigma_sigma_params = (5.,)).get_samples(pb) - sigma_plus = pm.math.log(1+pm.math.exp(sigma)) - y_like = pm.Normal('y_like',mu=mu, sigma=sigma_plus, observed=y) + mu = pb.make_param("mu", mu_slope_mu_params = (0.,2.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) + sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,), apply_softplus=True).get_samples(pb) + y_like = pm.Normal('y_like',mu=mu, sigma=sigma, observed=y) return model @@ -414,30 +410,32 @@ class Prior: - creates a fitted distribution from the trace, if one is present - overloads the __getitem__ function with something that switches between indexing or not, based on the shape """ - def __init__(self, name, dist, params, pb, shape=(1,)) -> None: + def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: self.dist = None self.name = name - self.shape = shape - self.has_random_effect = True if len(shape)>1 else False + self.has_random_effect = has_random_effect self.distmap = {'normal': pm.Normal, 'hnormal': pm.HalfNormal, 'gamma': pm.Gamma, 'uniform': pm.Uniform, 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy} + 'hcauchy': pm.HalfCauchy, + 'hstudt':pm.HalfStudentT, + 'studt':pm.StudentT} self.make_dist(dist, params, pb) def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" with pb.model as m: - if (pb.trace is not None) and (not self.has_random_effect): + if (pb.trace is not None): self.dist = from_posterior(param=self.name, samples=pb.trace[self.name], distribution=dist, freedom=pb.configs['freedom']) + elif self.has_random_effect: + self.dist = self.distmap[dist](self.name, *params, dims=pb.batch_effect_dim_names) else: - print(f"{self.name} \tdist = {dist}") - self.dist = self.distmap[dist](self.name, *params, shape=self.shape) + self.dist = self.distmap[dist](self.name, *params) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" @@ -445,7 +443,7 @@ def __getitem__(self, idx): if self.has_random_effect: return self.dist[idx] else: - return self.dist[tuple([0]*len(self.shape))] + return self.dist class ParamBuilder: @@ -455,7 +453,7 @@ class ParamBuilder: It also contains a lot of decision logic for creating the parameterizations. """ - def __init__(self, model, X, y, batch_effects, trace, configs): + def __init__(self, X, y, batch_effects, trace, configs): """ :param model: model to attach all the distributions to @@ -465,153 +463,153 @@ def __init__(self, model, X, y, batch_effects, trace, configs): :param trace: idem :param configs: idem """ - self.model = model + self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) self.trace = trace self.configs = configs - self.feature_num = X.shape[1].eval().item() self.y_shape = y.shape.eval() self.n_ys = y.shape[0].eval().item() self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - self.all_idx = [] - for i in range(self.batch_effects_num): - # Count the unique values for each batch effect - self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) - # Store the unique values for each batch effect - self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) - - # Make a cartesian product of all the unique values of each batch effect - self.be_idx = list(product(*self.all_idx)) - - # Make tuples of batch effects ID's and indices of datapoints with that specific combination of batch effects - self.be_idx_tups = [] - for be in self.be_idx: - a = [] - for i, b in enumerate(be): - a.append(self.batch_effects[:, i] == b) - idx = reduce(np.logical_and, a).nonzero() - if idx[0].shape[0] != 0: - self.be_idx_tups.append((be, idx)) + self.batch_effect_dim_names = [] + self.batch_effect_values = {} + self.batch_effect_indices = {} + self.coords = {} - def make_param(self, name, dim = (1,), **kwargs): + for i in range(self.batch_effects_num): + batch_effect_dim_name = f"batch_effect_{i}" + self.batch_effect_dim_names.append(batch_effect_dim_name) + this_be_values, this_be_indices = np.unique(self.batch_effects[:,i], return_inverse=True) + self.coords[batch_effect_dim_name] = this_be_values + self.batch_effect_values[batch_effect_dim_name] = this_be_values + self.batch_effect_indices[batch_effect_dim_name] = this_be_indices + + def make_param(self, name, **kwargs): if self.configs.get(f'linear_{name}', False): # First make a slope and intercept, and use those to make a linear parameterization - slope_parameterization = self.make_param(f'slope_{name}', dim=[self.feature_num], **kwargs) + slope_parameterization = self.make_param(f'slope_{name}',**kwargs) intercept_parameterization = self.make_param(f'intercept_{name}', **kwargs) - return LinearParameterization(name=name, dim=dim, + return LinearParameterization(name=name, slope_parameterization=slope_parameterization, intercept_parameterization=intercept_parameterization, - pb=self, **kwargs) elif self.configs.get(f'random_{name}', False): if self.configs.get(f'centered_{name}', True): - return CentralRandomFixedParameterization(name=name, pb=self, dim=dim, **kwargs) + return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return NonCentralRandomFixedParameterization(name=name, pb=self, dim=dim, **kwargs) + return NonCentralRandomFixedParameterization(name=name, pb=self,**kwargs) else: - return FixedParameterization(name=name, dim=dim, pb=self,**kwargs) + return FixedParameterization(name=name, pb=self,**kwargs) class Parameterization: """ This is the top-level parameterization class from which all the other parameterizations inherit. """ - def __init__(self, name, dim): + def __init__(self, name, apply_softplus=False): self.name = name - self.dim = dim + self.apply_softplus=apply_softplus print(name, type(self)) def get_samples(self, pb): - - with pb.model: - samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) - for be, idx in pb.be_idx_tups: - samples = pytensor.tensor.set_subtensor(samples[idx], self.dist[be]) - return samples - + pass class FixedParameterization(Parameterization): """ A parameterization that takes a single value for all input. It does not depend on anything except its hyperparameters """ - def __init__(self, name, dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): + super().__init__(name,apply_softplus) dist = kwargs.get(f'{name}_dist','normal') - params = kwargs.get(f'{name}_params',(0.,1.)) - self.dist = Prior(name, dist, params, pb, shape = dim) + params = kwargs.get(f'{name}_params',(0.,2.)) + self.dist = Prior(name, dist, params, pb) + def get_samples(self, pb): + with pb.model: + samples = self.dist[0] + if self.apply_softplus: + return pm.math.log(1+pm.math.exp(samples)) + return samples class CentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a central fashion; the values are sampled from normal distribution with a group mean and group variance """ - def __init__(self, name, dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): + super().__init__(name,apply_softplus) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb, shape = dim) + mu_params = kwargs.get(f'mu_{name}_params',(0.,2.)) + mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) - # HalfCauchy is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hcauchy') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb, shape = [*pb.batch_effects_size, *dim]) + # HalfStudent is default for sigma + sigma_dist = kwargs.get(f'sigma_{name}_dist','hstudt') + sigma_params = kwargs.get(f'sigma_{name}_params',(2.,)) + sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) - self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, shape = [*pb.batch_effects_size, *dim]) + self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=pb.batch_effect_dim_names) + def get_samples(self, pb:ParamBuilder): + with pb.model: + samples = self.dist[*list(pb.batch_effect_indices.values())] + if self.apply_softplus: + return pm.math.log(1+pm.math.exp(samples)) + return samples + class NonCentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a non-central fashion; the values are a sum of a group mean and noise values scaled with a group scaling factor """ - def __init__(self, name,dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): + super().__init__(name,apply_softplus) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb, shape = dim) + mu_params = kwargs.get(f'mu_{name}_params',(0.,2.)) + mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) - # HalfCauchy is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hcauchy') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb, shape = dim) + # HalfStudent is default for sigma + sigma_dist = kwargs.get(f'sigma_{name}_dist','hstudt') + sigma_params = kwargs.get(f'sigma_{name}_params',(2.,)) + sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) # Normal is default for offset offset_dist = kwargs.get(f'offset_{name}_dist','normal') - offset_params = kwargs.get(f'offset_{name}_params',(0.,1.)) - offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, shape = [*pb.batch_effects_size, *dim]) + offset_params = kwargs.get(f'offset_{name}_params',(1.,)) + offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, has_random_effect=True) - self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist) + self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist, dims=pb.batch_effect_dim_names) + def get_samples(self, pb:ParamBuilder): + with pb.model: + samples = self.dist[*list(pb.batch_effect_indices.values())] + if self.apply_softplus: + return pm.math.log(1+pm.math.exp(samples)) + return samples class LinearParameterization(Parameterization): """ A parameterization that can model a linear dependence on X. """ - def __init__(self, name, dim, slope_parameterization, intercept_parameterization, pb, **kwargs): - super().__init__( name, dim) + def __init__(self, name, slope_parameterization, intercept_parameterization,apply_softplus=False,**kwargs): + super().__init__(name, apply_softplus) self.slope_parameterization = slope_parameterization self.intercept_parameterization = intercept_parameterization - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb): with pb.model: - samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) - for be, idx in pb.be_idx_tups: - dot = pytensor.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T - intercept = self.intercept_parameterization.dist[be] - samples = pytensor.tensor.set_subtensor(samples[idx,:],dot+intercept) - return samples - + samples = self.intercept_parameterization.get_samples(pb) + pytensor.tensor.dot(pb.X,self.slope_parameterization.get_samples(pb)) + if self.apply_softplus: + return pm.math.log(1+pm.math.exp(samples)) + return samples def get_design_matrix(X, nm, basis="linear"): if basis == "bspline": @@ -623,7 +621,6 @@ def get_design_matrix(X, nm, basis="linear"): return Phi - def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): n_hidden = configs['nn_hidden_neuron_num'] n_layers = configs['nn_hidden_layers_num'] diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index a5f2419d..33ddf9e0 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -4,6 +4,7 @@ import numpy as np import pickle from matplotlib import pyplot as plt +import arviz as az processing_dir = "HBR_demo/" # replace with a path to your working directory if not os.path.isdir(processing_dir): os.makedirs(processing_dir) @@ -22,19 +23,21 @@ def main(): X_train = (fcon_tr['age']/100).to_numpy(dtype=float) Y_train = fcon_tr[idps].to_numpy(dtype=float) - # fcon_tr.loc[fcon_tr['sitenum'] == 21,'sitenum'] = 13 # fcon_te.loc[fcon_te['sitenum'] == 21,'sitenum'] = 13 +# # configure batch effects for site and sex batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) - # or only site # batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) - print(np.unique(batch_effects_train,axis=0)) # Here we see there are missing sites - + max_sitenum = 15 + tr_idxs = batch_effects_train[:,0] < max_sitenum + X_train = X_train[tr_idxs] + Y_train = Y_train[tr_idxs] + batch_effects_train=batch_effects_train[tr_idxs] with open('X_train.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_train), file) @@ -48,6 +51,11 @@ def main(): Y_test = fcon_te[idps].to_numpy(dtype=float) batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) # batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) + + te_idxs = batch_effects_test[:,0] < max_sitenum + X_test = X_test[te_idxs] + Y_test = Y_test[te_idxs] + batch_effects_test=batch_effects_test[te_idxs] with open('X_test.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_test), file) @@ -78,23 +86,30 @@ def ldpkl(filename: str): os.mkdir(log_dir) outputsuffix = '_estimate' # a string to name the output files, of use only to you, so adapt it for your needs.` - ptk.normative.estimate(covfile=covfile, + nm = ptk.normative.fit(covfile=covfile, respfile=respfile, - tsbefile=tsbefile, trbefile=trbefile, alg='hbr', linear_mu='True', - random_mu='True', random_intercept_mu='True', - random_slope_mu='True', + centered_intercept_mu='False', + random_slope_mu='False', random_sigma='True', log_path=log_dir, - binary=True, + binary='True', + n_samples=1000, + n_tuning=1000, + n_chains=4, + cores=4, + target_accept=0.99, + init='jitter+adapt_diag', + inscaler='standardize', + outscaler='standardize', output_path=output_path, - testcov= testcovfile_path, - testresp = testrespfile_path, outputsuffix=outputsuffix, savemodel=True) + az.plot_trace(nm.hbr.trace) + plt.show() if __name__=="__main__": main() \ No newline at end of file From dba80f58cc5c2ab6a48e095000ec243cb38d32b3 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 14 May 2023 20:16:50 +0200 Subject: [PATCH 12/43] ADapted the gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ff6f1fd0..f041b08a 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,6 @@ pcntoolkit.egg-info # Demo folder HBR_demo - ssh_error_log.txt +HBR_demo/* +dist/pcntoolkit-0.27-py3.11.egg From 6cd8ef97ccadeed4971fb5957707c0479dc2e7eb Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sun, 14 May 2023 22:09:36 +0200 Subject: [PATCH 13/43] Added the new pymc version of SHASH variants --- .gitignore | 1 + pcntoolkit/model/SHASH.py | 277 +++++++++++++++++++++++++++++++++++ pcntoolkit/model/hbr.py | 19 +++ pcntoolkit/util/hbr_utils.py | 68 ++++----- tests/test_hbr_pymc.py | 3 +- 5 files changed, 333 insertions(+), 35 deletions(-) create mode 100644 pcntoolkit/model/SHASH.py diff --git a/.gitignore b/.gitignore index f041b08a..079fb17f 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ HBR_demo ssh_error_log.txt HBR_demo/* dist/pcntoolkit-0.27-py3.11.egg +dist/pcntoolkit-0.27-py3.11.egg diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py new file mode 100644 index 00000000..ba8379d4 --- /dev/null +++ b/pcntoolkit/model/SHASH.py @@ -0,0 +1,277 @@ +import pymc as pm +from pymc import floatX +from pymc.distributions import Continuous + +import pytensor as pt +import pytensor.tensor as ptt +from pytensor.graph.op import Op +from pytensor.graph import Apply +from pytensor.gradient import grad_not_implemented +from pytensor.tensor.math import sinh, arcsinh, tanh, arctanh,cosh, arccosh +from pytensor.tensor.random.basic import normal +from pytensor.tensor.random.op import RandomVariable + + +import numpy as np +import scipy.special as spp +import matplotlib.pyplot as plt + + +""" +@author: Stijn de Boer (AuguB) +See: Jones et al. (2009), Sinh-Arcsinh distributions. +""" + + +class K(Op): + """ + Modified Bessel function of the second kind, pytensor implementation + """ + __props__ = () + + + def make_node(self, p, x): + p = pt.tensor.as_tensor_variable(p, 'floatX') + x = pt.tensor.as_tensor_variable(x, 'floatX') + return Apply(self, [p,x], [p.type()]) + + def perform(self, node, inputs_storage, output_storage): + # Doing this on the unique values avoids doing A LOT OF double work, apparently scipy doesn't do this by itself + unique_inputs, inverse_indices = np.unique(inputs_storage[0], return_inverse=True) + unique_outputs = spp.kv(unique_inputs, inputs_storage[1]) + outputs = unique_outputs[inverse_indices].reshape(inputs_storage[0].shape) + output_storage[0][0] = outputs + + def grad(self, inputs, output_grads): + # Approximation of the derivative. This should suffice for using NUTS + dp = 1e-10 + p = inputs[0] + x = inputs[1] + grad = (self(p+dp,x) - self(p, x))/dp + return [output_grads[0]*grad, grad_not_implemented(0,1,2,3)] + +def S(x, epsilon, delta): + """ + :param epsilon: + :param delta: + :param x: + :return: The sinharcsinh transformation of x + """ + return sinh(arcsinh(x) * delta - epsilon) + +def S_inv( x, epsilon, delta): + return sinh((arcsinh(x) + epsilon) / delta) + +def C( x, epsilon, delta): + """ + :param epsilon: + :param delta: + :param x: + :return: the cosharcsinh transformation of x + Be aware that this is sqrt(1+S(x)^2), so you may save some compute if you can re-use the result from S. + """ + return cosh(arcsinh(x) * delta - epsilon) + +def P(q): + """ + The P function as given in Jones et al. + :param q: + :return: + """ + frac = ptt.exp(1./ 4.) / ptt.power(8. * np.pi, 1./2.) + K1 = K()((q+1)/2,1./4.) + K2 = K()((q-1)/2,1./4.) + a = (K1 + K2) * frac + return a + +def m(epsilon, delta, r): + """ + :param epsilon: + :param delta: + :param r: + :return: The r'th uncentered moment of the SHASH distribution parameterized by epsilon and delta. Given by Jones et al. + The first four moments are given in closed form. + """ + if r == 1: + return sinh(epsilon/delta)*P(1/delta) + elif r==2: + return (cosh(2*epsilon/delta)*P(2/delta)-1)/2 + elif r==3: + return (sinh(3*epsilon/delta)*P(3/delta) - 3*sinh(epsilon/delta)*P(1/delta))/4 + elif r==4: + return (cosh(4*epsilon/delta)*P(4/delta)-4*cosh(2*epsilon/delta)*P(2/delta)+3)/8 + else: + frac1 = ptt.as_tensor_variable(1 / pm.power(2, r)) + acc = ptt.as_tensor_variable(0) + for i in range(r + 1): + combs = spp.comb(r, i) + flip = pm.power(-1, i) + ex = np.exp((r - 2 * i) * self.epsilon / self.delta) + p = self.P((r - 2 * i) / self.delta) + acc += combs * flip * ex * p + return frac1 * acc + + + +class SHASH(RandomVariable): + name = "shash" + ndim_supp = 0 + ndims_params = [0, 0] + dtype = "floatX" + _print_name = ("SHASH", "\\operatorname{SHASH}") + + @classmethod + def rng_fn(cls, rng, epsilon, delta, size=None) -> np.ndarray: + return sinh((arcsinh(rng.normal(loc=0,scale=1, size=size))+epsilon)/delta) + +shash = SHASH() + +class SHASH(Continuous): + + rv_op = shash + """ + SHASH described by Jones et al., based on a standard normal + All SHASH subclasses inherit from this + """ + @classmethod + def dist(cls, epsilon, delta, **kwargs): + epsilon = ptt.as_tensor_variable(floatX(epsilon)) + delta = ptt.as_tensor_variable(floatX(delta)) + return super().dist([epsilon, delta], **kwargs) + + def logp(value, epsilon, delta): + this_S = S(value, epsilon, delta) + this_S_sqr = ptt.sqr(this_S) + this_C_sqr = 1+this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 + frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(value)) / 2 + exp = -this_S_sqr / 2 + return frac1 + frac2 + exp + + + +class SHASHoRV(RandomVariable): + name = "shasho" + ndim_supp = 0 + ndims_params = [0, 0, 0, 0] + dtype = "floatX" + _print_name = ("SHASHo", "\\operatorname{SHASHo}") + + @classmethod + def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: + s = rng.normal(size=size) + return sinh((arcsinh(s)+epsilon)/delta)*sigma + mu + +shasho = SHASHoRV() + +class SHASHo(Continuous): + rv_op = shasho + """ + This is the shash where the location and scale parameters have simply been applied as an linear transformation + directly on the original shash. + """ + @classmethod + def dist(cls, mu, sigma, epsilon, delta, **kwargs): + mu = ptt.as_tensor_variable(floatX(mu)) + sigma= ptt.as_tensor_variable(floatX(sigma)) + epsilon = ptt.as_tensor_variable(floatX(epsilon)) + delta = ptt.as_tensor_variable(floatX(delta)) + return super().dist([mu, sigma, epsilon, delta], **kwargs) + + def logp(value, mu, sigma, epsilon, delta): + print(mu, sigma, epsilon, delta) + remapped_value = (value-mu)/sigma + this_S = S(remapped_value, epsilon, delta) + this_S_sqr = ptt.sqr(this_S) + this_C_sqr = 1+this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 + frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + exp = -this_S_sqr / 2 + return frac1 + frac2 + exp - ptt.log(sigma) + + + + +class SHASHo2RV(RandomVariable): + name = "shasho2" + ndim_supp = 0 + ndims_params = [0, 0, 0, 0] + dtype = "floatX" + _print_name = ("SHASHo2", "\\operatorname{SHASHo2}") + + @classmethod + def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: + s = rng.normal(size=size) + sigma_d = sigma/delta + return sinh((arcsinh(s)+epsilon)/delta)*sigma_d + mu + +shasho2 = SHASHo2RV() + +class SHASHo2(Continuous): + rv_op = shasho2 + """ + This is the shash where we apply the reparameterization provided in section 4.3 in Jones et al. + """ + @classmethod + def dist(cls, mu, sigma, epsilon, delta, **kwargs): + mu = ptt.as_tensor_variable(floatX(mu)) + sigma= ptt.as_tensor_variable(floatX(sigma)) + epsilon = ptt.as_tensor_variable(floatX(epsilon)) + delta = ptt.as_tensor_variable(floatX(delta)) + return super().dist([mu, sigma, epsilon, delta], **kwargs) + + def logp(value, mu, sigma, epsilon, delta): + sigma_d = sigma/delta + remapped_value = (value-mu)/sigma_d + this_S = S(remapped_value, epsilon, delta) + this_S_sqr = ptt.sqr(this_S) + this_C_sqr = 1+this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 + frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + exp = -this_S_sqr / 2 + return frac1 + frac2 + exp - ptt.log(sigma_d) + + + +class SHASHbRV(RandomVariable): + name = "shasho2" + ndim_supp = 0 + ndims_params = [0, 0, 0, 0] + dtype = "floatX" + _print_name = ("SHASHo2", "\\operatorname{SHASHo2}") + + @classmethod + def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: + s = rng.normal(size=size) + mean = (sinh(epsilon/delta)*P(1/delta)) + var = ((cosh(2*epsilon/delta)*P(2/delta)-1)/2)-mean**2 + return ((sinh((arcsinh(s)+epsilon)/delta)-mean)/pm.sqrt(var))*sigma + mu + +shashb = SHASHbRV() + +class SHASHb(Continuous): + rv_op = shashb + """ + This is the shash where the location and scale parameters been applied as an linear transformation on the shash + distribution which was corrected for mean and variance. + """ + + @classmethod + def dist(cls, mu, sigma, epsilon, delta, **kwargs): + mu = ptt.as_tensor_variable(floatX(mu)) + sigma= ptt.as_tensor_variable(floatX(sigma)) + epsilon = ptt.as_tensor_variable(floatX(epsilon)) + delta = ptt.as_tensor_variable(floatX(delta)) + return super().dist([mu, sigma, epsilon, delta], **kwargs) + + def logp(value, mu, sigma, epsilon, delta): + mean = sinh(epsilon/delta)*P(1/delta) + var = (cosh(2*epsilon/delta)*P(2/delta)-1)/2 - pm.math.sqr(mean) + remapped_value = ((value - mu) / sigma) * pm.math.sqrt(var) + mean + this_S = S(remapped_value, epsilon, delta) + this_S_sqr = pm.math.sqr(this_S) + this_C_sqr = 1+this_S_sqr + frac1 = -pm.math.log(ptt.constant(2 * np.pi))/2 + frac2 = pm.math.log(delta) + pm.math.log(this_C_sqr)/2 - pm.math.log(1 + pm.math.sqr(remapped_value)) / 2 + exp = -this_S_sqr / 2 + return frac1 + frac2 + exp + pm.math.log(var)/2 - pm.math.log(sigma) \ No newline at end of file diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index c882ae4a..27067c9d 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -27,6 +27,7 @@ from util.utils import create_poly_basis from util.utils import expand_all from pcntoolkit.util.utils import cartesian_product +from pcntoolkit.model.SHASH import * def bspline_fit(X, order, nknots): feature_num = X.shape[1] @@ -174,6 +175,24 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,), apply_softplus=True).get_samples(pb) y_like = pm.Normal('y_like',mu=mu, sigma=sigma, observed=y) + elif configs['likelihood'] in ['SHASHb','SHASHo','SHASHo2']: + """ + Comment 1 + The current parameterizations are tuned towards standardized in- and output data. + It is possible to adjust the priors through the XXX_dist and XXX_params kwargs, like here we do with epsilon_params. + Supported distributions are listed in the Prior class. + Comment 2 + Any mapping that is applied here after sampling should also be applied in util.hbr_utils.forward in order for the functions there to properly work. + For example, the softplus applied to sigma here is also applied in util.hbr_utils.forward + """ + SHASH_map = {'SHASHb':SHASHb,'SHASHo':SHASHo,'SHASHo2':SHASHo2} + + mu = pb.make_param("mu", slope_mu_params = (0.,3.), mu_intercept_mu_params=(0.,1.), sigma_intercept_mu_params = (2.,)).get_samples(pb) + sigma = pb.make_param("sigma", sigma_params = (1.,2.), slope_sigma_params=(0.,2.), intercept_sigma_params = (0., 2.), apply_softplus=True).get_samples(pb) + epsilon = pb.make_param("epsilon", epsilon_params = (0.,1.), slope_epsilon_params=(0.,1.), intercept_epsilon_params=(0.,1)).get_samples(pb) + delta = pb.make_param("delta", delta_params=(1.5,2.), slope_delta_params=(0.,2), intercept_delta_params=(0., 2), apply_softplus=True).get_samples(pb) + y_like = SHASH_map[configs['likelihood']]('y_like', mu=mu, sigma=sigma, epsilon=epsilon, delta=delta, observed = y) + return model diff --git a/pcntoolkit/util/hbr_utils.py b/pcntoolkit/util/hbr_utils.py index 0d93a8f8..89213100 100644 --- a/pcntoolkit/util/hbr_utils.py +++ b/pcntoolkit/util/hbr_utils.py @@ -8,7 +8,7 @@ import matplotlib.pyplot as plt import pandas as pd import pymc as pm -# from pcntoolkit.model.SHASH import * +from pcntoolkit.model.SHASH import * from pcntoolkit.model.hbr import bspline_transform """ @@ -41,24 +41,24 @@ def get_single_zscores(X, Y, Z, model, sample): def z_score(Y, params, likelihood = "Normal"): """Get the z-scores of Y, given likelihood parameters""" - # if likelihood.startswith('SHASH'): - # mu = params['mu'] - # sigma = params['sigma'] - # epsilon = params['epsilon'] - # delta = params['delta'] - # if likelihood == "SHASHo": - # SHASH = (Y-mu)/sigma - # Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) - # elif likelihood == "SHASHo2": - # sigma_d = sigma/delta - # SHASH = (Y-mu)/sigma_d - # Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) - # elif likelihood == "SHASHb": - # true_mu = m(epsilon, delta, 1) - # true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) - # SHASH_c = ((Y-mu)/sigma) - # SHASH = SHASH_c * true_sigma + true_mu - # Z = np.sinh(np.arcsinh(SHASH) * delta - epsilon) + if likelihood.startswith('SHASH'): + mu = params['mu'] + sigma = params['sigma'] + epsilon = params['epsilon'] + delta = params['delta'] + if likelihood == "SHASHo": + SHASH = (Y-mu)/sigma + Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + elif likelihood == "SHASHo2": + sigma_d = sigma/delta + SHASH = (Y-mu)/sigma_d + Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + elif likelihood == "SHASHb": + true_mu = m(epsilon, delta, 1) + true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + SHASH_c = ((Y-mu)/sigma) + SHASH = SHASH_c * true_sigma + true_mu + Z = np.sinh(np.arcsinh(SHASH) * delta - epsilon) if likelihood == 'Normal': Z = (Y-params['mu'])/params['sigma'] else: @@ -98,21 +98,21 @@ def get_single_quantiles(synthetic_X, z_scores, model, be, sample): def quantile(zs, params, likelihood = "Normal"): """Get the zs'th quantiles given likelihood parameters""" - # if likelihood.startswith('SHASH'): - # mu = params['mu'] - # sigma = params['sigma'] - # epsilon = params['epsilon'] - # delta = params['delta'] - # if likelihood == "SHASHo": - # quantiles = S_inv(zs,epsilon,delta)*sigma + mu - # elif likelihood == "SHASHo2": - # sigma_d = sigma/delta - # quantiles = S_inv(zs,epsilon,delta)*sigma_d + mu - # elif likelihood == "SHASHb": - # true_mu = m(epsilon, delta, 1) - # true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) - # SHASH_c = ((S_inv(zs,epsilon,delta)-true_mu)/true_sigma) - # quantiles = SHASH_c *sigma + mu + if likelihood.startswith('SHASH'): + mu = params['mu'] + sigma = params['sigma'] + epsilon = params['epsilon'] + delta = params['delta'] + if likelihood == "SHASHo": + quantiles = S_inv(zs,epsilon,delta)*sigma + mu + elif likelihood == "SHASHo2": + sigma_d = sigma/delta + quantiles = S_inv(zs,epsilon,delta)*sigma_d + mu + elif likelihood == "SHASHb": + true_mu = m(epsilon, delta, 1) + true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + SHASH_c = ((S_inv(zs,epsilon,delta)-true_mu)/true_sigma) + quantiles = SHASH_c *sigma + mu if likelihood == 'Normal': quantiles = zs*params['sigma'] + params['mu'] else: diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index 33ddf9e0..e50105ac 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -90,9 +90,10 @@ def ldpkl(filename: str): respfile=respfile, trbefile=trbefile, alg='hbr', + likelihood='SHASHb', linear_mu='True', random_intercept_mu='True', - centered_intercept_mu='False', + centered_intercept_mu='True', random_slope_mu='False', random_sigma='True', log_path=log_dir, From 1dd378309fe816221ad055a46cc12737cd11e7b3 Mon Sep 17 00:00:00 2001 From: Seyed Mostafa Kia Date: Mon, 15 May 2023 11:36:08 +0200 Subject: [PATCH 14/43] Bug resolved. --- tests/testHBR.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testHBR.py b/tests/testHBR.py index 6b98ed9f..8db809dd 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -62,7 +62,7 @@ for j in range(n_grps): plt.scatter(temp_X[temp_be==j,], temp_Y[temp_be==j,], label='Group' + str(j)) - plt.plot(temp_X[temp_be==j,], temp_yhat[[temp_be==j,]]) + plt.plot(temp_X[temp_be==j,], temp_yhat[temp_be==j,]) plt.fill_between(temp_X[temp_be==j,], temp_yhat[temp_be==j,] - 1.96 * np.sqrt(temp_s2[temp_be==j,]), temp_yhat[temp_be==j,] + From 22034e7612c61b0f2cacdcd7ea3547e93840900b Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 15 May 2023 20:15:09 +0200 Subject: [PATCH 15/43] Making small adaptations towards new pymc --- dist/pcntoolkit-0.27-py3.11.egg | Bin 286786 -> 286072 bytes pcntoolkit/model/hbr.py | 176 +++++++++++++++++--------------- 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/dist/pcntoolkit-0.27-py3.11.egg b/dist/pcntoolkit-0.27-py3.11.egg index 7ba241b2c7ffa993beb41a9c974a8a29c781b717..0fbe2f210fbafaacb33849bf8717bc8149c415de 100644 GIT binary patch delta 96759 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
b#NwwJb$WpsusSS5AmRi=* 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

%}HU-Co$_JGX@)Cq4Qq93yX5yMR8?VR*^ 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

8L@8rhLGxlL)_H7B47>P_bRGeB$H$#ZrxX_}< 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 diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 02d4d83f..53907629 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -15,6 +15,7 @@ import numpy as np import pymc as pm import pytensor +import arviz as az from itertools import product from functools import reduce @@ -24,9 +25,8 @@ import bspline from bspline import splinelab -from util.utils import create_poly_basis -from util.utils import expand_all -from pcntoolkit.util.utils import cartesian_product +from pcntoolkit.util.utils import expand_all, cartesian_product + def bspline_fit(X, order, nknots): feature_num = X.shape[1] @@ -70,10 +70,8 @@ def create_poly_basis(X, order): for d in range(1, order + 1): Phi[:, colid] = X ** d colid += D - return Phi - def from_posterior(param, samples, distribution=None, half=False, freedom=1): if len(samples.shape) > 1: shape = samples.shape[1:] @@ -147,14 +145,14 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) -def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): +def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures :param batch_effects: [N×M] array of batch effects :param batch_effects_size: [b1, b2,...,bM] List of counts of unique values of batch effects :param configs: - :param trace: + :param idata: :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ @@ -166,7 +164,7 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): with pm.Model() as model: # Make a param builder that will make the correct calls - pb = ParamBuilder(model, X, y, batch_effects, trace, configs) + pb = ParamBuilder(model, X, y, batch_effects, idata, configs) if configs['likelihood'] == 'Normal': mu = pb.make_param("mu", mu_slope_mu_params = (0.,10.), @@ -189,7 +187,7 @@ class HBR: Basic usage:: model = HBR(configs) - trace = model.estimate(X, y, batch_effects) + idata = model.estimate(X, y, batch_effects) ys,s2 = model.predict(X, batch_effects) where the variables are @@ -277,13 +275,16 @@ def estimate(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) X = self.transform_X(X) modeler = self.get_modeler() + print(X) + print(y) + print(batch_effects) with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: - self.trace = pm.sample(draws=self.configs['n_samples'], + self.idata = pm.sample(draws=self.configs['n_samples'], tune=self.configs['n_tuning'], chains=self.configs['n_chains'], init=self.configs['init'], n_init=500000, cores=self.configs['cores']) - return self.trace + return self.idata def predict(self, X, batch_effects, pred='single'): """ Function to make predictions from the model """ @@ -296,7 +297,7 @@ def predict(self, X, batch_effects, pred='single'): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) @@ -314,14 +315,14 @@ def estimate_on_new_site(self, X, y, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, - self.configs, trace=self.trace) as m: - self.trace = pm.sample(self.configs['n_samples'], + self.configs, idata=self.idata) as m: + self.idata = pm.sample(self.configs['n_samples'], tune=self.configs['n_tuning'], chains=self.configs['n_chains'], target_accept=self.configs['target_accept'], init=self.configs['init'], n_init=50000, cores=self.configs['cores']) - return self.trace + return self.idata def predict_on_new_site(self, X, batch_effects): """ Function to make predictions from the model """ @@ -332,8 +333,10 @@ def predict_on_new_site(self, X, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, trace=self.trace): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): + # TODO need to adapt this to new pymc + + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) @@ -348,7 +351,8 @@ def generate(self, X, batch_effects, samples): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + # TODO need to adapt this to new pymc + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) generated_samples = np.reshape(ppc.posterior_predictive['y_like'].squeeze().T, [X.shape[0] * samples, 1]) X = np.repeat(X, samples) @@ -360,7 +364,7 @@ def generate(self, X, batch_effects, samples): return X, batch_effects, generated_samples - def sample_prior_predictive(self, X, batch_effects, samples, trace=None): + def sample_prior_predictive(self, X, batch_effects, samples, idata=None): """ Function to sample from prior predictive distribution """ if len(X.shape) == 1: @@ -377,7 +381,9 @@ def sample_prior_predictive(self, X, batch_effects, samples, trace=None): if self.model_type == 'linear': with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, - trace): + idata): + # TODO need to adapt this to new pymc + ppc = pm.sample_prior_predictive(samples=samples) return ppc @@ -390,7 +396,7 @@ def get_model(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) modeler = self.get_modeler() X = self.transform_X(X) - return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.trace) + return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.idata) def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): @@ -408,46 +414,6 @@ def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): return X_dummy, batch_effects_dummy -class Prior: - """ - A wrapper class for a PyMC distribution. - - creates a fitted distribution from the trace, if one is present - - overloads the __getitem__ function with something that switches between indexing or not, based on the shape - """ - def __init__(self, name, dist, params, pb, shape=(1,)) -> None: - self.dist = None - self.name = name - self.shape = shape - self.has_random_effect = True if len(shape)>1 else False - self.distmap = {'normal': pm.Normal, - 'hnormal': pm.HalfNormal, - 'gamma': pm.Gamma, - 'uniform': pm.Uniform, - 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy} - self.make_dist(dist, params, pb) - - def make_dist(self, dist, params, pb): - """This creates a pymc distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" - with pb.model as m: - if (pb.trace is not None) and (not self.has_random_effect): - self.dist = from_posterior(param=self.name, - samples=pb.trace[self.name], - distribution=dist, - freedom=pb.configs['freedom']) - else: - print(f"{self.name} \tdist = {dist}") - self.dist = self.distmap[dist](self.name, *params, shape=self.shape) - - def __getitem__(self, idx): - """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" - assert self.dist is not None, "Distribution not initialized" - if self.has_random_effect: - return self.dist[idx] - else: - return self.dist[tuple([0]*len(self.shape))] - - class ParamBuilder: """ A class that simplifies the construction of parameterizations. @@ -455,21 +421,21 @@ class ParamBuilder: It also contains a lot of decision logic for creating the parameterizations. """ - def __init__(self, model, X, y, batch_effects, trace, configs): + def __init__(self, model, X, y, batch_effects, idata, configs): """ :param model: model to attach all the distributions to :param X: Covariates :param y: IDPs :param batch_effects: array of batch effects - :param trace: idem + :param idata: idem :param configs: idem """ self.model = model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) - self.trace = trace + self.idata:az.InferenceData = idata self.configs = configs self.feature_num = X.shape[1].eval().item() @@ -517,6 +483,46 @@ def make_param(self, name, dim = (1,), **kwargs): else: return FixedParameterization(name=name, dim=dim, pb=self,**kwargs) +class Prior: + """ + A wrapper class for a PyMC distribution. + - creates a fitted distribution from the idata, if one is present + - overloads the __getitem__ function with something that switches between indexing or not, based on the shape + """ + def __init__(self, name, dist, params, pb, shape=(1,)) -> None: + self.dist = None + self.name = name + self.shape = shape + self.has_random_effect = True if len(shape)>1 else False + self.distmap = {'normal': pm.Normal, + 'hnormal': pm.HalfNormal, + 'gamma': pm.Gamma, + 'uniform': pm.Uniform, + 'igamma': pm.InverseGamma, + 'hcauchy': pm.HalfCauchy} + self.make_dist(dist, params, pb) + + def make_dist(self, dist, params, pb:ParamBuilder): + """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" + with pb.model as m: + if (pb.idata is not None) and (not self.has_random_effect): + self.dist = from_posterior(param=self.name, + samples=pb.idata.posterior[self.name], + distribution=dist, + freedom=pb.configs['freedom']) + else: + print(f"{self.name} \tdist = {dist}") + self.dist = self.distmap[dist](self.name, *params, shape=self.shape) + + def __getitem__(self, idx): + """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" + assert self.dist is not None, "Distribution not initialized" + if self.has_random_effect: + return self.dist[idx] + else: + return self.dist[tuple([0]*len(self.shape))] + + class Parameterization: """ @@ -624,7 +630,7 @@ def get_design_matrix(X, nm, basis="linear"): -def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): +def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): n_hidden = configs['nn_hidden_neuron_num'] n_layers = configs['nn_hidden_layers_num'] feature_num = X.shape[1] @@ -660,29 +666,29 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): X = pm.Data('X', X) y = pm.Data('y', y) - if trace is not None: # Used when estimating/predicting on a new site - weights_in_1_grp = from_posterior('w_in_1_grp', trace['w_in_1_grp'], + if idata is not None: # Used when estimating/predicting on a new site + weights_in_1_grp = from_posterior('w_in_1_grp', idata['w_in_1_grp'], distribution='normal', freedom=configs['freedom']) - weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', trace['w_in_1_grp_sd'], + weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', idata['w_in_1_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) if n_layers == 2: - weights_1_2_grp = from_posterior('w_1_2_grp', trace['w_1_2_grp'], + weights_1_2_grp = from_posterior('w_1_2_grp', idata['w_1_2_grp'], distribution='normal', freedom=configs['freedom']) - weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', trace['w_1_2_grp_sd'], + weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', idata['w_1_2_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) - weights_2_out_grp = from_posterior('w_2_out_grp', trace['w_2_out_grp'], + weights_2_out_grp = from_posterior('w_2_out_grp', idata['w_2_out_grp'], distribution='normal', freedom=configs['freedom']) - weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', trace['w_2_out_grp_sd'], + weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', idata['w_2_out_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) - mu_prior_intercept = from_posterior('mu_prior_intercept', trace['mu_prior_intercept'], + mu_prior_intercept = from_posterior('mu_prior_intercept', idata['mu_prior_intercept'], distribution='normal', freedom=configs['freedom']) - sigma_prior_intercept = from_posterior('sigma_prior_intercept', trace['sigma_prior_intercept'], + sigma_prior_intercept = from_posterior('sigma_prior_intercept', idata['sigma_prior_intercept'], distribution='hcauchy', freedom=configs['freedom']) else: @@ -758,30 +764,30 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): # If we want to estimate varying noise terms across groups: if configs['random_noise']: if configs['hetero_noise']: - if trace is not None: # # Used when estimating/predicting on a new site + if idata is not None: # # Used when estimating/predicting on a new site weights_in_1_grp_noise = from_posterior('w_in_1_grp_noise', - trace['w_in_1_grp_noise'], + idata['w_in_1_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_in_1_grp_sd_noise = from_posterior('w_in_1_grp_sd_noise', - trace['w_in_1_grp_sd_noise'], + idata['w_in_1_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) if n_layers == 2: weights_1_2_grp_noise = from_posterior('w_1_2_grp_noise', - trace['w_1_2_grp_noise'], + idata['w_1_2_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_1_2_grp_sd_noise = from_posterior('w_1_2_grp_sd_noise', - trace['w_1_2_grp_sd_noise'], + idata['w_1_2_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) weights_2_out_grp_noise = from_posterior('w_2_out_grp_noise', - trace['w_2_out_grp_noise'], + idata['w_2_out_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_2_out_grp_sd_noise = from_posterior('w_2_out_grp_sd_noise', - trace['w_2_out_grp_sd_noise'], + idata['w_2_out_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) else: @@ -850,8 +856,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: - if trace is not None: # Used for transferring the priors - upper_bound = np.percentile(trace['sigma_noise'], 95) + if idata is not None: # Used for transferring the priors + upper_bound = np.percentile(idata['sigma_noise'], 95) sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound, shape=(batch_effects_size)) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) @@ -866,8 +872,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) else: # do not allow for random noise terms across groups: - if trace is not None: # Used for transferring the priors - upper_bound = np.percentile(trace['sigma_noise'], 95) + if idata is not None: # Used for transferring the priors + upper_bound = np.percentile(idata['sigma_noise'], 95) sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) From f3d3b555ce2475ec26d70e44f8ed7aae4c549167 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 15 May 2023 20:17:51 +0200 Subject: [PATCH 16/43] Merge branch 'dev_pymc_normal' into dev_pymc_coords --- pcntoolkit/model/hbr.py | 133 +++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 43 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index c882ae4a..eaf6f888 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -15,6 +15,7 @@ import numpy as np import pymc as pm import pytensor +import arviz as az from itertools import product from functools import reduce @@ -24,9 +25,8 @@ import bspline from bspline import splinelab -from util.utils import create_poly_basis -from util.utils import expand_all -from pcntoolkit.util.utils import cartesian_product +from pcntoolkit.util.utils import expand_all, cartesian_product + def bspline_fit(X, order, nknots): feature_num = X.shape[1] @@ -70,10 +70,8 @@ def create_poly_basis(X, order): for d in range(1, order + 1): Phi[:, colid] = X ** d colid += D - return Phi - def from_posterior(param, samples, distribution=None, half=False, freedom=1): if len(samples.shape) > 1: shape = samples.shape[1:] @@ -147,14 +145,14 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) -def hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): +def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures :param batch_effects: [N×M] array of batch effects :param batch_effects_size: [b1, b2,...,bM] List of counts of unique values of batch effects :param configs: - :param trace: + :param idata: :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ @@ -185,7 +183,7 @@ class HBR: Basic usage:: model = HBR(configs) - trace = model.estimate(X, y, batch_effects) + idata = model.estimate(X, y, batch_effects) ys,s2 = model.predict(X, batch_effects) where the variables are @@ -273,13 +271,16 @@ def estimate(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) X = self.transform_X(X) modeler = self.get_modeler() + print(X) + print(y) + print(batch_effects) with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: - self.trace = pm.sample(draws=self.configs['n_samples'], + self.idata = pm.sample(draws=self.configs['n_samples'], tune=self.configs['n_tuning'], chains=self.configs['n_chains'], init=self.configs['init'], n_init=500000, cores=self.configs['cores']) - return self.trace + return self.idata def predict(self, X, batch_effects, pred='single'): """ Function to make predictions from the model """ @@ -292,7 +293,7 @@ def predict(self, X, batch_effects, pred='single'): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) @@ -310,14 +311,14 @@ def estimate_on_new_site(self, X, y, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, - self.configs, trace=self.trace) as m: - self.trace = pm.sample(self.configs['n_samples'], + self.configs, idata=self.idata) as m: + self.idata = pm.sample(self.configs['n_samples'], tune=self.configs['n_tuning'], chains=self.configs['n_chains'], target_accept=self.configs['target_accept'], init=self.configs['init'], n_init=50000, cores=self.configs['cores']) - return self.trace + return self.idata def predict_on_new_site(self, X, batch_effects): """ Function to make predictions from the model """ @@ -328,8 +329,10 @@ def predict_on_new_site(self, X, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, trace=self.trace): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): + # TODO need to adapt this to new pymc + + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) @@ -344,7 +347,8 @@ def generate(self, X, batch_effects, samples): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.trace, progressbar=True) + # TODO need to adapt this to new pymc + ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) generated_samples = np.reshape(ppc.posterior_predictive['y_like'].squeeze().T, [X.shape[0] * samples, 1]) X = np.repeat(X, samples) @@ -356,7 +360,7 @@ def generate(self, X, batch_effects, samples): return X, batch_effects, generated_samples - def sample_prior_predictive(self, X, batch_effects, samples, trace=None): + def sample_prior_predictive(self, X, batch_effects, samples, idata=None): """ Function to sample from prior predictive distribution """ if len(X.shape) == 1: @@ -373,7 +377,9 @@ def sample_prior_predictive(self, X, batch_effects, samples, trace=None): if self.model_type == 'linear': with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, - trace): + idata): + # TODO need to adapt this to new pymc + ppc = pm.sample_prior_predictive(samples=samples) return ppc @@ -386,7 +392,7 @@ def get_model(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) modeler = self.get_modeler() X = self.transform_X(X) - return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.trace) + return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.idata) def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): @@ -460,14 +466,14 @@ def __init__(self, X, y, batch_effects, trace, configs): :param X: Covariates :param y: IDPs :param batch_effects: array of batch effects - :param trace: idem + :param idata: idem :param configs: idem """ self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) - self.trace = trace + self.idata:az.InferenceData = idata self.configs = configs self.y_shape = y.shape.eval() @@ -505,6 +511,46 @@ def make_param(self, name, **kwargs): else: return FixedParameterization(name=name, pb=self,**kwargs) +class Prior: + """ + A wrapper class for a PyMC distribution. + - creates a fitted distribution from the idata, if one is present + - overloads the __getitem__ function with something that switches between indexing or not, based on the shape + """ + def __init__(self, name, dist, params, pb, shape=(1,)) -> None: + self.dist = None + self.name = name + self.shape = shape + self.has_random_effect = True if len(shape)>1 else False + self.distmap = {'normal': pm.Normal, + 'hnormal': pm.HalfNormal, + 'gamma': pm.Gamma, + 'uniform': pm.Uniform, + 'igamma': pm.InverseGamma, + 'hcauchy': pm.HalfCauchy} + self.make_dist(dist, params, pb) + + def make_dist(self, dist, params, pb:ParamBuilder): + """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" + with pb.model as m: + if (pb.idata is not None) and (not self.has_random_effect): + self.dist = from_posterior(param=self.name, + samples=pb.idata.posterior[self.name], + distribution=dist, + freedom=pb.configs['freedom']) + else: + print(f"{self.name} \tdist = {dist}") + self.dist = self.distmap[dist](self.name, *params, shape=self.shape) + + def __getitem__(self, idx): + """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" + assert self.dist is not None, "Distribution not initialized" + if self.has_random_effect: + return self.dist[idx] + else: + return self.dist[tuple([0]*len(self.shape))] + + class Parameterization: """ @@ -621,7 +667,8 @@ def get_design_matrix(X, nm, basis="linear"): return Phi -def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): + +def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): n_hidden = configs['nn_hidden_neuron_num'] n_layers = configs['nn_hidden_layers_num'] feature_num = X.shape[1] @@ -657,29 +704,29 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): X = pm.Data('X', X) y = pm.Data('y', y) - if trace is not None: # Used when estimating/predicting on a new site - weights_in_1_grp = from_posterior('w_in_1_grp', trace['w_in_1_grp'], + if idata is not None: # Used when estimating/predicting on a new site + weights_in_1_grp = from_posterior('w_in_1_grp', idata['w_in_1_grp'], distribution='normal', freedom=configs['freedom']) - weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', trace['w_in_1_grp_sd'], + weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', idata['w_in_1_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) if n_layers == 2: - weights_1_2_grp = from_posterior('w_1_2_grp', trace['w_1_2_grp'], + weights_1_2_grp = from_posterior('w_1_2_grp', idata['w_1_2_grp'], distribution='normal', freedom=configs['freedom']) - weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', trace['w_1_2_grp_sd'], + weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', idata['w_1_2_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) - weights_2_out_grp = from_posterior('w_2_out_grp', trace['w_2_out_grp'], + weights_2_out_grp = from_posterior('w_2_out_grp', idata['w_2_out_grp'], distribution='normal', freedom=configs['freedom']) - weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', trace['w_2_out_grp_sd'], + weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', idata['w_2_out_grp_sd'], distribution='hcauchy', freedom=configs['freedom']) - mu_prior_intercept = from_posterior('mu_prior_intercept', trace['mu_prior_intercept'], + mu_prior_intercept = from_posterior('mu_prior_intercept', idata['mu_prior_intercept'], distribution='normal', freedom=configs['freedom']) - sigma_prior_intercept = from_posterior('sigma_prior_intercept', trace['sigma_prior_intercept'], + sigma_prior_intercept = from_posterior('sigma_prior_intercept', idata['sigma_prior_intercept'], distribution='hcauchy', freedom=configs['freedom']) else: @@ -755,30 +802,30 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): # If we want to estimate varying noise terms across groups: if configs['random_noise']: if configs['hetero_noise']: - if trace is not None: # # Used when estimating/predicting on a new site + if idata is not None: # # Used when estimating/predicting on a new site weights_in_1_grp_noise = from_posterior('w_in_1_grp_noise', - trace['w_in_1_grp_noise'], + idata['w_in_1_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_in_1_grp_sd_noise = from_posterior('w_in_1_grp_sd_noise', - trace['w_in_1_grp_sd_noise'], + idata['w_in_1_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) if n_layers == 2: weights_1_2_grp_noise = from_posterior('w_1_2_grp_noise', - trace['w_1_2_grp_noise'], + idata['w_1_2_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_1_2_grp_sd_noise = from_posterior('w_1_2_grp_sd_noise', - trace['w_1_2_grp_sd_noise'], + idata['w_1_2_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) weights_2_out_grp_noise = from_posterior('w_2_out_grp_noise', - trace['w_2_out_grp_noise'], + idata['w_2_out_grp_noise'], distribution='normal', freedom=configs['freedom']) weights_2_out_grp_sd_noise = from_posterior('w_2_out_grp_sd_noise', - trace['w_2_out_grp_sd_noise'], + idata['w_2_out_grp_sd_noise'], distribution='hcauchy', freedom=configs['freedom']) else: @@ -847,8 +894,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: - if trace is not None: # Used for transferring the priors - upper_bound = np.percentile(trace['sigma_noise'], 95) + if idata is not None: # Used for transferring the priors + upper_bound = np.percentile(idata['sigma_noise'], 95) sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound, shape=(batch_effects_size)) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) @@ -863,8 +910,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, trace=None): sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) else: # do not allow for random noise terms across groups: - if trace is not None: # Used for transferring the priors - upper_bound = np.percentile(trace['sigma_noise'], 95) + if idata is not None: # Used for transferring the priors + upper_bound = np.percentile(idata['sigma_noise'], 95) sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) else: sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) From c7850decf3619f4718702a6abd8f53d157c2d670 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Tue, 16 May 2023 20:44:26 +0200 Subject: [PATCH 17/43] Latest attempt to make hbr work with pymc5 --- pcntoolkit/model/hbr.py | 194 +++++++++++++++++----------------------- tests/testHBR.py | 11 +-- tests/test_hbr_pymc.py | 117 +++++++++++------------- 3 files changed, 143 insertions(+), 179 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index eaf6f888..af412dfe 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -9,6 +9,8 @@ from __future__ import print_function from __future__ import division +from collections import OrderedDict + from ast import Param from tkinter.font import names @@ -143,8 +145,7 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit) else: return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) - - + def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ :param X: [N×P] array of clinical covariates @@ -160,17 +161,22 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): X = pytensor.tensor.cast(X,'floatX') y = pytensor.shared(y) y = pytensor.tensor.cast(y,'floatX') + # Make a param builder that will make the correct calls - pb = ParamBuilder(X, y, batch_effects, trace, configs) + pb = ParamBuilder(X, y, batch_effects, idata, configs) with pm.Model(coords=pb.coords) as model: pb.model=model + pb.batch_effect_indices = [pm.Data(pb.batch_effect_dim_names[i], pb.batch_effect_indices[i],mutable=True) for i in range(len(pb.batch_effect_indices))] if configs['likelihood'] == 'Normal': - mu = pb.make_param("mu", mu_slope_mu_params = (0.,2.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) - sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,), apply_softplus=True).get_samples(pb) - y_like = pm.Normal('y_like',mu=mu, sigma=sigma, observed=y) + mu = pb.make_param("mu", mu_slope_mu_params = (0.,5.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) + # print(f"{mu.shape.eval()=}") + sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,)).get_samples(pb) + # print(f"{sigma.shape.eval()=}") + sigma_plus = pm.Deterministic('sigma_plus', pm.math.log(1+pm.math.exp(sigma))) + y_like = pm.Normal('y_like', mu, sigma=sigma_plus, observed=y) return model @@ -271,9 +277,6 @@ def estimate(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) X = self.transform_X(X) modeler = self.get_modeler() - print(X) - print(y) - print(batch_effects) with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: self.idata = pm.sample(draws=self.configs['n_samples'], tune=self.configs['n_tuning'], @@ -293,9 +296,9 @@ def predict(self, X, batch_effects, pred='single'): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) + self.idata = pm.sample_posterior_predictive(trace = self.idata, progressbar=True) + pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -329,12 +332,12 @@ def predict_on_new_site(self, X, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): - # TODO need to adapt this to new pymc - ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) + + with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): + self.idata = pm.sample_posterior_predictive(self.idata, extend_inferencedata=True, progressbar=True) + pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -378,10 +381,8 @@ def sample_prior_predictive(self, X, batch_effects, samples, idata=None): if self.model_type == 'linear': with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, idata): - # TODO need to adapt this to new pymc - - ppc = pm.sample_prior_predictive(samples=samples) - return ppc + idata = pm.sample_prior_predictive(samples=samples) + return idata def get_model(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) @@ -413,7 +414,7 @@ def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): class Prior: """ A wrapper class for a PyMC distribution. - - creates a fitted distribution from the trace, if one is present + - creates a fitted distribution from the idata, if one is present - overloads the __getitem__ function with something that switches between indexing or not, based on the shape """ def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: @@ -431,17 +432,25 @@ def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: self.make_dist(dist, params, pb) def make_dist(self, dist, params, pb): - """This creates a pymc distribution. If there is a trace, the distribution is fitted to the trace. If there isn't a trace, the prior is parameterized by the values in (params)""" + """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" with pb.model as m: - if (pb.trace is not None): + # If self.name.startswith slope: + # use the basis functions dim + # Otherwise, use the empty dim + dim = 'basis_functions' if (self.name.startswith('slope') or self.name.startswith('offset_slope')) else 'empty' + + if (pb.idata is not None): + samples = az.extract(pb.idata, var_names=self.name).to_numpy() + if not self.has_random_effect: + samples = np.reshape(samples, (-1,)) self.dist = from_posterior(param=self.name, - samples=pb.trace[self.name], + samples = samples, distribution=dist, freedom=pb.configs['freedom']) elif self.has_random_effect: - self.dist = self.distmap[dist](self.name, *params, dims=pb.batch_effect_dim_names) + self.dist = self.distmap[dist](self.name, *params, dims=[*pb.batch_effect_dim_names, dim]) else: - self.dist = self.distmap[dist](self.name, *params) + self.dist = self.distmap[dist](self.name, *params, dims=dim) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" @@ -451,7 +460,6 @@ def __getitem__(self, idx): else: return self.dist - class ParamBuilder: """ A class that simplifies the construction of parameterizations. @@ -459,7 +467,7 @@ class ParamBuilder: It also contains a lot of decision logic for creating the parameterizations. """ - def __init__(self, X, y, batch_effects, trace, configs): + def __init__(self, X, y, batch_effects, idata, configs): """ :param model: model to attach all the distributions to @@ -481,18 +489,20 @@ def __init__(self, X, y, batch_effects, trace, configs): self.batch_effects_num = batch_effects.shape[1] self.batch_effect_dim_names = [] - self.batch_effect_values = {} - self.batch_effect_indices = {} - self.coords = {} + self.batch_effect_indices = [] + self.coords = OrderedDict() for i in range(self.batch_effects_num): batch_effect_dim_name = f"batch_effect_{i}" self.batch_effect_dim_names.append(batch_effect_dim_name) this_be_values, this_be_indices = np.unique(self.batch_effects[:,i], return_inverse=True) self.coords[batch_effect_dim_name] = this_be_values - self.batch_effect_values[batch_effect_dim_name] = this_be_values - self.batch_effect_indices[batch_effect_dim_name] = this_be_indices + self.batch_effect_indices.append(this_be_indices) + self.coords['empty']=[0] + self.coords['basis_functions'] = [i for i in range(X.shape.eval()[1])] + + # TODO reinstigate the 'dim' keyword, for the slope parameters def make_param(self, name, **kwargs): if self.configs.get(f'linear_{name}', False): # First make a slope and intercept, and use those to make a linear parameterization @@ -507,48 +517,9 @@ def make_param(self, name, **kwargs): if self.configs.get(f'centered_{name}', True): return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return NonCentralRandomFixedParameterization(name=name, pb=self,**kwargs) - else: - return FixedParameterization(name=name, pb=self,**kwargs) - -class Prior: - """ - A wrapper class for a PyMC distribution. - - creates a fitted distribution from the idata, if one is present - - overloads the __getitem__ function with something that switches between indexing or not, based on the shape - """ - def __init__(self, name, dist, params, pb, shape=(1,)) -> None: - self.dist = None - self.name = name - self.shape = shape - self.has_random_effect = True if len(shape)>1 else False - self.distmap = {'normal': pm.Normal, - 'hnormal': pm.HalfNormal, - 'gamma': pm.Gamma, - 'uniform': pm.Uniform, - 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy} - self.make_dist(dist, params, pb) - - def make_dist(self, dist, params, pb:ParamBuilder): - """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" - with pb.model as m: - if (pb.idata is not None) and (not self.has_random_effect): - self.dist = from_posterior(param=self.name, - samples=pb.idata.posterior[self.name], - distribution=dist, - freedom=pb.configs['freedom']) - else: - print(f"{self.name} \tdist = {dist}") - self.dist = self.distmap[dist](self.name, *params, shape=self.shape) - - def __getitem__(self, idx): - """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" - assert self.dist is not None, "Distribution not initialized" - if self.has_random_effect: - return self.dist[idx] + return NonCentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return self.dist[tuple([0]*len(self.shape))] + return FixedParameterization(name=name, pb=self, **kwargs) @@ -556,9 +527,8 @@ class Parameterization: """ This is the top-level parameterization class from which all the other parameterizations inherit. """ - def __init__(self, name, apply_softplus=False): + def __init__(self, name): self.name = name - self.apply_softplus=apply_softplus print(name, type(self)) def get_samples(self, pb): @@ -568,93 +538,96 @@ class FixedParameterization(Parameterization): """ A parameterization that takes a single value for all input. It does not depend on anything except its hyperparameters """ - def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): - super().__init__(name,apply_softplus) + def __init__(self, name, pb:ParamBuilder,**kwargs): + super().__init__(name) dist = kwargs.get(f'{name}_dist','normal') - params = kwargs.get(f'{name}_params',(0.,2.)) + params = kwargs.get(f'{name}_params',(0.,1.)) self.dist = Prior(name, dist, params, pb) def get_samples(self, pb): with pb.model: - samples = self.dist[0] - if self.apply_softplus: - return pm.math.log(1+pm.math.exp(samples)) - return samples + return self.dist[0] class CentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a central fashion; the values are sampled from normal distribution with a group mean and group variance """ - def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): - super().__init__(name,apply_softplus) + def __init__(self, name, pb:ParamBuilder, **kwargs): + super().__init__(name) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,2.)) + mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) # HalfStudent is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hstudt') - sigma_params = kwargs.get(f'sigma_{name}_params',(2.,)) + sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') + sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) - self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=pb.batch_effect_dim_names) - + dim = 'basis_functions' if self.name.startswith('slope') else 'empty' + self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=[*pb.batch_effect_dim_names,dim]) + def get_samples(self, pb:ParamBuilder): with pb.model: - samples = self.dist[*list(pb.batch_effect_indices.values())] - if self.apply_softplus: - return pm.math.log(1+pm.math.exp(samples)) + samples = self.dist[*pb.batch_effect_indices] return samples - class NonCentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a non-central fashion; the values are a sum of a group mean and noise values scaled with a group scaling factor """ - def __init__(self, name, pb:ParamBuilder, apply_softplus = False, **kwargs): - super().__init__(name,apply_softplus) + def __init__(self, name, pb:ParamBuilder, **kwargs): + super().__init__(name) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,2.)) + mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) # HalfStudent is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hstudt') - sigma_params = kwargs.get(f'sigma_{name}_params',(2.,)) + sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') + sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) # Normal is default for offset offset_dist = kwargs.get(f'offset_{name}_dist','normal') - offset_params = kwargs.get(f'offset_{name}_params',(1.,)) + offset_params = kwargs.get(f'offset_{name}_params',(0.,1.)) offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, has_random_effect=True) - self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist, dims=pb.batch_effect_dim_names) + dim = 'basis_functions' if self.name.startswith('slope') else 'empty' + self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist,dims=[*pb.batch_effect_dim_names,dim]) + def get_samples(self, pb:ParamBuilder): with pb.model: - samples = self.dist[*list(pb.batch_effect_indices.values())] - if self.apply_softplus: - return pm.math.log(1+pm.math.exp(samples)) + # print(f"{self.dist.shape.eval()=}") + samples = self.dist[*pb.batch_effect_indices] return samples class LinearParameterization(Parameterization): """ A parameterization that can model a linear dependence on X. """ - def __init__(self, name, slope_parameterization, intercept_parameterization,apply_softplus=False,**kwargs): - super().__init__(name, apply_softplus) + def __init__(self, name, slope_parameterization, intercept_parameterization,**kwargs): + super().__init__(name) self.slope_parameterization = slope_parameterization self.intercept_parameterization = intercept_parameterization def get_samples(self, pb): with pb.model: - samples = self.intercept_parameterization.get_samples(pb) + pytensor.tensor.dot(pb.X,self.slope_parameterization.get_samples(pb)) - if self.apply_softplus: - return pm.math.log(1+pm.math.exp(samples)) + intc = self.intercept_parameterization.get_samples(pb) + slope_samples = self.slope_parameterization.get_samples(pb) + if pb.configs[f'random_slope_{self.name}']: + slope = pb.X*self.slope_parameterization.get_samples(pb) + slope = slope.sum(axis=-1) + else: + slope = pb.X@self.slope_parameterization.get_samples(pb) + + samples = pm.math.flatten(intc) + pm.math.flatten(slope) + samples = samples.reshape((samples.shape[0],1)) return samples def get_design_matrix(X, nm, basis="linear"): @@ -667,7 +640,6 @@ def get_design_matrix(X, nm, basis="linear"): return Phi - def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): n_hidden = configs['nn_hidden_neuron_num'] n_layers = configs['nn_hidden_layers_num'] diff --git a/tests/testHBR.py b/tests/testHBR.py index 8984bd27..176fef25 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # -*- coding: utf-8 -*- """ Created on Mon Jul 29 13:26:35 2019 @@ -16,6 +16,8 @@ import matplotlib.pyplot as plt from pcntoolkit.normative import estimate from warnings import filterwarnings +from pcntoolkit.util.utils import scaler + filterwarnings('ignore') @@ -25,7 +27,7 @@ working_dir = '/home/stijn/temp/tests/' # Specift a working directory # to save data and results. -simulation_method = 'linear' # 'non-linear' +simulation_method = 'non-linear' n_features = 1 # The number of input features of X n_grps = 2 # Number of batches in data n_samples = 500 # Number of samples in each group (use a list for different @@ -39,14 +41,13 @@ X_train, Y_train, grp_id_train, X_test, Y_test, grp_id_test, coef = \ simulate_data(simulation_method, n_samples, n_features, n_grps, working_dir=working_dir, plot=True) - ################################# Methods Tests ############################### for model_type in model_types: - nm = norm_init(X_train, Y_train, alg='hbr', model_type=model_type) + nm = norm_init(X_train, Y_train, alg='hbr', model_type=model_type,n_samples=100,n_tuning=10) nm.estimate(X_train, Y_train, trbefile=working_dir+'trbefile.pkl') yhat, ys2 = nm.predict(X_test, tsbefile=working_dir+'tsbefile.pkl') @@ -62,7 +63,7 @@ for j in range(n_grps): plt.scatter(temp_X[temp_be==j,], temp_Y[temp_be==j,], label='Group' + str(j)) - plt.plot(temp_X[temp_be==j,], temp_yhat[[temp_be==j,]]) + plt.plot(temp_X[temp_be==j,], temp_yhat[temp_be==j,]) plt.fill_between(temp_X[temp_be==j,], temp_yhat[temp_be==j,] - 1.96 * np.sqrt(temp_s2[temp_be==j,]), temp_yhat[temp_be==j,] + diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index 33ddf9e0..ff579049 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -1,10 +1,12 @@ import os import pandas as pd import pcntoolkit as ptk +import pymc as pm import numpy as np import pickle from matplotlib import pyplot as plt import arviz as az +from pcntoolkit.util.utils import scaler processing_dir = "HBR_demo/" # replace with a path to your working directory if not os.path.isdir(processing_dir): os.makedirs(processing_dir) @@ -13,31 +15,23 @@ def main(): - # Optional fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_tr.csv') fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_te.csv') - # fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_tr.csv') - # fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_te.csv') + idps = ['rh_MeanThickness_thickness'] + covs = ['age'] + batch_effects = ['sitenum','sex'] - X_train = (fcon_tr['age']/100).to_numpy(dtype=float) + X_train = fcon_tr[covs].to_numpy(dtype=float) Y_train = fcon_tr[idps].to_numpy(dtype=float) + batch_effects_train = fcon_tr[batch_effects].to_numpy(dtype=int) - # fcon_tr.loc[fcon_tr['sitenum'] == 21,'sitenum'] = 13 - # fcon_te.loc[fcon_te['sitenum'] == 21,'sitenum'] = 13 - -# - # configure batch effects for site and sex - batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) - # or only site - # batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) + X_test = fcon_te[covs].to_numpy(dtype=float) + Y_test = fcon_te[idps].to_numpy(dtype=float) + batch_effects_test = fcon_te[batch_effects].to_numpy(dtype=int) - max_sitenum = 15 - tr_idxs = batch_effects_train[:,0] < max_sitenum - X_train = X_train[tr_idxs] - Y_train = Y_train[tr_idxs] - batch_effects_train=batch_effects_train[tr_idxs] + print(X_test.shape, Y_test.shape, batch_effects_test.shape) with open('X_train.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_train), file) @@ -45,18 +39,6 @@ def main(): pickle.dump(pd.DataFrame(Y_train), file) with open('trbefile.pkl', 'wb') as file: pickle.dump(pd.DataFrame(batch_effects_train), file) - - - X_test = (fcon_te['age']/100).to_numpy(dtype=float) - Y_test = fcon_te[idps].to_numpy(dtype=float) - batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) - # batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) - - te_idxs = batch_effects_test[:,0] < max_sitenum - X_test = X_test[te_idxs] - Y_test = Y_test[te_idxs] - batch_effects_test=batch_effects_test[te_idxs] - with open('X_test.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_test), file) with open('Y_test.pkl', 'wb') as file: @@ -68,48 +50,57 @@ def main(): def ldpkl(filename: str): with open(filename, 'rb') as f: return pickle.load(f) - - respfile = os.path.join(processing_dir, 'Y_train.pkl') # measurements (eg cortical thickness) of the training samples (columns: the various features/ROIs, rows: observations or subjects) - covfile = os.path.join(processing_dir, 'X_train.pkl') # covariates (eg age) the training samples (columns: covariates, rows: observations or subjects) - testrespfile_path = os.path.join(processing_dir, 'Y_test.pkl') # measurements for the testing samples - testcovfile_path = os.path.join(processing_dir, 'X_test.pkl') # covariate file for the testing samples + respfile = os.path.join(processing_dir, 'Y_train.pkl') + covfile = os.path.join(processing_dir, 'X_train.pkl') - trbefile = os.path.join(processing_dir, 'trbefile.pkl') # training batch effects file (eg scanner_id, gender) (columns: the various batch effects, rows: observations or subjects) - tsbefile = os.path.join(processing_dir, 'tsbefile.pkl') # testing batch effects file + testrespfile_path = os.path.join(processing_dir, 'Y_test.pkl') + testcovfile_path = os.path.join(processing_dir, 'X_test.pkl') - output_path = os.path.join(processing_dir, 'Models/') # output path, where the models will be written - log_dir = os.path.join(processing_dir, 'log/') # + trbefile = os.path.join(processing_dir, 'trbefile.pkl') + tsbefile = os.path.join(processing_dir, 'tsbefile.pkl') + + output_path = os.path.join(processing_dir, 'Models/') + log_dir = os.path.join(processing_dir, 'log/') if not os.path.isdir(output_path): os.mkdir(output_path) if not os.path.isdir(log_dir): os.mkdir(log_dir) - outputsuffix = '_estimate' # a string to name the output files, of use only to you, so adapt it for your needs.` - nm = ptk.normative.fit(covfile=covfile, - respfile=respfile, - trbefile=trbefile, - alg='hbr', - linear_mu='True', - random_intercept_mu='True', - centered_intercept_mu='False', - random_slope_mu='False', - random_sigma='True', - log_path=log_dir, - binary='True', - n_samples=1000, - n_tuning=1000, - n_chains=4, - cores=4, - target_accept=0.99, - init='jitter+adapt_diag', - inscaler='standardize', - outscaler='standardize', - output_path=output_path, - outputsuffix=outputsuffix, - savemodel=True) - az.plot_trace(nm.hbr.trace) - plt.show() + + outputsuffix = '_estimate' + nm = ptk.normative.estimate(covfile=covfile, + respfile=respfile, + trbefile=trbefile, + testcov=testcovfile_path, + testresp=testrespfile_path, + tsbefile=tsbefile, + alg='hbr', + likelihood='SHASHb', + # model_type='bspline', + linear_mu='True', + random_intercept_mu = 'True', + centered_intercept_mu='True', + random_slope_mu='False', + centered_slope_mu='False', + random_sigma='True', + random_intercept_sigma = 'True', + centered_intercept_sigma='False', + random_slope_sigma='True', + centered_slope_sigma='True', + log_path=log_dir, + binary='True', + n_samples=17, + n_tuning=13, + n_chains=1, + cores=1, + target_accept=0.99, + init='adapt_diag', + inscaler='standardize', + outscaler='standardize', + output_path=output_path, + outputsuffix=outputsuffix, + savemodel=True) if __name__=="__main__": main() \ No newline at end of file From ae76ecccabc4adac8862595bb0faa87588f93f86 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Tue, 16 May 2023 21:03:00 +0200 Subject: [PATCH 18/43] Merged dev_pymc_coords into dev_pymc_normal --- .gitignore | 3 +- pcntoolkit/model/hbr.py | 276 +++++++++++++++++++++------------------- tests/testHBR.py | 10 +- tests/test_hbr_pymc.py | 104 ++++++++------- 4 files changed, 206 insertions(+), 187 deletions(-) diff --git a/.gitignore b/.gitignore index ff6f1fd0..f041b08a 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,6 @@ pcntoolkit.egg-info # Demo folder HBR_demo - ssh_error_log.txt +HBR_demo/* +dist/pcntoolkit-0.27-py3.11.egg diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 53907629..af412dfe 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -9,6 +9,8 @@ from __future__ import print_function from __future__ import division +from collections import OrderedDict + from ast import Param from tkinter.font import names @@ -143,8 +145,7 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit) else: return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) - - + def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ :param X: [N×P] array of clinical covariates @@ -160,21 +161,22 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): X = pytensor.tensor.cast(X,'floatX') y = pytensor.shared(y) y = pytensor.tensor.cast(y,'floatX') + - with pm.Model() as model: + # Make a param builder that will make the correct calls + pb = ParamBuilder(X, y, batch_effects, idata, configs) - # Make a param builder that will make the correct calls - pb = ParamBuilder(model, X, y, batch_effects, idata, configs) + with pm.Model(coords=pb.coords) as model: + pb.model=model + pb.batch_effect_indices = [pm.Data(pb.batch_effect_dim_names[i], pb.batch_effect_indices[i],mutable=True) for i in range(len(pb.batch_effect_indices))] if configs['likelihood'] == 'Normal': - mu = pb.make_param("mu", mu_slope_mu_params = (0.,10.), - sigma_slope_mu_params = (5.,), - mu_intercept_mu_params=(0.,10.), - sigma_intercept_mu_params = (5.,)).get_samples(pb) - sigma = pb.make_param("sigma", mu_sigma_params = (10., 5.), - sigma_sigma_params = (5.,)).get_samples(pb) - sigma_plus = pm.math.log(1+pm.math.exp(sigma)) - y_like = pm.Normal('y_like',mu=mu, sigma=sigma_plus, observed=y) + mu = pb.make_param("mu", mu_slope_mu_params = (0.,5.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) + # print(f"{mu.shape.eval()=}") + sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,)).get_samples(pb) + # print(f"{sigma.shape.eval()=}") + sigma_plus = pm.Deterministic('sigma_plus', pm.math.log(1+pm.math.exp(sigma))) + y_like = pm.Normal('y_like', mu, sigma=sigma_plus, observed=y) return model @@ -275,9 +277,6 @@ def estimate(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) X = self.transform_X(X) modeler = self.get_modeler() - print(X) - print(y) - print(batch_effects) with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: self.idata = pm.sample(draws=self.configs['n_samples'], tune=self.configs['n_tuning'], @@ -297,9 +296,9 @@ def predict(self, X, batch_effects, pred='single'): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) + self.idata = pm.sample_posterior_predictive(trace = self.idata, progressbar=True) + pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -333,12 +332,12 @@ def predict_on_new_site(self, X, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): - # TODO need to adapt this to new pymc - ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - pred_mean = ppc.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = ppc.posterior_predictive['y_like'].var(axis=(0,1)) + + with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): + self.idata = pm.sample_posterior_predictive(self.idata, extend_inferencedata=True, progressbar=True) + pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) + pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) return pred_mean, pred_var @@ -382,10 +381,8 @@ def sample_prior_predictive(self, X, batch_effects, samples, idata=None): if self.model_type == 'linear': with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, idata): - # TODO need to adapt this to new pymc - - ppc = pm.sample_prior_predictive(samples=samples) - return ppc + idata = pm.sample_prior_predictive(samples=samples) + return idata def get_model(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) @@ -414,6 +411,55 @@ def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): return X_dummy, batch_effects_dummy +class Prior: + """ + A wrapper class for a PyMC distribution. + - creates a fitted distribution from the idata, if one is present + - overloads the __getitem__ function with something that switches between indexing or not, based on the shape + """ + def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: + self.dist = None + self.name = name + self.has_random_effect = has_random_effect + self.distmap = {'normal': pm.Normal, + 'hnormal': pm.HalfNormal, + 'gamma': pm.Gamma, + 'uniform': pm.Uniform, + 'igamma': pm.InverseGamma, + 'hcauchy': pm.HalfCauchy, + 'hstudt':pm.HalfStudentT, + 'studt':pm.StudentT} + self.make_dist(dist, params, pb) + + def make_dist(self, dist, params, pb): + """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" + with pb.model as m: + # If self.name.startswith slope: + # use the basis functions dim + # Otherwise, use the empty dim + dim = 'basis_functions' if (self.name.startswith('slope') or self.name.startswith('offset_slope')) else 'empty' + + if (pb.idata is not None): + samples = az.extract(pb.idata, var_names=self.name).to_numpy() + if not self.has_random_effect: + samples = np.reshape(samples, (-1,)) + self.dist = from_posterior(param=self.name, + samples = samples, + distribution=dist, + freedom=pb.configs['freedom']) + elif self.has_random_effect: + self.dist = self.distmap[dist](self.name, *params, dims=[*pb.batch_effect_dim_names, dim]) + else: + self.dist = self.distmap[dist](self.name, *params, dims=dim) + + def __getitem__(self, idx): + """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" + assert self.dist is not None, "Distribution not initialized" + if self.has_random_effect: + return self.dist[idx] + else: + return self.dist + class ParamBuilder: """ A class that simplifies the construction of parameterizations. @@ -421,7 +467,7 @@ class ParamBuilder: It also contains a lot of decision logic for creating the parameterizations. """ - def __init__(self, model, X, y, batch_effects, idata, configs): + def __init__(self, X, y, batch_effects, idata, configs): """ :param model: model to attach all the distributions to @@ -431,96 +477,49 @@ def __init__(self, model, X, y, batch_effects, idata, configs): :param idata: idem :param configs: idem """ - self.model = model + self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) self.idata:az.InferenceData = idata self.configs = configs - self.feature_num = X.shape[1].eval().item() self.y_shape = y.shape.eval() self.n_ys = y.shape[0].eval().item() self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - self.all_idx = [] + self.batch_effect_dim_names = [] + self.batch_effect_indices = [] + self.coords = OrderedDict() + for i in range(self.batch_effects_num): - # Count the unique values for each batch effect - self.batch_effects_size.append(len(np.unique(self.batch_effects[:, i]))) - # Store the unique values for each batch effect - self.all_idx.append(np.int16(np.unique(self.batch_effects[:, i]))) + batch_effect_dim_name = f"batch_effect_{i}" + self.batch_effect_dim_names.append(batch_effect_dim_name) + this_be_values, this_be_indices = np.unique(self.batch_effects[:,i], return_inverse=True) + self.coords[batch_effect_dim_name] = this_be_values + self.batch_effect_indices.append(this_be_indices) + self.coords['empty']=[0] + self.coords['basis_functions'] = [i for i in range(X.shape.eval()[1])] - # Make a cartesian product of all the unique values of each batch effect - self.be_idx = list(product(*self.all_idx)) - # Make tuples of batch effects ID's and indices of datapoints with that specific combination of batch effects - self.be_idx_tups = [] - for be in self.be_idx: - a = [] - for i, b in enumerate(be): - a.append(self.batch_effects[:, i] == b) - idx = reduce(np.logical_and, a).nonzero() - if idx[0].shape[0] != 0: - self.be_idx_tups.append((be, idx)) - - def make_param(self, name, dim = (1,), **kwargs): + # TODO reinstigate the 'dim' keyword, for the slope parameters + def make_param(self, name, **kwargs): if self.configs.get(f'linear_{name}', False): # First make a slope and intercept, and use those to make a linear parameterization - slope_parameterization = self.make_param(f'slope_{name}', dim=[self.feature_num], **kwargs) + slope_parameterization = self.make_param(f'slope_{name}',**kwargs) intercept_parameterization = self.make_param(f'intercept_{name}', **kwargs) - return LinearParameterization(name=name, dim=dim, + return LinearParameterization(name=name, slope_parameterization=slope_parameterization, intercept_parameterization=intercept_parameterization, - pb=self, **kwargs) elif self.configs.get(f'random_{name}', False): if self.configs.get(f'centered_{name}', True): - return CentralRandomFixedParameterization(name=name, pb=self, dim=dim, **kwargs) - else: - return NonCentralRandomFixedParameterization(name=name, pb=self, dim=dim, **kwargs) - else: - return FixedParameterization(name=name, dim=dim, pb=self,**kwargs) - -class Prior: - """ - A wrapper class for a PyMC distribution. - - creates a fitted distribution from the idata, if one is present - - overloads the __getitem__ function with something that switches between indexing or not, based on the shape - """ - def __init__(self, name, dist, params, pb, shape=(1,)) -> None: - self.dist = None - self.name = name - self.shape = shape - self.has_random_effect = True if len(shape)>1 else False - self.distmap = {'normal': pm.Normal, - 'hnormal': pm.HalfNormal, - 'gamma': pm.Gamma, - 'uniform': pm.Uniform, - 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy} - self.make_dist(dist, params, pb) - - def make_dist(self, dist, params, pb:ParamBuilder): - """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" - with pb.model as m: - if (pb.idata is not None) and (not self.has_random_effect): - self.dist = from_posterior(param=self.name, - samples=pb.idata.posterior[self.name], - distribution=dist, - freedom=pb.configs['freedom']) + return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - print(f"{self.name} \tdist = {dist}") - self.dist = self.distmap[dist](self.name, *params, shape=self.shape) - - def __getitem__(self, idx): - """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" - assert self.dist is not None, "Distribution not initialized" - if self.has_random_effect: - return self.dist[idx] + return NonCentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return self.dist[tuple([0]*len(self.shape))] + return FixedParameterization(name=name, pb=self, **kwargs) @@ -528,96 +527,108 @@ class Parameterization: """ This is the top-level parameterization class from which all the other parameterizations inherit. """ - def __init__(self, name, dim): + def __init__(self, name): self.name = name - self.dim = dim print(name, type(self)) def get_samples(self, pb): - - with pb.model: - samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) - for be, idx in pb.be_idx_tups: - samples = pytensor.tensor.set_subtensor(samples[idx], self.dist[be]) - return samples - + pass class FixedParameterization(Parameterization): """ A parameterization that takes a single value for all input. It does not depend on anything except its hyperparameters """ - def __init__(self, name, dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder,**kwargs): + super().__init__(name) dist = kwargs.get(f'{name}_dist','normal') params = kwargs.get(f'{name}_params',(0.,1.)) - self.dist = Prior(name, dist, params, pb, shape = dim) + self.dist = Prior(name, dist, params, pb) + def get_samples(self, pb): + with pb.model: + return self.dist[0] class CentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a central fashion; the values are sampled from normal distribution with a group mean and group variance """ - def __init__(self, name, dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder, **kwargs): + super().__init__(name) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb, shape = dim) + mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) - # HalfCauchy is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hcauchy') + # HalfStudent is default for sigma + sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb, shape = [*pb.batch_effects_size, *dim]) + sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) - self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, shape = [*pb.batch_effects_size, *dim]) - + dim = 'basis_functions' if self.name.startswith('slope') else 'empty' + self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=[*pb.batch_effect_dim_names,dim]) + + def get_samples(self, pb:ParamBuilder): + with pb.model: + samples = self.dist[*pb.batch_effect_indices] + return samples class NonCentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a non-central fashion; the values are a sum of a group mean and noise values scaled with a group scaling factor """ - def __init__(self, name,dim, pb:ParamBuilder, **kwargs): - super().__init__(name, dim) + def __init__(self, name, pb:ParamBuilder, **kwargs): + super().__init__(name) # Normal distribution is default for mean mu_dist = kwargs.get(f'mu_{name}_dist','normal') mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb, shape = dim) + mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) - # HalfCauchy is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hcauchy') + # HalfStudent is default for sigma + sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb, shape = dim) + sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) # Normal is default for offset offset_dist = kwargs.get(f'offset_{name}_dist','normal') offset_params = kwargs.get(f'offset_{name}_params',(0.,1.)) - offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, shape = [*pb.batch_effects_size, *dim]) + offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, has_random_effect=True) - self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist) + dim = 'basis_functions' if self.name.startswith('slope') else 'empty' + self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist,dims=[*pb.batch_effect_dim_names,dim]) + def get_samples(self, pb:ParamBuilder): + with pb.model: + # print(f"{self.dist.shape.eval()=}") + samples = self.dist[*pb.batch_effect_indices] + return samples + class LinearParameterization(Parameterization): """ A parameterization that can model a linear dependence on X. """ - def __init__(self, name, dim, slope_parameterization, intercept_parameterization, pb, **kwargs): - super().__init__( name, dim) + def __init__(self, name, slope_parameterization, intercept_parameterization,**kwargs): + super().__init__(name) self.slope_parameterization = slope_parameterization self.intercept_parameterization = intercept_parameterization - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb): with pb.model: - samples = pytensor.tensor.zeros([pb.n_ys, *self.dim]) - for be, idx in pb.be_idx_tups: - dot = pytensor.tensor.dot(pb.X[idx,:], self.slope_parameterization.dist[be]).T - intercept = self.intercept_parameterization.dist[be] - samples = pytensor.tensor.set_subtensor(samples[idx,:],dot+intercept) - return samples + intc = self.intercept_parameterization.get_samples(pb) + slope_samples = self.slope_parameterization.get_samples(pb) + if pb.configs[f'random_slope_{self.name}']: + slope = pb.X*self.slope_parameterization.get_samples(pb) + slope = slope.sum(axis=-1) + else: + slope = pb.X@self.slope_parameterization.get_samples(pb) + samples = pm.math.flatten(intc) + pm.math.flatten(slope) + samples = samples.reshape((samples.shape[0],1)) + return samples def get_design_matrix(X, nm, basis="linear"): if basis == "bspline": @@ -629,7 +640,6 @@ def get_design_matrix(X, nm, basis="linear"): return Phi - def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): n_hidden = configs['nn_hidden_neuron_num'] n_layers = configs['nn_hidden_layers_num'] diff --git a/tests/testHBR.py b/tests/testHBR.py index 8984bd27..5844f1f9 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # -*- coding: utf-8 -*- """ Created on Mon Jul 29 13:26:35 2019 @@ -16,6 +16,8 @@ import matplotlib.pyplot as plt from pcntoolkit.normative import estimate from warnings import filterwarnings +from pcntoolkit.util.utils import scaler + filterwarnings('ignore') @@ -25,7 +27,7 @@ working_dir = '/home/stijn/temp/tests/' # Specift a working directory # to save data and results. -simulation_method = 'linear' # 'non-linear' +simulation_method = 'non-linear' n_features = 1 # The number of input features of X n_grps = 2 # Number of batches in data n_samples = 500 # Number of samples in each group (use a list for different @@ -39,7 +41,6 @@ X_train, Y_train, grp_id_train, X_test, Y_test, grp_id_test, coef = \ simulate_data(simulation_method, n_samples, n_features, n_grps, working_dir=working_dir, plot=True) - ################################# Methods Tests ############################### @@ -62,7 +63,7 @@ for j in range(n_grps): plt.scatter(temp_X[temp_be==j,], temp_Y[temp_be==j,], label='Group' + str(j)) - plt.plot(temp_X[temp_be==j,], temp_yhat[[temp_be==j,]]) + plt.plot(temp_X[temp_be==j,], temp_yhat[temp_be==j,]) plt.fill_between(temp_X[temp_be==j,], temp_yhat[temp_be==j,] - 1.96 * np.sqrt(temp_s2[temp_be==j,]), temp_yhat[temp_be==j,] + @@ -70,6 +71,7 @@ color='gray', alpha=0.2) plt.title('Model %s, Feature %d' %(model_type, i)) plt.legend() + plt.show() ############################## Normative Modelling Test ####################### diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index a5f2419d..ff579049 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -1,9 +1,12 @@ import os import pandas as pd import pcntoolkit as ptk +import pymc as pm import numpy as np import pickle from matplotlib import pyplot as plt +import arviz as az +from pcntoolkit.util.utils import scaler processing_dir = "HBR_demo/" # replace with a path to your working directory if not os.path.isdir(processing_dir): os.makedirs(processing_dir) @@ -12,29 +15,23 @@ def main(): - # Optional fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_tr.csv') fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_te.csv') - # fcon_tr = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_tr.csv') - # fcon_te = pd.read_csv('https://raw.githubusercontent.com/predictive-clinical-neuroscience/PCNtoolkit-demo/main/data/fcon1000_icbm_te.csv') + idps = ['rh_MeanThickness_thickness'] + covs = ['age'] + batch_effects = ['sitenum','sex'] - X_train = (fcon_tr['age']/100).to_numpy(dtype=float) + X_train = fcon_tr[covs].to_numpy(dtype=float) Y_train = fcon_tr[idps].to_numpy(dtype=float) + batch_effects_train = fcon_tr[batch_effects].to_numpy(dtype=int) - # fcon_tr.loc[fcon_tr['sitenum'] == 21,'sitenum'] = 13 - # fcon_te.loc[fcon_te['sitenum'] == 21,'sitenum'] = 13 - - # configure batch effects for site and sex - batch_effects_train = fcon_tr[['sitenum','sex']].to_numpy(dtype=int) - - - # or only site - # batch_effects_train = fcon_tr[['sitenum']].to_numpy(dtype=int) - - print(np.unique(batch_effects_train,axis=0)) # Here we see there are missing sites + X_test = fcon_te[covs].to_numpy(dtype=float) + Y_test = fcon_te[idps].to_numpy(dtype=float) + batch_effects_test = fcon_te[batch_effects].to_numpy(dtype=int) + print(X_test.shape, Y_test.shape, batch_effects_test.shape) with open('X_train.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_train), file) @@ -42,13 +39,6 @@ def main(): pickle.dump(pd.DataFrame(Y_train), file) with open('trbefile.pkl', 'wb') as file: pickle.dump(pd.DataFrame(batch_effects_train), file) - - - X_test = (fcon_te['age']/100).to_numpy(dtype=float) - Y_test = fcon_te[idps].to_numpy(dtype=float) - batch_effects_test = fcon_te[['sitenum','sex']].to_numpy(dtype=int) - # batch_effects_test = fcon_te[['sitenum']].to_numpy(dtype=int) - with open('X_test.pkl', 'wb') as file: pickle.dump(pd.DataFrame(X_test), file) with open('Y_test.pkl', 'wb') as file: @@ -60,41 +50,57 @@ def main(): def ldpkl(filename: str): with open(filename, 'rb') as f: return pickle.load(f) - - respfile = os.path.join(processing_dir, 'Y_train.pkl') # measurements (eg cortical thickness) of the training samples (columns: the various features/ROIs, rows: observations or subjects) - covfile = os.path.join(processing_dir, 'X_train.pkl') # covariates (eg age) the training samples (columns: covariates, rows: observations or subjects) - testrespfile_path = os.path.join(processing_dir, 'Y_test.pkl') # measurements for the testing samples - testcovfile_path = os.path.join(processing_dir, 'X_test.pkl') # covariate file for the testing samples + respfile = os.path.join(processing_dir, 'Y_train.pkl') + covfile = os.path.join(processing_dir, 'X_train.pkl') - trbefile = os.path.join(processing_dir, 'trbefile.pkl') # training batch effects file (eg scanner_id, gender) (columns: the various batch effects, rows: observations or subjects) - tsbefile = os.path.join(processing_dir, 'tsbefile.pkl') # testing batch effects file + testrespfile_path = os.path.join(processing_dir, 'Y_test.pkl') + testcovfile_path = os.path.join(processing_dir, 'X_test.pkl') - output_path = os.path.join(processing_dir, 'Models/') # output path, where the models will be written - log_dir = os.path.join(processing_dir, 'log/') # + trbefile = os.path.join(processing_dir, 'trbefile.pkl') + tsbefile = os.path.join(processing_dir, 'tsbefile.pkl') + + output_path = os.path.join(processing_dir, 'Models/') + log_dir = os.path.join(processing_dir, 'log/') if not os.path.isdir(output_path): os.mkdir(output_path) if not os.path.isdir(log_dir): os.mkdir(log_dir) - outputsuffix = '_estimate' # a string to name the output files, of use only to you, so adapt it for your needs.` - ptk.normative.estimate(covfile=covfile, - respfile=respfile, - tsbefile=tsbefile, - trbefile=trbefile, - alg='hbr', - linear_mu='True', - random_mu='True', - random_intercept_mu='True', - random_slope_mu='True', - random_sigma='True', - log_path=log_dir, - binary=True, - output_path=output_path, - testcov= testcovfile_path, - testresp = testrespfile_path, - outputsuffix=outputsuffix, - savemodel=True) + + outputsuffix = '_estimate' + nm = ptk.normative.estimate(covfile=covfile, + respfile=respfile, + trbefile=trbefile, + testcov=testcovfile_path, + testresp=testrespfile_path, + tsbefile=tsbefile, + alg='hbr', + likelihood='SHASHb', + # model_type='bspline', + linear_mu='True', + random_intercept_mu = 'True', + centered_intercept_mu='True', + random_slope_mu='False', + centered_slope_mu='False', + random_sigma='True', + random_intercept_sigma = 'True', + centered_intercept_sigma='False', + random_slope_sigma='True', + centered_slope_sigma='True', + log_path=log_dir, + binary='True', + n_samples=17, + n_tuning=13, + n_chains=1, + cores=1, + target_accept=0.99, + init='adapt_diag', + inscaler='standardize', + outscaler='standardize', + output_path=output_path, + outputsuffix=outputsuffix, + savemodel=True) if __name__=="__main__": main() \ No newline at end of file From 52401a8e5874d66654042325d31df39c87e62de8 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Tue, 16 May 2023 21:27:01 +0200 Subject: [PATCH 19/43] Formatting hbr.py --- pcntoolkit/model/hbr.py | 856 +++++++++++++++++++++++++--------------- 1 file changed, 531 insertions(+), 325 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index af412dfe..4e149eb0 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -35,18 +35,19 @@ def bspline_fit(X, order, nknots): bsp_basis = [] for i in range(feature_num): - minx = np.min(X[:,i]) - maxx = np.max(X[:,i]) - delta = maxx-minx + minx = np.min(X[:, i]) + maxx = np.max(X[:, i]) + delta = maxx - minx # Expand range by 20% (10% on both sides) - splinemin = minx-0.1*delta - splinemax = maxx+0.1*delta + splinemin = minx - 0.1 * delta + splinemax = maxx + 0.1 * delta knots = np.linspace(splinemin, splinemax, nknots) k = splinelab.augknt(knots, order) bsp_basis.append(bspline.Bspline(k, order)) return bsp_basis + def bspline_transform(X, bsp_basis): if type(bsp_basis) != list: temp = [] @@ -61,8 +62,9 @@ def bspline_transform(X, bsp_basis): return X_transformed + def create_poly_basis(X, order): - """ compute a polynomial basis expansion of the specified order""" + """compute a polynomial basis expansion of the specified order""" if len(X.shape) == 1: X = X[:, np.newaxis] @@ -70,17 +72,18 @@ def create_poly_basis(X, order): Phi = np.zeros((X.shape[0], D * order)) colid = np.arange(0, D) for d in range(1, order + 1): - Phi[:, colid] = X ** d + Phi[:, colid] = X**d colid += D return Phi + def from_posterior(param, samples, distribution=None, half=False, freedom=1): if len(samples.shape) > 1: shape = samples.shape[1:] else: shape = None - if (distribution is None): + if distribution is None: smin, smax = np.min(samples), np.max(samples) width = smax - smin x = np.linspace(smin, smax, 1000) @@ -95,57 +98,76 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.distributions.Interpolated(param, x, y) else: return pm.distributions.Interpolated(param, x, y, shape=shape) - elif (distribution == 'normal'): + elif distribution == "normal": temp = stats.norm.fit(samples) if shape is None: return pm.Normal(param, mu=temp[0], sigma=freedom * temp[1]) else: return pm.Normal(param, mu=temp[0], sigma=freedom * temp[1], shape=shape) - elif (distribution == 'hnormal'): + elif distribution == "hnormal": temp = stats.halfnorm.fit(samples) if shape is None: return pm.HalfNormal(param, sigma=freedom * temp[1]) else: return pm.HalfNormal(param, sigma=freedom * temp[1], shape=shape) - elif (distribution == 'hcauchy'): + elif distribution == "hcauchy": temp = stats.halfcauchy.fit(samples) if shape is None: return pm.HalfCauchy(param, freedom * temp[1]) else: return pm.HalfCauchy(param, freedom * temp[1], shape=shape) - elif (distribution == 'uniform'): + elif distribution == "uniform": upper_bound = np.percentile(samples, 95) lower_bound = np.percentile(samples, 5) r = np.abs(upper_bound - lower_bound) if shape is None: - return pm.Uniform(param, lower=lower_bound - freedom * r, - upper=upper_bound + freedom * r) + return pm.Uniform( + param, lower=lower_bound - freedom * r, upper=upper_bound + freedom * r + ) else: - return pm.Uniform(param, lower=lower_bound - freedom * r, - upper=upper_bound + freedom * r, shape=shape) - elif (distribution == 'huniform'): + return pm.Uniform( + param, + lower=lower_bound - freedom * r, + upper=upper_bound + freedom * r, + shape=shape, + ) + elif distribution == "huniform": upper_bound = np.percentile(samples, 95) lower_bound = np.percentile(samples, 5) r = np.abs(upper_bound - lower_bound) if shape is None: return pm.Uniform(param, lower=0, upper=upper_bound + freedom * r) else: - return pm.Uniform(param, lower=0, upper=upper_bound + freedom * r, shape=shape) + return pm.Uniform( + param, lower=0, upper=upper_bound + freedom * r, shape=shape + ) - elif (distribution == 'gamma'): + elif distribution == "gamma": alpha_fit, loc_fit, invbeta_fit = stats.gamma.fit(samples) if shape is None: - return pm.Gamma(param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit) + return pm.Gamma( + param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit + ) else: - return pm.Gamma(param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit, shape=shape) - - elif (distribution == 'igamma'): + return pm.Gamma( + param, + alpha=freedom * alpha_fit, + beta=freedom / invbeta_fit, + shape=shape, + ) + + elif distribution == "igamma": alpha_fit, loc_fit, beta_fit = stats.gamma.fit(samples) if shape is None: - return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit) + return pm.InverseGamma( + param, alpha=freedom * alpha_fit, beta=freedom * beta_fit + ) else: - return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) - + return pm.InverseGamma( + param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape + ) + + def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ :param X: [N×P] array of clinical covariates @@ -154,34 +176,47 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): :param batch_effects_size: [b1, b2,...,bM] List of counts of unique values of batch effects :param configs: :param idata: - :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. + :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ X = pytensor.shared(X) - X = pytensor.tensor.cast(X,'floatX') + X = pytensor.tensor.cast(X, "floatX") y = pytensor.shared(y) - y = pytensor.tensor.cast(y,'floatX') - + y = pytensor.tensor.cast(y, "floatX") # Make a param builder that will make the correct calls pb = ParamBuilder(X, y, batch_effects, idata, configs) with pm.Model(coords=pb.coords) as model: - pb.model=model - pb.batch_effect_indices = [pm.Data(pb.batch_effect_dim_names[i], pb.batch_effect_indices[i],mutable=True) for i in range(len(pb.batch_effect_indices))] - - if configs['likelihood'] == 'Normal': - mu = pb.make_param("mu", mu_slope_mu_params = (0.,5.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) + pb.model = model + pb.batch_effect_indices = [ + pm.Data( + pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], mutable=True + ) + for i in range(len(pb.batch_effect_indices)) + ] + + if configs["likelihood"] == "Normal": + mu = pb.make_param( + "mu", + mu_slope_mu_params=(0.0, 5.0), + sigma_slope_mu_params=(5.0,), + mu_intercept_mu_params=(0.0, 5.0), + sigma_intercept_mu_params=(5.0,), + ).get_samples(pb) # print(f"{mu.shape.eval()=}") - sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,)).get_samples(pb) + sigma = pb.make_param( + "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + ).get_samples(pb) # print(f"{sigma.shape.eval()=}") - sigma_plus = pm.Deterministic('sigma_plus', pm.math.log(1+pm.math.exp(sigma))) - y_like = pm.Normal('y_like', mu, sigma=sigma_plus, observed=y) + sigma_plus = pm.Deterministic( + "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)) + ) + y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) return model - class HBR: """Hierarchical Bayesian Regression for normative modeling @@ -205,54 +240,28 @@ class HBR: Written by S.M. Kia """ - def get_step_methods(self, m): - """ - This can be used to assign default step functions. However, the nuts initialization keyword doesnt work together with this... so better not use it. - - STEP_METHODS = ( - NUTS, - HamiltonianMC, - Metropolis, - BinaryMetropolis, - BinaryGibbsMetropolis, - Slice, - CategoricalGibbsMetropolis, - ) - :param m: a PyMC model - :return: - """ - samplermap = {'NUTS': NUTS, 'MH': Metropolis, 'Slice': Slice, 'HMC': HamiltonianMC} - fallbacks = [Metropolis] # We are using MH as a fallback method here - if self.configs['sampler'] == 'NUTS': - step_kwargs = {'nuts': {'target_accept': self.configs['target_accept']}} - else: - step_kwargs = None - return pm.sampling.assign_step_methods(m, methods=[samplermap[self.configs['sampler']]] + fallbacks, - step_kwargs=step_kwargs) - def __init__(self, configs): self.bsp = None - self.model_type = configs['type'] + self.model_type = configs["type"] self.configs = configs def get_modeler(self): - return {'nn': nn_hbr}.get(self.model_type, hbr) - + return {"nn": nn_hbr}.get(self.model_type, hbr) + def transform_X(self, X): - if self.model_type == 'polynomial': - Phi = create_poly_basis(X, self.configs['order']) - elif self.model_type == 'bspline': + if self.model_type == "polynomial": + Phi = create_poly_basis(X, self.configs["order"]) + elif self.model_type == "bspline": if self.bsp is None: - self.bsp = bspline_fit(X, self.configs['order'], self.configs['nknots']) + self.bsp = bspline_fit(X, self.configs["order"], self.configs["nknots"]) bspline = bspline_transform(X, self.bsp) - Phi = np.concatenate((X, bspline), axis = 1) + Phi = np.concatenate((X, bspline), axis=1) else: Phi = X return Phi - - def find_map(self, X, y, batch_effects,method='L-BFGS-B'): - """ Function to estimate the model """ + def find_map(self, X, y, batch_effects, method="L-BFGS-B"): + """Function to estimate the model""" X, y, batch_effects = expand_all(X, y, batch_effects) self.batch_effects_num = batch_effects.shape[1] @@ -267,8 +276,7 @@ def find_map(self, X, y, batch_effects,method='L-BFGS-B'): return self.MAP def estimate(self, X, y, batch_effects): - - """ Function to estimate the model """ + """Function to estimate the model""" X, y, batch_effects = expand_all(X, y, batch_effects) self.batch_effects_num = batch_effects.shape[1] @@ -278,32 +286,37 @@ def estimate(self, X, y, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: - self.idata = pm.sample(draws=self.configs['n_samples'], - tune=self.configs['n_tuning'], - chains=self.configs['n_chains'], - init=self.configs['init'], n_init=500000, - cores=self.configs['cores']) + self.idata = pm.sample( + draws=self.configs["n_samples"], + tune=self.configs["n_tuning"], + chains=self.configs["n_chains"], + init=self.configs["init"], + n_init=500000, + cores=self.configs["cores"], + ) return self.idata - def predict(self, X, batch_effects, pred='single'): - """ Function to make predictions from the model """ + def predict(self, X, batch_effects, pred="single"): + """Function to make predictions from the model""" X, batch_effects = expand_all(X, batch_effects) - samples = self.configs['n_samples'] + samples = self.configs["n_samples"] y = np.zeros([X.shape[0], 1]) - if pred == 'single': + if pred == "single": X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - self.idata = pm.sample_posterior_predictive(trace = self.idata, progressbar=True) - pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) + self.idata = pm.sample_posterior_predictive( + trace=self.idata, progressbar=True + ) + pred_mean = self.idata.posterior_predictive["y_like"].mean(axis=(0, 1)) + pred_var = self.idata.posterior_predictive["y_like"].var(axis=(0, 1)) return pred_mean, pred_var def estimate_on_new_site(self, X, y, batch_effects): - """ Function to adapt the model """ + """Function to adapt the model""" X, y, batch_effects = expand_all(X, y, batch_effects) self.batch_effects_num = batch_effects.shape[1] @@ -313,36 +326,39 @@ def estimate_on_new_site(self, X, y, batch_effects): X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, - self.configs, idata=self.idata) as m: - self.idata = pm.sample(self.configs['n_samples'], - tune=self.configs['n_tuning'], - chains=self.configs['n_chains'], - target_accept=self.configs['target_accept'], - init=self.configs['init'], n_init=50000, - cores=self.configs['cores']) + with modeler( + X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata + ) as m: + self.idata = pm.sample( + self.configs["n_samples"], + tune=self.configs["n_tuning"], + chains=self.configs["n_chains"], + target_accept=self.configs["target_accept"], + init=self.configs["init"], + n_init=50000, + cores=self.configs["cores"], + ) return self.idata def predict_on_new_site(self, X, batch_effects): - """ Function to make predictions from the model """ + """Function to make predictions from the model""" X, batch_effects = expand_all(X, batch_effects) - - samples = self.configs['n_samples'] y = np.zeros([X.shape[0], 1]) - X = self.transform_X(X) modeler = self.get_modeler() - - - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): - self.idata = pm.sample_posterior_predictive(self.idata, extend_inferencedata=True, progressbar=True) - pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) + with modeler( + X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata + ): + self.idata = pm.sample_posterior_predictive( + self.idata, extend_inferencedata=True, progressbar=True + ) + pred_mean = self.idata.posterior_predictive["y_like"].mean(axis=(0, 1)) + pred_var = self.idata.posterior_predictive["y_like"].var(axis=(0, 1)) return pred_mean, pred_var def generate(self, X, batch_effects, samples): - """ Function to generate samples from posterior predictive distribution """ + """Function to generate samples from posterior predictive distribution""" X, batch_effects = expand_all(X, batch_effects) y = np.zeros([X.shape[0], 1]) @@ -353,7 +369,9 @@ def generate(self, X, batch_effects, samples): # TODO need to adapt this to new pymc ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - generated_samples = np.reshape(ppc.posterior_predictive['y_like'].squeeze().T, [X.shape[0] * samples, 1]) + generated_samples = np.reshape( + ppc.posterior_predictive["y_like"].squeeze().T, [X.shape[0] * samples, 1] + ) X = np.repeat(X, samples) if len(X.shape) == 1: X = np.expand_dims(X, axis=1) @@ -364,7 +382,7 @@ def generate(self, X, batch_effects, samples): return X, batch_effects, generated_samples def sample_prior_predictive(self, X, batch_effects, samples, idata=None): - """ Function to sample from prior predictive distribution """ + """Function to sample from prior predictive distribution""" if len(X.shape) == 1: X = np.expand_dims(X, axis=1) @@ -378,9 +396,8 @@ def sample_prior_predictive(self, X, batch_effects, samples, idata=None): y = np.zeros([X.shape[0], 1]) - if self.model_type == 'linear': - with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, - idata): + if self.model_type == "linear": + with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, idata): idata = pm.sample_prior_predictive(samples=samples) return idata @@ -393,13 +410,20 @@ def get_model(self, X, y, batch_effects): self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) modeler = self.get_modeler() X = self.transform_X(X) - return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.idata) - - def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): + return modeler( + X, y, batch_effects, self.batch_effects_size, self.configs, self.idata + ) + def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): arrays = [] for i in range(len(covariate_ranges)): - arrays.append(np.arange(covariate_ranges[i][0],covariate_ranges[i][1], covariate_ranges[i][2])) + arrays.append( + np.arange( + covariate_ranges[i][0], + covariate_ranges[i][1], + covariate_ranges[i][2], + ) + ) X = cartesian_product(arrays) X_dummy = np.concatenate([X for i in range(np.prod(self.batch_effects_size))]) @@ -409,46 +433,61 @@ def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): batch_effects = cartesian_product(arrays) batch_effects_dummy = np.repeat(batch_effects, X.shape[0], axis=0) - return X_dummy, batch_effects_dummy + return X_dummy, batch_effects_dummy + class Prior: """ - A wrapper class for a PyMC distribution. + A wrapper class for a PyMC distribution. - creates a fitted distribution from the idata, if one is present - overloads the __getitem__ function with something that switches between indexing or not, based on the shape """ + def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: self.dist = None self.name = name self.has_random_effect = has_random_effect - self.distmap = {'normal': pm.Normal, - 'hnormal': pm.HalfNormal, - 'gamma': pm.Gamma, - 'uniform': pm.Uniform, - 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy, - 'hstudt':pm.HalfStudentT, - 'studt':pm.StudentT} + self.distmap = { + "normal": pm.Normal, + "hnormal": pm.HalfNormal, + "gamma": pm.Gamma, + "uniform": pm.Uniform, + "igamma": pm.InverseGamma, + "hcauchy": pm.HalfCauchy, + "hstudt": pm.HalfStudentT, + "studt": pm.StudentT, + } self.make_dist(dist, params, pb) - + def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" with pb.model as m: # If self.name.startswith slope: # use the basis functions dim # Otherwise, use the empty dim - dim = 'basis_functions' if (self.name.startswith('slope') or self.name.startswith('offset_slope')) else 'empty' - - if (pb.idata is not None): + dim = ( + "basis_functions" + if ( + self.name.startswith("slope") + or self.name.startswith("offset_slope") + ) + else "empty" + ) + + if pb.idata is not None: samples = az.extract(pb.idata, var_names=self.name).to_numpy() if not self.has_random_effect: samples = np.reshape(samples, (-1,)) - self.dist = from_posterior(param=self.name, - samples = samples, - distribution=dist, - freedom=pb.configs['freedom']) + self.dist = from_posterior( + param=self.name, + samples=samples, + distribution=dist, + freedom=pb.configs["freedom"], + ) elif self.has_random_effect: - self.dist = self.distmap[dist](self.name, *params, dims=[*pb.batch_effect_dim_names, dim]) + self.dist = self.distmap[dist]( + self.name, *params, dims=[*pb.batch_effect_dim_names, dim] + ) else: self.dist = self.distmap[dist](self.name, *params, dims=dim) @@ -460,10 +499,11 @@ def __getitem__(self, idx): else: return self.dist + class ParamBuilder: """ - A class that simplifies the construction of parameterizations. - It has a lot of attributes necessary for creating the model, including the data, but it is never saved with the model. + A class that simplifies the construction of parameterizations. + It has a lot of attributes necessary for creating the model, including the data, but it is never saved with the model. It also contains a lot of decision logic for creating the parameterizations. """ @@ -477,11 +517,11 @@ def __init__(self, X, y, batch_effects, idata, configs): :param idata: idem :param configs: idem """ - self.model = None # Needs to be set later, because coords need to be passed at construction of Model + self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) - self.idata:az.InferenceData = idata + self.idata: az.InferenceData = idata self.configs = configs self.y_shape = y.shape.eval() @@ -495,38 +535,43 @@ def __init__(self, X, y, batch_effects, idata, configs): for i in range(self.batch_effects_num): batch_effect_dim_name = f"batch_effect_{i}" self.batch_effect_dim_names.append(batch_effect_dim_name) - this_be_values, this_be_indices = np.unique(self.batch_effects[:,i], return_inverse=True) + this_be_values, this_be_indices = np.unique( + self.batch_effects[:, i], return_inverse=True + ) self.coords[batch_effect_dim_name] = this_be_values self.batch_effect_indices.append(this_be_indices) - self.coords['empty']=[0] - self.coords['basis_functions'] = [i for i in range(X.shape.eval()[1])] - + self.coords["empty"] = [0] + self.coords["basis_functions"] = [i for i in range(X.shape.eval()[1])] # TODO reinstigate the 'dim' keyword, for the slope parameters def make_param(self, name, **kwargs): - if self.configs.get(f'linear_{name}', False): - # First make a slope and intercept, and use those to make a linear parameterization - slope_parameterization = self.make_param(f'slope_{name}',**kwargs) - intercept_parameterization = self.make_param(f'intercept_{name}', **kwargs) - return LinearParameterization(name=name, - slope_parameterization=slope_parameterization, - intercept_parameterization=intercept_parameterization, - **kwargs) - - elif self.configs.get(f'random_{name}', False): - if self.configs.get(f'centered_{name}', True): - return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) + if self.configs.get(f"linear_{name}", False): + # First make a slope and intercept, and use those to make a linear parameterization + slope_parameterization = self.make_param(f"slope_{name}", **kwargs) + intercept_parameterization = self.make_param(f"intercept_{name}", **kwargs) + return LinearParameterization( + name=name, + slope_parameterization=slope_parameterization, + intercept_parameterization=intercept_parameterization, + **kwargs, + ) + + elif self.configs.get(f"random_{name}", False): + if self.configs.get(f"centered_{name}", True): + return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return NonCentralRandomFixedParameterization(name=name, pb=self, **kwargs) + return NonCentralRandomFixedParameterization( + name=name, pb=self, **kwargs + ) else: return FixedParameterization(name=name, pb=self, **kwargs) - class Parameterization: """ This is the top-level parameterization class from which all the other parameterizations inherit. """ + def __init__(self, name): self.name = name print(name, type(self)) @@ -534,84 +579,103 @@ def __init__(self, name): def get_samples(self, pb): pass + class FixedParameterization(Parameterization): """ A parameterization that takes a single value for all input. It does not depend on anything except its hyperparameters """ - def __init__(self, name, pb:ParamBuilder,**kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) - dist = kwargs.get(f'{name}_dist','normal') - params = kwargs.get(f'{name}_params',(0.,1.)) + dist = kwargs.get(f"{name}_dist", "normal") + params = kwargs.get(f"{name}_params", (0.0, 1.0)) self.dist = Prior(name, dist, params, pb) def get_samples(self, pb): with pb.model: return self.dist[0] + class CentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a central fashion; - the values are sampled from normal distribution with a group mean and group variance + the values are sampled from normal distribution with a group mean and group variance """ - def __init__(self, name, pb:ParamBuilder, **kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) # Normal distribution is default for mean - mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) - - # HalfStudent is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) - - dim = 'basis_functions' if self.name.startswith('slope') else 'empty' - self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=[*pb.batch_effect_dim_names,dim]) + mu_dist = kwargs.get(f"mu_{name}_dist", "normal") + mu_params = kwargs.get(f"mu_{name}_params", (0.0, 1.0)) + mu_prior = Prior(f"mu_{name}", mu_dist, mu_params, pb) + + # HalfNormal is default for sigma + sigma_dist = kwargs.get(f"sigma_{name}_dist", "hnormal") + sigma_params = kwargs.get(f"sigma_{name}_params", (1.0,)) + sigma_prior = Prior(f"sigma_{name}", sigma_dist, sigma_params, pb) + + dim = "basis_functions" if self.name.startswith("slope") else "empty" + self.dist = pm.Normal( + name=name, + mu=mu_prior.dist, + sigma=sigma_prior.dist, + dims=[*pb.batch_effect_dim_names, dim], + ) - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb: ParamBuilder): with pb.model: samples = self.dist[*pb.batch_effect_indices] return samples + class NonCentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a non-central fashion; - the values are a sum of a group mean and noise values scaled with a group scaling factor + the values are a sum of a group mean and noise values scaled with a group scaling factor """ - def __init__(self, name, pb:ParamBuilder, **kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) # Normal distribution is default for mean - mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) + mu_dist = kwargs.get(f"mu_{name}_dist", "normal") + mu_params = kwargs.get(f"mu_{name}_params", (0.0, 1.0)) + mu_prior = Prior(f"mu_{name}", mu_dist, mu_params, pb) - # HalfStudent is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) + # HalfNormal is default for sigma + sigma_dist = kwargs.get(f"sigma_{name}_dist", "hnormal") + sigma_params = kwargs.get(f"sigma_{name}_params", (1.0,)) + sigma_prior = Prior(f"sigma_{name}", sigma_dist, sigma_params, pb) # Normal is default for offset - offset_dist = kwargs.get(f'offset_{name}_dist','normal') - offset_params = kwargs.get(f'offset_{name}_params',(0.,1.)) - offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, has_random_effect=True) - - dim = 'basis_functions' if self.name.startswith('slope') else 'empty' - self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist,dims=[*pb.batch_effect_dim_names,dim]) + offset_dist = kwargs.get(f"offset_{name}_dist", "normal") + offset_params = kwargs.get(f"offset_{name}_params", (0.0, 1.0)) + offset_prior = Prior( + f"offset_{name}", offset_dist, offset_params, pb, has_random_effect=True + ) + dim = "basis_functions" if self.name.startswith("slope") else "empty" + self.dist = pm.Deterministic( + name=name, + var=mu_prior.dist + sigma_prior.dist * offset_prior.dist, + dims=[*pb.batch_effect_dim_names, dim], + ) - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb: ParamBuilder): with pb.model: - # print(f"{self.dist.shape.eval()=}") samples = self.dist[*pb.batch_effect_indices] return samples + class LinearParameterization(Parameterization): """ - A parameterization that can model a linear dependence on X. + A parameterization that can model a linear dependence on X. """ - def __init__(self, name, slope_parameterization, intercept_parameterization,**kwargs): + + def __init__( + self, name, slope_parameterization, intercept_parameterization, **kwargs + ): super().__init__(name) self.slope_parameterization = slope_parameterization self.intercept_parameterization = intercept_parameterization @@ -619,17 +683,17 @@ def __init__(self, name, slope_parameterization, intercept_parameterization,**kw def get_samples(self, pb): with pb.model: intc = self.intercept_parameterization.get_samples(pb) - slope_samples = self.slope_parameterization.get_samples(pb) - if pb.configs[f'random_slope_{self.name}']: - slope = pb.X*self.slope_parameterization.get_samples(pb) + if pb.configs[f"random_slope_{self.name}"]: + slope = pb.X * self.slope_parameterization.get_samples(pb) slope = slope.sum(axis=-1) else: - slope = pb.X@self.slope_parameterization.get_samples(pb) + slope = pb.X @ self.slope_parameterization.get_samples(pb) samples = pm.math.flatten(intc) + pm.math.flatten(slope) - samples = samples.reshape((samples.shape[0],1)) + samples = samples.reshape((samples.shape[0], 1)) return samples + def get_design_matrix(X, nm, basis="linear"): if basis == "bspline": Phi = bspline_transform(X, nm.hbr.bsp) @@ -641,8 +705,8 @@ def get_design_matrix(X, nm, basis="linear"): def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): - n_hidden = configs['nn_hidden_neuron_num'] - n_layers = configs['nn_hidden_layers_num'] + n_hidden = configs["nn_hidden_neuron_num"] + n_layers = configs["nn_hidden_layers_num"] feature_num = X.shape[1] batch_effects_num = batch_effects.shape[1] all_idx = [] @@ -651,14 +715,18 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): be_idx = list(product(*all_idx)) # Initialize random weights between each layer for the mu: - init_1 = pm.floatX(np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num)) + init_1 = pm.floatX( + np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num) + ) init_out = pm.floatX(np.random.randn(n_hidden) * np.sqrt(1 / n_hidden)) std_init_1 = pm.floatX(np.random.rand(feature_num, n_hidden)) std_init_out = pm.floatX(np.random.rand(n_hidden)) # And initialize random weights between each layer for sigma_noise: - init_1_noise = pm.floatX(np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num)) + init_1_noise = pm.floatX( + np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num) + ) init_out_noise = pm.floatX(np.random.randn(n_hidden) * np.sqrt(1 / n_hidden)) std_init_1_noise = pm.floatX(np.random.rand(feature_num, n_hidden)) @@ -668,90 +736,134 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): if n_layers == 2: init_2 = pm.floatX(np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden)) std_init_2 = pm.floatX(np.random.rand(n_hidden, n_hidden)) - init_2_noise = pm.floatX(np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden)) + init_2_noise = pm.floatX( + np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden) + ) std_init_2_noise = pm.floatX(np.random.rand(n_hidden, n_hidden)) with pm.Model() as model: - - X = pm.Data('X', X) - y = pm.Data('y', y) + X = pm.Data("X", X) + y = pm.Data("y", y) if idata is not None: # Used when estimating/predicting on a new site - weights_in_1_grp = from_posterior('w_in_1_grp', idata['w_in_1_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', idata['w_in_1_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) + weights_in_1_grp = from_posterior( + "w_in_1_grp", + idata["w_in_1_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_in_1_grp_sd = from_posterior( + "w_in_1_grp_sd", + idata["w_in_1_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) if n_layers == 2: - weights_1_2_grp = from_posterior('w_1_2_grp', idata['w_1_2_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', idata['w_1_2_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) - - weights_2_out_grp = from_posterior('w_2_out_grp', idata['w_2_out_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', idata['w_2_out_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) - - mu_prior_intercept = from_posterior('mu_prior_intercept', idata['mu_prior_intercept'], - distribution='normal', freedom=configs['freedom']) - sigma_prior_intercept = from_posterior('sigma_prior_intercept', idata['sigma_prior_intercept'], - distribution='hcauchy', freedom=configs['freedom']) + weights_1_2_grp = from_posterior( + "w_1_2_grp", + idata["w_1_2_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_1_2_grp_sd = from_posterior( + "w_1_2_grp_sd", + idata["w_1_2_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + weights_2_out_grp = from_posterior( + "w_2_out_grp", + idata["w_2_out_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_2_out_grp_sd = from_posterior( + "w_2_out_grp_sd", + idata["w_2_out_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + mu_prior_intercept = from_posterior( + "mu_prior_intercept", + idata["mu_prior_intercept"], + distribution="normal", + freedom=configs["freedom"], + ) + sigma_prior_intercept = from_posterior( + "sigma_prior_intercept", + idata["sigma_prior_intercept"], + distribution="hcauchy", + freedom=configs["freedom"], + ) else: # Group the mean distribution for input to the hidden layer: - weights_in_1_grp = pm.Normal('w_in_1_grp', 0, sd=1, - shape=(feature_num, n_hidden), testval=init_1) + weights_in_1_grp = pm.Normal( + "w_in_1_grp", 0, sd=1, shape=(feature_num, n_hidden), testval=init_1 + ) # Group standard deviation: - weights_in_1_grp_sd = pm.HalfCauchy('w_in_1_grp_sd', 1., - shape=(feature_num, n_hidden), testval=std_init_1) + weights_in_1_grp_sd = pm.HalfCauchy( + "w_in_1_grp_sd", 1.0, shape=(feature_num, n_hidden), testval=std_init_1 + ) if n_layers == 2: # Group the mean distribution for hidden layer 1 to hidden layer 2: - weights_1_2_grp = pm.Normal('w_1_2_grp', 0, sd=1, - shape=(n_hidden, n_hidden), testval=init_2) + weights_1_2_grp = pm.Normal( + "w_1_2_grp", 0, sd=1, shape=(n_hidden, n_hidden), testval=init_2 + ) # Group standard deviation: - weights_1_2_grp_sd = pm.HalfCauchy('w_1_2_grp_sd', 1., - shape=(n_hidden, n_hidden), testval=std_init_2) + weights_1_2_grp_sd = pm.HalfCauchy( + "w_1_2_grp_sd", 1.0, shape=(n_hidden, n_hidden), testval=std_init_2 + ) # Group the mean distribution for hidden to output: - weights_2_out_grp = pm.Normal('w_2_out_grp', 0, sd=1, - shape=(n_hidden,), testval=init_out) + weights_2_out_grp = pm.Normal( + "w_2_out_grp", 0, sd=1, shape=(n_hidden,), testval=init_out + ) # Group standard deviation: - weights_2_out_grp_sd = pm.HalfCauchy('w_2_out_grp_sd', 1., - shape=(n_hidden,), testval=std_init_out) + weights_2_out_grp_sd = pm.HalfCauchy( + "w_2_out_grp_sd", 1.0, shape=(n_hidden,), testval=std_init_out + ) # mu_prior_intercept = pm.Uniform('mu_prior_intercept', lower=-100, upper=100) - mu_prior_intercept = pm.Normal('mu_prior_intercept', mu=0., sigma=1e3) - sigma_prior_intercept = pm.HalfCauchy('sigma_prior_intercept', 5) + mu_prior_intercept = pm.Normal("mu_prior_intercept", mu=0.0, sigma=1e3) + sigma_prior_intercept = pm.HalfCauchy("sigma_prior_intercept", 5) # Now create separate weights for each group, by doing # weights * group_sd + group_mean, we make sure the new weights are # coming from the (group_mean, group_sd) distribution. - weights_in_1_raw = pm.Normal('w_in_1', 0, sd=1, - shape=(batch_effects_size + [feature_num, n_hidden])) + weights_in_1_raw = pm.Normal( + "w_in_1", 0, sd=1, shape=(batch_effects_size + [feature_num, n_hidden]) + ) weights_in_1 = weights_in_1_raw * weights_in_1_grp_sd + weights_in_1_grp if n_layers == 2: - weights_1_2_raw = pm.Normal('w_1_2', 0, sd=1, - shape=(batch_effects_size + [n_hidden, n_hidden])) + weights_1_2_raw = pm.Normal( + "w_1_2", 0, sd=1, shape=(batch_effects_size + [n_hidden, n_hidden]) + ) weights_1_2 = weights_1_2_raw * weights_1_2_grp_sd + weights_1_2_grp - weights_2_out_raw = pm.Normal('w_2_out', 0, sd=1, - shape=(batch_effects_size + [n_hidden])) + weights_2_out_raw = pm.Normal( + "w_2_out", 0, sd=1, shape=(batch_effects_size + [n_hidden]) + ) weights_2_out = weights_2_out_raw * weights_2_out_grp_sd + weights_2_out_grp - intercepts_offset = pm.Normal('intercepts_offset', mu=0, sd=1, - shape=(batch_effects_size)) + intercepts_offset = pm.Normal( + "intercepts_offset", mu=0, sd=1, shape=(batch_effects_size) + ) - intercepts = pm.Deterministic('intercepts', intercepts_offset + - mu_prior_intercept * sigma_prior_intercept) + intercepts = pm.Deterministic( + "intercepts", intercepts_offset + mu_prior_intercept * sigma_prior_intercept + ) # Build the neural network and estimate y_hat: y_hat = pytensor.tensor.zeros(y.shape) @@ -765,83 +877,144 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): act_1 = pm.math.tanh(pytensor.tensor.dot(X[idx, :], weights_in_1[be])) if n_layers == 2: act_2 = pm.math.tanh(pytensor.tensor.dot(act_1, weights_1_2[be])) - y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + pytensor.tensor.dot(act_2, weights_2_out[be])) + y_hat = pytensor.tensor.set_subtensor( + y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_2, weights_2_out[be]), + ) else: - y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + pytensor.tensor.dot(act_1, weights_2_out[be])) + y_hat = pytensor.tensor.set_subtensor( + y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_1, weights_2_out[be]), + ) # If we want to estimate varying noise terms across groups: - if configs['random_noise']: - if configs['hetero_noise']: + if configs["random_noise"]: + if configs["hetero_noise"]: if idata is not None: # # Used when estimating/predicting on a new site - weights_in_1_grp_noise = from_posterior('w_in_1_grp_noise', - idata['w_in_1_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_in_1_grp_sd_noise = from_posterior('w_in_1_grp_sd_noise', - idata['w_in_1_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) + weights_in_1_grp_noise = from_posterior( + "w_in_1_grp_noise", + idata["w_in_1_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_in_1_grp_sd_noise = from_posterior( + "w_in_1_grp_sd_noise", + idata["w_in_1_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) if n_layers == 2: - weights_1_2_grp_noise = from_posterior('w_1_2_grp_noise', - idata['w_1_2_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_1_2_grp_sd_noise = from_posterior('w_1_2_grp_sd_noise', - idata['w_1_2_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) - - weights_2_out_grp_noise = from_posterior('w_2_out_grp_noise', - idata['w_2_out_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_2_out_grp_sd_noise = from_posterior('w_2_out_grp_sd_noise', - idata['w_2_out_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) + weights_1_2_grp_noise = from_posterior( + "w_1_2_grp_noise", + idata["w_1_2_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_1_2_grp_sd_noise = from_posterior( + "w_1_2_grp_sd_noise", + idata["w_1_2_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + weights_2_out_grp_noise = from_posterior( + "w_2_out_grp_noise", + idata["w_2_out_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_2_out_grp_sd_noise = from_posterior( + "w_2_out_grp_sd_noise", + idata["w_2_out_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) else: # The input layer to the first hidden layer: - weights_in_1_grp_noise = pm.Normal('w_in_1_grp_noise', 0, sd=1, - shape=(feature_num, n_hidden), - testval=init_1_noise) - weights_in_1_grp_sd_noise = pm.HalfCauchy('w_in_1_grp_sd_noise', 1, - shape=(feature_num, n_hidden), - testval=std_init_1_noise) + weights_in_1_grp_noise = pm.Normal( + "w_in_1_grp_noise", + 0, + sd=1, + shape=(feature_num, n_hidden), + testval=init_1_noise, + ) + weights_in_1_grp_sd_noise = pm.HalfCauchy( + "w_in_1_grp_sd_noise", + 1, + shape=(feature_num, n_hidden), + testval=std_init_1_noise, + ) # The first hidden layer to second hidden layer: if n_layers == 2: - weights_1_2_grp_noise = pm.Normal('w_1_2_grp_noise', 0, sd=1, - shape=(n_hidden, n_hidden), - testval=init_2_noise) - weights_1_2_grp_sd_noise = pm.HalfCauchy('w_1_2_grp_sd_noise', 1, - shape=(n_hidden, n_hidden), - testval=std_init_2_noise) + weights_1_2_grp_noise = pm.Normal( + "w_1_2_grp_noise", + 0, + sd=1, + shape=(n_hidden, n_hidden), + testval=init_2_noise, + ) + weights_1_2_grp_sd_noise = pm.HalfCauchy( + "w_1_2_grp_sd_noise", + 1, + shape=(n_hidden, n_hidden), + testval=std_init_2_noise, + ) # The second hidden layer to output layer: - weights_2_out_grp_noise = pm.Normal('w_2_out_grp_noise', 0, sd=1, - shape=(n_hidden,), - testval=init_out_noise) - weights_2_out_grp_sd_noise = pm.HalfCauchy('w_2_out_grp_sd_noise', 1, - shape=(n_hidden,), - testval=std_init_out_noise) + weights_2_out_grp_noise = pm.Normal( + "w_2_out_grp_noise", + 0, + sd=1, + shape=(n_hidden,), + testval=init_out_noise, + ) + weights_2_out_grp_sd_noise = pm.HalfCauchy( + "w_2_out_grp_sd_noise", + 1, + shape=(n_hidden,), + testval=std_init_out_noise, + ) # mu_prior_intercept_noise = pm.HalfNormal('mu_prior_intercept_noise', sigma=1e3) # sigma_prior_intercept_noise = pm.HalfCauchy('sigma_prior_intercept_noise', 5) # Now create separate weights for each group: - weights_in_1_raw_noise = pm.Normal('w_in_1_noise', 0, sd=1, - shape=(batch_effects_size + [feature_num, n_hidden])) - weights_in_1_noise = weights_in_1_raw_noise * weights_in_1_grp_sd_noise + weights_in_1_grp_noise + weights_in_1_raw_noise = pm.Normal( + "w_in_1_noise", + 0, + sd=1, + shape=(batch_effects_size + [feature_num, n_hidden]), + ) + weights_in_1_noise = ( + weights_in_1_raw_noise * weights_in_1_grp_sd_noise + + weights_in_1_grp_noise + ) if n_layers == 2: - weights_1_2_raw_noise = pm.Normal('w_1_2_noise', 0, sd=1, - shape=(batch_effects_size + [n_hidden, n_hidden])) - weights_1_2_noise = weights_1_2_raw_noise * weights_1_2_grp_sd_noise + weights_1_2_grp_noise - - weights_2_out_raw_noise = pm.Normal('w_2_out_noise', 0, sd=1, - shape=(batch_effects_size + [n_hidden])) - weights_2_out_noise = weights_2_out_raw_noise * weights_2_out_grp_sd_noise + weights_2_out_grp_noise + weights_1_2_raw_noise = pm.Normal( + "w_1_2_noise", + 0, + sd=1, + shape=(batch_effects_size + [n_hidden, n_hidden]), + ) + weights_1_2_noise = ( + weights_1_2_raw_noise * weights_1_2_grp_sd_noise + + weights_1_2_grp_noise + ) + + weights_2_out_raw_noise = pm.Normal( + "w_2_out_noise", 0, sd=1, shape=(batch_effects_size + [n_hidden]) + ) + weights_2_out_noise = ( + weights_2_out_raw_noise * weights_2_out_grp_sd_noise + + weights_2_out_grp_noise + ) # intercepts_offset_noise = pm.Normal('intercepts_offset_noise', mu=0, sd=1, # shape=(batch_effects_size)) @@ -857,20 +1030,45 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1_noise = pm.math.sigmoid(pytensor.tensor.dot(X[idx, :], weights_in_1_noise[be])) + act_1_noise = pm.math.sigmoid( + pytensor.tensor.dot(X[idx, :], weights_in_1_noise[be]) + ) if n_layers == 2: - act_2_noise = pm.math.sigmoid(pytensor.tensor.dot(act_1_noise, weights_1_2_noise[be])) - temp = pm.math.log1pexp(pytensor.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 + act_2_noise = pm.math.sigmoid( + pytensor.tensor.dot(act_1_noise, weights_1_2_noise[be]) + ) + temp = ( + pm.math.log1pexp( + pytensor.tensor.dot( + act_2_noise, weights_2_out_noise[be] + ) + ) + + 1e-5 + ) else: - temp = pm.math.log1pexp(pytensor.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 + temp = ( + pm.math.log1pexp( + pytensor.tensor.dot( + act_1_noise, weights_2_out_noise[be] + ) + ) + + 1e-5 + ) sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: if idata is not None: # Used for transferring the priors - upper_bound = np.percentile(idata['sigma_noise'], 95) - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound, shape=(batch_effects_size)) + upper_bound = np.percentile(idata["sigma_noise"], 95) + sigma_noise = pm.Uniform( + "sigma_noise", + lower=0, + upper=2 * upper_bound, + shape=(batch_effects_size), + ) else: - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) + sigma_noise = pm.Uniform( + "sigma_noise", lower=0, upper=100, shape=(batch_effects_size) + ) sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: @@ -879,14 +1077,16 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) + sigma_y = pytensor.tensor.set_subtensor( + sigma_y[idx, 0], sigma_noise[be] + ) else: # do not allow for random noise terms across groups: if idata is not None: # Used for transferring the priors - upper_bound = np.percentile(idata['sigma_noise'], 95) - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) + upper_bound = np.percentile(idata["sigma_noise"], 95) + sigma_noise = pm.Uniform("sigma_noise", lower=0, upper=2 * upper_bound) else: - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) + sigma_noise = pm.Uniform("sigma_noise", lower=0, upper=100) sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] @@ -894,10 +1094,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) - - if configs['skewed_likelihood']: - skewness = pm.Uniform('skewness', lower=-10, upper=10, shape=(batch_effects_size)) + sigma_y = pytensor.tensor.set_subtensor( + sigma_y[idx, 0], sigma_noise + ) + + if configs["skewed_likelihood"]: + skewness = pm.Uniform( + "skewness", lower=-10, upper=10, shape=(batch_effects_size) + ) alpha = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] @@ -909,6 +1113,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): else: alpha = 0 # symmetrical normal distribution - y_like = pm.SkewNormal('y_like', mu=y_hat, sigma=sigma_y, alpha=alpha, observed=y) + y_like = pm.SkewNormal( + "y_like", mu=y_hat, sigma=sigma_y, alpha=alpha, observed=y + ) return model From 838a1bb6320ea48a514d036753679e62a3d6f83f Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Wed, 17 May 2023 01:01:02 +0200 Subject: [PATCH 20/43] An attempt of SHASH distributions in HBR --- pcntoolkit/model/SHASH.py | 219 ++++++--- pcntoolkit/model/hbr.py | 971 +++++++++++++++++++++++--------------- tests/test_hbr_pymc.py | 16 +- 3 files changed, 750 insertions(+), 456 deletions(-) diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index ba8379d4..78424bd4 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -1,3 +1,4 @@ +from typing import Union, List, Optional import pymc as pm from pymc import floatX from pymc.distributions import Continuous @@ -7,7 +8,6 @@ from pytensor.graph.op import Op from pytensor.graph import Apply from pytensor.gradient import grad_not_implemented -from pytensor.tensor.math import sinh, arcsinh, tanh, arctanh,cosh, arccosh from pytensor.tensor.random.basic import normal from pytensor.tensor.random.op import RandomVariable @@ -22,34 +22,58 @@ See: Jones et al. (2009), Sinh-Arcsinh distributions. """ +def numpy_P(q): + """ + The P function as given in Jones et al. + :param q: + :return: + """ + frac = np.exp(1.0 / 4.0) / np.power(8.0 * np.pi, 1.0 / 2.0) + K1 = numpy_K((q + 1) / 2, 1.0 / 4.0) + K2 = numpy_K((q - 1) / 2, 1.0 / 4.0) + a = (K1 + K2) * frac + return a + +def numpy_K(p, x): + """ + Computes the values of spp.kv(p,x) for only the unique values of p + """ + + ps, idxs = np.unique(p, return_inverse=True) + return spp.kv(ps, x)[idxs].reshape(p.shape) class K(Op): """ Modified Bessel function of the second kind, pytensor implementation """ + __props__ = () - def make_node(self, p, x): - p = pt.tensor.as_tensor_variable(p, 'floatX') - x = pt.tensor.as_tensor_variable(x, 'floatX') - return Apply(self, [p,x], [p.type()]) + p = pt.tensor.as_tensor_variable(p, "floatX") + x = pt.tensor.as_tensor_variable(x, "floatX") + return Apply(self, [p, x], [p.type()]) def perform(self, node, inputs_storage, output_storage): # Doing this on the unique values avoids doing A LOT OF double work, apparently scipy doesn't do this by itself - unique_inputs, inverse_indices = np.unique(inputs_storage[0], return_inverse=True) + + unique_inputs, inverse_indices = np.unique( + inputs_storage[0], return_inverse=True + ) unique_outputs = spp.kv(unique_inputs, inputs_storage[1]) outputs = unique_outputs[inverse_indices].reshape(inputs_storage[0].shape) output_storage[0][0] = outputs + # TODO see if writing a non-pytensor perform and using it in grad instead of self() will speed it up def grad(self, inputs, output_grads): # Approximation of the derivative. This should suffice for using NUTS dp = 1e-10 p = inputs[0] x = inputs[1] - grad = (self(p+dp,x) - self(p, x))/dp - return [output_grads[0]*grad, grad_not_implemented(0,1,2,3)] - + grad = (self(p + dp, x) - self(p, x)) / dp + return [output_grads[0] * grad, grad_not_implemented(0, 1, 2, 3)] + + def S(x, epsilon, delta): """ :param epsilon: @@ -57,12 +81,14 @@ def S(x, epsilon, delta): :param x: :return: The sinharcsinh transformation of x """ - return sinh(arcsinh(x) * delta - epsilon) + return np.sinh(np.arcsinh(x) * delta - epsilon) -def S_inv( x, epsilon, delta): - return sinh((arcsinh(x) + epsilon) / delta) -def C( x, epsilon, delta): +def S_inv(x, epsilon, delta): + return np.sinh((np.arcsinh(x) + epsilon) / delta) + + +def C(x, epsilon, delta): """ :param epsilon: :param delta: @@ -70,7 +96,8 @@ def C( x, epsilon, delta): :return: the cosharcsinh transformation of x Be aware that this is sqrt(1+S(x)^2), so you may save some compute if you can re-use the result from S. """ - return cosh(arcsinh(x) * delta - epsilon) + return np.cosh(np.arcsinh(x) * delta - epsilon) + def P(q): """ @@ -78,39 +105,46 @@ def P(q): :param q: :return: """ - frac = ptt.exp(1./ 4.) / ptt.power(8. * np.pi, 1./2.) - K1 = K()((q+1)/2,1./4.) - K2 = K()((q-1)/2,1./4.) + frac = np.exp(1.0 / 4.0) / np.power(8.0 * np.pi, 1.0 / 2.0) + K1 = K()((q + 1) / 2, 1.0 / 4.0) + K2 = K()((q - 1) / 2, 1.0 / 4.0) a = (K1 + K2) * frac return a + def m(epsilon, delta, r): """ :param epsilon: :param delta: :param r: :return: The r'th uncentered moment of the SHASH distribution parameterized by epsilon and delta. Given by Jones et al. - The first four moments are given in closed form. + The first four moments are given in closed form. """ if r == 1: - return sinh(epsilon/delta)*P(1/delta) - elif r==2: - return (cosh(2*epsilon/delta)*P(2/delta)-1)/2 - elif r==3: - return (sinh(3*epsilon/delta)*P(3/delta) - 3*sinh(epsilon/delta)*P(1/delta))/4 - elif r==4: - return (cosh(4*epsilon/delta)*P(4/delta)-4*cosh(2*epsilon/delta)*P(2/delta)+3)/8 + return np.sinh(epsilon / delta) * P(1 / delta) + elif r == 2: + return (np.cosh(2 * epsilon / delta) * P(2 / delta) - 1) / 2 + elif r == 3: + return ( + np.sinh(3 * epsilon / delta) * P(3 / delta) + - 3 * np.sinh(epsilon / delta) * P(1 / delta) + ) / 4 + elif r == 4: + return ( + np.cosh(4 * epsilon / delta) * P(4 / delta) + - 4 * np.cosh(2 * epsilon / delta) * P(2 / delta) + + 3 + ) / 8 else: frac1 = ptt.as_tensor_variable(1 / pm.power(2, r)) acc = ptt.as_tensor_variable(0) for i in range(r + 1): combs = spp.comb(r, i) flip = pm.power(-1, i) - ex = np.exp((r - 2 * i) * self.epsilon / self.delta) - p = self.P((r - 2 * i) / self.delta) + ex = np.exp((r - 2 * i) * epsilon / delta) + p = P((r - 2 * i) / delta) acc += combs * flip * ex * p - return frac1 * acc - + return frac1 * acc class SHASH(RandomVariable): @@ -122,17 +156,19 @@ class SHASH(RandomVariable): @classmethod def rng_fn(cls, rng, epsilon, delta, size=None) -> np.ndarray: - return sinh((arcsinh(rng.normal(loc=0,scale=1, size=size))+epsilon)/delta) - + return np.sinh((np.arcsinh(rng.normal(loc=0, scale=1, size=size)) + epsilon) / delta) + + shash = SHASH() + class SHASH(Continuous): - rv_op = shash """ SHASH described by Jones et al., based on a standard normal All SHASH subclasses inherit from this """ + @classmethod def dist(cls, epsilon, delta, **kwargs): epsilon = ptt.as_tensor_variable(floatX(epsilon)) @@ -142,14 +178,15 @@ def dist(cls, epsilon, delta, **kwargs): def logp(value, epsilon, delta): this_S = S(value, epsilon, delta) this_S_sqr = ptt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 - frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(value)) / 2 + this_C_sqr = 1 + this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi)) / 2 + frac2 = ( + ptt.log(delta) + ptt.log(this_C_sqr) / 2 - ptt.log(1 + ptt.sqr(value)) / 2 + ) exp = -this_S_sqr / 2 return frac1 + frac2 + exp - class SHASHoRV(RandomVariable): name = "shasho" ndim_supp = 0 @@ -160,36 +197,41 @@ class SHASHoRV(RandomVariable): @classmethod def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: s = rng.normal(size=size) - return sinh((arcsinh(s)+epsilon)/delta)*sigma + mu - + return np.sinh((np.arcsinh(s) + epsilon) / delta) * sigma + mu + + shasho = SHASHoRV() - + + class SHASHo(Continuous): rv_op = shasho """ This is the shash where the location and scale parameters have simply been applied as an linear transformation directly on the original shash. """ + @classmethod def dist(cls, mu, sigma, epsilon, delta, **kwargs): mu = ptt.as_tensor_variable(floatX(mu)) - sigma= ptt.as_tensor_variable(floatX(sigma)) + sigma = ptt.as_tensor_variable(floatX(sigma)) epsilon = ptt.as_tensor_variable(floatX(epsilon)) delta = ptt.as_tensor_variable(floatX(delta)) return super().dist([mu, sigma, epsilon, delta], **kwargs) - + def logp(value, mu, sigma, epsilon, delta): print(mu, sigma, epsilon, delta) - remapped_value = (value-mu)/sigma + remapped_value = (value - mu) / sigma this_S = S(remapped_value, epsilon, delta) this_S_sqr = ptt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 - frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + this_C_sqr = 1 + this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi)) / 2 + frac2 = ( + ptt.log(delta) + + ptt.log(this_C_sqr) / 2 + - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + ) exp = -this_S_sqr / 2 return frac1 + frac2 + exp - ptt.log(sigma) - - class SHASHo2RV(RandomVariable): @@ -202,53 +244,72 @@ class SHASHo2RV(RandomVariable): @classmethod def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: s = rng.normal(size=size) - sigma_d = sigma/delta - return sinh((arcsinh(s)+epsilon)/delta)*sigma_d + mu - + sigma_d = sigma / delta + return np.sinh((np.arcsinh(s) + epsilon) / delta) * sigma_d + mu + + shasho2 = SHASHo2RV() - + + class SHASHo2(Continuous): rv_op = shasho2 """ This is the shash where we apply the reparameterization provided in section 4.3 in Jones et al. """ + @classmethod def dist(cls, mu, sigma, epsilon, delta, **kwargs): mu = ptt.as_tensor_variable(floatX(mu)) - sigma= ptt.as_tensor_variable(floatX(sigma)) + sigma = ptt.as_tensor_variable(floatX(sigma)) epsilon = ptt.as_tensor_variable(floatX(epsilon)) delta = ptt.as_tensor_variable(floatX(delta)) return super().dist([mu, sigma, epsilon, delta], **kwargs) - + def logp(value, mu, sigma, epsilon, delta): - sigma_d = sigma/delta - remapped_value = (value-mu)/sigma_d + sigma_d = sigma / delta + remapped_value = (value - mu) / sigma_d this_S = S(remapped_value, epsilon, delta) this_S_sqr = ptt.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -ptt.log(ptt.constant(2 * np.pi))/2 - frac2 = ptt.log(delta) + ptt.log(this_C_sqr)/2 - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + this_C_sqr = 1 + this_S_sqr + frac1 = -ptt.log(ptt.constant(2 * np.pi)) / 2 + frac2 = ( + ptt.log(delta) + + ptt.log(this_C_sqr) / 2 + - ptt.log(1 + ptt.sqr(remapped_value)) / 2 + ) exp = -this_S_sqr / 2 return frac1 + frac2 + exp - ptt.log(sigma_d) - - + class SHASHbRV(RandomVariable): - name = "shasho2" + name = "shashb" ndim_supp = 0 ndims_params = [0, 0, 0, 0] dtype = "floatX" _print_name = ("SHASHo2", "\\operatorname{SHASHo2}") @classmethod - def rng_fn(cls, rng, mu, sigma, epsilon, delta, size=None) -> np.ndarray: + def rng_fn( + cls, + rng: np.random.RandomState, + mu: Union[np.ndarray, float], + sigma: Union[np.ndarray, float], + epsilon: Union[np.ndarray, float], + delta: Union[np.ndarray, float], + size: Optional[Union[List[int], int]], + ) -> np.ndarray: s = rng.normal(size=size) - mean = (sinh(epsilon/delta)*P(1/delta)) - var = ((cosh(2*epsilon/delta)*P(2/delta)-1)/2)-mean**2 - return ((sinh((arcsinh(s)+epsilon)/delta)-mean)/pm.sqrt(var))*sigma + mu - + mean = np.sinh(epsilon / delta) * numpy_P(1 / delta) + var = ((np.cosh(2 * epsilon / delta) * numpy_P(2 / delta) - 1) / 2) - mean**2 + out = ( + (np.sinh((np.arcsinh(s) + epsilon) / delta) - mean) / np.sqrt(var) + ) * sigma + mu + return out + + shashb = SHASHbRV() - + + class SHASHb(Continuous): rv_op = shashb """ @@ -259,19 +320,23 @@ class SHASHb(Continuous): @classmethod def dist(cls, mu, sigma, epsilon, delta, **kwargs): mu = ptt.as_tensor_variable(floatX(mu)) - sigma= ptt.as_tensor_variable(floatX(sigma)) + sigma = ptt.as_tensor_variable(floatX(sigma)) epsilon = ptt.as_tensor_variable(floatX(epsilon)) delta = ptt.as_tensor_variable(floatX(delta)) return super().dist([mu, sigma, epsilon, delta], **kwargs) - + def logp(value, mu, sigma, epsilon, delta): - mean = sinh(epsilon/delta)*P(1/delta) - var = (cosh(2*epsilon/delta)*P(2/delta)-1)/2 - pm.math.sqr(mean) - remapped_value = ((value - mu) / sigma) * pm.math.sqrt(var) + mean + mean = m(epsilon,delta,1) + var = m(epsilon,delta,2) + remapped_value = ((value - mu) / sigma) * np.sqrt(var) + mean this_S = S(remapped_value, epsilon, delta) - this_S_sqr = pm.math.sqr(this_S) - this_C_sqr = 1+this_S_sqr - frac1 = -pm.math.log(ptt.constant(2 * np.pi))/2 - frac2 = pm.math.log(delta) + pm.math.log(this_C_sqr)/2 - pm.math.log(1 + pm.math.sqr(remapped_value)) / 2 + this_S_sqr = np.square(this_S) + this_C_sqr = 1 + this_S_sqr + frac1 = -np.log(2 * np.pi) / 2 + frac2 = ( + np.log(delta) + + np.log(this_C_sqr) / 2 + - np.log(1 + np.square(remapped_value)) / 2 + ) exp = -this_S_sqr / 2 - return frac1 + frac2 + exp + pm.math.log(var)/2 - pm.math.log(sigma) \ No newline at end of file + return frac1 + frac2 + exp + np.log(var)/2 - np.log(sigma) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 7c4c93cf..45658b1b 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -32,23 +32,25 @@ from pcntoolkit.util.utils import cartesian_product from pcntoolkit.model.SHASH import * + def bspline_fit(X, order, nknots): feature_num = X.shape[1] bsp_basis = [] for i in range(feature_num): - minx = np.min(X[:,i]) - maxx = np.max(X[:,i]) - delta = maxx-minx + minx = np.min(X[:, i]) + maxx = np.max(X[:, i]) + delta = maxx - minx # Expand range by 20% (10% on both sides) - splinemin = minx-0.1*delta - splinemax = maxx+0.1*delta + splinemin = minx - 0.1 * delta + splinemax = maxx + 0.1 * delta knots = np.linspace(splinemin, splinemax, nknots) k = splinelab.augknt(knots, order) bsp_basis.append(bspline.Bspline(k, order)) return bsp_basis + def bspline_transform(X, bsp_basis): if type(bsp_basis) != list: temp = [] @@ -63,8 +65,9 @@ def bspline_transform(X, bsp_basis): return X_transformed + def create_poly_basis(X, order): - """ compute a polynomial basis expansion of the specified order""" + """compute a polynomial basis expansion of the specified order""" if len(X.shape) == 1: X = X[:, np.newaxis] @@ -72,17 +75,18 @@ def create_poly_basis(X, order): Phi = np.zeros((X.shape[0], D * order)) colid = np.arange(0, D) for d in range(1, order + 1): - Phi[:, colid] = X ** d + Phi[:, colid] = X**d colid += D return Phi + def from_posterior(param, samples, distribution=None, half=False, freedom=1): if len(samples.shape) > 1: shape = samples.shape[1:] else: shape = None - if (distribution is None): + if distribution is None: smin, smax = np.min(samples), np.max(samples) width = smax - smin x = np.linspace(smin, smax, 1000) @@ -97,58 +101,77 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): return pm.distributions.Interpolated(param, x, y) else: return pm.distributions.Interpolated(param, x, y, shape=shape) - elif (distribution == 'normal'): + elif distribution == "normal": temp = stats.norm.fit(samples) if shape is None: return pm.Normal(param, mu=temp[0], sigma=freedom * temp[1]) else: return pm.Normal(param, mu=temp[0], sigma=freedom * temp[1], shape=shape) - elif (distribution == 'hnormal'): + elif distribution == "hnormal": temp = stats.halfnorm.fit(samples) if shape is None: return pm.HalfNormal(param, sigma=freedom * temp[1]) else: return pm.HalfNormal(param, sigma=freedom * temp[1], shape=shape) - elif (distribution == 'hcauchy'): + elif distribution == "hcauchy": temp = stats.halfcauchy.fit(samples) if shape is None: return pm.HalfCauchy(param, freedom * temp[1]) else: return pm.HalfCauchy(param, freedom * temp[1], shape=shape) - elif (distribution == 'uniform'): + elif distribution == "uniform": upper_bound = np.percentile(samples, 95) lower_bound = np.percentile(samples, 5) r = np.abs(upper_bound - lower_bound) if shape is None: - return pm.Uniform(param, lower=lower_bound - freedom * r, - upper=upper_bound + freedom * r) + return pm.Uniform( + param, lower=lower_bound - freedom * r, upper=upper_bound + freedom * r + ) else: - return pm.Uniform(param, lower=lower_bound - freedom * r, - upper=upper_bound + freedom * r, shape=shape) - elif (distribution == 'huniform'): + return pm.Uniform( + param, + lower=lower_bound - freedom * r, + upper=upper_bound + freedom * r, + shape=shape, + ) + elif distribution == "huniform": upper_bound = np.percentile(samples, 95) lower_bound = np.percentile(samples, 5) r = np.abs(upper_bound - lower_bound) if shape is None: return pm.Uniform(param, lower=0, upper=upper_bound + freedom * r) else: - return pm.Uniform(param, lower=0, upper=upper_bound + freedom * r, shape=shape) + return pm.Uniform( + param, lower=0, upper=upper_bound + freedom * r, shape=shape + ) - elif (distribution == 'gamma'): + elif distribution == "gamma": alpha_fit, loc_fit, invbeta_fit = stats.gamma.fit(samples) if shape is None: - return pm.Gamma(param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit) + return pm.Gamma( + param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit + ) else: - return pm.Gamma(param, alpha=freedom * alpha_fit, beta=freedom / invbeta_fit, shape=shape) - - elif (distribution == 'igamma'): + return pm.Gamma( + param, + alpha=freedom * alpha_fit, + beta=freedom / invbeta_fit, + shape=shape, + ) + + elif distribution == "igamma": alpha_fit, loc_fit, beta_fit = stats.gamma.fit(samples) if shape is None: - return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit) + return pm.InverseGamma( + param, alpha=freedom * alpha_fit, beta=freedom * beta_fit + ) else: - return pm.InverseGamma(param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape) - -def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): + return pm.InverseGamma( + param, alpha=freedom * alpha_fit, beta=freedom * beta_fit, shape=shape + ) + + +def hbr(X, y, batch_effects, configs, idata=None): """ :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures @@ -156,51 +179,100 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): :param batch_effects_size: [b1, b2,...,bM] List of counts of unique values of batch effects :param configs: :param idata: - :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. + :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ X = pytensor.shared(X) - X = pytensor.tensor.cast(X,'floatX') + X = pytensor.tensor.cast(X, "floatX") y = pytensor.shared(y) - y = pytensor.tensor.cast(y,'floatX') - + y = pytensor.tensor.cast(y, "floatX") # Make a param builder that will make the correct calls pb = ParamBuilder(X, y, batch_effects, idata, configs) with pm.Model(coords=pb.coords) as model: - pb.model=model - pb.batch_effect_indices = [pm.Data(pb.batch_effect_dim_names[i], pb.batch_effect_indices[i],mutable=True) for i in range(len(pb.batch_effect_indices))] - - if configs['likelihood'] == 'Normal': - mu = pb.make_param("mu", mu_slope_mu_params = (0.,5.), sigma_slope_mu_params = (5.,), mu_intercept_mu_params=(0.,5.), sigma_intercept_mu_params = (5.,)).get_samples(pb) + pb.model = model + pb.batch_effect_indices = [ + pm.Data( + pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], mutable=True + ) + for i in range(len(pb.batch_effect_indices)) + ] + + if configs["likelihood"] == "Normal": + mu = pb.make_param( + "mu", + mu_slope_mu_params=(0.0, 1.0), + sigma_slope_mu_params=(1.0,), + mu_intercept_mu_params=(0.0, 1.0), + sigma_intercept_mu_params=(1.0,), + ).get_samples(pb) # print(f"{mu.shape.eval()=}") - sigma = pb.make_param("sigma", mu_sigma_params = (0., 2.), sigma_sigma_params = (5.,)).get_samples(pb) + sigma = pb.make_param( + "sigma", mu_sigma_params=(1.0, 0.2), + sigma_sigma_params=(0.2,), + slope_sigma_params=(0.0, 0.3), + intercept_sigma_params=(1.0, 0.2) + ).get_samples(pb) # print(f"{sigma.shape.eval()=}") - sigma_plus = pm.Deterministic('sigma_plus', pm.math.log(1+pm.math.exp(sigma))) - y_like = pm.Normal('y_like', mu, sigma=sigma_plus, observed=y) - - elif configs['likelihood'] in ['SHASHb','SHASHo','SHASHo2']: - """ - Comment 1 - The current parameterizations are tuned towards standardized in- and output data. - It is possible to adjust the priors through the XXX_dist and XXX_params kwargs, like here we do with epsilon_params. - Supported distributions are listed in the Prior class. - Comment 2 - Any mapping that is applied here after sampling should also be applied in util.hbr_utils.forward in order for the functions there to properly work. - For example, the softplus applied to sigma here is also applied in util.hbr_utils.forward - """ - SHASH_map = {'SHASHb':SHASHb,'SHASHo':SHASHo,'SHASHo2':SHASHo2} - - mu = pb.make_param("mu", slope_mu_params = (0.,3.), mu_intercept_mu_params=(0.,1.), sigma_intercept_mu_params = (2.,)).get_samples(pb) - sigma = pb.make_param("sigma", sigma_params = (1.,2.), slope_sigma_params=(0.,2.), intercept_sigma_params = (0., 2.), apply_softplus=True).get_samples(pb) - epsilon = pb.make_param("epsilon", epsilon_params = (0.,1.), slope_epsilon_params=(0.,1.), intercept_epsilon_params=(0.,1)).get_samples(pb) - delta = pb.make_param("delta", delta_params=(1.5,2.), slope_delta_params=(0.,2), intercept_delta_params=(0., 2), apply_softplus=True).get_samples(pb) - y_like = SHASH_map[configs['likelihood']]('y_like', mu=mu, sigma=sigma, epsilon=epsilon, delta=delta, observed = y) + sigma_plus = np.log(1 + np.exp(sigma)) + y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) + + elif configs["likelihood"] in ["SHASHb", "SHASHo", "SHASHo2"]: + """ + Comment 1 + The current parameterizations are tuned towards standardized in- and output data. + It is possible to adjust the priors through the XXX_dist and XXX_params kwargs, like here we do with epsilon_params. + Supported distributions are listed in the Prior class. + Comment 2 + Any mapping that is applied here after sampling should also be applied in util.hbr_utils.forward in order for the functions there to properly work. + For example, the softplus applied to sigma here is also applied in util.hbr_utils.forward + """ + SHASH_map = {"SHASHb": SHASHb, "SHASHo": SHASHo, "SHASHo2": SHASHo2} + + mu = pb.make_param( + "mu", + slope_mu_params=(0.0, 1.0), + mu_slope_mu_params=(0.0, 1.0), + sigma_slope_mu_params=(1.0,), + mu_intercept_mu_params=(0.0, 1.0), + sigma_intercept_mu_params=(1.0,), + ).get_samples(pb) + sigma = pb.make_param( + "sigma", + sigma_params=(1.0, 0.2), + sigma_dist="normal", + slope_sigma_params=(0.0, 0.3), + intercept_sigma_params=(1.0, 0.2), + ).get_samples(pb) + sigma_plus = np.log(1 + np.exp(sigma)) + sigma_plus = sigma + epsilon = pb.make_param( + "epsilon", + epsilon_params=(0.0, 1.), + slope_epsilon_params=(0.0, 0.2), + intercept_epsilon_params=(0.0, 0.2), + ).get_samples(pb) + delta = pb.make_param( + "delta", + delta_params=(1.0, 0.1), + delta_dist="normal", + slope_epsilon_params=(0.0, 0.2), + intercept_epsilon_params=(1.0, 0.05), + ).get_samples(pb) + delta_plus = np.log(1 + np.exp(delta * 10)) / 10 + 0.3 + # delta_plus = delta + y_like = SHASH_map[configs["likelihood"]]( + "y_like", + mu=mu, + sigma=sigma_plus, + epsilon=epsilon, + delta=delta_plus, + observed=y, + ) return model - class HBR: """Hierarchical Bayesian Regression for normative modeling @@ -226,7 +298,7 @@ class HBR: def get_step_methods(self, m): """ - This can be used to assign default step functions. However, the nuts initialization keyword doesnt work together with this... so better not use it. + This can be used to assign default step functions. However, the nuts initialization keyword doesnt work together with this... so better not use it. STEP_METHODS = ( NUTS, @@ -240,234 +312,228 @@ def get_step_methods(self, m): :param m: a PyMC model :return: """ - samplermap = {'NUTS': NUTS, 'MH': Metropolis, 'Slice': Slice, 'HMC': HamiltonianMC} - fallbacks = [Metropolis] # We are using MH as a fallback method here - if self.configs['sampler'] == 'NUTS': - step_kwargs = {'nuts': {'target_accept': self.configs['target_accept']}} + samplermap = { + "NUTS": NUTS, + "MH": Metropolis, + "Slice": Slice, + "HMC": HamiltonianMC, + } + fallbacks = [Metropolis] # We are using MH as a fallback method here + if self.configs["sampler"] == "NUTS": + step_kwargs = {"nuts": {"target_accept": self.configs["target_accept"]}} else: step_kwargs = None - return pm.sampling.assign_step_methods(m, methods=[samplermap[self.configs['sampler']]] + fallbacks, - step_kwargs=step_kwargs) + return pm.sampling.assign_step_methods( + m, + methods=[samplermap[self.configs["sampler"]]] + fallbacks, + step_kwargs=step_kwargs, + ) def __init__(self, configs): self.bsp = None - self.model_type = configs['type'] + self.model_type = configs["type"] self.configs = configs def get_modeler(self): - return {'nn': nn_hbr}.get(self.model_type, hbr) - + return hbr + def transform_X(self, X): - if self.model_type == 'polynomial': - Phi = create_poly_basis(X, self.configs['order']) - elif self.model_type == 'bspline': + if self.model_type == "polynomial": + Phi = create_poly_basis(X, self.configs["order"]) + elif self.model_type == "bspline": if self.bsp is None: - self.bsp = bspline_fit(X, self.configs['order'], self.configs['nknots']) + self.bsp = bspline_fit(X, self.configs["order"], self.configs["nknots"]) bspline = bspline_transform(X, self.bsp) - Phi = np.concatenate((X, bspline), axis = 1) + Phi = np.concatenate((X, bspline), axis=1) else: Phi = X return Phi - - def find_map(self, X, y, batch_effects,method='L-BFGS-B'): - """ Function to estimate the model """ + def find_map(self, X, y, batch_effects, method="L-BFGS-B"): + """Function to estimate the model""" X, y, batch_effects = expand_all(X, y, batch_effects) - - self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - for i in range(self.batch_effects_num): - self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) - X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: + with modeler(X, y, batch_effects, self.configs) as m: self.MAP = pm.find_MAP(method=method) return self.MAP def estimate(self, X, y, batch_effects): - - """ Function to estimate the model """ + """Function to estimate the model""" X, y, batch_effects = expand_all(X, y, batch_effects) - - self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - for i in range(self.batch_effects_num): - self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs) as m: - self.idata = pm.sample(draws=self.configs['n_samples'], - tune=self.configs['n_tuning'], - chains=self.configs['n_chains'], - init=self.configs['init'], n_init=500000, - cores=self.configs['cores']) + with modeler(X, y, batch_effects, self.configs) as m: + self.idata = pm.sample( + draws=self.configs["n_samples"], + tune=self.configs["n_tuning"], + chains=self.configs["n_chains"], + init=self.configs["init"], + n_init=500000, + cores=self.configs["cores"], + ) return self.idata - def predict(self, X, batch_effects, pred='single'): - """ Function to make predictions from the model """ + def predict(self, X, batch_effects, pred="single"): + """Function to make predictions from the model""" X, batch_effects = expand_all(X, batch_effects) - - samples = self.configs['n_samples'] + samples = self.configs["n_samples"] y = np.zeros([X.shape[0], 1]) - - if pred == 'single': - X = self.transform_X(X) - modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - self.idata = pm.sample_posterior_predictive(trace = self.idata, progressbar=True) - pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) + X = self.transform_X(X) + modeler = self.get_modeler() + with modeler(X, y, batch_effects, self.configs): + self.idata = pm.sample_posterior_predictive( + trace=self.idata, progressbar=True + ) + pred_mean = self.idata.posterior_predictive["y_like"].mean(axis=(0, 1)) + pred_var = self.idata.posterior_predictive["y_like"].var(axis=(0, 1)) return pred_mean, pred_var def estimate_on_new_site(self, X, y, batch_effects): - """ Function to adapt the model """ + """Function to adapt the model""" X, y, batch_effects = expand_all(X, y, batch_effects) - - self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - for i in range(self.batch_effects_num): - self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) - X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, - self.configs, idata=self.idata) as m: - self.idata = pm.sample(self.configs['n_samples'], - tune=self.configs['n_tuning'], - chains=self.configs['n_chains'], - target_accept=self.configs['target_accept'], - init=self.configs['init'], n_init=50000, - cores=self.configs['cores']) + with modeler(X, y, batch_effects, self.configs, idata=self.idata) as m: + self.idata = pm.sample( + self.configs["n_samples"], + tune=self.configs["n_tuning"], + chains=self.configs["n_chains"], + target_accept=self.configs["target_accept"], + init=self.configs["init"], + n_init=50000, + cores=self.configs["cores"], + ) return self.idata def predict_on_new_site(self, X, batch_effects): - """ Function to make predictions from the model """ + """Function to make predictions from the model""" X, batch_effects = expand_all(X, batch_effects) - - samples = self.configs['n_samples'] + samples = self.configs["n_samples"] y = np.zeros([X.shape[0], 1]) - X = self.transform_X(X) modeler = self.get_modeler() - - - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs, idata=self.idata): - self.idata = pm.sample_posterior_predictive(self.idata, extend_inferencedata=True, progressbar=True) - pred_mean = self.idata.posterior_predictive['y_like'].mean(axis=(0,1)) - pred_var = self.idata.posterior_predictive['y_like'].var(axis=(0,1)) + with modeler(X, y, batch_effects, self.configs, idata=self.idata): + self.idata = pm.sample_posterior_predictive( + self.idata, extend_inferencedata=True, progressbar=True + ) + pred_mean = self.idata.posterior_predictive["y_like"].mean(axis=(0, 1)) + pred_var = self.idata.posterior_predictive["y_like"].var(axis=(0, 1)) return pred_mean, pred_var def generate(self, X, batch_effects, samples): - """ Function to generate samples from posterior predictive distribution """ + """Function to generate samples from posterior predictive distribution""" X, batch_effects = expand_all(X, batch_effects) y = np.zeros([X.shape[0], 1]) X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.batch_effects_size, self.configs): - # TODO need to adapt this to new pymc + with modeler(X, y, batch_effects, self.configs): ppc = pm.sample_posterior_predictive(self.idata, progressbar=True) - - generated_samples = np.reshape(ppc.posterior_predictive['y_like'].squeeze().T, [X.shape[0] * samples, 1]) + generated_samples = np.reshape( + ppc.posterior_predictive["y_like"].squeeze().T, [X.shape[0] * samples, 1] + ) X = np.repeat(X, samples) if len(X.shape) == 1: X = np.expand_dims(X, axis=1) batch_effects = np.repeat(batch_effects, samples, axis=0) if len(batch_effects.shape) == 1: batch_effects = np.expand_dims(batch_effects, axis=1) - return X, batch_effects, generated_samples def sample_prior_predictive(self, X, batch_effects, samples, idata=None): - """ Function to sample from prior predictive distribution """ - - if len(X.shape) == 1: - X = np.expand_dims(X, axis=1) - if len(batch_effects.shape) == 1: - batch_effects = np.expand_dims(batch_effects, axis=1) - - self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - for i in range(self.batch_effects_num): - self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) - + """Function to sample from prior predictive distribution""" + X, batch_effects = expand_all(X, batch_effects) y = np.zeros([X.shape[0], 1]) - - if self.model_type == 'linear': - with hbr(X, y, batch_effects, self.batch_effects_size, self.configs, - idata): - idata = pm.sample_prior_predictive(samples=samples) + X = self.transform_X(X) + modeler = self.get_modeler() + with modeler(X, y, batch_effects, self.configs, idata): + idata = pm.sample_prior_predictive(samples=samples) return idata def get_model(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) - - self.batch_effects_num = batch_effects.shape[1] - self.batch_effects_size = [] - for i in range(self.batch_effects_num): - self.batch_effects_size.append(len(np.unique(batch_effects[:, i]))) modeler = self.get_modeler() X = self.transform_X(X) - return modeler(X, y, batch_effects, self.batch_effects_size, self.configs, self.idata) - - def create_dummy_inputs(self, covariate_ranges = [[0.1,0.9,0.01]]): + idata = self.idata if hasattr(self, 'idata') else None + return modeler(X, y, batch_effects, self.configs, idata=idata) + def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): + #TODO this function needs to be able to compute the batch_effects_size somehow arrays = [] for i in range(len(covariate_ranges)): - arrays.append(np.arange(covariate_ranges[i][0],covariate_ranges[i][1], covariate_ranges[i][2])) + arrays.append( + np.arange( + covariate_ranges[i][0], + covariate_ranges[i][1], + covariate_ranges[i][2], + ) + ) X = cartesian_product(arrays) X_dummy = np.concatenate([X for i in range(np.prod(self.batch_effects_size))]) - arrays = [] for i in range(self.batch_effects_num): arrays.append(np.arange(0, self.batch_effects_size[i])) batch_effects = cartesian_product(arrays) batch_effects_dummy = np.repeat(batch_effects, X.shape[0], axis=0) + return X_dummy, batch_effects_dummy - return X_dummy, batch_effects_dummy class Prior: """ - A wrapper class for a PyMC distribution. + A wrapper class for a PyMC distribution. - creates a fitted distribution from the idata, if one is present - overloads the __getitem__ function with something that switches between indexing or not, based on the shape """ + def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: self.dist = None self.name = name self.has_random_effect = has_random_effect - self.distmap = {'normal': pm.Normal, - 'hnormal': pm.HalfNormal, - 'gamma': pm.Gamma, - 'uniform': pm.Uniform, - 'igamma': pm.InverseGamma, - 'hcauchy': pm.HalfCauchy, - 'hstudt':pm.HalfStudentT, - 'studt':pm.StudentT} + self.distmap = { + "normal": pm.Normal, + "hnormal": pm.HalfNormal, + "gamma": pm.Gamma, + "uniform": pm.Uniform, + "igamma": pm.InverseGamma, + "hcauchy": pm.HalfCauchy, + "hstudt": pm.HalfStudentT, + "studt": pm.StudentT, + } self.make_dist(dist, params, pb) - + def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" with pb.model as m: # If self.name.startswith slope: # use the basis functions dim # Otherwise, use the empty dim - dim = 'basis_functions' if (self.name.startswith('slope') or self.name.startswith('offset_slope')) else 'empty' - - if (pb.idata is not None): + dim = ( + "basis_functions" + if ( + self.name.startswith("slope") + or self.name.startswith("offset_slope") + ) + else "empty" + ) + + if pb.idata is not None: samples = az.extract(pb.idata, var_names=self.name).to_numpy() if not self.has_random_effect: samples = np.reshape(samples, (-1,)) - self.dist = from_posterior(param=self.name, - samples = samples, - distribution=dist, - freedom=pb.configs['freedom']) + self.dist = from_posterior( + param=self.name, + samples=samples, + distribution=dist, + freedom=pb.configs["freedom"], + ) elif self.has_random_effect: - self.dist = self.distmap[dist](self.name, *params, dims=[*pb.batch_effect_dim_names, dim]) + self.dist = self.distmap[dist]( + self.name, *params, dims=[*pb.batch_effect_dim_names, dim] + ) else: self.dist = self.distmap[dist](self.name, *params, dims=dim) @@ -479,10 +545,11 @@ def __getitem__(self, idx): else: return self.dist + class ParamBuilder: """ - A class that simplifies the construction of parameterizations. - It has a lot of attributes necessary for creating the model, including the data, but it is never saved with the model. + A class that simplifies the construction of parameterizations. + It has a lot of attributes necessary for creating the model, including the data, but it is never saved with the model. It also contains a lot of decision logic for creating the parameterizations. """ @@ -496,11 +563,11 @@ def __init__(self, X, y, batch_effects, idata, configs): :param idata: idem :param configs: idem """ - self.model = None # Needs to be set later, because coords need to be passed at construction of Model + self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X self.y = y self.batch_effects = batch_effects.astype(np.int16) - self.idata:az.InferenceData = idata + self.idata: az.InferenceData = idata self.configs = configs self.y_shape = y.shape.eval() @@ -514,38 +581,43 @@ def __init__(self, X, y, batch_effects, idata, configs): for i in range(self.batch_effects_num): batch_effect_dim_name = f"batch_effect_{i}" self.batch_effect_dim_names.append(batch_effect_dim_name) - this_be_values, this_be_indices = np.unique(self.batch_effects[:,i], return_inverse=True) + this_be_values, this_be_indices = np.unique( + self.batch_effects[:, i], return_inverse=True + ) self.coords[batch_effect_dim_name] = this_be_values self.batch_effect_indices.append(this_be_indices) - self.coords['empty']=[0] - self.coords['basis_functions'] = [i for i in range(X.shape.eval()[1])] - + self.coords["empty"] = [0] + self.coords["basis_functions"] = [i for i in range(X.shape.eval()[1])] # TODO reinstigate the 'dim' keyword, for the slope parameters def make_param(self, name, **kwargs): - if self.configs.get(f'linear_{name}', False): - # First make a slope and intercept, and use those to make a linear parameterization - slope_parameterization = self.make_param(f'slope_{name}',**kwargs) - intercept_parameterization = self.make_param(f'intercept_{name}', **kwargs) - return LinearParameterization(name=name, - slope_parameterization=slope_parameterization, - intercept_parameterization=intercept_parameterization, - **kwargs) - - elif self.configs.get(f'random_{name}', False): - if self.configs.get(f'centered_{name}', True): - return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) + if self.configs.get(f"linear_{name}", False): + # First make a slope and intercept, and use those to make a linear parameterization + slope_parameterization = self.make_param(f"slope_{name}", **kwargs) + intercept_parameterization = self.make_param(f"intercept_{name}", **kwargs) + return LinearParameterization( + name=name, + slope_parameterization=slope_parameterization, + intercept_parameterization=intercept_parameterization, + **kwargs, + ) + + elif self.configs.get(f"random_{name}", False): + if self.configs.get(f"centered_{name}", True): + return CentralRandomFixedParameterization(name=name, pb=self, **kwargs) else: - return NonCentralRandomFixedParameterization(name=name, pb=self, **kwargs) + return NonCentralRandomFixedParameterization( + name=name, pb=self, **kwargs + ) else: return FixedParameterization(name=name, pb=self, **kwargs) - class Parameterization: """ This is the top-level parameterization class from which all the other parameterizations inherit. """ + def __init__(self, name): self.name = name print(name, type(self)) @@ -553,85 +625,104 @@ def __init__(self, name): def get_samples(self, pb): pass + class FixedParameterization(Parameterization): """ A parameterization that takes a single value for all input. It does not depend on anything except its hyperparameters """ - def __init__(self, name, pb:ParamBuilder,**kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) - dist = kwargs.get(f'{name}_dist','normal') - params = kwargs.get(f'{name}_params',(0.,1.)) + dist = kwargs.get(f"{name}_dist", "normal") + params = kwargs.get(f"{name}_params", (0.0, 1.0)) self.dist = Prior(name, dist, params, pb) def get_samples(self, pb): with pb.model: return self.dist[0] + class CentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a central fashion; - the values are sampled from normal distribution with a group mean and group variance + the values are sampled from normal distribution with a group mean and group variance """ - def __init__(self, name, pb:ParamBuilder, **kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) # Normal distribution is default for mean - mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) + mu_dist = kwargs.get(f"mu_{name}_dist", "normal") + mu_params = kwargs.get(f"mu_{name}_params", (0.0, 1.0)) + mu_prior = Prior(f"mu_{name}", mu_dist, mu_params, pb) # HalfNormal is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) - - dim = 'basis_functions' if self.name.startswith('slope') else 'empty' - self.dist = pm.Normal(name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, dims=[*pb.batch_effect_dim_names,dim]) + sigma_dist = kwargs.get(f"sigma_{name}_dist", "hnormal") + sigma_params = kwargs.get(f"sigma_{name}_params", (1.0,)) + sigma_prior = Prior(f"sigma_{name}", sigma_dist, sigma_params, pb) + + dim = "basis_functions" if self.name.startswith("slope") else "empty" + self.dist = pm.Normal( + name=name, + mu=mu_prior.dist, + sigma=sigma_prior.dist, + dims=[*pb.batch_effect_dim_names, dim], + ) - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb: ParamBuilder): with pb.model: samples = self.dist[*pb.batch_effect_indices] return samples + class NonCentralRandomFixedParameterization(Parameterization): """ A parameterization that is fixed for each batch effect. This is sampled in a non-central fashion; - the values are a sum of a group mean and noise values scaled with a group scaling factor + the values are a sum of a group mean and noise values scaled with a group scaling factor """ - def __init__(self, name, pb:ParamBuilder, **kwargs): + + def __init__(self, name, pb: ParamBuilder, **kwargs): super().__init__(name) # Normal distribution is default for mean - mu_dist = kwargs.get(f'mu_{name}_dist','normal') - mu_params = kwargs.get(f'mu_{name}_params',(0.,1.)) - mu_prior = Prior(f'mu_{name}', mu_dist, mu_params, pb) + mu_dist = kwargs.get(f"mu_{name}_dist", "normal") + mu_params = kwargs.get(f"mu_{name}_params", (0.0, 1.0)) + mu_prior = Prior(f"mu_{name}", mu_dist, mu_params, pb) # HalfNormal is default for sigma - sigma_dist = kwargs.get(f'sigma_{name}_dist','hnormal') - sigma_params = kwargs.get(f'sigma_{name}_params',(1.,)) - sigma_prior = Prior(f'sigma_{name}',sigma_dist, sigma_params, pb) + sigma_dist = kwargs.get(f"sigma_{name}_dist", "hnormal") + sigma_params = kwargs.get(f"sigma_{name}_params", (1.0,)) + sigma_prior = Prior(f"sigma_{name}", sigma_dist, sigma_params, pb) # Normal is default for offset - offset_dist = kwargs.get(f'offset_{name}_dist','normal') - offset_params = kwargs.get(f'offset_{name}_params',(0.,1.)) - offset_prior = Prior(f'offset_{name}',offset_dist, offset_params, pb, has_random_effect=True) - - dim = 'basis_functions' if self.name.startswith('slope') else 'empty' - self.dist = pm.Deterministic(name=name, var=mu_prior.dist+sigma_prior.dist*offset_prior.dist,dims=[*pb.batch_effect_dim_names,dim]) - + offset_dist = kwargs.get(f"offset_{name}_dist", "normal") + offset_params = kwargs.get(f"offset_{name}_params", (0.0, 1.0)) + offset_prior = Prior( + f"offset_{name}", offset_dist, offset_params, pb, has_random_effect=True + ) + dim = "basis_functions" if self.name.startswith("slope") else "empty" + self.dist = pm.Deterministic( + name=name, + var=mu_prior.dist + sigma_prior.dist * offset_prior.dist, + dims=[*pb.batch_effect_dim_names, dim], + ) - def get_samples(self, pb:ParamBuilder): + def get_samples(self, pb: ParamBuilder): with pb.model: # print(f"{self.dist.shape.eval()=}") samples = self.dist[*pb.batch_effect_indices] return samples + class LinearParameterization(Parameterization): """ - A parameterization that can model a linear dependence on X. + A parameterization that can model a linear dependence on X. """ - def __init__(self, name, slope_parameterization, intercept_parameterization,**kwargs): + + def __init__( + self, name, slope_parameterization, intercept_parameterization, **kwargs + ): super().__init__(name) self.slope_parameterization = slope_parameterization self.intercept_parameterization = intercept_parameterization @@ -640,14 +731,14 @@ def get_samples(self, pb): with pb.model: intc = self.intercept_parameterization.get_samples(pb) slope_samples = self.slope_parameterization.get_samples(pb) - if pb.configs[f'random_slope_{self.name}']: - slope = pb.X*self.slope_parameterization.get_samples(pb) + if pb.configs[f"random_slope_{self.name}"]: + slope = pb.X * self.slope_parameterization.get_samples(pb) slope = slope.sum(axis=-1) else: - slope = pb.X@self.slope_parameterization.get_samples(pb) + slope = pb.X @ self.slope_parameterization.get_samples(pb) samples = pm.math.flatten(intc) + pm.math.flatten(slope) - samples = samples.reshape((samples.shape[0],1)) + samples = samples.reshape((samples.shape[0], 1)) return samples @@ -662,8 +753,8 @@ def get_design_matrix(X, nm, basis="linear"): def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): - n_hidden = configs['nn_hidden_neuron_num'] - n_layers = configs['nn_hidden_layers_num'] + n_hidden = configs["nn_hidden_neuron_num"] + n_layers = configs["nn_hidden_layers_num"] feature_num = X.shape[1] batch_effects_num = batch_effects.shape[1] all_idx = [] @@ -672,14 +763,18 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): be_idx = list(product(*all_idx)) # Initialize random weights between each layer for the mu: - init_1 = pm.floatX(np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num)) + init_1 = pm.floatX( + np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num) + ) init_out = pm.floatX(np.random.randn(n_hidden) * np.sqrt(1 / n_hidden)) std_init_1 = pm.floatX(np.random.rand(feature_num, n_hidden)) std_init_out = pm.floatX(np.random.rand(n_hidden)) # And initialize random weights between each layer for sigma_noise: - init_1_noise = pm.floatX(np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num)) + init_1_noise = pm.floatX( + np.random.randn(feature_num, n_hidden) * np.sqrt(1 / feature_num) + ) init_out_noise = pm.floatX(np.random.randn(n_hidden) * np.sqrt(1 / n_hidden)) std_init_1_noise = pm.floatX(np.random.rand(feature_num, n_hidden)) @@ -689,90 +784,134 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): if n_layers == 2: init_2 = pm.floatX(np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden)) std_init_2 = pm.floatX(np.random.rand(n_hidden, n_hidden)) - init_2_noise = pm.floatX(np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden)) + init_2_noise = pm.floatX( + np.random.randn(n_hidden, n_hidden) * np.sqrt(1 / n_hidden) + ) std_init_2_noise = pm.floatX(np.random.rand(n_hidden, n_hidden)) with pm.Model() as model: - - X = pm.Data('X', X) - y = pm.Data('y', y) + X = pm.Data("X", X) + y = pm.Data("y", y) if idata is not None: # Used when estimating/predicting on a new site - weights_in_1_grp = from_posterior('w_in_1_grp', idata['w_in_1_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_in_1_grp_sd = from_posterior('w_in_1_grp_sd', idata['w_in_1_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) + weights_in_1_grp = from_posterior( + "w_in_1_grp", + idata["w_in_1_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_in_1_grp_sd = from_posterior( + "w_in_1_grp_sd", + idata["w_in_1_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) if n_layers == 2: - weights_1_2_grp = from_posterior('w_1_2_grp', idata['w_1_2_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_1_2_grp_sd = from_posterior('w_1_2_grp_sd', idata['w_1_2_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) - - weights_2_out_grp = from_posterior('w_2_out_grp', idata['w_2_out_grp'], - distribution='normal', freedom=configs['freedom']) - - weights_2_out_grp_sd = from_posterior('w_2_out_grp_sd', idata['w_2_out_grp_sd'], - distribution='hcauchy', freedom=configs['freedom']) - - mu_prior_intercept = from_posterior('mu_prior_intercept', idata['mu_prior_intercept'], - distribution='normal', freedom=configs['freedom']) - sigma_prior_intercept = from_posterior('sigma_prior_intercept', idata['sigma_prior_intercept'], - distribution='hcauchy', freedom=configs['freedom']) + weights_1_2_grp = from_posterior( + "w_1_2_grp", + idata["w_1_2_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_1_2_grp_sd = from_posterior( + "w_1_2_grp_sd", + idata["w_1_2_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + weights_2_out_grp = from_posterior( + "w_2_out_grp", + idata["w_2_out_grp"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_2_out_grp_sd = from_posterior( + "w_2_out_grp_sd", + idata["w_2_out_grp_sd"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + mu_prior_intercept = from_posterior( + "mu_prior_intercept", + idata["mu_prior_intercept"], + distribution="normal", + freedom=configs["freedom"], + ) + sigma_prior_intercept = from_posterior( + "sigma_prior_intercept", + idata["sigma_prior_intercept"], + distribution="hcauchy", + freedom=configs["freedom"], + ) else: # Group the mean distribution for input to the hidden layer: - weights_in_1_grp = pm.Normal('w_in_1_grp', 0, sd=1, - shape=(feature_num, n_hidden), testval=init_1) + weights_in_1_grp = pm.Normal( + "w_in_1_grp", 0, sd=1, shape=(feature_num, n_hidden), testval=init_1 + ) # Group standard deviation: - weights_in_1_grp_sd = pm.HalfCauchy('w_in_1_grp_sd', 1., - shape=(feature_num, n_hidden), testval=std_init_1) + weights_in_1_grp_sd = pm.HalfCauchy( + "w_in_1_grp_sd", 1.0, shape=(feature_num, n_hidden), testval=std_init_1 + ) if n_layers == 2: # Group the mean distribution for hidden layer 1 to hidden layer 2: - weights_1_2_grp = pm.Normal('w_1_2_grp', 0, sd=1, - shape=(n_hidden, n_hidden), testval=init_2) + weights_1_2_grp = pm.Normal( + "w_1_2_grp", 0, sd=1, shape=(n_hidden, n_hidden), testval=init_2 + ) # Group standard deviation: - weights_1_2_grp_sd = pm.HalfCauchy('w_1_2_grp_sd', 1., - shape=(n_hidden, n_hidden), testval=std_init_2) + weights_1_2_grp_sd = pm.HalfCauchy( + "w_1_2_grp_sd", 1.0, shape=(n_hidden, n_hidden), testval=std_init_2 + ) # Group the mean distribution for hidden to output: - weights_2_out_grp = pm.Normal('w_2_out_grp', 0, sd=1, - shape=(n_hidden,), testval=init_out) + weights_2_out_grp = pm.Normal( + "w_2_out_grp", 0, sd=1, shape=(n_hidden,), testval=init_out + ) # Group standard deviation: - weights_2_out_grp_sd = pm.HalfCauchy('w_2_out_grp_sd', 1., - shape=(n_hidden,), testval=std_init_out) + weights_2_out_grp_sd = pm.HalfCauchy( + "w_2_out_grp_sd", 1.0, shape=(n_hidden,), testval=std_init_out + ) # mu_prior_intercept = pm.Uniform('mu_prior_intercept', lower=-100, upper=100) - mu_prior_intercept = pm.Normal('mu_prior_intercept', mu=0., sigma=1e3) - sigma_prior_intercept = pm.HalfCauchy('sigma_prior_intercept', 5) + mu_prior_intercept = pm.Normal("mu_prior_intercept", mu=0.0, sigma=1e3) + sigma_prior_intercept = pm.HalfCauchy("sigma_prior_intercept", 5) # Now create separate weights for each group, by doing # weights * group_sd + group_mean, we make sure the new weights are # coming from the (group_mean, group_sd) distribution. - weights_in_1_raw = pm.Normal('w_in_1', 0, sd=1, - shape=(batch_effects_size + [feature_num, n_hidden])) + weights_in_1_raw = pm.Normal( + "w_in_1", 0, sd=1, shape=(batch_effects_size + [feature_num, n_hidden]) + ) weights_in_1 = weights_in_1_raw * weights_in_1_grp_sd + weights_in_1_grp if n_layers == 2: - weights_1_2_raw = pm.Normal('w_1_2', 0, sd=1, - shape=(batch_effects_size + [n_hidden, n_hidden])) + weights_1_2_raw = pm.Normal( + "w_1_2", 0, sd=1, shape=(batch_effects_size + [n_hidden, n_hidden]) + ) weights_1_2 = weights_1_2_raw * weights_1_2_grp_sd + weights_1_2_grp - weights_2_out_raw = pm.Normal('w_2_out', 0, sd=1, - shape=(batch_effects_size + [n_hidden])) + weights_2_out_raw = pm.Normal( + "w_2_out", 0, sd=1, shape=(batch_effects_size + [n_hidden]) + ) weights_2_out = weights_2_out_raw * weights_2_out_grp_sd + weights_2_out_grp - intercepts_offset = pm.Normal('intercepts_offset', mu=0, sd=1, - shape=(batch_effects_size)) + intercepts_offset = pm.Normal( + "intercepts_offset", mu=0, sd=1, shape=(batch_effects_size) + ) - intercepts = pm.Deterministic('intercepts', intercepts_offset + - mu_prior_intercept * sigma_prior_intercept) + intercepts = pm.Deterministic( + "intercepts", intercepts_offset + mu_prior_intercept * sigma_prior_intercept + ) # Build the neural network and estimate y_hat: y_hat = pytensor.tensor.zeros(y.shape) @@ -786,83 +925,144 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): act_1 = pm.math.tanh(pytensor.tensor.dot(X[idx, :], weights_in_1[be])) if n_layers == 2: act_2 = pm.math.tanh(pytensor.tensor.dot(act_1, weights_1_2[be])) - y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + pytensor.tensor.dot(act_2, weights_2_out[be])) + y_hat = pytensor.tensor.set_subtensor( + y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_2, weights_2_out[be]), + ) else: - y_hat = pytensor.tensor.set_subtensor(y_hat[idx, 0], - intercepts[be] + pytensor.tensor.dot(act_1, weights_2_out[be])) + y_hat = pytensor.tensor.set_subtensor( + y_hat[idx, 0], + intercepts[be] + pytensor.tensor.dot(act_1, weights_2_out[be]), + ) # If we want to estimate varying noise terms across groups: - if configs['random_noise']: - if configs['hetero_noise']: + if configs["random_noise"]: + if configs["hetero_noise"]: if idata is not None: # # Used when estimating/predicting on a new site - weights_in_1_grp_noise = from_posterior('w_in_1_grp_noise', - idata['w_in_1_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_in_1_grp_sd_noise = from_posterior('w_in_1_grp_sd_noise', - idata['w_in_1_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) + weights_in_1_grp_noise = from_posterior( + "w_in_1_grp_noise", + idata["w_in_1_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_in_1_grp_sd_noise = from_posterior( + "w_in_1_grp_sd_noise", + idata["w_in_1_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) if n_layers == 2: - weights_1_2_grp_noise = from_posterior('w_1_2_grp_noise', - idata['w_1_2_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_1_2_grp_sd_noise = from_posterior('w_1_2_grp_sd_noise', - idata['w_1_2_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) - - weights_2_out_grp_noise = from_posterior('w_2_out_grp_noise', - idata['w_2_out_grp_noise'], - distribution='normal', freedom=configs['freedom']) - - weights_2_out_grp_sd_noise = from_posterior('w_2_out_grp_sd_noise', - idata['w_2_out_grp_sd_noise'], - distribution='hcauchy', freedom=configs['freedom']) + weights_1_2_grp_noise = from_posterior( + "w_1_2_grp_noise", + idata["w_1_2_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_1_2_grp_sd_noise = from_posterior( + "w_1_2_grp_sd_noise", + idata["w_1_2_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) + + weights_2_out_grp_noise = from_posterior( + "w_2_out_grp_noise", + idata["w_2_out_grp_noise"], + distribution="normal", + freedom=configs["freedom"], + ) + + weights_2_out_grp_sd_noise = from_posterior( + "w_2_out_grp_sd_noise", + idata["w_2_out_grp_sd_noise"], + distribution="hcauchy", + freedom=configs["freedom"], + ) else: # The input layer to the first hidden layer: - weights_in_1_grp_noise = pm.Normal('w_in_1_grp_noise', 0, sd=1, - shape=(feature_num, n_hidden), - testval=init_1_noise) - weights_in_1_grp_sd_noise = pm.HalfCauchy('w_in_1_grp_sd_noise', 1, - shape=(feature_num, n_hidden), - testval=std_init_1_noise) + weights_in_1_grp_noise = pm.Normal( + "w_in_1_grp_noise", + 0, + sd=1, + shape=(feature_num, n_hidden), + testval=init_1_noise, + ) + weights_in_1_grp_sd_noise = pm.HalfCauchy( + "w_in_1_grp_sd_noise", + 1, + shape=(feature_num, n_hidden), + testval=std_init_1_noise, + ) # The first hidden layer to second hidden layer: if n_layers == 2: - weights_1_2_grp_noise = pm.Normal('w_1_2_grp_noise', 0, sd=1, - shape=(n_hidden, n_hidden), - testval=init_2_noise) - weights_1_2_grp_sd_noise = pm.HalfCauchy('w_1_2_grp_sd_noise', 1, - shape=(n_hidden, n_hidden), - testval=std_init_2_noise) + weights_1_2_grp_noise = pm.Normal( + "w_1_2_grp_noise", + 0, + sd=1, + shape=(n_hidden, n_hidden), + testval=init_2_noise, + ) + weights_1_2_grp_sd_noise = pm.HalfCauchy( + "w_1_2_grp_sd_noise", + 1, + shape=(n_hidden, n_hidden), + testval=std_init_2_noise, + ) # The second hidden layer to output layer: - weights_2_out_grp_noise = pm.Normal('w_2_out_grp_noise', 0, sd=1, - shape=(n_hidden,), - testval=init_out_noise) - weights_2_out_grp_sd_noise = pm.HalfCauchy('w_2_out_grp_sd_noise', 1, - shape=(n_hidden,), - testval=std_init_out_noise) + weights_2_out_grp_noise = pm.Normal( + "w_2_out_grp_noise", + 0, + sd=1, + shape=(n_hidden,), + testval=init_out_noise, + ) + weights_2_out_grp_sd_noise = pm.HalfCauchy( + "w_2_out_grp_sd_noise", + 1, + shape=(n_hidden,), + testval=std_init_out_noise, + ) # mu_prior_intercept_noise = pm.HalfNormal('mu_prior_intercept_noise', sigma=1e3) # sigma_prior_intercept_noise = pm.HalfCauchy('sigma_prior_intercept_noise', 5) # Now create separate weights for each group: - weights_in_1_raw_noise = pm.Normal('w_in_1_noise', 0, sd=1, - shape=(batch_effects_size + [feature_num, n_hidden])) - weights_in_1_noise = weights_in_1_raw_noise * weights_in_1_grp_sd_noise + weights_in_1_grp_noise + weights_in_1_raw_noise = pm.Normal( + "w_in_1_noise", + 0, + sd=1, + shape=(batch_effects_size + [feature_num, n_hidden]), + ) + weights_in_1_noise = ( + weights_in_1_raw_noise * weights_in_1_grp_sd_noise + + weights_in_1_grp_noise + ) if n_layers == 2: - weights_1_2_raw_noise = pm.Normal('w_1_2_noise', 0, sd=1, - shape=(batch_effects_size + [n_hidden, n_hidden])) - weights_1_2_noise = weights_1_2_raw_noise * weights_1_2_grp_sd_noise + weights_1_2_grp_noise - - weights_2_out_raw_noise = pm.Normal('w_2_out_noise', 0, sd=1, - shape=(batch_effects_size + [n_hidden])) - weights_2_out_noise = weights_2_out_raw_noise * weights_2_out_grp_sd_noise + weights_2_out_grp_noise + weights_1_2_raw_noise = pm.Normal( + "w_1_2_noise", + 0, + sd=1, + shape=(batch_effects_size + [n_hidden, n_hidden]), + ) + weights_1_2_noise = ( + weights_1_2_raw_noise * weights_1_2_grp_sd_noise + + weights_1_2_grp_noise + ) + + weights_2_out_raw_noise = pm.Normal( + "w_2_out_noise", 0, sd=1, shape=(batch_effects_size + [n_hidden]) + ) + weights_2_out_noise = ( + weights_2_out_raw_noise * weights_2_out_grp_sd_noise + + weights_2_out_grp_noise + ) # intercepts_offset_noise = pm.Normal('intercepts_offset_noise', mu=0, sd=1, # shape=(batch_effects_size)) @@ -878,20 +1078,45 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - act_1_noise = pm.math.sigmoid(pytensor.tensor.dot(X[idx, :], weights_in_1_noise[be])) + act_1_noise = pm.math.sigmoid( + pytensor.tensor.dot(X[idx, :], weights_in_1_noise[be]) + ) if n_layers == 2: - act_2_noise = pm.math.sigmoid(pytensor.tensor.dot(act_1_noise, weights_1_2_noise[be])) - temp = pm.math.log1pexp(pytensor.tensor.dot(act_2_noise, weights_2_out_noise[be])) + 1e-5 + act_2_noise = pm.math.sigmoid( + pytensor.tensor.dot(act_1_noise, weights_1_2_noise[be]) + ) + temp = ( + pm.math.log1pexp( + pytensor.tensor.dot( + act_2_noise, weights_2_out_noise[be] + ) + ) + + 1e-5 + ) else: - temp = pm.math.log1pexp(pytensor.tensor.dot(act_1_noise, weights_2_out_noise[be])) + 1e-5 + temp = ( + pm.math.log1pexp( + pytensor.tensor.dot( + act_1_noise, weights_2_out_noise[be] + ) + ) + + 1e-5 + ) sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], temp) else: # homoscedastic noise: if idata is not None: # Used for transferring the priors - upper_bound = np.percentile(idata['sigma_noise'], 95) - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound, shape=(batch_effects_size)) + upper_bound = np.percentile(idata["sigma_noise"], 95) + sigma_noise = pm.Uniform( + "sigma_noise", + lower=0, + upper=2 * upper_bound, + shape=(batch_effects_size), + ) else: - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100, shape=(batch_effects_size)) + sigma_noise = pm.Uniform( + "sigma_noise", lower=0, upper=100, shape=(batch_effects_size) + ) sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: @@ -900,14 +1125,16 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise[be]) + sigma_y = pytensor.tensor.set_subtensor( + sigma_y[idx, 0], sigma_noise[be] + ) else: # do not allow for random noise terms across groups: if idata is not None: # Used for transferring the priors - upper_bound = np.percentile(idata['sigma_noise'], 95) - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=2 * upper_bound) + upper_bound = np.percentile(idata["sigma_noise"], 95) + sigma_noise = pm.Uniform("sigma_noise", lower=0, upper=2 * upper_bound) else: - sigma_noise = pm.Uniform('sigma_noise', lower=0, upper=100) + sigma_noise = pm.Uniform("sigma_noise", lower=0, upper=100) sigma_y = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] @@ -915,10 +1142,14 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): a.append(batch_effects[:, i] == b) idx = reduce(np.logical_and, a).nonzero() if idx[0].shape[0] != 0: - sigma_y = pytensor.tensor.set_subtensor(sigma_y[idx, 0], sigma_noise) - - if configs['skewed_likelihood']: - skewness = pm.Uniform('skewness', lower=-10, upper=10, shape=(batch_effects_size)) + sigma_y = pytensor.tensor.set_subtensor( + sigma_y[idx, 0], sigma_noise + ) + + if configs["skewed_likelihood"]: + skewness = pm.Uniform( + "skewness", lower=-10, upper=10, shape=(batch_effects_size) + ) alpha = pytensor.tensor.zeros(y.shape) for be in be_idx: a = [] @@ -930,6 +1161,8 @@ def nn_hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): else: alpha = 0 # symmetrical normal distribution - y_like = pm.SkewNormal('y_like', mu=y_hat, sigma=sigma_y, alpha=alpha, observed=y) + y_like = pm.SkewNormal( + "y_like", mu=y_hat, sigma=sigma_y, alpha=alpha, observed=y + ) return model diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index cda2d27a..8e947dac 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -77,16 +77,12 @@ def ldpkl(filename: str): alg='hbr', likelihood='SHASHb', # model_type='bspline', - linear_mu='True', - random_intercept_mu = 'True', - centered_intercept_mu='True', - random_slope_mu='False', - centered_slope_mu='False', - random_sigma='True', - random_intercept_sigma = 'True', - centered_intercept_sigma='False', - random_slope_sigma='True', - centered_slope_sigma='True', + linear_mu='False', + random_intercept_mu = 'False', + random_slope_mu = 'False', + random_intercept_sigma='False', + random_sigma='False', + random_mu='False', log_path=log_dir, binary='True', n_samples=17, From 682a445434485dc1fa04740955a1a3202a3c0639 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Wed, 17 May 2023 01:03:05 +0200 Subject: [PATCH 21/43] Removed the get_step_method function --- pcntoolkit/model/hbr.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 45658b1b..11240234 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -296,39 +296,6 @@ class HBR: Written by S.M. Kia """ - def get_step_methods(self, m): - """ - This can be used to assign default step functions. However, the nuts initialization keyword doesnt work together with this... so better not use it. - - STEP_METHODS = ( - NUTS, - HamiltonianMC, - Metropolis, - BinaryMetropolis, - BinaryGibbsMetropolis, - Slice, - CategoricalGibbsMetropolis, - ) - :param m: a PyMC model - :return: - """ - samplermap = { - "NUTS": NUTS, - "MH": Metropolis, - "Slice": Slice, - "HMC": HamiltonianMC, - } - fallbacks = [Metropolis] # We are using MH as a fallback method here - if self.configs["sampler"] == "NUTS": - step_kwargs = {"nuts": {"target_accept": self.configs["target_accept"]}} - else: - step_kwargs = None - return pm.sampling.assign_step_methods( - m, - methods=[samplermap[self.configs["sampler"]]] + fallbacks, - step_kwargs=step_kwargs, - ) - def __init__(self, configs): self.bsp = None self.model_type = configs["type"] From a71ff96928a50e92bd904a9d50f136bf45f88ccf Mon Sep 17 00:00:00 2001 From: Andre Marquand Date: Thu, 18 May 2023 14:39:31 +0200 Subject: [PATCH 22/43] unpacking fix --- pcntoolkit/model/hbr.py | 4 ++-- tests/testHBR.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 9ee10b8c..4eb5bc9a 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -624,7 +624,7 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): def get_samples(self, pb: ParamBuilder): with pb.model: - samples = self.dist[*pb.batch_effect_indices] + samples = self.dist[(*pb.batch_effect_indices,)] return samples @@ -663,7 +663,7 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): def get_samples(self, pb: ParamBuilder): with pb.model: - samples = self.dist[*pb.batch_effect_indices] + samples = self.dist[(*pb.batch_effect_indices,)] return samples diff --git a/tests/testHBR.py b/tests/testHBR.py index 176fef25..38cb6fbd 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -24,7 +24,7 @@ ########################### Experiment Settings ############################### -working_dir = '/home/stijn/temp/tests/' # Specift a working directory +working_dir = '/home/preclineu/andmar/py.sandbox/tmp/' # Specift a working directory # to save data and results. simulation_method = 'non-linear' From 34d6af845e23d32cd7a6f3b51d036c866301202f Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 14:41:12 +0200 Subject: [PATCH 23/43] Small changes --- pcntoolkit/model/hbr.py | 2 -- tests/testHBR.py | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 714d7e20..6e182d0c 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -207,14 +207,12 @@ def hbr(X, y, batch_effects, configs, idata=None): mu_intercept_mu_params=(0.0, 1.0), sigma_intercept_mu_params=(1.0,), ).get_samples(pb) - # print(f"{mu.shape.eval()=}") sigma = pb.make_param( "sigma", mu_sigma_params=(1.0, 0.2), sigma_sigma_params=(0.2,), slope_sigma_params=(0.0, 0.3), intercept_sigma_params=(1.0, 0.2) ).get_samples(pb) - # print(f"{sigma.shape.eval()=}") sigma_plus = np.log(1 + np.exp(sigma)) y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) diff --git a/tests/testHBR.py b/tests/testHBR.py index 176fef25..8101cc40 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -47,7 +47,7 @@ for model_type in model_types: - nm = norm_init(X_train, Y_train, alg='hbr', model_type=model_type,n_samples=100,n_tuning=10) + nm = norm_init(X_train, Y_train, alg='hbr',likelihood='SHASHb', model_type=model_type,n_samples=100,n_tuning=10) nm.estimate(X_train, Y_train, trbefile=working_dir+'trbefile.pkl') yhat, ys2 = nm.predict(X_test, tsbefile=working_dir+'tsbefile.pkl') @@ -71,6 +71,7 @@ color='gray', alpha=0.2) plt.title('Model %s, Feature %d' %(model_type, i)) plt.legend() + plt.show() ############################## Normative Modelling Test ####################### From 157377e63e6f3ad648f5835d49fc636fee154b14 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 14:51:53 +0200 Subject: [PATCH 24/43] Removed one deterministic node from the graph --- pcntoolkit/model/hbr.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 9ee10b8c..7673c07b 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -203,14 +203,10 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): mu_intercept_mu_params=(0.0, 5.0), sigma_intercept_mu_params=(5.0,), ).get_samples(pb) - # print(f"{mu.shape.eval()=}") sigma = pb.make_param( "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) ).get_samples(pb) - # print(f"{sigma.shape.eval()=}") - sigma_plus = pm.Deterministic( - "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)) - ) + sigma_plus = np.log(1 + np.exp(sigma)) y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) return model From 9ec430f144c673cc717ddaed3450781aa13393ea Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 14:53:15 +0200 Subject: [PATCH 25/43] Renamed the sigma parameter in HBR pytensor graph --- pcntoolkit/model/hbr.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 7673c07b..83dedaef 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -203,10 +203,10 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): mu_intercept_mu_params=(0.0, 5.0), sigma_intercept_mu_params=(5.0,), ).get_samples(pb) - sigma = pb.make_param( - "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + inv_softplus_sigma = pb.make_param( + "inv_softplus_(sigma)", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) ).get_samples(pb) - sigma_plus = np.log(1 + np.exp(sigma)) + sigma = np.log(1 + np.exp(inv_softplus_sigma)) y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) return model From d444f2aadd258e9990b4017389c72634f323e03f Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 15:00:46 +0200 Subject: [PATCH 26/43] Changed the type of batch_effect_indices to tuple --- pcntoolkit/model/hbr.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 4eb5bc9a..ed9ffdd1 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -188,12 +188,12 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): with pm.Model(coords=pb.coords) as model: pb.model = model - pb.batch_effect_indices = [ + pb.batch_effect_indices = tuple([ pm.Data( pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], mutable=True ) for i in range(len(pb.batch_effect_indices)) - ] + ]) if configs["likelihood"] == "Normal": mu = pb.make_param( @@ -624,7 +624,7 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): def get_samples(self, pb: ParamBuilder): with pb.model: - samples = self.dist[(*pb.batch_effect_indices,)] + samples = self.dist[pb.batch_effect_indices] return samples @@ -663,7 +663,7 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): def get_samples(self, pb: ParamBuilder): with pb.model: - samples = self.dist[(*pb.batch_effect_indices,)] + samples = self.dist[pb.batch_effect_indices] return samples From f8074f94857e135f9ff02069dddeef0281cf329e Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 15:03:09 +0200 Subject: [PATCH 27/43] Remove the sigma_plus node from the pytensor graph --- pcntoolkit/model/hbr.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index ed9ffdd1..e2cccb63 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -203,15 +203,11 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): mu_intercept_mu_params=(0.0, 5.0), sigma_intercept_mu_params=(5.0,), ).get_samples(pb) - # print(f"{mu.shape.eval()=}") - sigma = pb.make_param( - "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + inv_softplus_sigma = pb.make_param( + "inv_softplus_(sigma)", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) ).get_samples(pb) - # print(f"{sigma.shape.eval()=}") - sigma_plus = pm.Deterministic( - "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)) - ) - y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) + sigma = np.log(1 + np.exp(inv_softplus_sigma)) + y_like = pm.Normal("y_like", mu, sigma=sigma, observed=y) return model From 0a4068cabfd8aed49d678536dd7f14b86afa260e Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 15:03:44 +0200 Subject: [PATCH 28/43] Formatting --- pcntoolkit/model/hbr.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index e2cccb63..5f219d6a 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -76,6 +76,7 @@ def create_poly_basis(X, order): colid += D return Phi + def from_posterior(param, samples, distribution=None, half=False, freedom=1): if len(samples.shape) > 1: shape = samples.shape[1:] @@ -188,12 +189,16 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): with pm.Model(coords=pb.coords) as model: pb.model = model - pb.batch_effect_indices = tuple([ - pm.Data( - pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], mutable=True - ) - for i in range(len(pb.batch_effect_indices)) - ]) + pb.batch_effect_indices = tuple( + [ + pm.Data( + pb.batch_effect_dim_names[i], + pb.batch_effect_indices[i], + mutable=True, + ) + for i in range(len(pb.batch_effect_indices)) + ] + ) if configs["likelihood"] == "Normal": mu = pb.make_param( @@ -204,7 +209,9 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): sigma_intercept_mu_params=(5.0,), ).get_samples(pb) inv_softplus_sigma = pb.make_param( - "inv_softplus_(sigma)", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + "inv_softplus_(sigma)", + mu_sigma_params=(0.0, 2.0), + sigma_sigma_params=(5.0,), ).get_samples(pb) sigma = np.log(1 + np.exp(inv_softplus_sigma)) y_like = pm.Normal("y_like", mu, sigma=sigma, observed=y) @@ -495,6 +502,7 @@ def __getitem__(self, idx): else: return self.dist + class ParamBuilder: """ A class that simplifies the construction of parameterizations. @@ -689,6 +697,7 @@ def get_samples(self, pb): samples = samples.reshape((samples.shape[0], 1)) return samples + def get_design_matrix(X, nm, basis="linear"): if basis == "bspline": Phi = bspline_transform(X, nm.hbr.bsp) From a540978cc6c492bee8be6838ce9b81149a2f6705 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 17:49:10 +0200 Subject: [PATCH 29/43] Removed an empty dim from the samples, also changed name of sigma back --- pcntoolkit/model/hbr.py | 54 +++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 592be133..956cad62 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -181,6 +181,7 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): """ X = pytensor.shared(X) X = pytensor.tensor.cast(X, "floatX") + y = np.squeeze(y) y = pytensor.shared(y) y = pytensor.tensor.cast(y, "floatX") @@ -208,14 +209,13 @@ def hbr(X, y, batch_effects, batch_effects_size, configs, idata=None): mu_intercept_mu_params=(0.0, 5.0), sigma_intercept_mu_params=(5.0,), ).get_samples(pb) - inv_softplus_sigma = pb.make_param( - "inv_softplus_(sigma)", + sigma = pb.make_param( + "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,), ).get_samples(pb) - sigma = np.log(1 + np.exp(inv_softplus_sigma)) - y_like = pm.Normal("y_like", mu, sigma=sigma, observed=y) - + sigma_plus = np.log(1 + np.exp(sigma)) + y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) return model @@ -466,18 +466,6 @@ def __init__(self, name, dist, params, pb, has_random_effect=False) -> None: def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" with pb.model as m: - # If self.name.startswith slope: - # use the basis functions dim - # Otherwise, use the empty dim - dim = ( - "basis_functions" - if ( - self.name.startswith("slope") - or self.name.startswith("offset_slope") - ) - else "empty" - ) - if pb.idata is not None: samples = az.extract(pb.idata, var_names=self.name).to_numpy() if not self.has_random_effect: @@ -488,12 +476,15 @@ def make_dist(self, dist, params, pb): distribution=dist, freedom=pb.configs["freedom"], ) - elif self.has_random_effect: - self.dist = self.distmap[dist]( - self.name, *params, dims=[*pb.batch_effect_dim_names, dim] - ) + dims = [] + if self.has_random_effect: + dims = dims + pb.batch_effect_dim_names + if self.name.startswith("slope") or self.name.startswith("offset_slope"): + dims = dims + ["basis_functions"] + if dims == []: + self.dist = self.distmap[dist](self.name, *params) else: - self.dist = self.distmap[dist](self.name, *params, dims=dim) + self.dist = self.distmap[dist](self.name, *params, dims=dims) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" @@ -544,7 +535,6 @@ def __init__(self, X, y, batch_effects, idata, configs): ) self.coords[batch_effect_dim_name] = this_be_values self.batch_effect_indices.append(this_be_indices) - self.coords["empty"] = [0] self.coords["basis_functions"] = [i for i in range(X.shape.eval()[1])] # TODO reinstigate the 'dim' keyword, for the slope parameters @@ -619,12 +609,16 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): sigma_params = kwargs.get(f"sigma_{name}_params", (1.0,)) sigma_prior = Prior(f"sigma_{name}", sigma_dist, sigma_params, pb) - dim = "basis_functions" if self.name.startswith("slope") else "empty" + dims = ( + [*pb.batch_effect_dim_names, "basis_functions"] + if self.name.startswith("slope") + else pb.batch_effect_dim_names + ) self.dist = pm.Normal( name=name, mu=mu_prior.dist, sigma=sigma_prior.dist, - dims=[*pb.batch_effect_dim_names, dim], + dims=dims, ) def get_samples(self, pb: ParamBuilder): @@ -658,12 +652,15 @@ def __init__(self, name, pb: ParamBuilder, **kwargs): offset_prior = Prior( f"offset_{name}", offset_dist, offset_params, pb, has_random_effect=True ) - - dim = "basis_functions" if self.name.startswith("slope") else "empty" + dims = ( + [*pb.batch_effect_dim_names, "basis_functions"] + if self.name.startswith("slope") + else pb.batch_effect_dim_names + ) self.dist = pm.Deterministic( name=name, var=mu_prior.dist + sigma_prior.dist * offset_prior.dist, - dims=[*pb.batch_effect_dim_names, dim], + dims=dims, ) def get_samples(self, pb: ParamBuilder): @@ -695,7 +692,6 @@ def get_samples(self, pb): slope = pb.X @ self.slope_parameterization.get_samples(pb) samples = pm.math.flatten(intc) + pm.math.flatten(slope) - samples = samples.reshape((samples.shape[0], 1)) return samples From 494f71701c5f2ec7e8b9584e7b132e13a86081b6 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 17:51:21 +0200 Subject: [PATCH 30/43] Formatting --- pcntoolkit/model/hbr.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index a3f4c3b4..ea221c0b 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -213,12 +213,13 @@ def hbr(X, y, batch_effects, configs, idata=None): sigma_intercept_mu_params=(1.0,), ).get_samples(pb) sigma = pb.make_param( - "sigma", mu_sigma_params=(1.0, 0.2), + "sigma", + mu_sigma_params=(1.0, 0.2), sigma_sigma_params=(0.2,), slope_sigma_params=(0.0, 0.3), - intercept_sigma_params=(1.0, 0.2) + intercept_sigma_params=(1.0, 0.2), ).get_samples(pb) - sigma_plus = np.log(1 + np.exp(sigma)) + sigma_plus = np.log(1 + np.exp(sigma)) y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) elif configs["likelihood"] in ["SHASHb", "SHASHo", "SHASHo2"]: @@ -252,7 +253,7 @@ def hbr(X, y, batch_effects, configs, idata=None): sigma_plus = sigma epsilon = pb.make_param( "epsilon", - epsilon_params=(0.0, 1.), + epsilon_params=(0.0, 1.0), slope_epsilon_params=(0.0, 0.2), intercept_epsilon_params=(0.0, 0.2), ).get_samples(pb) @@ -275,6 +276,7 @@ def hbr(X, y, batch_effects, configs, idata=None): ) return model + class HBR: """Hierarchical Bayesian Regression for normative modeling @@ -428,11 +430,11 @@ def get_model(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) modeler = self.get_modeler() X = self.transform_X(X) - idata = self.idata if hasattr(self, 'idata') else None + idata = self.idata if hasattr(self, "idata") else None return modeler(X, y, batch_effects, self.configs, idata=idata) def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): - #TODO this function needs to be able to compute the batch_effects_size somehow + # TODO this function needs to be able to compute the batch_effects_size somehow arrays = [] for i in range(len(covariate_ranges)): arrays.append( @@ -451,6 +453,7 @@ def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): batch_effects_dummy = np.repeat(batch_effects, X.shape[0], axis=0) return X_dummy, batch_effects_dummy + class Prior: """ A wrapper class for a PyMC distribution. From 098c68c15fe1913f3fe8a28295a04c80a305cda9 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Thu, 18 May 2023 21:45:32 +0200 Subject: [PATCH 31/43] Got nm.estimate and nm.predict working like that --- pcntoolkit/model/hbr.py | 174 ++++++++++++++++--------- pcntoolkit/normative_model/norm_hbr.py | 6 +- 2 files changed, 117 insertions(+), 63 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index ea221c0b..504ba478 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -172,6 +172,10 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): def hbr(X, y, batch_effects, configs, idata=None): + + print(f"{X.shape=}") + print(f"{y.shape=}") + print(f"{batch_effects.shape=}") """ :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures @@ -182,45 +186,49 @@ def hbr(X, y, batch_effects, configs, idata=None): :param return_shared_variables: If true, returns references to the shared variables. The values of the shared variables can be set manually, allowing running the same model on different data without re-compiling it. :return: """ - X = pytensor.shared(X) - X = pytensor.tensor.cast(X, "floatX") - y = np.squeeze(y) - y = pytensor.shared(y) - y = pytensor.tensor.cast(y, "floatX") # Make a param builder that will make the correct calls pb = ParamBuilder(X, y, batch_effects, idata, configs) with pm.Model(coords=pb.coords) as model: + X = pm.MutableData("X", X) + pb.X = X + y = pm.MutableData("y", np.squeeze(y)) pb.model = model pb.batch_effect_indices = tuple( [ pm.Data( pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], - mutable=True, + mutable=True ) for i in range(len(pb.batch_effect_indices)) ] ) if configs["likelihood"] == "Normal": - mu = pb.make_param( - "mu", - mu_slope_mu_params=(0.0, 1.0), - sigma_slope_mu_params=(1.0,), - mu_intercept_mu_params=(0.0, 1.0), - sigma_intercept_mu_params=(1.0,), - ).get_samples(pb) - sigma = pb.make_param( - "sigma", - mu_sigma_params=(1.0, 0.2), - sigma_sigma_params=(0.2,), - slope_sigma_params=(0.0, 0.3), - intercept_sigma_params=(1.0, 0.2), - ).get_samples(pb) - sigma_plus = np.log(1 + np.exp(sigma)) - y_like = pm.Normal("y_like", mu, sigma=sigma_plus, observed=y) + mu = pm.Deterministic( + "mu_samples", + pb.make_param( + "mu", + mu_slope_mu_params=(0.0, 1.0), + sigma_slope_mu_params=(1.0,), + mu_intercept_mu_params=(0.0, 1.0), + sigma_intercept_mu_params=(1.0,), + ).get_samples(pb) + ) + sigma = pm.Deterministic( + "sigma_samples", + pb.make_param( + "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + ).get_samples(pb) + ) + sigma_plus = pm.Deterministic( + "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)) + ) + y_like = pm.Normal( + "y_like", mu, sigma=sigma_plus, observed=y + ) elif configs["likelihood"] in ["SHASHb", "SHASHo", "SHASHo2"]: """ @@ -234,45 +242,59 @@ def hbr(X, y, batch_effects, configs, idata=None): """ SHASH_map = {"SHASHb": SHASHb, "SHASHo": SHASHo, "SHASHo2": SHASHo2} - mu = pb.make_param( - "mu", - slope_mu_params=(0.0, 1.0), - mu_slope_mu_params=(0.0, 1.0), - sigma_slope_mu_params=(1.0,), - mu_intercept_mu_params=(0.0, 1.0), - sigma_intercept_mu_params=(1.0,), - ).get_samples(pb) - sigma = pb.make_param( - "sigma", - sigma_params=(1.0, 0.2), - sigma_dist="normal", - slope_sigma_params=(0.0, 0.3), - intercept_sigma_params=(1.0, 0.2), - ).get_samples(pb) - sigma_plus = np.log(1 + np.exp(sigma)) - sigma_plus = sigma - epsilon = pb.make_param( - "epsilon", - epsilon_params=(0.0, 1.0), - slope_epsilon_params=(0.0, 0.2), - intercept_epsilon_params=(0.0, 0.2), - ).get_samples(pb) - delta = pb.make_param( - "delta", - delta_params=(1.0, 0.1), - delta_dist="normal", - slope_epsilon_params=(0.0, 0.2), - intercept_epsilon_params=(1.0, 0.05), - ).get_samples(pb) - delta_plus = np.log(1 + np.exp(delta * 10)) / 10 + 0.3 - # delta_plus = delta + mu = pm.Deterministic( + "mu_samples", + pb.make_param( + "mu", + slope_mu_params=(0.0, 1.0), + mu_slope_mu_params=(0.0, 1.0), + sigma_slope_mu_params=(1.0,), + mu_intercept_mu_params=(0.0, 1.0), + sigma_intercept_mu_params=(1.0,), + ).get_samples(pb) + ) + sigma = pm.Deterministic( + "sigma_samples", + pb.make_param( + "sigma", + sigma_params=(1.0, 0.2), + sigma_dist="normal", + slope_sigma_params=(0.0, 0.3), + intercept_sigma_params=(1.0, 0.2), + ).get_samples(pb), + ) + sigma_plus = pm.Deterministic( + "sigma_plus", np.log(1 + np.exp(sigma)) + ) + epsilon = pm.Deterministic( + "epsilon_samples", + pb.make_param( + "epsilon", + epsilon_params=(0.0, 1.0), + slope_epsilon_params=(0.0, 0.2), + intercept_epsilon_params=(0.0, 0.2), + ).get_samples(pb) + ) + delta = pm.Deterministic( + "delta_samples", + pb.make_param( + "delta", + delta_params=(1.0, 0.1), + delta_dist="normal", + slope_epsilon_params=(0.0, 0.2), + intercept_epsilon_params=(1.0, 0.05), + ).get_samples(pb) + ) + delta_plus = pm.Deterministic( + "delta_plus", np.log(1 + np.exp(delta * 10)) / 10 + 0.3 + ) y_like = SHASH_map[configs["likelihood"]]( "y_like", mu=mu, sigma=sigma_plus, epsilon=epsilon, delta=delta_plus, - observed=y, + observed=y ) return model @@ -345,15 +367,43 @@ def estimate(self, X, y, batch_effects): ) return self.idata - def predict(self, X, batch_effects, pred="single"): - """Function to make predictions from the model""" + def predict(self, X, batch_effects, batch_effects_train, pred="single"): + """Function to make predictions from the model + Args: + X: Covariates + batch_effects: batch effects corresponding to X + all_batch_effects: combinations of all batch effects that were present the training data + """ X, batch_effects = expand_all(X, batch_effects) samples = self.configs["n_samples"] y = np.zeros([X.shape[0], 1]) X = self.transform_X(X) modeler = self.get_modeler() - with modeler(X, y, batch_effects, self.configs): + + # Make an array with occurences of all the values in be_train, but with the same size as be_test + truncated_batch_effects_train = np.stack( + [ + np.resize(np.unique(batch_effects_train[:, i]), X.shape[0]) + for i in range(batch_effects.shape[1]) + ], + axis=1, + ) + n_samples = X.shape[0] + with modeler(X, y, truncated_batch_effects_train, self.configs) as model: + # TODO + # This fails because the batch_effects provided here may not contain the same batch_effects as in the training set. If some are missing, the dimensions of the distributions don't match + # For each batch effect dim + for i in range(batch_effects.shape[1]): + # Find the unique values for that batch effect in the train data + values = np.unique(batch_effects_train[:, i]) + # Make a map that maps values to their index + valmap = {v: i for i, v in enumerate(values)} + # Compute those indices for the test data + indices = list(map(lambda x: valmap[x], batch_effects[:, i])) + # Those indices need to be used by the model + pm.set_data({f"batch_effect_{i}": indices}) + self.idata = pm.sample_posterior_predictive( trace=self.idata, progressbar=True ) @@ -528,18 +578,21 @@ def __init__(self, X, y, batch_effects, idata, configs): """ self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X + self.n_samples = X.shape[0] + self.n_basis_functions = X.shape[1] self.y = y self.batch_effects = batch_effects.astype(np.int16) self.idata: az.InferenceData = idata self.configs = configs - self.y_shape = y.shape.eval() - self.n_ys = y.shape[0].eval().item() + self.y_shape = y.shape + self.n_ys = y.shape[0] self.batch_effects_num = batch_effects.shape[1] self.batch_effect_dim_names = [] self.batch_effect_indices = [] self.coords = OrderedDict() + self.coords["basis_functions"] = np.arange(self.n_basis_functions) for i in range(self.batch_effects_num): batch_effect_dim_name = f"batch_effect_{i}" @@ -549,7 +602,6 @@ def __init__(self, X, y, batch_effects, idata, configs): ) self.coords[batch_effect_dim_name] = this_be_values self.batch_effect_indices.append(this_be_indices) - self.coords["basis_functions"] = [i for i in range(X.shape.eval()[1])] # TODO reinstigate the 'dim' keyword, for the slope parameters def make_param(self, name, **kwargs): diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index 65903ace..d63fb9e9 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -10,7 +10,7 @@ from __future__ import print_function from __future__ import division - +from itertools import product import os import warnings import sys @@ -220,6 +220,7 @@ def estimate(self, X, y, **kwargs): print('Could not find batch-effects file! Initilizing all as zeros ...') batch_effects_train = np.zeros([X.shape[0], 1]) + self.batch_effects_train = batch_effects_train self.hbr.estimate(X, y, batch_effects_train) return self @@ -236,7 +237,8 @@ def predict(self, Xs, X=None, Y=None, **kwargs): pred_type = self.configs['pred_type'] if self.configs['transferred'] == False: - yhat, s2 = self.hbr.predict(Xs, batch_effects_test, pred=pred_type) + + yhat, s2 = self.hbr.predict(X=Xs, batch_effects = batch_effects_test, batch_effects_train=self.batch_effects_train, pred=pred_type) else: raise ValueError("This is a transferred model. Please use predict_on_new_sites function.") From de0af99a66aa186779d50182a943f3936d171ee0 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 22:28:59 +0200 Subject: [PATCH 32/43] Removed pytensor from reqs, as it comes with pymc --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 29c5baa5..e5a7f650 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,6 @@ 'torch>=1.1.0', 'sphinx-tabs', 'pymc>=5.1.0', - 'pytensor', 'arviz==0.13.0' ], #python_requires='<3.10', From a932e5397985fdb5bed12b163e492477b05022a6 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 22:31:21 +0200 Subject: [PATCH 33/43] Updated estimate and predict functions Added get_mcmc_quantiles and get_mcmc_zscores Formatting --- pcntoolkit/normative_model/norm_hbr.py | 597 ++++++++++++++++++------- 1 file changed, 446 insertions(+), 151 deletions(-) diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index d63fb9e9..1009c2c8 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -9,12 +9,18 @@ from __future__ import print_function from __future__ import division +from sys import exit from itertools import product + import os import warnings import sys + +import xarray +import arviz as az import numpy as np +from scipy import special as spp from ast import literal_eval as make_tuple try: @@ -34,19 +40,19 @@ class NormHBR(NormBase): - - """ HBR multi-batch normative modelling class. By default, this function - estimates a linear model with random intercept, random slope, and random + + """HBR multi-batch normative modelling class. By default, this function + estimates a linear model with random intercept, random slope, and random homoscedastic noise. - + :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures :param trbefile: the address to the batch effects file for the training set. the batch effect array should be a [N×M] array where M is the number of the type of batch effects. For example when the site and gender is modeled - as batch effects M=2. Each column in the batch effect array contains the + as batch effects M=2. Each column in the batch effect array contains the batch ID (starting from 0) for each sample. If not specified (default=None) - then all samples assumed to be from the same batch (i.e., the batch effect + then all samples assumed to be from the same batch (i.e., the batch effect is not modelled). :param tsbefile: Similar to trbefile for the test set. :param model_type: Specifies the type of the model from 'linear', 'plynomial', @@ -59,146 +65,178 @@ class NormHBR(NormBase): parametrized on a linear function (heteroscedastic noise) or it is fixed for each batch (homoscedastic noise). :param linear_epsilon: Boolean (default='False') to decide the parametrization - of epsilon for the SHASH likelihood that controls its skewness. - If True, epsilon is parametrized on a linear function + of epsilon for the SHASH likelihood that controls its skewness. + If True, epsilon is parametrized on a linear function (thus changes with covariates) otherwise it is fixed for each batch. :param linear_delta: Boolean (default='False') to decide the parametrization - of delta for the SHASH likelihood that controls its kurtosis. - If True, delta is parametrized on a linear function + of delta for the SHASH likelihood that controls its kurtosis. + If True, delta is parametrized on a linear function (thus changes with covariates) otherwise it is fixed for each batch. - :param random_intercept_{parameter}: if parameters mu (default='True'), + :param random_intercept_{parameter}: if parameters mu (default='True'), sigma (default='False'), epsilon (default='False'), and delta (default='False') - are parametrized on a linear function, then this boolean decides + are parametrized on a linear function, then this boolean decides whether the intercept can vary across batches. - :param random_slope_{parameter}: if parameters mu (default='True'), + :param random_slope_{parameter}: if parameters mu (default='True'), sigma (default='False'), epsilon (default='False'), and delta (default='False') - are parametrized on a linear function, then this boolean decides + are parametrized on a linear function, then this boolean decides whether the slope can vary across batches. - :param centered_intercept_{parameter}: if parameters mu (default='False'), + :param centered_intercept_{parameter}: if parameters mu (default='False'), sigma (default='False'), epsilon (default='False'), and delta (default='False') - are parametrized on a linear function, then this boolean decides + are parametrized on a linear function, then this boolean decides whether the parameters of intercept are estimated in a centered or non-centered manner (default). While centered estimation runs faster it may cause some problems for the sampler (the funnel of hell). - :param centered_slope_{parameter}: if parameters mu (default='False'), + :param centered_slope_{parameter}: if parameters mu (default='False'), sigma (default='False'), epsilon (default='False'), and delta (default='False') - are parametrized on a linear function, then this boolean decides + are parametrized on a linear function, then this boolean decides whether the parameters of slope are estimated in a centered or non-centered manner (default). While centered estimation runs faster it may cause some problems for the sampler (the funnel of hell). :param sampler: specifies the type of PyMC sampler (Defauls is 'NUTS'). - :param n_samples: The number of samples to draw (Default is '1000'). Please - note that this parameter must be specified in a string fromat ('1000' and - not 1000). - :param n_tuning: String that specifies the number of iterations to adjust + :param n_samples: The number of samples to draw (Default is '1000'). Please + note that this parameter must be specified in a string fromat ('1000' and + not 1000). + :param n_tuning: String that specifies the number of iterations to adjust the samplers's step sizes, scalings or similar (defauls is '500'). - :param n_chains: String that specifies the number of chains to sample. Defauls - is '1' for faster estimation, but note that sampling independent chains + :param n_chains: String that specifies the number of chains to sample. Defauls + is '1' for faster estimation, but note that sampling independent chains is important for some convergence checks. :param cores: String that specifies the number of chains to run in parallel. (defauls is '1'). :param Initialization method to use for auto-assigned NUTS samplers. The - defauls is 'jitter+adapt_diag' that starts with a identity mass matrix + defauls is 'jitter+adapt_diag' that starts with a identity mass matrix and then adapt a diagonal based on the variance of the tuning samples while adding a uniform jitter in [-1, 1] to the starting point in each chain. - :param target_accept: String that of a float in [0, 1] that regulates the + :param target_accept: String that of a float in [0, 1] that regulates the step size such that we approximate this acceptance rate. The defauls is '0.8' but higher values like 0.9 or 0.95 often work better for problematic posteriors. :param order: String that defines the order of bspline or polynomial model. The defauls is '3'. :param nknots: String that defines the numbers of knots for the bspline model. - The defauls is '5'. Higher values increase the model complexity with negative + The defauls is '5'. Higher values increase the model complexity with negative effect on the spped of estimations. :param nn_hidden_layers_num: String the specifies the number of hidden layers in neural network model. It can be either '1' or '2'. The default is set to '2'. - :param nn_hidden_neuron_num: String that specifies the number of neurons in + :param nn_hidden_neuron_num: String that specifies the number of neurons in the hidden layers. The defauls is set to '2'. - + Written by S.de Boer and S.M. Kia - + """ def __init__(self, **kwargs): - self.configs = dict() # inputs - self.configs['trbefile'] = kwargs.get('trbefile', None) - self.configs['tsbefile'] = kwargs.get('tsbefile', None) + self.configs["trbefile"] = kwargs.get("trbefile", None) + self.configs["tsbefile"] = kwargs.get("tsbefile", None) # Model settings - self.configs['type'] = kwargs.get('model_type', 'linear') - self.configs['random_noise'] = kwargs.get('random_noise', 'True') == 'True' - self.configs['likelihood'] = kwargs.get('likelihood', 'Normal') + self.configs["type"] = kwargs.get("model_type", "linear") + self.configs["random_noise"] = kwargs.get("random_noise", "True") == "True" + self.configs["likelihood"] = kwargs.get("likelihood", "Normal") # sampler settings - self.configs['n_samples'] = int(kwargs.get('n_samples', '1000')) - self.configs['n_tuning'] = int(kwargs.get('n_tuning', '500')) - self.configs['n_chains'] = int(kwargs.get('n_chains', '1')) - self.configs['sampler'] = kwargs.get('sampler', 'NUTS') - self.configs['target_accept'] = float(kwargs.get('target_accept', '0.8')) - self.configs['init'] = kwargs.get('init', 'jitter+adapt_diag') - self.configs['cores'] = int(kwargs.get('cores', '1')) + self.configs["n_samples"] = int(kwargs.get("n_samples", "1000")) + self.configs["n_tuning"] = int(kwargs.get("n_tuning", "500")) + self.configs["n_chains"] = int(kwargs.get("n_chains", "1")) + self.configs["sampler"] = kwargs.get("sampler", "NUTS") + self.configs["target_accept"] = float(kwargs.get("target_accept", "0.8")) + self.configs["init"] = kwargs.get("init", "jitter+adapt_diag") + self.configs["cores"] = int(kwargs.get("cores", "1")) # model transfer setting - self.configs['freedom'] = int(kwargs.get('freedom', '1')) - self.configs['transferred'] = False + self.configs["freedom"] = int(kwargs.get("freedom", "1")) + self.configs["transferred"] = False # deprecated settings - self.configs['skewed_likelihood'] = kwargs.get('skewed_likelihood', 'False') == 'True' + self.configs["skewed_likelihood"] = ( + kwargs.get("skewed_likelihood", "False") == "True" + ) # misc - self.configs['pred_type'] = kwargs.get('pred_type', 'single') - - if self.configs['type'] == 'bspline': - self.configs['order'] = int(kwargs.get('order', '3')) - self.configs['nknots'] = int(kwargs.get('nknots', '5')) - elif self.configs['type'] == 'polynomial': - self.configs['order'] = int(kwargs.get('order', '3')) - elif self.configs['type'] == 'nn': - self.configs['nn_hidden_neuron_num'] = int(kwargs.get('nn_hidden_neuron_num', '2')) - self.configs['nn_hidden_layers_num'] = int(kwargs.get('nn_hidden_layers_num', '2')) - if self.configs['nn_hidden_layers_num'] > 2: - raise ValueError("Using " + str(self.configs['nn_hidden_layers_num']) \ - + " layers was not implemented. The number of " \ - + " layers has to be less than 3.") - elif self.configs['type'] == 'linear': + self.configs["pred_type"] = kwargs.get("pred_type", "single") + + if self.configs["type"] == "bspline": + self.configs["order"] = int(kwargs.get("order", "3")) + self.configs["nknots"] = int(kwargs.get("nknots", "5")) + elif self.configs["type"] == "polynomial": + self.configs["order"] = int(kwargs.get("order", "3")) + elif self.configs["type"] == "nn": + self.configs["nn_hidden_neuron_num"] = int( + kwargs.get("nn_hidden_neuron_num", "2") + ) + self.configs["nn_hidden_layers_num"] = int( + kwargs.get("nn_hidden_layers_num", "2") + ) + if self.configs["nn_hidden_layers_num"] > 2: + raise ValueError( + "Using " + + str(self.configs["nn_hidden_layers_num"]) + + " layers was not implemented. The number of " + + " layers has to be less than 3." + ) + elif self.configs["type"] == "linear": pass else: - raise ValueError("Unknown model type, please specify from 'linear', \ - 'polynomial', 'bspline', or 'nn'.") - - if self.configs['type'] in ['bspline', 'polynomial', 'linear']: + raise ValueError( + "Unknown model type, please specify from 'linear', \ + 'polynomial', 'bspline', or 'nn'." + ) - for p in ['mu', 'sigma', 'epsilon', 'delta']: - self.configs[f'linear_{p}'] = kwargs.get(f'linear_{p}', 'False') == 'True' + if self.configs["type"] in ["bspline", "polynomial", "linear"]: + for p in ["mu", "sigma", "epsilon", "delta"]: + self.configs[f"linear_{p}"] = ( + kwargs.get(f"linear_{p}", "False") == "True" + ) ######## Deprecations (remove in later version) - if f'{p}_linear' in kwargs.keys(): - print(f'The keyword \'{p}_linear\' is deprecated. It is now automatically replaced with \'linear_{p}\'') - self.configs[f'linear_{p}'] = kwargs.get(f'{p}_linear', 'False') == 'True' - ##### End Deprecations - - for c in ['centered','random']: - self.configs[f'{c}_{p}'] = kwargs.get(f'{c}_{p}', 'False') == 'True' - for sp in ['slope','intercept']: - self.configs[f'{c}_{sp}_{p}'] = kwargs.get(f'{c}_{sp}_{p}', 'False') == 'True' + if f"{p}_linear" in kwargs.keys(): + print( + f"The keyword '{p}_linear' is deprecated. It is now automatically replaced with 'linear_{p}'" + ) + self.configs[f"linear_{p}"] = ( + kwargs.get(f"{p}_linear", "False") == "True" + ) + ##### End Deprecations + + for c in ["centered", "random"]: + self.configs[f"{c}_{p}"] = kwargs.get(f"{c}_{p}", "False") == "True" + for sp in ["slope", "intercept"]: + self.configs[f"{c}_{sp}_{p}"] = ( + kwargs.get(f"{c}_{sp}_{p}", "False") == "True" + ) ######## Deprecations (remove in later version) - if self.configs['linear_sigma']: - if 'random_noise' in kwargs.keys(): - print("The keyword \'random_noise\' is deprecated. It is now automatically replaced with \'random_intercept_sigma\', because sigma is linear") - self.configs['random_intercept_sigma'] = kwargs.get('random_noise','True') == 'True' - elif 'random_noise' in kwargs.keys(): - print("The keyword \'random_noise\' is deprecated. It is now automatically replaced with \'random_sigma\', because sigma is fixed") - self.configs['random_sigma'] = kwargs.get('random_noise','True') == 'True' - if 'random_slope' in kwargs.keys(): - print("The keyword \'random_slope\' is deprecated. It is now automatically replaced with \'random_intercept_mu\'") - self.configs['random_slope_mu'] = kwargs.get('random_slope','True') == 'True' - ##### End Deprecations - + if self.configs["linear_sigma"]: + if "random_noise" in kwargs.keys(): + print( + "The keyword 'random_noise' is deprecated. It is now automatically replaced with 'random_intercept_sigma', because sigma is linear" + ) + self.configs["random_intercept_sigma"] = ( + kwargs.get("random_noise", "True") == "True" + ) + elif "random_noise" in kwargs.keys(): + print( + "The keyword 'random_noise' is deprecated. It is now automatically replaced with 'random_sigma', because sigma is fixed" + ) + self.configs["random_sigma"] = ( + kwargs.get("random_noise", "True") == "True" + ) + if "random_slope" in kwargs.keys(): + print( + "The keyword 'random_slope' is deprecated. It is now automatically replaced with 'random_intercept_mu'" + ) + self.configs["random_slope_mu"] = ( + kwargs.get("random_slope", "True") == "True" + ) + ##### End Deprecations ## Default parameters - self.configs['linear_mu'] = kwargs.get('linear_mu','True') == 'True' - self.configs['random_mu'] = kwargs.get('random_mu','True') == 'True' - self.configs['random_intercept_mu'] = kwargs.get('random_intercept_mu','True') == 'True' - self.configs['random_slope_mu'] = kwargs.get('random_slope_mu','True') == 'True' - self.configs['random_sigma'] = kwargs.get('random_sigma','True') == 'True' - self.configs['centered_sigma'] = kwargs.get('centered_sigma','True') == 'True' + self.configs["linear_mu"] = kwargs.get("linear_mu", "True") == "True" + self.configs["random_mu"] = kwargs.get("random_mu", "True") == "True" + self.configs["random_intercept_mu"] = ( + kwargs.get("random_intercept_mu", "True") == "True" + ) + self.configs["random_slope_mu"] = ( + kwargs.get("random_slope_mu", "True") == "True" + ) + self.configs["random_sigma"] = kwargs.get("random_sigma", "True") == "True" + self.configs["centered_sigma"] = kwargs.get("centered_sigma", "True") == "True" ## End default parameters self.hbr = HBR(self.configs) @@ -212,118 +250,375 @@ def neg_log_lik(self): return -1 def estimate(self, X, y, **kwargs): - - trbefile = kwargs.get('trbefile', None) + trbefile = kwargs.get("trbefile", None) if trbefile is not None: batch_effects_train = fileio.load(trbefile) else: - print('Could not find batch-effects file! Initilizing all as zeros ...') + print("Could not find batch-effects file! Initilizing all as zeros ...") batch_effects_train = np.zeros([X.shape[0], 1]) - self.batch_effects_train = batch_effects_train + self.batch_effects_maps = [ + {v: i for i, v in enumerate(np.unique(batch_effects_train[:, j]))} + for j in range(batch_effects_train.shape[1]) + ] + self.hbr.estimate(X, y, batch_effects_train) return self def predict(self, Xs, X=None, Y=None, **kwargs): - - tsbefile = kwargs.get('tsbefile', None) + tsbefile = kwargs.get("tsbefile", None) if tsbefile is not None: batch_effects_test = fileio.load(tsbefile) else: - print('Could not find batch-effects file! Initilizing all as zeros ...') + print("Could not find batch-effects file! Initilizing all as zeros ...") batch_effects_test = np.zeros([Xs.shape[0], 1]) - pred_type = self.configs['pred_type'] + pred_type = self.configs["pred_type"] - if self.configs['transferred'] == False: - - yhat, s2 = self.hbr.predict(X=Xs, batch_effects = batch_effects_test, batch_effects_train=self.batch_effects_train, pred=pred_type) + if self.configs["transferred"] == False: + yhat, s2 = self.hbr.predict( + X=Xs, + batch_effects=batch_effects_test, + batch_effects_maps=self.batch_effects_maps, + pred=pred_type, + **kwargs, + ) else: - raise ValueError("This is a transferred model. Please use predict_on_new_sites function.") + raise ValueError( + "This is a transferred model. Please use predict_on_new_sites function." + ) return yhat.squeeze(), s2.squeeze() + + def estimate_on_new_sites(self, X, y, batch_effects): self.hbr.estimate_on_new_site(X, y, batch_effects) - self.configs['transferred'] = True + self.configs["transferred"] = True return self def predict_on_new_sites(self, X, batch_effects): - yhat, s2 = self.hbr.predict_on_new_site(X, batch_effects) return yhat, s2 - def extend(self, X, y, batch_effects, X_dummy_ranges=[[0.1, 0.9, 0.01]], - merge_batch_dim=0, samples=10, informative_prior=False): - + def extend( + self, + X, + y, + batch_effects, + X_dummy_ranges=[[0.1, 0.9, 0.01]], + merge_batch_dim=0, + samples=10, + informative_prior=False, + ): X_dummy, batch_effects_dummy = self.hbr.create_dummy_inputs(X_dummy_ranges) - X_dummy, batch_effects_dummy, Y_dummy = self.hbr.generate(X_dummy, - batch_effects_dummy, samples) + X_dummy, batch_effects_dummy, Y_dummy = self.hbr.generate( + X_dummy, batch_effects_dummy, samples + ) - batch_effects[:, merge_batch_dim] = batch_effects[:, merge_batch_dim] + \ - np.max(batch_effects_dummy[:, merge_batch_dim]) + 1 + batch_effects[:, merge_batch_dim] = ( + batch_effects[:, merge_batch_dim] + + np.max(batch_effects_dummy[:, merge_batch_dim]) + + 1 + ) if informative_prior: - self.hbr.adapt(np.concatenate((X_dummy, X)), - np.concatenate((Y_dummy, y)), - np.concatenate((batch_effects_dummy, batch_effects))) + self.hbr.adapt( + np.concatenate((X_dummy, X)), + np.concatenate((Y_dummy, y)), + np.concatenate((batch_effects_dummy, batch_effects)), + ) else: - self.hbr.estimate(np.concatenate((X_dummy, X)), - np.concatenate((Y_dummy, y)), - np.concatenate((batch_effects_dummy, batch_effects))) + self.hbr.estimate( + np.concatenate((X_dummy, X)), + np.concatenate((Y_dummy, y)), + np.concatenate((batch_effects_dummy, batch_effects)), + ) return self - def tune(self, X, y, batch_effects, X_dummy_ranges=[[0.1, 0.9, 0.01]], - merge_batch_dim=0, samples=10, informative_prior=False): - + def tune( + self, + X, + y, + batch_effects, + X_dummy_ranges=[[0.1, 0.9, 0.01]], + merge_batch_dim=0, + samples=10, + informative_prior=False, + ): tune_ids = list(np.unique(batch_effects[:, merge_batch_dim])) X_dummy, batch_effects_dummy = self.hbr.create_dummy_inputs(X_dummy_ranges) for idx in tune_ids: X_dummy = X_dummy[batch_effects_dummy[:, merge_batch_dim] != idx, :] - batch_effects_dummy = batch_effects_dummy[batch_effects_dummy[:, merge_batch_dim] != idx, :] + batch_effects_dummy = batch_effects_dummy[ + batch_effects_dummy[:, merge_batch_dim] != idx, : + ] - X_dummy, batch_effects_dummy, Y_dummy = self.hbr.generate(X_dummy, - batch_effects_dummy, samples) + X_dummy, batch_effects_dummy, Y_dummy = self.hbr.generate( + X_dummy, batch_effects_dummy, samples + ) if informative_prior: - self.hbr.adapt(np.concatenate((X_dummy, X)), - np.concatenate((Y_dummy, y)), - np.concatenate((batch_effects_dummy, batch_effects))) + self.hbr.adapt( + np.concatenate((X_dummy, X)), + np.concatenate((Y_dummy, y)), + np.concatenate((batch_effects_dummy, batch_effects)), + ) else: - self.hbr.estimate(np.concatenate((X_dummy, X)), - np.concatenate((Y_dummy, y)), - np.concatenate((batch_effects_dummy, batch_effects))) + self.hbr.estimate( + np.concatenate((X_dummy, X)), + np.concatenate((Y_dummy, y)), + np.concatenate((batch_effects_dummy, batch_effects)), + ) return self - def merge(self, nm, X_dummy_ranges=[[0.1, 0.9, 0.01]], merge_batch_dim=0, - samples=10): - + def merge( + self, nm, X_dummy_ranges=[[0.1, 0.9, 0.01]], merge_batch_dim=0, samples=10 + ): X_dummy1, batch_effects_dummy1 = self.hbr.create_dummy_inputs(X_dummy_ranges) X_dummy2, batch_effects_dummy2 = nm.hbr.create_dummy_inputs(X_dummy_ranges) - X_dummy1, batch_effects_dummy1, Y_dummy1 = self.hbr.generate(X_dummy1, - batch_effects_dummy1, samples) - X_dummy2, batch_effects_dummy2, Y_dummy2 = nm.hbr.generate(X_dummy2, - batch_effects_dummy2, samples) - - batch_effects_dummy2[:, merge_batch_dim] = batch_effects_dummy2[:, merge_batch_dim] + \ - np.max(batch_effects_dummy1[:, merge_batch_dim]) + 1 - - self.hbr.estimate(np.concatenate((X_dummy1, X_dummy2)), - np.concatenate((Y_dummy1, Y_dummy2)), - np.concatenate((batch_effects_dummy1, - batch_effects_dummy2))) + X_dummy1, batch_effects_dummy1, Y_dummy1 = self.hbr.generate( + X_dummy1, batch_effects_dummy1, samples + ) + X_dummy2, batch_effects_dummy2, Y_dummy2 = nm.hbr.generate( + X_dummy2, batch_effects_dummy2, samples + ) + + batch_effects_dummy2[:, merge_batch_dim] = ( + batch_effects_dummy2[:, merge_batch_dim] + + np.max(batch_effects_dummy1[:, merge_batch_dim]) + + 1 + ) + + self.hbr.estimate( + np.concatenate((X_dummy1, X_dummy2)), + np.concatenate((Y_dummy1, Y_dummy2)), + np.concatenate((batch_effects_dummy1, batch_effects_dummy2)), + ) return self def generate(self, X, batch_effects, samples=10): - - X, batch_effects, generated_samples = self.hbr.generate(X, batch_effects, - samples) + X, batch_effects, generated_samples = self.hbr.generate( + X, batch_effects, samples + ) return X, batch_effects, generated_samples + + def get_mcmc_quantiles(self, X, batch_effects=None, z_scores=None): + + """ + Computes quantiles of an estimated normative model. + + Args: + X ([N*p]ndarray): covariates for which the quantiles are computed (must be scaled if scaler is set) + batch_effects (ndarray): the batch effects corresponding to X + z_scores (ndarray): Use this to determine which quantiles will be computed. The resulting quantiles will have the z-scores given in this list. + """ + # Set batch effects to zero if none are provided + if batch_effects is None: + batch_effects = batch_effects_test = np.zeros([X.shape[0], 1]) + + + # Set the z_scores for which the quantiles are computed + if z_scores is None: + z_scores = np.arange(-3, 4) + likelihood=self.configs['likelihood'] + print(f"{likelihood=}") + + # Determine the variables to predict + if self.configs["likelihood"] == "Normal": + var_names = ["mu_samples", "sigma_plus_samples"] + elif self.configs["likelihood"].startswith("SHASH"): + var_names = [ + "mu_samples", + "sigma_plus_samples", + "epsilon_samples", + "delta_plus_samples", + ] + else: + exit("Unknown likelihood: " + self.configs["likelihood"]) + + # Delete the posterior predictive if it already exists + if 'posterior_predictive' in self.hbr.idata.groups(): + del self.hbr.idata.posterior_predictive + + # Do a forward to get the posterior predictive in the idata + self.hbr.predict( + X=X, + batch_effects=batch_effects, + batch_effects_maps=self.batch_effects_maps, + pred="single", + var_names=var_names+["y_like"], + ) + + # Extract the relevant samples from the idata + post_pred = az.extract( + self.hbr.idata, "posterior_predictive", var_names=var_names + ) + + # Separate the samples into a list so that they can be unpacked + array_of_vars = list(map(lambda x: post_pred[x], var_names)) + + # Create an array to hold the quantiles + len_synth_data, n_mcmc_samples = post_pred["mu_samples"].shape + quantiles = np.zeros((z_scores.shape[0], len_synth_data, n_mcmc_samples)) + + # Compute the quantile iteratively for each z-score + for i, j in enumerate(z_scores): + zs = np.full((len_synth_data, n_mcmc_samples), j, dtype=float) + quantiles[i] = xarray.apply_ufunc( + quantile, + *array_of_vars, + kwargs={"zs": zs, "likelihood": self.configs['likelihood']}, + ) + return quantiles.mean(axis=-1) + + + def get_mcmc_zscores(self, X, y, batch_effects=None): + + """ + Computes zscores of data given an estimated model + + Args: + X ([N*p]ndarray): covariates + y ([N*1]ndarray): response variables + batch_effects (ndarray): the batch effects corresponding to X + """ + # Set batch effects to zero if none are provided + print(self.configs['likelihood']) + if batch_effects is None: + batch_effects = batch_effects_test = np.zeros([X.shape[0], 1]) + + # Determine the variables to predict + if self.configs["likelihood"] == "Normal": + var_names = ["mu_samples", "sigma_plus_samples"] + elif self.configs["likelihood"].startswith("SHASH"): + var_names = [ + "mu_samples", + "sigma_plus_samples", + "epsilon_samples", + "delta_plus_samples", + ] + else: + exit("Unknown likelihood: " + self.configs["likelihood"]) + + # Delete the posterior predictive if it already exists + if 'posterior_predictive' in self.hbr.idata.groups(): + del self.hbr.idata.posterior_predictive + + # Do a forward to get the posterior predictive in the idata + self.hbr.predict( + X=X, + batch_effects=batch_effects, + batch_effects_maps=self.batch_effects_maps, + pred="single", + var_names=var_names+["y_like"], + ) + + # Extract the relevant samples from the idata + post_pred = az.extract( + self.hbr.idata, "posterior_predictive", var_names=var_names + ) + + # Separate the samples into a list so that they can be unpacked + array_of_vars = list(map(lambda x: post_pred[x], var_names)) + + # Create an array to hold the quantiles + len_data, n_mcmc_samples = post_pred["mu_samples"].shape + + # Compute the quantile iteratively for each z-score + z_scores = xarray.apply_ufunc( + z_score, + *array_of_vars, + kwargs={"y": y, "likelihood": self.configs['likelihood']}, + ) + return z_scores.mean(axis=-1) + + + +def S_inv(x, e, d): + return np.sinh((np.arcsinh(x) + e) / d) + +def K(p, x): + """ + Computes the values of spp.kv(p,x) for only the unique values of p + """ + + ps, idxs = np.unique(p, return_inverse=True) + return spp.kv(ps, x)[idxs].reshape(p.shape) + +def P(q): + """ + The P function as given in Jones et al. + :param q: + :return: + """ + frac = np.exp(1 / 4) / np.sqrt(8 * np.pi) + K1 = K((q + 1) / 2, 1 / 4) + K2 = K((q - 1) / 2, 1 / 4) + a = (K1 + K2) * frac + return a + +def m(epsilon, delta, r): + """ + The r'th uncentered moment. Given by Jones et al. + """ + frac1 = 1 / np.power(2, r) + acc = 0 + for i in range(r + 1): + combs = spp.comb(r, i) + flip = np.power(-1, i) + ex = np.exp((r - 2 * i) * epsilon / delta) + p = P((r - 2 * i) / delta) + acc += combs * flip * ex * p + return frac1 * acc + +def quantile( mu, sigma, epsilon, delta, zs=0, likelihood = "Normal"): + """Get the zs'th quantiles given likelihood parameters""" + if likelihood.startswith('SHASH'): + if likelihood == "SHASHo": + quantiles = S_inv(zs,epsilon,delta)*sigma + mu + elif likelihood == "SHASHo2": + sigma_d = sigma/delta + quantiles = S_inv(zs,epsilon,delta)*sigma_d + mu + elif likelihood == "SHASHb": + true_mu = m(epsilon, delta, 1) + true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + SHASH_c = ((S_inv(zs,epsilon,delta)-true_mu)/true_sigma) + quantiles = SHASH_c *sigma + mu + elif likelihood == 'Normal': + quantiles = zs*sigma + mu + else: + exit("Unsupported likelihood") + return quantiles + + +def z_score(mu, sigma, epsilon, delta, y, likelihood = "Normal"): + """Get the z-scores of Y, given likelihood parameters""" + if likelihood.startswith('SHASH'): + if likelihood == "SHASHo": + SHASH = (y-mu)/sigma + Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + elif likelihood == "SHASHo2": + sigma_d = sigma/delta + SHASH = (y-mu)/sigma_d + Z = np.sinh(np.arcsinh(SHASH)*delta - epsilon) + elif likelihood == "SHASHb": + true_mu = m(epsilon, delta, 1) + true_sigma = np.sqrt((m(epsilon, delta, 2) - true_mu ** 2)) + SHASH_c = ((y-mu)/sigma) + SHASH = SHASH_c * true_sigma + true_mu + Z = np.sinh(np.arcsinh(SHASH) * delta - epsilon) + elif likelihood == 'Normal': + Z = (y-mu)/sigma + else: + exit("Unsupported likelihood") + return Z + From f53c56fe02f16802a06bede86ad4a743ac567b8d Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 22:32:25 +0200 Subject: [PATCH 34/43] Updated estimate and predict functions --- pcntoolkit/model/hbr.py | 96 +++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 504ba478..bf1679ea 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -172,10 +172,6 @@ def from_posterior(param, samples, distribution=None, half=False, freedom=1): def hbr(X, y, batch_effects, configs, idata=None): - - print(f"{X.shape=}") - print(f"{y.shape=}") - print(f"{batch_effects.shape=}") """ :param X: [N×P] array of clinical covariates :param y: [N×1] array of neuroimaging measures @@ -191,16 +187,18 @@ def hbr(X, y, batch_effects, configs, idata=None): pb = ParamBuilder(X, y, batch_effects, idata, configs) with pm.Model(coords=pb.coords) as model: - X = pm.MutableData("X", X) + model.add_coord("datapoints", np.arange(X.shape[0]), mutable=True) + X = pm.MutableData("X", X, dims=("datapoints", "basis_functions")) pb.X = X - y = pm.MutableData("y", np.squeeze(y)) + y = pm.MutableData("y", np.squeeze(y), dims="datapoints") pb.model = model pb.batch_effect_indices = tuple( [ pm.Data( pb.batch_effect_dim_names[i], pb.batch_effect_indices[i], - mutable=True + mutable=True, + dims="datapoints", ) for i in range(len(pb.batch_effect_indices)) ] @@ -215,19 +213,21 @@ def hbr(X, y, batch_effects, configs, idata=None): sigma_slope_mu_params=(1.0,), mu_intercept_mu_params=(0.0, 1.0), sigma_intercept_mu_params=(1.0,), - ).get_samples(pb) + ).get_samples(pb), + dims="datapoints", ) sigma = pm.Deterministic( "sigma_samples", pb.make_param( "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) - ).get_samples(pb) + ).get_samples(pb), + dims="datapoints", ) sigma_plus = pm.Deterministic( - "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)) + "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)), dims="datapoints" ) y_like = pm.Normal( - "y_like", mu, sigma=sigma_plus, observed=y + "y_like", mu, sigma=sigma_plus, observed=y, dims="datapoints" ) elif configs["likelihood"] in ["SHASHb", "SHASHo", "SHASHo2"]: @@ -246,47 +246,53 @@ def hbr(X, y, batch_effects, configs, idata=None): "mu_samples", pb.make_param( "mu", - slope_mu_params=(0.0, 1.0), - mu_slope_mu_params=(0.0, 1.0), - sigma_slope_mu_params=(1.0,), - mu_intercept_mu_params=(0.0, 1.0), - sigma_intercept_mu_params=(1.0,), - ).get_samples(pb) + slope_mu_params=(0.0, 2.0), + mu_slope_mu_params=(0.0, 2.0), + sigma_slope_mu_params=(2.0,), + mu_intercept_mu_params=(0.0, 2.0), + sigma_intercept_mu_params=(2.0,), + ).get_samples(pb), + dims="datapoints", ) sigma = pm.Deterministic( "sigma_samples", pb.make_param( "sigma", - sigma_params=(1.0, 0.2), + sigma_params=(1.0, 1.0), sigma_dist="normal", - slope_sigma_params=(0.0, 0.3), - intercept_sigma_params=(1.0, 0.2), + slope_sigma_params=(0.0, 1.0), + intercept_sigma_params=(1.0, 1.0), ).get_samples(pb), + dims="datapoints", ) sigma_plus = pm.Deterministic( - "sigma_plus", np.log(1 + np.exp(sigma)) + "sigma_plus_samples", np.log(1 + np.exp(sigma)), dims="datapoints" ) epsilon = pm.Deterministic( "epsilon_samples", pb.make_param( "epsilon", epsilon_params=(0.0, 1.0), - slope_epsilon_params=(0.0, 0.2), - intercept_epsilon_params=(0.0, 0.2), - ).get_samples(pb) + slope_epsilon_params=(0.0, 1.0), + intercept_epsilon_params=(0.0, 1.0), + ).get_samples(pb), + dims="datapoints", ) delta = pm.Deterministic( "delta_samples", pb.make_param( "delta", - delta_params=(1.0, 0.1), + delta_params=(1.0, 1.0), delta_dist="normal", - slope_epsilon_params=(0.0, 0.2), - intercept_epsilon_params=(1.0, 0.05), - ).get_samples(pb) + slope_epsilon_params=(0.0, 1.0), + intercept_epsilon_params=(1.0, 1.0), + ).get_samples(pb), + dims="datapoints", ) delta_plus = pm.Deterministic( - "delta_plus", np.log(1 + np.exp(delta * 10)) / 10 + 0.3 + "delta_plus_samples", + np.log(1 + np.exp(delta * 10)) / 10 + 0.3, + dims="datapoints", ) y_like = SHASH_map[configs["likelihood"]]( "y_like", @@ -294,7 +300,8 @@ def hbr(X, y, batch_effects, configs, idata=None): sigma=sigma_plus, epsilon=epsilon, delta=delta_plus, - observed=y + observed=y, + dims="datapoints", ) return model @@ -356,6 +363,8 @@ def estimate(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) X = self.transform_X(X) modeler = self.get_modeler() + if self.idata: + del self.idata with modeler(X, y, batch_effects, self.configs) as m: self.idata = pm.sample( draws=self.configs["n_samples"], @@ -367,7 +376,9 @@ def estimate(self, X, y, batch_effects): ) return self.idata - def predict(self, X, batch_effects, batch_effects_train, pred="single"): + def predict( + self, X, batch_effects, batch_effects_maps, pred="single", var_names=["y_like"] + ): """Function to make predictions from the model Args: X: Covariates @@ -384,7 +395,7 @@ def predict(self, X, batch_effects, batch_effects_train, pred="single"): # Make an array with occurences of all the values in be_train, but with the same size as be_test truncated_batch_effects_train = np.stack( [ - np.resize(np.unique(batch_effects_train[:, i]), X.shape[0]) + np.resize(np.array(list(batch_effects_maps[i].keys())), X.shape[0]) for i in range(batch_effects.shape[1]) ], axis=1, @@ -395,20 +406,21 @@ def predict(self, X, batch_effects, batch_effects_train, pred="single"): # This fails because the batch_effects provided here may not contain the same batch_effects as in the training set. If some are missing, the dimensions of the distributions don't match # For each batch effect dim for i in range(batch_effects.shape[1]): - # Find the unique values for that batch effect in the train data - values = np.unique(batch_effects_train[:, i]) - # Make a map that maps values to their index - valmap = {v: i for i, v in enumerate(values)} + # Make a map that maps batch effect values to their index + valmap = batch_effects_maps[i] # Compute those indices for the test data indices = list(map(lambda x: valmap[x], batch_effects[:, i])) # Those indices need to be used by the model pm.set_data({f"batch_effect_{i}": indices}) self.idata = pm.sample_posterior_predictive( - trace=self.idata, progressbar=True + trace=self.idata, + extend_inferencedata=True, + progressbar=True, + var_names=var_names, ) - pred_mean = self.idata.posterior_predictive["y_like"].mean(axis=(0, 1)) - pred_var = self.idata.posterior_predictive["y_like"].var(axis=(0, 1)) + pred_mean = self.idata.posterior_predictive["y_like"].to_numpy().mean(axis=(0, 1)) + pred_var = self.idata.posterior_predictive["y_like"].to_numpy().var(axis=(0, 1)) return pred_mean, pred_var @@ -473,8 +485,8 @@ def sample_prior_predictive(self, X, batch_effects, samples, idata=None): X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.configs, idata): - idata = pm.sample_prior_predictive(samples=samples) - return idata + self.idata = pm.sample_prior_predictive(samples=samples) + return self.idata def get_model(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) @@ -578,7 +590,6 @@ def __init__(self, X, y, batch_effects, idata, configs): """ self.model = None # Needs to be set later, because coords need to be passed at construction of Model self.X = X - self.n_samples = X.shape[0] self.n_basis_functions = X.shape[1] self.y = y self.batch_effects = batch_effects.astype(np.int16) @@ -603,7 +614,6 @@ def __init__(self, X, y, batch_effects, idata, configs): self.coords[batch_effect_dim_name] = this_be_values self.batch_effect_indices.append(this_be_indices) - # TODO reinstigate the 'dim' keyword, for the slope parameters def make_param(self, name, **kwargs): if self.configs.get(f"linear_{name}", False): # First make a slope and intercept, and use those to make a linear parameterization From d871dfa192aecd2208949188adae1fee4bc36fe0 Mon Sep 17 00:00:00 2001 From: Barbora Buckova Date: Fri, 19 May 2023 23:04:33 +0200 Subject: [PATCH 35/43] adding Y as output to predict function for longitudinal analysis --- pcntoolkit/normative.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pcntoolkit/normative.py b/pcntoolkit/normative.py index d4a8cbed..275f8e99 100755 --- a/pcntoolkit/normative.py +++ b/pcntoolkit/normative.py @@ -217,7 +217,7 @@ def evaluate(Y, Yhat, S2=None, mY=None, sY=None, nlZ=None, nm=None, Xz_tr=None, return results -def save_results(respfile, Yhat, S2, maskvol, Z=None, outputsuffix=None, +def save_results(respfile, Yhat, S2, maskvol, Z=None, Y=None, outputsuffix=None, results=None, save_path=''): print("Writing outputs ...") @@ -244,7 +244,9 @@ def save_results(respfile, Yhat, S2, maskvol, Z=None, outputsuffix=None, if Z is not None: fileio.save(Z, os.path.join(save_path, 'Z' + ext), example=exfile, mask=maskvol) - + if Y is not None: + fileio.save(Y, os.path.join(save_path, 'Y' + ext), example=exfile, + mask=maskvol) if results is not None: for metric in list(results.keys()): if (metric == 'NLL' or metric == 'BIC') and file_ext == '.nii.gz': @@ -670,12 +672,14 @@ def predict(covfile, respfile, maskfile=None, **kwargs): :param job_id: batch id :param fold: which cross-validation fold to use (default = 0) :param fold: list of model IDs to predict (if not specified all are computed) + :param return_y: return the (transformed) response variable (default = False) All outputs are written to disk in the same format as the input. These are: :outputs: * Yhat - predictive mean * S2 - predictive variance * Z - Z scores + * Y - response variable (if return_y is True) ''' @@ -689,6 +693,7 @@ def predict(covfile, respfile, maskfile=None, **kwargs): alg = kwargs.pop('alg') fold = kwargs.pop('fold',0) models = kwargs.pop('models', None) + return_y = kwargs.pop('return_y', False) if alg == 'gpr': raise(ValueError, "gpr is not supported with predict()") @@ -816,10 +821,15 @@ def predict(covfile, respfile, maskfile=None, **kwargs): metrics = ['Rho', 'RMSE', 'SMSE', 'EXPV']) print("Evaluations Writing outputs ...") - save_results(respfile, Yhat, S2, maskvol, Z=Z, - outputsuffix=outputsuffix, results=results) - return (Yhat, S2, Z) + if return_y: + save_results(respfile, Yhat, S2, maskvol, Z=Z, Y=Y, + outputsuffix=outputsuffix, results=results) + return (Yhat, S2, Z, Y) + else: + save_results(respfile, Yhat, S2, maskvol, Z=Z, + outputsuffix=outputsuffix, results=results) + return (Yhat, S2, Z) def transfer(covfile, respfile, testcov=None, testresp=None, maskfile=None, @@ -1437,4 +1447,4 @@ def main(*args): # For running from the command line: if __name__ == "__main__": - main(sys.argv[1:]) + main(sys.argv[1:]) \ No newline at end of file From 78d4d8b61510d7bb20740c4104d4045b32e77df5 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 23:26:59 +0200 Subject: [PATCH 36/43] Added Rhats function Fixed a bug --- pcntoolkit/model/hbr.py | 48 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index bf1679ea..674f6300 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -186,6 +186,17 @@ def hbr(X, y, batch_effects, configs, idata=None): # Make a param builder that will make the correct calls pb = ParamBuilder(X, y, batch_effects, idata, configs) + def get_sample_dims(var): + if configs[f'random_{var}']: + return 'datapoints' + elif configs[f'random_slope_{var}']: + return 'datapoints' + elif configs[f'random_intercept_{var}']: + return 'datapoints' + elif configs[f'linear_{var}']: + return 'datapoints' + return None + with pm.Model(coords=pb.coords) as model: model.add_coord("datapoints", np.arange(X.shape[0]), mutable=True) X = pm.MutableData("X", X, dims=("datapoints", "basis_functions")) @@ -205,6 +216,7 @@ def hbr(X, y, batch_effects, configs, idata=None): ) if configs["likelihood"] == "Normal": + mu = pm.Deterministic( "mu_samples", pb.make_param( @@ -214,17 +226,17 @@ def hbr(X, y, batch_effects, configs, idata=None): mu_intercept_mu_params=(0.0, 1.0), sigma_intercept_mu_params=(1.0,), ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('mu'), ) sigma = pm.Deterministic( "sigma_samples", pb.make_param( "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('sigma'), ) sigma_plus = pm.Deterministic( - "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)), dims="datapoints" + "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)), dims=get_sample_dims('sigma') ) y_like = pm.Normal( "y_like", mu, sigma=sigma_plus, observed=y, dims="datapoints" @@ -252,7 +264,7 @@ def hbr(X, y, batch_effects, configs, idata=None): mu_intercept_mu_params=(0.0, 2.0), sigma_intercept_mu_params=(2.0,), ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('mu'), ) sigma = pm.Deterministic( "sigma_samples", @@ -263,10 +275,10 @@ def hbr(X, y, batch_effects, configs, idata=None): slope_sigma_params=(0.0, 1.0), intercept_sigma_params=(1.0, 1.0), ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('sigma'), ) sigma_plus = pm.Deterministic( - "sigma_plus_samples", np.log(1 + np.exp(sigma)), dims="datapoints" + "sigma_plus_samples", np.log(1 + np.exp(sigma)), dims=get_sample_dims('sigma') ) epsilon = pm.Deterministic( "epsilon_samples", @@ -276,7 +288,7 @@ def hbr(X, y, batch_effects, configs, idata=None): slope_epsilon_params=(0.0, 1.0), intercept_epsilon_params=(0.0, 1.0), ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('epsilon'), ) delta = pm.Deterministic( "delta_samples", @@ -287,12 +299,12 @@ def hbr(X, y, batch_effects, configs, idata=None): slope_epsilon_params=(0.0, 1.0), intercept_epsilon_params=(1.0, 1.0), ).get_samples(pb), - dims="datapoints", + dims=get_sample_dims('delta'), ) delta_plus = pm.Deterministic( "delta_plus_samples", np.log(1 + np.exp(delta * 10)) / 10 + 0.3, - dims="datapoints", + dims=get_sample_dims('delta'), ) y_like = SHASH_map[configs["likelihood"]]( "y_like", @@ -305,6 +317,7 @@ def hbr(X, y, batch_effects, configs, idata=None): ) return model + class HBR: @@ -496,7 +509,6 @@ def get_model(self, X, y, batch_effects): return modeler(X, y, batch_effects, self.configs, idata=idata) def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): - # TODO this function needs to be able to compute the batch_effects_size somehow arrays = [] for i in range(len(covariate_ranges)): arrays.append( @@ -515,6 +527,22 @@ def create_dummy_inputs(self, covariate_ranges=[[0.1, 0.9, 0.01]]): batch_effects_dummy = np.repeat(batch_effects, X.shape[0], axis=0) return X_dummy, batch_effects_dummy + def Rhats(self, var_names, thin = 1, resolution = 100): + """Get Rhat of posterior samples as function of sampling iteration""" + idata = self.idata + testvars = az.extract(idata, group='posterior', var_names=var_names, combined=False) + rhat_dict={} + for var_name in var_names: + var = np.stack(testvars[var_name].to_numpy())[:,::thin] + var = var.reshape((var.shape[0], var.shape[1], -1)) + vardim = var.shape[2] + interval_skip=var.shape[1]//resolution + rhats_var = np.zeros((resolution, vardim)) + for v in range(vardim): + for j in range(resolution): + rhats_var[j,v] = az.rhat(var[:,:j*interval_skip,v]) + rhat_dict[var_name] = rhats_var + return rhat_dict class Prior: """ From 9f46bbe3542f55d0ba5988928d4fd89daac9d266 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 23:27:19 +0200 Subject: [PATCH 37/43] Removed a print statement --- pcntoolkit/model/SHASH.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index 78424bd4..e3760962 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -219,7 +219,6 @@ def dist(cls, mu, sigma, epsilon, delta, **kwargs): return super().dist([mu, sigma, epsilon, delta], **kwargs) def logp(value, mu, sigma, epsilon, delta): - print(mu, sigma, epsilon, delta) remapped_value = (value - mu) / sigma this_S = S(remapped_value, epsilon, delta) this_S_sqr = ptt.sqr(this_S) From 16b07c24abf0d70d04ddc73fe7abd790710a791d Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Fri, 19 May 2023 23:32:29 +0200 Subject: [PATCH 38/43] Formatting --- pcntoolkit/model/SHASH.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index e3760962..b9fffe98 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -22,6 +22,7 @@ See: Jones et al. (2009), Sinh-Arcsinh distributions. """ + def numpy_P(q): """ The P function as given in Jones et al. @@ -34,6 +35,7 @@ def numpy_P(q): a = (K1 + K2) * frac return a + def numpy_K(p, x): """ Computes the values of spp.kv(p,x) for only the unique values of p @@ -42,6 +44,7 @@ def numpy_K(p, x): ps, idxs = np.unique(p, return_inverse=True) return spp.kv(ps, x)[idxs].reshape(p.shape) + class K(Op): """ Modified Bessel function of the second kind, pytensor implementation @@ -135,16 +138,16 @@ def m(epsilon, delta, r): - 4 * np.cosh(2 * epsilon / delta) * P(2 / delta) + 3 ) / 8 - else: - frac1 = ptt.as_tensor_variable(1 / pm.power(2, r)) - acc = ptt.as_tensor_variable(0) - for i in range(r + 1): - combs = spp.comb(r, i) - flip = pm.power(-1, i) - ex = np.exp((r - 2 * i) * epsilon / delta) - p = P((r - 2 * i) / delta) - acc += combs * flip * ex * p - return frac1 * acc + # else: + # frac1 = ptt.as_tensor_variable(1 / pm.power(2, r)) + # acc = ptt.as_tensor_variable(0) + # for i in range(r + 1): + # combs = spp.comb(r, i) + # flip = pm.power(-1, i) + # ex = np.exp((r - 2 * i) * epsilon / delta) + # p = P((r - 2 * i) / delta) + # acc += combs * flip * ex * p + # return frac1 * acc class SHASH(RandomVariable): @@ -156,7 +159,9 @@ class SHASH(RandomVariable): @classmethod def rng_fn(cls, rng, epsilon, delta, size=None) -> np.ndarray: - return np.sinh((np.arcsinh(rng.normal(loc=0, scale=1, size=size)) + epsilon) / delta) + return np.sinh( + (np.arcsinh(rng.normal(loc=0, scale=1, size=size)) + epsilon) / delta + ) shash = SHASH() @@ -325,8 +330,8 @@ def dist(cls, mu, sigma, epsilon, delta, **kwargs): return super().dist([mu, sigma, epsilon, delta], **kwargs) def logp(value, mu, sigma, epsilon, delta): - mean = m(epsilon,delta,1) - var = m(epsilon,delta,2) + mean = m(epsilon, delta, 1) + var = m(epsilon, delta, 2) remapped_value = ((value - mu) / sigma) * np.sqrt(var) + mean this_S = S(remapped_value, epsilon, delta) this_S_sqr = np.square(this_S) @@ -338,4 +343,4 @@ def logp(value, mu, sigma, epsilon, delta): - np.log(1 + np.square(remapped_value)) / 2 ) exp = -this_S_sqr / 2 - return frac1 + frac2 + exp + np.log(var)/2 - np.log(sigma) + return frac1 + frac2 + exp + np.log(var) / 2 - np.log(sigma) From 797ffb36a0047aa8d90bb178c79b022c1d62bbb1 Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Sat, 20 May 2023 16:41:44 +0200 Subject: [PATCH 39/43] Small changes is SHASH.py --- dist/pcntoolkit-0.28-py3.10.egg | Bin 0 -> 213057 bytes pcntoolkit/model/SHASH.py | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 dist/pcntoolkit-0.28-py3.10.egg diff --git a/dist/pcntoolkit-0.28-py3.10.egg b/dist/pcntoolkit-0.28-py3.10.egg new file mode 100644 index 0000000000000000000000000000000000000000..d9d9b9f2619f692a7caca36cbcae1280f2ffd802 GIT binary patch literal 213057 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%0Agwm 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 z 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

?ZJe_f{cW$7~ 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

+AzW=m6$3MAB~4Qj<;)3B}Voft6&7 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

XUXans*hosQq9GP^EJ>?O)km4@2Kp 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 CCK{ap literal 0 HcmV?d00001 diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index b9fffe98..160710dd 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -67,13 +67,12 @@ def perform(self, node, inputs_storage, output_storage): outputs = unique_outputs[inverse_indices].reshape(inputs_storage[0].shape) output_storage[0][0] = outputs - # TODO see if writing a non-pytensor perform and using it in grad instead of self() will speed it up def grad(self, inputs, output_grads): # Approximation of the derivative. This should suffice for using NUTS dp = 1e-10 p = inputs[0] x = inputs[1] - grad = (self(p + dp, x) - self(p, x)) / dp + grad = (numpy_K(p + dp, x) - numpy_K(p, x)) / dp return [output_grads[0] * grad, grad_not_implemented(0, 1, 2, 3)] From 7e250ef34e7d9c3d3cec92c0bc9e80072159d3f5 Mon Sep 17 00:00:00 2001 From: Augub Date: Mon, 5 Jun 2023 19:51:20 +0200 Subject: [PATCH 40/43] Small bugfixes --- pcntoolkit/model/SHASH.py | 6 +++--- pcntoolkit/model/hbr.py | 20 +++++++++++--------- pcntoolkit/normative_model/norm_hbr.py | 5 ++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pcntoolkit/model/SHASH.py b/pcntoolkit/model/SHASH.py index 160710dd..1312020e 100644 --- a/pcntoolkit/model/SHASH.py +++ b/pcntoolkit/model/SHASH.py @@ -53,8 +53,8 @@ class K(Op): __props__ = () def make_node(self, p, x): - p = pt.tensor.as_tensor_variable(p, "floatX") - x = pt.tensor.as_tensor_variable(x, "floatX") + p = pt.tensor.as_tensor_variable(p) + x = pt.tensor.as_tensor_variable(x) return Apply(self, [p, x], [p.type()]) def perform(self, node, inputs_storage, output_storage): @@ -72,7 +72,7 @@ def grad(self, inputs, output_grads): dp = 1e-10 p = inputs[0] x = inputs[1] - grad = (numpy_K(p + dp, x) - numpy_K(p, x)) / dp + grad = (self(p + dp, x) - self(p, x)) / dp return [output_grads[0] * grad, grad_not_implemented(0, 1, 2, 3)] diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 674f6300..a642d3da 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -236,7 +236,7 @@ def get_sample_dims(var): dims=get_sample_dims('sigma'), ) sigma_plus = pm.Deterministic( - "sigma_plus", pm.math.log(1 + pm.math.exp(sigma)), dims=get_sample_dims('sigma') + "sigma_plus_samples", pm.math.log(1 + pm.math.exp(sigma)), dims=get_sample_dims('sigma') ) y_like = pm.Normal( "y_like", mu, sigma=sigma_plus, observed=y, dims="datapoints" @@ -285,8 +285,8 @@ def get_sample_dims(var): pb.make_param( "epsilon", epsilon_params=(0.0, 1.0), - slope_epsilon_params=(0.0, 1.0), - intercept_epsilon_params=(0.0, 1.0), + slope_epsilon_params=(0.0, 0.2), + intercept_epsilon_params=(0.0, 0.2), ).get_samples(pb), dims=get_sample_dims('epsilon'), ) @@ -296,8 +296,8 @@ def get_sample_dims(var): "delta", delta_params=(1.0, 1.0), delta_dist="normal", - slope_epsilon_params=(0.0, 1.0), - intercept_epsilon_params=(1.0, 1.0), + slope_delta_params=(0.0, 0.2), + intercept_delta_params=(1.0, 0.3), ).get_samples(pb), dims=get_sample_dims('delta'), ) @@ -376,7 +376,7 @@ def estimate(self, X, y, batch_effects): X, y, batch_effects = expand_all(X, y, batch_effects) X = self.transform_X(X) modeler = self.get_modeler() - if self.idata: + if hasattr(self, 'idata'): del self.idata with modeler(X, y, batch_effects, self.configs) as m: self.idata = pm.sample( @@ -491,10 +491,12 @@ def generate(self, X, batch_effects, samples): batch_effects = np.expand_dims(batch_effects, axis=1) return X, batch_effects, generated_samples - def sample_prior_predictive(self, X, batch_effects, samples, idata=None): + def sample_prior_predictive(self, X, batch_effects, samples, y = None, idata=None): """Function to sample from prior predictive distribution""" - X, batch_effects = expand_all(X, batch_effects) - y = np.zeros([X.shape[0], 1]) + if y is None: + y = np.zeros([X.shape[0], 1]) + X, y, batch_effects = expand_all(X, y, batch_effects) + X = self.transform_X(X) modeler = self.get_modeler() with modeler(X, y, batch_effects, self.configs, idata): diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index 1009c2c8..36710488 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -430,7 +430,6 @@ def get_mcmc_quantiles(self, X, batch_effects=None, z_scores=None): if z_scores is None: z_scores = np.arange(-3, 4) likelihood=self.configs['likelihood'] - print(f"{likelihood=}") # Determine the variables to predict if self.configs["likelihood"] == "Normal": @@ -580,7 +579,7 @@ def m(epsilon, delta, r): acc += combs * flip * ex * p return frac1 * acc -def quantile( mu, sigma, epsilon, delta, zs=0, likelihood = "Normal"): +def quantile( mu, sigma, epsilon=None, delta=None, zs=0, likelihood = "Normal"): """Get the zs'th quantiles given likelihood parameters""" if likelihood.startswith('SHASH'): if likelihood == "SHASHo": @@ -600,7 +599,7 @@ def quantile( mu, sigma, epsilon, delta, zs=0, likelihood = "Normal"): return quantiles -def z_score(mu, sigma, epsilon, delta, y, likelihood = "Normal"): +def z_score(mu, sigma, epsilon=None, delta=None, y=None, likelihood = "Normal"): """Get the z-scores of Y, given likelihood parameters""" if likelihood.startswith('SHASH'): if likelihood == "SHASHo": From 77030add0ee93bedfce972bd3a4ade3b8fe5f3ac Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Mon, 5 Jun 2023 22:23:10 +0200 Subject: [PATCH 41/43] Replace datapoints-sized matrices in the posterior with dummies by default. Added a keyword 'remove_datapoints_from_posterior' Succesfully ran testHBR.py --- pcntoolkit/model/hbr.py | 21 +++++++++++++++++---- pcntoolkit/normative_model/norm_hbr.py | 1 + tests/testHBR.py | 10 ++++++---- tests/test_hbr_pymc.py | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index a642d3da..9086c483 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -18,6 +18,7 @@ import pymc as pm import pytensor import arviz as az +import xarray from itertools import product from functools import reduce @@ -371,7 +372,7 @@ def find_map(self, X, y, batch_effects, method="L-BFGS-B"): self.MAP = pm.find_MAP(method=method) return self.MAP - def estimate(self, X, y, batch_effects): + def estimate(self, X, y, batch_effects, **kwargs): """Function to estimate the model""" X, y, batch_effects = expand_all(X, y, batch_effects) X = self.transform_X(X) @@ -387,10 +388,19 @@ def estimate(self, X, y, batch_effects): n_init=500000, cores=self.configs["cores"], ) + self.vars_to_sample = ['y_like'] + if self.configs['remove_datapoints_from_posterior']: + chain = self.idata.posterior.coords['chain'].data + draw = self.idata.posterior.coords['draw'].data + for j in self.idata.posterior.variables.mapping.keys(): + if j.endswith('_samples'): + dummy_array = xarray.DataArray(data = np.zeros((len(chain), len(draw), 1)), coords = {'chain':chain, 'draw':draw,'empty':np.array([0])}, name=j) + self.idata.posterior[j] = dummy_array + self.vars_to_sample.append(j) return self.idata def predict( - self, X, batch_effects, batch_effects_maps, pred="single", var_names=["y_like"] + self, X, batch_effects, batch_effects_maps, pred="single", var_names=None, **kwargs ): """Function to make predictions from the model Args: @@ -413,10 +423,13 @@ def predict( ], axis=1, ) + + # See if a list of var_names is provided, set to self.vars_to_sample otherwise + if (var_names is None) or (var_names == ['y_like']): + var_names = self.vars_to_sample + n_samples = X.shape[0] with modeler(X, y, truncated_batch_effects_train, self.configs) as model: - # TODO - # This fails because the batch_effects provided here may not contain the same batch_effects as in the training set. If some are missing, the dimensions of the distributions don't match # For each batch effect dim for i in range(batch_effects.shape[1]): # Make a map that maps batch effect values to their index diff --git a/pcntoolkit/normative_model/norm_hbr.py b/pcntoolkit/normative_model/norm_hbr.py index 36710488..f3b145ac 100644 --- a/pcntoolkit/normative_model/norm_hbr.py +++ b/pcntoolkit/normative_model/norm_hbr.py @@ -141,6 +141,7 @@ def __init__(self, **kwargs): self.configs["target_accept"] = float(kwargs.get("target_accept", "0.8")) self.configs["init"] = kwargs.get("init", "jitter+adapt_diag") self.configs["cores"] = int(kwargs.get("cores", "1")) + self.configs["remove_datapoints_from_posterior"] = kwargs.get("remove_datapoints_from_posterior","True") == "True" # model transfer setting self.configs["freedom"] = int(kwargs.get("freedom", "1")) self.configs["transferred"] = False diff --git a/tests/testHBR.py b/tests/testHBR.py index 6c062746..0a65a2ed 100644 --- a/tests/testHBR.py +++ b/tests/testHBR.py @@ -17,17 +17,19 @@ from pcntoolkit.normative import estimate from warnings import filterwarnings from pcntoolkit.util.utils import scaler +import xarray filterwarnings('ignore') +np.random.seed(10) ########################### Experiment Settings ############################### -working_dir = '/home/preclineu/andmar/py.sandbox/tmp/' # Specift a working directory +working_dir = '/home/stijn/temp/' # Specifyexit() a working directory # to save data and results. -simulation_method = 'non-linear' +simulation_method = 'linear' n_features = 1 # The number of input features of X n_grps = 2 # Number of batches in data n_samples = 500 # Number of samples in each group (use a list for different @@ -47,10 +49,10 @@ for model_type in model_types: - nm = norm_init(X_train, Y_train, alg='hbr',likelihood='SHASHb', model_type=model_type,n_samples=100,n_tuning=10) + nm = norm_init(X_train, Y_train, alg='hbr',likelihood='Normal', model_type=model_type,n_samples=100,n_tuning=10) nm.estimate(X_train, Y_train, trbefile=working_dir+'trbefile.pkl') yhat, ys2 = nm.predict(X_test, tsbefile=working_dir+'tsbefile.pkl') - + for i in range(n_features): sorted_idx = X_test[:,i].argsort(axis=0).squeeze() temp_X = X_test[sorted_idx,i] diff --git a/tests/test_hbr_pymc.py b/tests/test_hbr_pymc.py index 8e947dac..49935864 100644 --- a/tests/test_hbr_pymc.py +++ b/tests/test_hbr_pymc.py @@ -75,7 +75,7 @@ def ldpkl(filename: str): testresp=testrespfile_path, tsbefile=tsbefile, alg='hbr', - likelihood='SHASHb', + likelihood='Normal', # model_type='bspline', linear_mu='False', random_intercept_mu = 'False', From e86ce0cc63b63fc1a95b6b6088fc60c30efb6e9c Mon Sep 17 00:00:00 2001 From: Stijn de Boer Date: Tue, 6 Jun 2023 21:21:22 +0200 Subject: [PATCH 42/43] Transfer functionalty implemented --- pcntoolkit/model/hbr.py | 62 ++++++++++-------- tests/testHBR_transfer.py | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 tests/testHBR_transfer.py diff --git a/pcntoolkit/model/hbr.py b/pcntoolkit/model/hbr.py index 9086c483..9a608d52 100644 --- a/pcntoolkit/model/hbr.py +++ b/pcntoolkit/model/hbr.py @@ -81,12 +81,7 @@ def create_poly_basis(X, order): return Phi -def from_posterior(param, samples, distribution=None, half=False, freedom=1): - if len(samples.shape) > 1: - shape = samples.shape[1:] - else: - shape = None - +def from_posterior(param, samples, shape, distribution=None, half=False, freedom=1): if distribution is None: smin, smax = np.min(samples), np.max(samples) width = smax - smin @@ -222,22 +217,22 @@ def get_sample_dims(var): "mu_samples", pb.make_param( "mu", - mu_slope_mu_params=(0.0, 1.0), - sigma_slope_mu_params=(1.0,), - mu_intercept_mu_params=(0.0, 1.0), - sigma_intercept_mu_params=(1.0,), + mu_slope_mu_params=(0.0, 3.0), + sigma_slope_mu_params=(3.0,), + mu_intercept_mu_params=(0.0, 3.0), + sigma_intercept_mu_params=(3.0,), ).get_samples(pb), dims=get_sample_dims('mu'), ) sigma = pm.Deterministic( "sigma_samples", pb.make_param( - "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(5.0,) + "sigma", mu_sigma_params=(0.0, 2.0), sigma_sigma_params=(2.0,) ).get_samples(pb), dims=get_sample_dims('sigma'), ) sigma_plus = pm.Deterministic( - "sigma_plus_samples", pm.math.log(1 + pm.math.exp(sigma)), dims=get_sample_dims('sigma') + "sigma_plus_samples", pm.math.log(1 + pm.math.exp(sigma/3))*3, dims=get_sample_dims('sigma') ) y_like = pm.Normal( "y_like", mu, sigma=sigma_plus, observed=y, dims="datapoints" @@ -586,24 +581,39 @@ def make_dist(self, dist, params, pb): """This creates a pymc distribution. If there is a idata, the distribution is fitted to the idata. If there isn't a idata, the prior is parameterized by the values in (params)""" with pb.model as m: if pb.idata is not None: - samples = az.extract(pb.idata, var_names=self.name).to_numpy() - if not self.has_random_effect: - samples = np.reshape(samples, (-1,)) + # Get samples + samples = az.extract(pb.idata, var_names=self.name) + # Define mapping to new shape + def get_new_dim_size(tup): + oldsize, name = tup + if name.startswith('batch_effect_'): + ind = pb.batch_effect_dim_names.index(name) + return len(np.unique(pb.batch_effect_indices[ind].container.data)) + return oldsize + + new_shape = list(map(get_new_dim_size, zip(samples.shape,samples.dims))) + if len(new_shape) == 1: + new_shape = None + else: + new_shape = new_shape[:-1] self.dist = from_posterior( param=self.name, - samples=samples, + samples=samples.to_numpy(), + shape = new_shape, distribution=dist, freedom=pb.configs["freedom"], ) - dims = [] - if self.has_random_effect: - dims = dims + pb.batch_effect_dim_names - if self.name.startswith("slope") or self.name.startswith("offset_slope"): - dims = dims + ["basis_functions"] - if dims == []: - self.dist = self.distmap[dist](self.name, *params) + else: - self.dist = self.distmap[dist](self.name, *params, dims=dims) + dims = [] + if self.has_random_effect: + dims = dims + pb.batch_effect_dim_names + if self.name.startswith("slope") or self.name.startswith("offset_slope"): + dims = dims + ["basis_functions"] + if dims == []: + self.dist = self.distmap[dist](self.name, *params) + else: + self.dist = self.distmap[dist](self.name, *params, dims=dims) def __getitem__(self, idx): """The idx here is the index of the batch-effect. If the prior does not model batch effects, this should return the same value for each index""" @@ -687,7 +697,7 @@ class Parameterization: def __init__(self, name): self.name = name - print(name, type(self)) + # print(name, type(self)) def get_samples(self, pb): pass @@ -805,7 +815,7 @@ def get_samples(self, pb): intc = self.intercept_parameterization.get_samples(pb) slope_samples = self.slope_parameterization.get_samples(pb) if pb.configs[f"random_slope_{self.name}"]: - slope = pb.X * self.slope_parameterization.get_samples(pb) + slope = pb.X * slope_samples slope = slope.sum(axis=-1) else: slope = pb.X @ self.slope_parameterization.get_samples(pb) diff --git a/tests/testHBR_transfer.py b/tests/testHBR_transfer.py new file mode 100644 index 00000000..93c3d7f2 --- /dev/null +++ b/tests/testHBR_transfer.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on Mon Jul 29 13:26:35 2019 + +@author: seykia + +This script tests HBR models with default configs on toy data. + +""" + +import os +import numpy as np +from pcntoolkit.normative_model.norm_utils import norm_init +from pcntoolkit.util.utils import simulate_data +import matplotlib.pyplot as plt +from pcntoolkit.normative import estimate +from warnings import filterwarnings +from pcntoolkit.util.utils import scaler +import xarray + +filterwarnings('ignore') + +np.random.seed(10) + +########################### Experiment Settings ############################### + + +working_dir = '/home/stijn/temp/' # Specifyexit() a working directory + # to save data and results. + +simulation_method = 'linear' +n_features = 1 # The number of input features of X +n_grps = 5 # Number of batches in data +n_transfer_groups = 2 # number of batches in transfer data +n_samples = 500 # Number of samples in each group (use a list for different + # sample numbers across different batches) +n_transfer_samples = 100 + +model_types = ['linear'] # models to try + +############################## Data Simulation ################################ + + +X_train, Y_train, grp_id_train, X_test, Y_test, grp_id_test, coef = \ + simulate_data(simulation_method, n_samples, n_features, n_grps, + working_dir=working_dir, plot=True) + +X_train_transfer, Y_train_transfer, grp_id_train_transfer, X_test_transfer, Y_test_transfer, grp_id_test_transfer, coef= simulate_data(simulation_method, n_transfer_samples, n_features = n_features, n_grps=n_transfer_groups, plot=True) + +################################# Methods Tests ############################### + + +for model_type in model_types: + nm = norm_init(X_train, Y_train, alg='hbr',likelihood='Normal', model_type=model_type,n_chains = 4,cores=4,n_samples=100,n_tuning=50, freedom = 5, nknots=8, target_accept="0.99") + + print("Now Estimating on original train data ==============================================") + nm.estimate(X_train, Y_train, trbefile=working_dir+'trbefile.pkl') + print("Now Predicting on original test data ==============================================") + yhat, ys2 = nm.predict(X_test, tsbefile=working_dir+'tsbefile.pkl') + + for i in range(n_features): + sorted_idx = X_test[:,i].argsort(axis=0).squeeze() + temp_X = X_test[sorted_idx,i] + temp_Y = Y_test[sorted_idx,] + temp_be = grp_id_test[sorted_idx,:].squeeze() + temp_yhat = yhat[sorted_idx,] + temp_s2 = ys2[sorted_idx,] + + plt.figure() + for j in range(n_grps): + plt.scatter(temp_X[temp_be==j,], temp_Y[temp_be==j,], + label='Group' + str(j)) + plt.plot(temp_X[temp_be==j,], temp_yhat[temp_be==j,]) + plt.fill_between(temp_X[temp_be==j,], temp_yhat[temp_be==j,] - + 1.96 * np.sqrt(temp_s2[temp_be==j,]), + temp_yhat[temp_be==j,] + + 1.96 * np.sqrt(temp_s2[temp_be==j,]), + color='gray', alpha=0.2) + plt.title('Model %s, Feature %d' %(model_type, i)) + plt.legend() + plt.show() + + print("Now Estimating on transfer train data ==============================================") + nm.estimate_on_new_sites(X_train_transfer, Y_train_transfer, grp_id_train_transfer) + print("Now Estimating on transfer test data ==============================================") + yhat, s2 = nm.predict_on_new_sites(X_test_transfer, grp_id_test_transfer) + + for i in range(n_features): + sorted_idx = X_test_transfer[:,i].argsort(axis=0).squeeze() + temp_X = X_test_transfer[sorted_idx,i] + temp_Y = Y_test_transfer[sorted_idx,] + temp_be = grp_id_test_transfer[sorted_idx,:].squeeze() + temp_yhat = yhat[sorted_idx,] + temp_s2 = ys2[sorted_idx,] + + for j in range(n_transfer_groups): + plt.scatter(temp_X[temp_be==j,], temp_Y[temp_be==j,], + label='Group' + str(j)) + plt.plot(temp_X[temp_be==j,], temp_yhat[temp_be==j,]) + plt.fill_between(temp_X[temp_be==j,], temp_yhat[temp_be==j,] - + 1.96 * np.sqrt(temp_s2[temp_be==j,]), + temp_yhat[temp_be==j,] + + 1.96 * np.sqrt(temp_s2[temp_be==j,]), + color='gray', alpha=0.2) + plt.title('Transfer model %s, Feature %d' %(model_type, i)) + plt.legend() + plt.show() + + +############################## Normative Modelling Test ####################### + + +model_type = model_types[0] + +covfile = working_dir + 'X_train.pkl' +respfile = working_dir + 'Y_train.pkl' +testcov = working_dir + 'X_test.pkl' +testresp = working_dir + 'Y_test.pkl' +trbefile = working_dir + 'trbefile.pkl' +tsbefile = working_dir + 'tsbefile.pkl' + +os.chdir(working_dir) + +estimate(covfile, respfile, testcov=testcov, testresp=testresp, trbefile=trbefile, + tsbefile=tsbefile, alg='hbr', outputsuffix='_' + model_type, + inscaler='None', outscaler='None', model_type=model_type, + savemodel='True', saveoutput='True') + + +############################################################################### + From 5464f44d3fb670268c04fc90cdebd2e022d92a27 Mon Sep 17 00:00:00 2001 From: Andre Marquand Date: Fri, 9 Jun 2023 11:40:48 +0200 Subject: [PATCH 43/43] prepare for version release --- CHANGES | 5 +++++ setup.py | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index c41977f2..93e0fa36 100644 --- a/CHANGES +++ b/CHANGES @@ -73,3 +73,8 @@ version 0.27 - Fixed a translation problem between the previous naming convention for HBR models (only Gaussian models) and the current naming (also SHASH models) - Minor updates to fix synchronisation problems in PCNportal (related to the HBR updates above) - Added configuration files for containerisation with Docker + +version 0.28 +- Updated to PyMC5 (including migrating back-end to PyTensor +- Added support for longitudinal normative modelling with BLR (see Buckova-Rehak et al 2023) +- Changed default optimiser for trend surface models (for scalability) diff --git a/setup.py b/setup.py index e5a7f650..811f7cb1 100644 --- a/setup.py +++ b/setup.py @@ -23,5 +23,4 @@ 'pymc>=5.1.0', 'arviz==0.13.0' ], - #python_requires='<3.10', zip_safe=False)