diff --git a/cca_zoo/data/toy.py b/cca_zoo/data/toy.py index bae2b882..236f8125 100644 --- a/cca_zoo/data/toy.py +++ b/cca_zoo/data/toy.py @@ -34,7 +34,6 @@ def __init__( self.dataset = datasets.KMNIST("../../data", train=train, download=True) self.data = self.dataset.data - self.base_transform = transforms.ToTensor() self.targets = self.dataset.targets self.flatten = flatten diff --git a/cca_zoo/deepmodels/__init__.py b/cca_zoo/deepmodels/__init__.py index 9aeda2af..97d43dec 100644 --- a/cca_zoo/deepmodels/__init__.py +++ b/cca_zoo/deepmodels/__init__.py @@ -2,9 +2,12 @@ import cca_zoo.deepmodels.objectives from ._dcca_base import _DCCA_base from .dcca import DCCA +from .dcca_barlow_twins import BarlowTwins from .dcca_noi import DCCA_NOI +from .dcca_sdl import DCCA_SDL from .dccae import DCCAE -from .deepwrapper import DeepWrapper from .dtcca import DTCCA from .dvcca import DVCCA from .splitae import SplitAE +from .trainers import CCALightning +from .utils import get_dataloaders, process_data diff --git a/cca_zoo/deepmodels/_dcca_base.py b/cca_zoo/deepmodels/_dcca_base.py index c1bb1f40..7ad94a86 100644 --- a/cca_zoo/deepmodels/_dcca_base.py +++ b/cca_zoo/deepmodels/_dcca_base.py @@ -19,6 +19,13 @@ def forward(self, *args): """ raise NotImplementedError + @abstractmethod + def loss(self, *args, **kwargs): + """ + Required when using the LightningTrainer + """ + raise NotImplementedError + def post_transform(self, *z_list, train=False) -> Iterable[np.ndarray]: """ Some models require a final linear CCA after model training. diff --git a/cca_zoo/deepmodels/dcca.py b/cca_zoo/deepmodels/dcca.py index 7e05e09e..9a58c863 100644 --- a/cca_zoo/deepmodels/dcca.py +++ b/cca_zoo/deepmodels/dcca.py @@ -10,10 +10,10 @@ class DCCA(_DCCA_base): """ A class used to fit a DCCA model. - Examples - -------- - >>> from cca_zoo.deepmodels import DCCA - >>> model = DCCA() + :Citation: + + Andrew, Galen, et al. "Deep canonical correlation analysis." International conference on machine learning. PMLR, 2013. + """ def __init__( diff --git a/cca_zoo/deepmodels/dcca_barlow_twins.py b/cca_zoo/deepmodels/dcca_barlow_twins.py new file mode 100644 index 00000000..27c1da94 --- /dev/null +++ b/cca_zoo/deepmodels/dcca_barlow_twins.py @@ -0,0 +1,51 @@ +from typing import Iterable + +import torch + +from cca_zoo.deepmodels import DCCA +from cca_zoo.deepmodels.architectures import BaseEncoder, Encoder + + +class BarlowTwins(DCCA): + """ + A class used to fit a Barlow Twins model. + + :Citation: + + Zbontar, Jure, et al. "Barlow twins: Self-supervised learning via redundancy reduction." arXiv preprint arXiv:2103.03230 (2021). + + """ + + def __init__( + self, + latent_dims: int, + encoders: Iterable[BaseEncoder] = [Encoder, Encoder], + lam=1, + ): + """ + Constructor class for Barlow Twins + + :param latent_dims: # latent dimensions + :param encoders: list of encoder networks + :param lam: weighting of off diagonal loss terms + """ + super().__init__(latent_dims=latent_dims, encoders=encoders) + self.lam = lam + self.bns = torch.nn.ModuleList( + [torch.nn.BatchNorm1d(latent_dims, affine=False) for _ in self.encoders] + ) + + def forward(self, *args): + z = [] + for i, (encoder, bn) in enumerate(zip(self.encoders, self.bns)): + z.append(bn(encoder(args[i]))) + return tuple(z) + + def loss(self, *args): + z = self(*args) + cross_cov = z[0].T @ z[1] / (z[0].shape[0] - 1) + invariance = torch.mean(torch.pow(1 - torch.diag(cross_cov), 2)) + covariance = torch.mean( + torch.triu(torch.pow(cross_cov, 2), diagonal=1) + ) + torch.mean(torch.tril(torch.pow(cross_cov, 2), diagonal=-1)) + return invariance + covariance diff --git a/cca_zoo/deepmodels/dcca_noi.py b/cca_zoo/deepmodels/dcca_noi.py index 5bcefd4f..6ab09929 100644 --- a/cca_zoo/deepmodels/dcca_noi.py +++ b/cca_zoo/deepmodels/dcca_noi.py @@ -8,6 +8,11 @@ class DCCA_NOI(DCCA): """ A class used to fit a DCCA model by non-linear orthogonal iterations + + :Citation: + + Wang, Weiran, et al. "Stochastic optimization for deep CCA via nonlinear orthogonal iterations." 2015 53rd Annual Allerton Conference on Communication, Control, and Computing (Allerton). IEEE, 2015. + """ def __init__( @@ -17,7 +22,7 @@ def __init__( encoders=None, r: float = 0, rho: float = 0.2, - eps: float = 1e-3, + eps: float = 1e-9, shared_target: bool = False, ): """ @@ -39,7 +44,7 @@ def __init__( self.eps = eps self.rho = rho self.shared_target = shared_target - self.mse = torch.nn.MSELoss() + self.mse = torch.nn.MSELoss(reduction="sum") # Authors state that a final linear layer is an important part of their algorithmic implementation self.linear_layers = torch.nn.ModuleList( [ @@ -61,7 +66,7 @@ def forward(self, *args): def loss(self, *args): z = self(*args) z_copy = [z_.detach().clone() for z_ in z] - self.update_covariances(*z_copy) + self._update_covariances(*z_copy) covariance_inv = [ torch.linalg.inv(objectives.MatrixSquareRoot.apply(cov)) for cov in self.covs @@ -70,25 +75,14 @@ def loss(self, *args): loss = self.mse(z[0], preds[1]) + self.mse(z[1], preds[0]) return loss - def update_mean(self, *z): - batch_means = [torch.mean(z_, dim=0) for z_ in z] - if self.means is not None: - self.means = [ - self.rho * self.means[i].detach() + (1 - self.rho) * batch_mean - for i, batch_mean in enumerate(batch_means) - ] - else: - self.means = batch_means - z = [z_ - mean for (z_, mean) in zip(z, self.means)] - return z - - def update_covariances(self, *z): + def _update_covariances(self, *z, train=True): b = z[0].shape[0] batch_covs = [self.N * z_.T @ z_ / b for z_ in z] - if self.covs is not None: - self.covs = [ - self.rho * self.covs[i] + (1 - self.rho) * batch_cov - for i, batch_cov in enumerate(batch_covs) - ] - else: - self.covs = batch_covs + if train: + if self.covs is not None: + self.covs = [ + self.rho * self.covs[i] + (1 - self.rho) * batch_cov + for i, batch_cov in enumerate(batch_covs) + ] + else: + self.covs = batch_covs diff --git a/cca_zoo/deepmodels/dcca_sdl.py b/cca_zoo/deepmodels/dcca_sdl.py new file mode 100644 index 00000000..dcbde8ac --- /dev/null +++ b/cca_zoo/deepmodels/dcca_sdl.py @@ -0,0 +1,92 @@ +import torch +import torch.nn.functional as F + +from cca_zoo.deepmodels import DCCA_NOI + + +class DCCA_SDL(DCCA_NOI): + """ + A class used to fit a Deep CCA by Stochastic Decorrelation model. + + :Citation: + + Chang, Xiaobin, Tao Xiang, and Timothy M. Hospedales. "Scalable and effective deep CCA via soft decorrelation." Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2018. + + """ + + def __init__( + self, + latent_dims: int, + N: int, + encoders=None, + r: float = 0, + rho: float = 0.2, + eps: float = 1e-3, + shared_target: bool = False, + lam=0.5, + ): + """ + Constructor class for DCCA + :param latent_dims: # latent dimensions + :param encoders: list of encoder networks + :param r: regularisation parameter of tracenorm CCA like ridge CCA + :param rho: covariance memory like DCCA non-linear orthogonal iterations paper + :param eps: epsilon used throughout + :param shared_target: not used + """ + super().__init__( + latent_dims=latent_dims, + N=N, + encoders=encoders, + r=r, + rho=rho, + eps=eps, + shared_target=shared_target, + ) + self.c = None + self.cross_cov = None + self.lam = lam + self.bns = torch.nn.ModuleList( + [ + torch.nn.BatchNorm1d(latent_dims, affine=False) + for _ in range(latent_dims) + ] + ) + + def forward(self, *args): + z = [] + for i, (encoder, bn) in enumerate(zip(self.encoders, self.bns)): + z.append(bn(encoder(args[i]))) + return tuple(z) + + def loss(self, *args): + z = self(*args) + self._update_covariances(*z, train=self.training) + SDL_loss = self._sdl_loss(self.covs) + l2_loss = F.mse_loss(z[0], z[1]) + return l2_loss + self.lam * SDL_loss + + def _sdl_loss(self, covs): + loss = 0 + for cov in covs: + cov = cov + sgn = torch.sign(cov) + sgn.fill_diagonal_(0) + loss += torch.mean(cov * sgn) + return loss + + def _update_covariances(self, *z, train=True): + batch_covs = [z_.T @ z_ for z_ in z] + if train: + if self.c is not None: + self.c = self.rho * self.c + 1 + self.covs = [ + self.rho * self.covs[i].detach() + (1 - self.rho) * batch_cov + for i, batch_cov in enumerate(batch_covs) + ] + else: + self.c = 1 + self.covs = batch_covs + # pytorch-lightning runs validation once so this just fixes the bug + elif self.covs is None: + self.covs = batch_covs diff --git a/cca_zoo/deepmodels/dccae.py b/cca_zoo/deepmodels/dccae.py index 9e38ba01..f9185480 100644 --- a/cca_zoo/deepmodels/dccae.py +++ b/cca_zoo/deepmodels/dccae.py @@ -10,10 +10,10 @@ class DCCAE(_DCCA_base): """ A class used to fit a DCCAE model. - Examples - -------- - >>> from cca_zoo.deepmodels import DCCAE - >>> model = DCCAE() + :Citation: + + Wang, Weiran, et al. "On deep multi-view representation learning." International conference on machine learning. PMLR, 2015. + """ def __init__( @@ -57,7 +57,6 @@ def decode(self, *z): """ This method is used to decode from the latent space to the best prediction of the original views - :param args: """ recon = [] for i, decoder in enumerate(self.decoders): @@ -67,11 +66,11 @@ def decode(self, *z): def loss(self, *args): z = self(*args) recon = self.decode(*z) - recon_loss = self.recon_loss(args[: len(recon)], recon) + recon_loss = self._recon_loss(args[: len(recon)], recon) return self.lam * recon_loss + self.objective.loss(*z) @staticmethod - def recon_loss(x, recon): + def _recon_loss(x, recon): recons = [ F.mse_loss(recon_, x_, reduction="mean") for recon_, x_ in zip(recon, x) ] diff --git a/cca_zoo/deepmodels/deepwrapper.py b/cca_zoo/deepmodels/deepwrapper.py deleted file mode 100644 index 5cd83492..00000000 --- a/cca_zoo/deepmodels/deepwrapper.py +++ /dev/null @@ -1,348 +0,0 @@ -import copy -import itertools -from typing import Union, Iterable - -import numpy as np -import torch -from torch.utils.data import DataLoader - -from cca_zoo.data import CCA_Dataset -from cca_zoo.deepmodels import _DCCA_base, DCCA, DCCAE -from cca_zoo.models import _CCA_Base -from ..utils.check_values import _check_batch_size - - -class DeepWrapper(_CCA_Base): - """ - This class is used as a wrapper for DCCA, DCCAE, DVCCA, DTCCA, SplitAE. It can be inherited and adapted to - customise the training loop. By inheriting _CCA_Base, the DeepWrapper class gives access to fit_transform. - """ - - def __init__( - self, - model: _DCCA_base, - device: str = "cuda", - optimizer: torch.optim.Optimizer = None, - scheduler=None, - lr: float = 1e-3, - clip_value=float("inf"), - random_state: int = 1, - ): - """ - - :param model: An instance of a model - :param device: device to train on - :param optimizer: optimizer used to update model parameters each iteration - :param scheduler: scheduler used to update the optimizer e.g. learning rate decay - :param lr: learning rate if not specified in the optimizer - :param clip_value: - """ - super().__init__(latent_dims=model.latent_dims) - self.model = model - self.device = device - if not torch.cuda.is_available(): - self.device = "cpu" - torch.manual_seed(random_state) - torch.cuda.manual_seed(random_state) - self.latent_dims = model.latent_dims - self.optimizer = optimizer - if optimizer is None: - if isinstance(self.model, DCCA): - # Andrew G, Arora R, Bilmes J, Livescu K. Deep canonical correlation analysis. InInternational conference on machine learning 2013 May 26 (pp. 1247-1255). PMLR. - self.optimizer = torch.optim.LBFGS(self.model.parameters(), lr=lr) - elif isinstance(self.model, DCCAE): - # Wang W, Arora R, Livescu K, Bilmes J. On deep multi-view representation learning. InInternational conference on machine learning 2015 Jun 1 (pp. 1083-1092). PMLR. - self.optimizer = torch.optim.SGD( - self.model.parameters(), lr=lr, weight_decay=1e-4 - ) - else: - self.optimizer = torch.optim.SGD(self.model.parameters(), lr=lr) - self.scheduler = scheduler - self.clip_value = clip_value - - def fit( - self, - train_dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]], - val_dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]] = None, - train_labels=None, - val_labels=None, - val_split: float = 0, - batch_size: int = 0, - val_batch_size: int = 0, - patience: int = 0, - epochs: int = 1, - post_transform=True, - ): - """ - - :param train_dataset: either tuple of 2d numpy arrays (one for each view) or torch dataset - :param val_dataset: either tuple of 2d numpy arrays (one for each view), torch dataset or None - :param train_labels: - :param val_labels: - :param val_split: if val_dataset is None, - :param batch_size: the minibatch size - :param patience: if 0 train to num_epochs, else if validation score doesn't improve after patience epochs stop training - :param epochs: maximum number of epochs to train - """ - train_dataset, val_dataset = self._process_data( - train_dataset, val_dataset, train_labels, val_labels, val_split - ) - train_dataloader, val_dataloader = self._get_dataloaders( - train_dataset, batch_size, val_dataset, val_batch_size - ) - num_params = sum(p.numel() for p in self.model.parameters()) - print("total parameters: ", num_params) - best_model = copy.deepcopy(self.model.state_dict()) - self.model.to(self.device) - min_val_loss = torch.tensor(np.inf) - epochs_no_improve = 0 - early_stop = False - - for epoch in range(1, epochs + 1): - if not early_stop: - # Train - epoch_train_loss = self._train_epoch(train_dataloader) - print( - "====> Epoch: {} Average train loss: {:.4f}".format( - epoch, epoch_train_loss - ) - ) - # Val - if val_dataset: - epoch_val_loss = self._val_epoch(val_dataloader) - print( - "====> Epoch: {} Average val loss: {:.4f}".format( - epoch, epoch_val_loss - ) - ) - if epoch_val_loss < min_val_loss or epoch == 1: - min_val_loss = epoch_val_loss - best_model = copy.deepcopy(self.model.state_dict()) - print("Min loss %0.2f" % min_val_loss) - epochs_no_improve = 0 - else: - epochs_no_improve += 1 - # Check early stopping condition - if epochs_no_improve == patience and patience > 0: - print("Early stopping!") - early_stop = True - self.model.load_state_dict(best_model) - # Scheduler step - if self.scheduler: - try: - self.scheduler.step() - except: - self.scheduler.step(epoch_train_loss) - if post_transform: - self.transform(train_dataset, batch_size=batch_size, train=True) - return self - - def _train_epoch(self, train_dataloader: torch.utils.data.DataLoader): - """ - Train a single epoch - - :param train_dataloader: a dataloader for training data - :return: average loss over the epoch - """ - self.model.train() - train_loss = 0 - for batch_idx, (data, label) in enumerate(train_dataloader): - data = [d.to(self.device) for d in list(data)] - loss = self._update_weights(*data) - train_loss += loss.item() - return train_loss / len(train_dataloader) - - def _update_weights(self, *args): - """ - A complete update of the weights used every batch - - :param args: batches for each view separated by commas - :return: - """ - if type(self.optimizer) == torch.optim.LBFGS: - - def closure(): - """ - Required by LBFGS optimizer - """ - self.optimizer.zero_grad() - loss = self.model.loss(*args) - loss.backward() - return loss - - torch.nn.utils.clip_grad_value_( - self.model.parameters(), clip_value=self.clip_value - ) - self.optimizer.step(closure) - loss = closure() - else: - for p in self.model.parameters(): - p.grad = None - loss = self.model.loss(*args) - loss.backward() - torch.nn.utils.clip_grad_value_( - self.model.parameters(), clip_value=self.clip_value - ) - self.optimizer.step() - return loss - - def _val_epoch(self, val_dataloader: torch.utils.data.DataLoader): - """ - Validate a single epoch - - :param val_dataloader: a dataloder for validation data - :return: average validation loss over the epoch - """ - self.model.eval() - for param in self.model.parameters(): - param.grad = None - total_val_loss = 0 - for batch_idx, (data, label) in enumerate(val_dataloader): - data = [d.to(self.device) for d in list(data)] - loss = self.model.loss(*data) - total_val_loss += loss.item() - return total_val_loss / len(val_dataloader) - - def correlations( - self, - test_dataset: Union[ - torch.utils.data.Dataset, Iterable[np.ndarray], torch.utils.data.DataLoader - ], - train: bool = False, - batch_size: int = 0, - ): - """ - - - :return: numpy array containing correlations between each pair of views for each dimension (#views*#views*#latent_dimensions) - """ - transformed_views = self.transform( - test_dataset, train=train, batch_size=batch_size - ) - all_corrs = [] - for x, y in itertools.product(transformed_views, repeat=2): - all_corrs.append(np.diag(np.corrcoef(x.T, y.T)[: x.shape[1], y.shape[1]:])) - all_corrs = np.array(all_corrs).reshape( - (len(transformed_views), len(transformed_views), -1) - ) - return all_corrs - - def transform( - self, - test_dataset: Union[ - torch.utils.data.Dataset, Iterable[np.ndarray], torch.utils.data.DataLoader - ], - test_labels=None, - train: bool = False, - batch_size: int = 0, - ): - if isinstance(test_dataset, torch.utils.data.DataLoader): - test_dataloader = test_dataset - else: - test_dataset = self._process_data(test_dataset, labels=test_labels)[0] - if batch_size > 0: - test_dataloader = DataLoader(test_dataset, batch_size=batch_size) - else: - test_dataloader = DataLoader(test_dataset, batch_size=len(test_dataset)) - with torch.no_grad(): - for batch_idx, (data, label) in enumerate(test_dataloader): - data = [d.to(self.device) for d in list(data)] - z = self.model(*data) - if batch_idx == 0: - z_list = [z_i.detach().cpu().numpy() for i, z_i in enumerate(z)] - else: - z_list = [ - np.append(z_list[i], z_i.detach().cpu().numpy(), axis=0) - for i, z_i in enumerate(z) - ] - z_list = self.model.post_transform(*z_list, train=train) - return z_list - - def predict_view( - self, - test_dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]], - test_labels=None, - ): - test_dataset = self._process_data(test_dataset, labels=test_labels)[0] - test_dataloader = DataLoader(test_dataset, batch_size=len(test_dataset)) - with torch.no_grad(): - for batch_idx, (data, label) in enumerate(test_dataloader): - data = [d.to(self.device) for d in list(data)] - x = self.model.recon(*data) - if batch_idx == 0: - x_list = [x_i.detach().cpu().numpy() for i, x_i in enumerate(x)] - else: - x_list = [ - np.append(x_list[i], x_i.detach().cpu().numpy(), axis=0) - for i, x_i in enumerate(x) - ] - return x_list - - def score( - self, - test_dataset: Union[ - torch.utils.data.Dataset, Iterable[np.ndarray], torch.utils.data.DataLoader - ], - train: bool = False, - batch_size: int = 0, - ): - # by default return the average pairwise correlation in each dimension (for 2 views just the correlation) - pair_corrs = self.correlations( - test_dataset=test_dataset, train=train, batch_size=batch_size - ) - # n views - n_views = pair_corrs.shape[0] - # sum all the pairwise correlations for each dimension. Subtract the self correlations. Divide by the number of views. Gives average correlation - dim_corrs = ( - pair_corrs.sum(axis=tuple(range(pair_corrs.ndim - 1))) - n_views - ) / (n_views ** 2 - n_views) - return dim_corrs - - def _process_data( - self, - dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]], - val_dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]] = None, - labels=None, - val_labels=None, - val_split: float = 0, - ): - # Ensure datasets are in the right form (e.g. if numpy arrays are passed turn them into - if isinstance(dataset, tuple): - dataset = CCA_Dataset(dataset, labels=labels) - if val_dataset is None and val_split > 0: - lengths = [ - len(dataset) - int(len(dataset) * val_split), - int(len(dataset) * val_split), - ] - dataset, val_dataset = torch.utils.data.random_split(dataset, lengths) - elif isinstance(val_dataset, tuple): - val_dataset = CCA_Dataset(val_dataset, labels=val_labels) - return dataset, val_dataset - - def _get_dataloaders( - self, dataset, batch_size, val_dataset=None, val_batch_size=None, num_workers=0 - ): - if batch_size == 0: - batch_size = len(dataset) - train_dataloader = DataLoader( - dataset, - batch_size=batch_size, - drop_last=True, - num_workers=num_workers, - pin_memory=True, - shuffle=True, - ) - _check_batch_size(batch_size, self.latent_dims) - if val_dataset: - if val_batch_size == 0: - val_batch_size = len(val_dataset) - val_dataloader = DataLoader( - val_dataset, - batch_size=val_batch_size, - drop_last=True, - num_workers=num_workers, - pin_memory=True, - ) - _check_batch_size(batch_size, self.latent_dims) - return train_dataloader, val_dataloader - return train_dataloader, None diff --git a/cca_zoo/deepmodels/dtcca.py b/cca_zoo/deepmodels/dtcca.py index b824fd86..6d0b0535 100644 --- a/cca_zoo/deepmodels/dtcca.py +++ b/cca_zoo/deepmodels/dtcca.py @@ -13,10 +13,10 @@ class DTCCA(DCCA): Is just a thin wrapper round DCCA with the DTCCA objective and a TCCA post-processing - Examples - -------- - >>> from cca_zoo.deepmodels import DTCCA - >>> model = DTCCA() + :Citation: + + Wong, Hok Shing, et al. "Deep Tensor CCA for Multi-view Learning." IEEE Transactions on Big Data (2021). + """ def __init__( diff --git a/cca_zoo/deepmodels/dvcca.py b/cca_zoo/deepmodels/dvcca.py index 861f6f7d..65e1c311 100644 --- a/cca_zoo/deepmodels/dvcca.py +++ b/cca_zoo/deepmodels/dvcca.py @@ -12,9 +12,14 @@ class DVCCA(_DCCA_base): """ A class used to fit a DVCCA model. + :Citation: + + Wang, Weiran, et al. "Deep variational canonical correlation analysis." arXiv preprint arXiv:1610.03454 (2016). + https: // arxiv.org / pdf / 1610.03454.pdf - With pieces borrowed from the variational autoencoder implementation @ - # https: // github.com / pytorch / examples / blob / master / vae / main.py + + https: // github.com / pytorch / examples / blob / master / vae / main.py + """ def __init__( @@ -49,7 +54,7 @@ def forward(self, *args, mle=True): :return: """ # Used when we get reconstructions - mu, logvar = self.encode(*args) + mu, logvar = self._encode(*args) if mle: z = mu else: @@ -59,7 +64,7 @@ def forward(self, *args, mle=True): if len(self.encoders) == 1: z = z * len(args) if self.private_encoders: - mu_p, logvar_p = self.encode_private(*args) + mu_p, logvar_p = self._encode_private(*args) if mle: z_p = mu_p else: @@ -68,7 +73,7 @@ def forward(self, *args, mle=True): z = [torch.cat([z_] + z_p, dim=-1) for z_ in z] return z - def encode(self, *args): + def _encode(self, *args): """ :param args: :return: @@ -81,7 +86,7 @@ def encode(self, *args): logvar.append(logvar_i) return mu, logvar - def encode_private(self, *args): + def _encode_private(self, *args): """ :param args: :return: @@ -94,14 +99,14 @@ def encode_private(self, *args): logvar.append(logvar_i) return mu, logvar - def decode(self, z): + def _decode(self, z): """ :param z: :return: """ x = [] for i, decoder in enumerate(self.decoders): - x_i = decoder(z) + x_i = F.sigmoid(decoder(z)) x.append(x_i) return x @@ -111,16 +116,16 @@ def recon(self, *args): :return: """ z = self(*args) - return [self.decode(z_i) for z_i in z][0] + return [self._decode(z_i) for z_i in z][0] def loss(self, *args): """ :param args: :return: """ - mus, logvars = self.encode(*args) + mus, logvars = self._encode(*args) if self.private_encoders: - mus_p, logvars_p = self.encode_private(*args) + mus_p, logvars_p = self._encode_private(*args) losses = [ self.vcca_private_loss( *args, mu=mu, logvar=logvar, mu_p=mu_p, logvar_p=logvar_p @@ -147,7 +152,7 @@ def vcca_loss(self, *args, mu, logvar): kl = torch.mean( -0.5 * torch.sum(1 + logvar - logvar.exp() - mu.pow(2), dim=1), dim=0 ) - recons = self.decode(z) + recons = self._decode(z) bces = torch.stack( [ F.binary_cross_entropy(recon, arg, reduction="sum") / batch_n @@ -182,7 +187,7 @@ def vcca_private_loss(self, *args, mu, logvar, mu_p, logvar_p): -0.5 * torch.sum(1 + logvar - logvar.exp() - mu.pow(2), dim=1), dim=0 ) z_combined = torch.cat([z, z_p], dim=-1) - recon = self.decode(z_combined) + recon = self._decode(z_combined) bces = torch.stack( [ F.binary_cross_entropy(recon[i], args[i], reduction="sum") / batch_n diff --git a/cca_zoo/deepmodels/objectives.py b/cca_zoo/deepmodels/objectives.py index 96287069..7a094f57 100644 --- a/cca_zoo/deepmodels/objectives.py +++ b/cca_zoo/deepmodels/objectives.py @@ -273,6 +273,6 @@ def loss(self, *z): M = torch.unsqueeze(M, -1) @ el M = torch.mean(M, 0) tl.set_backend("pytorch") - M_parafac = parafac(M.detach(), self.latent_dims) + M_parafac = parafac(M.detach(), self.latent_dims, verbose=False) M_hat = cp_to_tensor(M_parafac) return torch.norm(M - M_hat) diff --git a/cca_zoo/deepmodels/splitae.py b/cca_zoo/deepmodels/splitae.py index 967707d6..a71a2a73 100644 --- a/cca_zoo/deepmodels/splitae.py +++ b/cca_zoo/deepmodels/splitae.py @@ -9,10 +9,10 @@ class SplitAE(_DCCA_base): """ A class used to fit a Split Autoencoder model. - Examples - -------- - >>> from cca_zoo.deepmodels import SplitAE - >>> model = SplitAE() + :Citation: + + Ngiam, Jiquan, et al. "Multimodal deep learning." ICML. 2011. + """ def __init__(self, latent_dims: int, encoder: BaseEncoder = Encoder, decoders=None): @@ -30,7 +30,7 @@ def __init__(self, latent_dims: int, encoder: BaseEncoder = Encoder, decoders=No def forward(self, *args): z = self.encoder(args[0]) - return z + return [z] def decode(self, z): """ @@ -45,11 +45,13 @@ def decode(self, z): def loss(self, *args): z = self(*args) - recon = self.decode(z) + recon = self.decode(*z) recon_loss = self.recon_loss(args, recon) return recon_loss @staticmethod def recon_loss(x, recon): - recons = [F.mse_loss(recon[i], x[i], reduction="mean") for i in range(len(x))] + recons = [ + F.mse_loss(recon[i], x[i], reduction="mean") for i in range(len(recon)) + ] return torch.stack(recons).sum(dim=0) diff --git a/cca_zoo/deepmodels/trainers.py b/cca_zoo/deepmodels/trainers.py new file mode 100644 index 00000000..1462b267 --- /dev/null +++ b/cca_zoo/deepmodels/trainers.py @@ -0,0 +1,229 @@ +import itertools +import sys +from typing import Optional, Union + +import numpy as np +import torch +from pytorch_lightning import LightningModule +from torch.utils.data import DataLoader + +from cca_zoo.deepmodels import _DCCA_base + + +class CCALightning(LightningModule): + def __init__( + self, + model: _DCCA_base, + optimizer: Union[torch.optim.Optimizer, str] = "Adam", + learning_rate: float = 1e-3, + weight_decay: float = 0.1, + lr_scheduler: torch.optim.lr_scheduler._LRScheduler = None, + StepLR_step_size: float = None, + StepLR_gamma: float = None, + lr_factor: float = None, + lr_patience: float = None, + OneCycleLR_max_lr: float = None, + OneCycleLR_epochs: float = None, + train_trajectories: float = None, + T: float = None, + ): + """ + + :param model: a model instance from deepmodels + :param optimizer: a pytorch optimizer with parameters from model or a string like 'Adam' to use Adam optimizer with default parameters or those specified by the user + :param learning_rate: learning rate used when optimizer is instantiated with a string + :param weight_decay: weight decay used when optimizer is instantiated with a string + :param lr_scheduler: a pytorch learning rate scheduler or a string like "StepLR" or None + :param StepLR_step_size: step size used by "StepLR" + :param StepLR_gamma: gamma used by "StepLR" + :param lr_factor: factor used by "ReduceLROnPlateau" + :param lr_patience: patience used by "ReduceLROnPlateau" + :param OneCycleLR_max_lr: max lr used by "OneCycleLR" + :param OneCycleLR_epochs: epochs used by "OneCycleLR" + :param train_trajectories: train trajectories used by "OneCycleLR" + :param T: T used by "OneCycleLR" + """ + super().__init__() + self.save_hyperparameters() + self.model = model + + def forward(self, *args): + z = self.encode(*args) + return z + + def loss(self, *args, **kwargs): + return self.model.loss(*args, **kwargs) + + # Configuration. Add more for learning schedulers, etc.? + def configure_optimizers(self): + if isinstance(self.hparams.optimizer, torch.optim.Optimizer): + optimizer = self.hparams.optimizer + elif self.hparams.optimizer == "Adam": + optimizer = torch.optim.Adam( + self.parameters(), + lr=self.hparams.learning_rate, + weight_decay=self.hparams.weight_decay, + ) + elif self.hparams.optimizer == "SGD": + # Left out the momentum options for now + optimizer = torch.optim.SGD( + self.parameters(), + lr=self.hparams.learning_rate, + weight_decay=self.hparams.weight_decay, + ) + elif self.hparams.optimizer == "LBFGS": + optimizer = torch.optim.LBFGS( + self.parameters(), + # or can have self.hparams.learning_rate with warning if too low. + lr=1, + tolerance_grad=1e-5, # can add to parameters if useful. + tolerance_change=1e-9, # can add to parameters if useful. + ) + else: + print("Invalid optimizer. See --help") + sys.exit() + + if self.hparams.lr_scheduler is None: + return optimizer + elif isinstance( + self.hparams.lr_scheduler, torch.optim.lr_scheduler._LRScheduler + ): + scheduler = self.hparams.lr_scheduler + elif self.hparams.lr_scheduler == "StepLR": + step_size = self.hparams.StepLR_step_size + gamma = self.hparams.StepLR_gamma + scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma) + elif self.hparams.lr_scheduler == "ReduceLROnPlateau": + factor = self.hparams.lr_factor + patience = self.hparams.lr_patience + scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( + optimizer, mode="min", factor=factor, patience=patience + ) + return { + "optimizer": optimizer, + "lr_scheduler": scheduler, + "monitor": self.hparams.LRScheduler_metric, + } + elif self.hparams.lr_scheduler == "OneCycleLR": + max_lr = self.hparams.OneCycleLR_max_lr + epochs = self.hparams.OneCycleLR_epochs + steps_per_epoch = self.hparams.train_trajectories * (self.hparams.T + 1) + scheduler = torch.optim.lr_scheduler.OneCycleLR( + optimizer, + max_lr=max_lr, + epochs=epochs, + steps_per_epoch=steps_per_epoch, + ) + else: + print("Invalid scheduler configuration. See --help") + raise + return [optimizer], [scheduler] + + def training_step(self, batch, batch_idx): + data, label = batch + loss = self.model.loss(*data) + return loss + + def validation_step(self, batch, batch_idx): + data, label = batch + loss = self.model.loss(*data) + return loss + + def test_step(self, batch, batch_idx): + data, label = batch + loss = self.model.loss(*data) + return loss + + def on_train_epoch_end(self, unused: Optional = None) -> None: + score = self.score(self.trainer.train_dataloader, train=True).sum() + self.log("train corr", score) + + def on_validation_epoch_end(self, unused: Optional = None) -> None: + score = self.score(self.trainer.val_dataloaders[0], train=True).sum() + self.log("val corr", score) + + def correlations( + self, + loader: torch.utils.data.DataLoader, + train: bool = False, + ): + """ + + :param loader: a dataloader that matches the structure of that used for training + :param train: if True and the model requires a final linear CCA this solves and stores the linear CCA + :return: numpy array containing correlations between each pair of views for each dimension (#views*#views*#latent_dimensions) + """ + transformed_views = self.transform(loader, train=train) + if len(transformed_views) < 2: + return None + all_corrs = [] + for x, y in itertools.product(transformed_views, repeat=2): + all_corrs.append(np.diag(np.corrcoef(x.T, y.T)[: x.shape[1], y.shape[1]:])) + all_corrs = np.array(all_corrs).reshape( + (len(transformed_views), len(transformed_views), -1) + ) + return all_corrs + + def transform( + self, + loader: torch.utils.data.DataLoader, + train: bool = False, + ): + """ + + :param loader: a dataloader that matches the structure of that used for training + :param train: if True and the model requires a final linear CCA this solves and stores the linear CCA + :return: transformed views + """ + with torch.no_grad(): + for batch_idx, (data, label) in enumerate(loader): + data = [d.to(self.device) for d in list(data)] + z = self.model(*data) + if batch_idx == 0: + z_list = [z_i.detach().cpu().numpy() for i, z_i in enumerate(z)] + else: + z_list = [ + np.append(z_list[i], z_i.detach().cpu().numpy(), axis=0) + for i, z_i in enumerate(z) + ] + z_list = self.model.post_transform(*z_list, train=train) + return z_list + + def score( + self, + loader: torch.utils.data.DataLoader, + train: bool = False, + ): + """ + + :param loader: a dataloader that matches the structure of that used for training + :param train: if True and the model requires a final linear CCA this solves and stores the linear CCA + :return: by default returns the average pairwise correlation in each dimension (for 2 views just the correlation) + """ + pair_corrs = self.correlations(loader, train=train) + if pair_corrs is None: + return np.zeros(1) + # n views + n_views = pair_corrs.shape[0] + # sum all the pairwise correlations for each dimension. Subtract the self correlations. Divide by the number of views. Gives average correlation + dim_corrs = ( + pair_corrs.sum(axis=tuple(range(pair_corrs.ndim - 1))) - n_views + ) / (n_views ** 2 - n_views) + return dim_corrs + + def predict_view( + self, + loader: torch.utils.data.DataLoader, + ): + with torch.no_grad(): + for batch_idx, (data, label) in enumerate(loader): + data = [d.to(self.device) for d in list(data)] + x = self.model.recon(*data) + if batch_idx == 0: + x_list = [x_i.detach().cpu().numpy() for i, x_i in enumerate(x)] + else: + x_list = [ + np.append(x_list[i], x_i.detach().cpu().numpy(), axis=0) + for i, x_i in enumerate(x) + ] + return x_list diff --git a/cca_zoo/deepmodels/utils.py b/cca_zoo/deepmodels/utils.py new file mode 100644 index 00000000..8c04edc3 --- /dev/null +++ b/cca_zoo/deepmodels/utils.py @@ -0,0 +1,55 @@ +from typing import Union, Iterable + +import numpy as np +import torch +from torch.utils.data import DataLoader + +from cca_zoo.data.utils import CCA_Dataset + + +def process_data( + dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]], + val_dataset: Union[torch.utils.data.Dataset, Iterable[np.ndarray]] = None, + labels=None, + val_labels=None, + val_split: float = 0, +): + # Ensure datasets are in the right form (e.g. if numpy arrays are passed turn them into + if isinstance(dataset, tuple): + dataset = CCA_Dataset(dataset, labels=labels) + if val_dataset is None and val_split > 0: + lengths = [ + len(dataset) - int(len(dataset) * val_split), + int(len(dataset) * val_split), + ] + dataset, val_dataset = torch.utils.data.random_split(dataset, lengths) + elif isinstance(val_dataset, tuple): + val_dataset = CCA_Dataset(val_dataset, labels=val_labels) + return dataset, val_dataset + + +def get_dataloaders( + dataset, val_dataset=None, batch_size=None, val_batch_size=None, num_workers=0 +): + if batch_size is None: + batch_size = len(dataset) + train_dataloader = DataLoader( + dataset, + batch_size=batch_size, + drop_last=True, + num_workers=num_workers, + pin_memory=True, + shuffle=True, + ) + if val_dataset: + if val_batch_size is None: + val_batch_size = len(val_dataset) + val_dataloader = DataLoader( + val_dataset, + batch_size=val_batch_size, + drop_last=True, + num_workers=num_workers, + pin_memory=True, + ) + return train_dataloader, val_dataloader + return train_dataloader diff --git a/cca_zoo/model_selection/_search.py b/cca_zoo/model_selection/_search.py index 35521597..57b20826 100644 --- a/cca_zoo/model_selection/_search.py +++ b/cca_zoo/model_selection/_search.py @@ -51,8 +51,9 @@ def param2grid(params): --------- >>> params = {'regs': [[1, 2], [3, 4]]} >>> param2grid(params) - [[1,3], [1,4], [2,3], [2,4]] + {'regs': [[1, 3], [1, 4], [2, 3], [2, 4]]} """ + params = params.copy() for k, v in params.items(): if any([isinstance(v_, list) for v_ in v]): # itertools expects all lists to perform product diff --git a/cca_zoo/models/gcca.py b/cca_zoo/models/gcca.py index 01695eff..dd5bc322 100644 --- a/cca_zoo/models/gcca.py +++ b/cca_zoo/models/gcca.py @@ -6,25 +6,38 @@ from sklearn.utils.validation import check_is_fitted from cca_zoo.models import rCCA -from ..utils.check_values import _process_parameter, check_views +from cca_zoo.utils.check_values import _process_parameter, check_views class GCCA(rCCA): - """ + r""" A class used to fit GCCA model. For more than 2 views, GCCA optimizes the sum of correlations with a shared auxiliary vector - Citation - -------- + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ \sum_iw_i^TX_i^TT \}\\ + + \text{subject to:} + + T^TT=1 + + :Citation: + Tenenhaus, Arthur, and Michel Tenenhaus. "Regularized generalized canonical correlation analysis." Psychometrika 76.2 (2011): 257. :Example: >>> from cca_zoo.models import GCCA + >>> import numpy as np >>> rng=np.random.RandomState(0) - >>> X1 = rng.random(10,5) - >>> X2 = np.random.rand(10,5) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = GCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([0.97229856]) """ def __init__( @@ -95,21 +108,34 @@ def _solve_evp(self, views: Iterable[np.ndarray], C, D=None, **kwargs): class KGCCA(GCCA): - """ + r""" A class used to fit KGCCA model. For more than 2 views, KGCCA optimizes the sum of correlations with a shared auxiliary vector - Citation - -------- + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ \sum_i\alpha_i^TK_i^TT \}\\ + + \text{subject to:} + + T^TT=1 + + :Citation: + Tenenhaus, Arthur, Cathy Philippe, and Vincent Frouin. "Kernel generalized canonical correlation analysis." Computational Statistics & Data Analysis 90 (2015): 114-131. :Example: >>> from cca_zoo.models import KGCCA + >>> import numpy as np >>> rng=np.random.RandomState(0) - >>> X1 = rng.random(10,5) - >>> X2 = np.random.rand(10,5) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = KGCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([0.97019284]) """ def __init__( @@ -128,6 +154,8 @@ def __init__( kernel_params: Iterable[dict] = None, ): """ + Constructor for PLS + :param latent_dims: number of latent dimensions to fit :param scale: normalize variance in each column before fitting :param centre: demean data by column before fitting (and before transforming out of sample @@ -168,7 +196,7 @@ def _check_params(self): ) def _get_kernel(self, view, X, Y=None): - if callable(self.kernel): + if callable(self.kernel[view]): params = self.kernel_params[view] or {} else: params = { diff --git a/cca_zoo/models/innerloop.py b/cca_zoo/models/innerloop.py index 1c7f5b31..1dc52afa 100644 --- a/cca_zoo/models/innerloop.py +++ b/cca_zoo/models/innerloop.py @@ -26,17 +26,14 @@ def __init__( self, max_iter: int = 100, tol: float = 1e-5, - generalized: bool = False, initialization: str = "unregularized", random_state=None, ): """ :param max_iter: maximum number of iterations to perform if tol is not reached :param tol: tolerance value used for stopping criteria - :param generalized: use an auxiliary variable to :param initialization: initialise the optimisation with either the 'unregularized' (CCA/PLS) solution, or a 'random' initialisation """ - self.generalized = generalized self.initialization = initialization self.max_iter = max_iter self.tol = tol @@ -124,14 +121,12 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", random_state=None, ): super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) @@ -179,7 +174,6 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", c=None, positive=None, @@ -188,7 +182,6 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) @@ -242,7 +235,6 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", c=None, random_state=None, @@ -250,14 +242,13 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) self.c = c def _check_params(self): - self.c = _process_parameter("c", self.c, [0.0001], len(self.views)) + self.c = _process_parameter("c", self.c, 0.0001, len(self.views)) if any(c <= 0 for c in self.c): raise ("All regularisation parameters should be above 0. " f"c=[{self.c}]") @@ -283,11 +274,10 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", c=None, l1_ratio=None, - constrained=False, + maxvar=True, stochastic=True, positive=None, random_state=None, @@ -295,15 +285,14 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) self.stochastic = stochastic - self.constrained = constrained self.c = c self.l1_ratio = l1_ratio self.positive = positive + self.maxvar = maxvar def _check_params(self): self.c = _process_parameter("c", self.c, 0, len(self.views)) @@ -313,8 +302,6 @@ def _check_params(self): self.positive = _process_parameter( "positive", self.positive, False, len(self.views) ) - if self.constrained: - self.gamma = np.zeros(len(self.views)) self.regressors = [] for alpha, l1_ratio, positive in zip(self.c, self.l1_ratio, self.positive): if self.stochastic: @@ -398,14 +385,12 @@ def _update_view(self, view_index: int): :param view_index: index of view being updated :return: updated weights """ - if self.generalized: + if self.maxvar: target = self.scores.mean(axis=0) + target /= np.linalg.norm(target) else: target = self.scores[view_index - 1] - if self.constrained: - self._elastic_solver_constrained(self.views[view_index], target, view_index) - else: - self._elastic_solver(self.views[view_index], target, view_index) + self._elastic_solver(self.views[view_index], target, view_index) _check_converged_weights(self.weights[view_index], view_index) self.scores[view_index] = self.views[view_index] @ self.weights[view_index] @@ -418,35 +403,6 @@ def _elastic_solver(self, X, y, view_index): self.views[view_index] @ self.weights[view_index] ) / np.sqrt(self.n) - @ignore_warnings(category=ConvergenceWarning) - def _elastic_solver_constrained(self, X, y, view_index): - converged = False - min_ = -1 - max_ = 1 - previous = self.gamma[view_index] - previous_val = None - i = 0 - while not converged: - i += 1 - coef = ( - self.regressors[view_index] - .fit( - np.sqrt(self.gamma[view_index] + 1) * X, - y.ravel() / np.sqrt(self.gamma[view_index] + 1), - ) - .coef_ - ) - current_val = 1 - (np.linalg.norm(X @ coef) ** 2) / self.n - self.gamma[view_index], previous, min_, max_ = _bin_search( - self.gamma[view_index], previous, current_val, previous_val, min_, max_ - ) - previous_val = current_val - if np.abs(current_val) < 1e-5: - converged = True - elif np.abs(max_ - min_) < 1e-30 or i == 50: - converged = True - self.weights[view_index] = coef - def _objective(self): views = len(self.views) c = np.array(self.c) @@ -455,7 +411,6 @@ def _objective(self): l2 = c * (1 - ratio) total_objective = 0 for i in range(views): - # TODO this looks like it could be tidied up. In particular can we make the generalized objective correspond to the 2 view target = self.scores.mean(axis=0) objective = ( views @@ -469,7 +424,7 @@ def _objective(self): def _early_stop(self) -> bool: # Some kind of early stopping - if np.abs(self.track['objective'][-2] - self.track['objective'][-1]) < self.tol: + if np.abs(self.track["objective"][-2] - self.track["objective"][-1]) < self.tol: return True else: return False @@ -480,7 +435,6 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", mu=None, lam=None, @@ -491,7 +445,6 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) @@ -601,7 +554,6 @@ def __init__( self, max_iter: int = 100, tol=1e-5, - generalized: bool = False, initialization: str = "unregularized", c=None, regularisation="l0", @@ -612,7 +564,6 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) @@ -664,7 +615,6 @@ def __init__( self, max_iter: int = 100, tol=1e-20, - generalized: bool = False, initialization: str = "unregularized", regularisation="l0", c=None, @@ -675,7 +625,6 @@ def __init__( super().__init__( max_iter=max_iter, tol=tol, - generalized=generalized, initialization=initialization, random_state=random_state, ) @@ -688,9 +637,11 @@ def __init__( self.positive = positive def _check_params(self): + if self.sample_support is None: + self.sample_support = self.views[0].shape[0] self.sample_weights = np.ones((self.views[0].shape[0], 1)) self.sample_weights /= np.linalg.norm(self.sample_weights) - self.c = _process_parameter("c", self.c, 1, len(self.views)) + self.c = _process_parameter("c", self.c, 2, len(self.views)) self.positive = _process_parameter( "positive", self.positive, False, len(self.views) ) diff --git a/cca_zoo/models/iterative.py b/cca_zoo/models/iterative.py index a365c4f7..aab90457 100644 --- a/cca_zoo/models/iterative.py +++ b/cca_zoo/models/iterative.py @@ -5,8 +5,8 @@ import numpy as np -from .cca_base import _CCA_Base -from .innerloop import ( +from cca_zoo.models.cca_base import _CCA_Base +from cca_zoo.models.innerloop import ( PLSInnerLoop, PMDInnerLoop, ParkhomenkoInnerLoop, @@ -15,7 +15,7 @@ SpanCCAInnerLoop, SWCCAInnerLoop, ) -from ..utils import check_views +from cca_zoo.utils import check_views class _Iterative(_CCA_Base): @@ -33,7 +33,6 @@ def __init__( random_state=None, deflation="cca", max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, ): @@ -47,7 +46,7 @@ def __init__( :param random_state: Pass for reproducible output across multiple function calls :param deflation: the type of deflation. :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) + :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping """ @@ -59,7 +58,6 @@ def __init__( accept_sparse=["csc", "csr"], ) self.max_iter = max_iter - self.generalized = generalized self.initialization = initialization self.tol = tol self.deflation = deflation @@ -126,25 +124,35 @@ def _set_loop_params(self): """ self.loop = PLSInnerLoop( max_iter=self.max_iter, - generalized=self.generalized, initialization=self.initialization, random_state=self.random_state, ) class PLS_ALS(_Iterative): - """ + r""" A class used to fit a PLS model Fits a partial least squares model with CCA deflation by NIPALS algorithm + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2\}\\ + + \text{subject to:} + + w_i^Tw_i=1 + :Example: >>> from cca_zoo.models import PLS - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = PLS_ALS() - >>> model.fit([X1,X2]) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = PLS_ALS(random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.81796873]) """ def __init__( @@ -155,7 +163,6 @@ def __init__( copy_data=True, random_state=None, max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, ): @@ -168,7 +175,6 @@ def __init__( :param copy_data: If True, X will be copied; else, it may be overwritten :param random_state: Pass for reproducible output across multiple function calls :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping """ @@ -179,7 +185,6 @@ def __init__( copy_data=copy_data, deflation="pls", max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -188,7 +193,6 @@ def __init__( def _set_loop_params(self): self.loop = PLSInnerLoop( max_iter=self.max_iter, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, random_state=self.random_state, @@ -196,20 +200,31 @@ def _set_loop_params(self): class ElasticCCA(_Iterative): - """ - Fits an elastic CCA by iterative rescaled elastic net regression + r""" + Fits an elastic CCA by iterating elastic net regression - Citation - -------- - Waaijenborg, Sandra, Philip C. Verselewel de Witt Hamer, and Aeilko H. Zwinderman. "Quantifying the association between gene expressions and DNA-markers by penalized canonical correlation analysis." Statistical applications in genetics and molecular biology 7.1 (2008). + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2 + c\|w_i\|^2_2 + \text{l1_ratio}\|w_i\|_1\}\\ + + \text{subject to:} + + w_i^TX_i^TX_iw_i=1 + + :Citation: + + Fu, Xiao, et al. "Scalable and flexible multiview MAX-VAR canonical correlation analysis." IEEE Transactions on Signal Processing 65.16 (2017): 4150-4165. :Example: >>> from cca_zoo.models import ElasticCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = ElasticCCA(c=[0.1,0.1],l1_ratio=[0.5,0.5]) - >>> model.fit([X1,X2]) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = ElasticCCA(c=[0.1,0.1],l1_ratio=[0.5,0.5], random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.95818397]) """ def __init__( @@ -221,12 +236,11 @@ def __init__( random_state=None, deflation="cca", max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, c: Union[Iterable[float], float] = None, l1_ratio: Union[Iterable[float], float] = None, - constrained: bool = False, + maxvar: bool = True, stochastic=False, positive: Union[Iterable[bool], bool] = None, ): @@ -240,18 +254,17 @@ def __init__( :param random_state: Pass for reproducible output across multiple function calls :param deflation: the type of deflation. :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param c: lasso alpha :param l1_ratio: l1 ratio in lasso subproblems - :param constrained: force unit norm constraint with binary search + :param maxvar: use auxiliary variable "maxvar" formulation :param stochastic: use stochastic regression optimisers for subproblems :param positive: constrain model weights to be positive """ self.c = c self.l1_ratio = l1_ratio - self.constrained = constrained + self.maxvar = maxvar self.stochastic = stochastic self.positive = positive if self.positive is not None and stochastic: @@ -266,7 +279,6 @@ def __init__( copy_data=copy_data, deflation=deflation, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -277,10 +289,9 @@ def _set_loop_params(self): max_iter=self.max_iter, c=self.c, l1_ratio=self.l1_ratio, - generalized=self.generalized, + maxvar=self.maxvar, initialization=self.initialization, tol=self.tol, - constrained=self.constrained, stochastic=self.stochastic, positive=self.positive, random_state=self.random_state, @@ -288,20 +299,31 @@ def _set_loop_params(self): class CCA_ALS(ElasticCCA): - """ + r""" Fits a CCA model with CCA deflation by NIPALS algorithm. Implemented by ElasticCCA with 0 regularisation - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2 }\\ + + \text{subject to:} + + w_i^TX_i^TX_iw_i=1 + + :Citation: + Golub, Gene H., and Hongyuan Zha. "The canonical correlations of matrix pairs and their numerical computation." Linear algebra for signal processing. Springer, New York, NY, 1995. 27-49. :Example: >>> from cca_zoo.models import CCA_ALS - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = CCA_ALS() - >>> model.fit(X1,X2) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,3)) + >>> X2 = rng.random((10,3)) + >>> model = CCA_ALS(random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.85890619]) """ def __init__( @@ -312,7 +334,6 @@ def __init__( copy_data=True, random_state=None, max_iter: int = 100, - generalized: bool = False, initialization: str = "random", tol: float = 1e-9, stochastic=True, @@ -327,8 +348,7 @@ def __init__( :param copy_data: If True, X will be copied; else, it may be overwritten :param random_state: Pass for reproducible output across multiple function calls :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) - :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores + :param initialization: initialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param stochastic: use stochastic regression optimisers for subproblems :param positive: constrain model weights to be positive @@ -337,10 +357,8 @@ def __init__( super().__init__( latent_dims=latent_dims, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, - constrained=False, stochastic=stochastic, centre=centre, copy_data=copy_data, @@ -348,24 +366,36 @@ def __init__( positive=positive, random_state=random_state, c=1e-3, + maxvar=False, ) class SCCA(ElasticCCA): - """ + r""" Fits a sparse CCA model by iterative rescaled lasso regression. Implemented by ElasticCCA with l1 ratio=1 - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2 + \text{l1_ratio}\|w_i\|_1\}\\ + + \text{subject to:} + + w_i^TX_i^TX_iw_i=1 + + :Citation: + Mai, Qing, and Xin Zhang. "An iterative penalized least squares approach to sparse canonical correlation analysis." Biometrics 75.3 (2019): 734-744. :Example: >>> from cca_zoo.models import SCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = SCCA(c=[0.001,0.001]) - >>> model.fit(X1,X2) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = SCCA(c=[0.001,0.001], random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.99998919]) """ def __init__( @@ -377,7 +407,7 @@ def __init__( random_state=None, c: Union[Iterable[float], float] = None, max_iter: int = 100, - generalized: bool = False, + maxvar: bool = False, initialization: str = "unregularized", tol: float = 1e-9, stochastic=False, @@ -392,7 +422,7 @@ def __init__( :param copy_data: If True, X will be copied; else, it may be overwritten :param random_state: Pass for reproducible output across multiple function calls :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) + :param maxvar: use auxiliary variable "maxvar" form :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param c: lasso alpha @@ -405,12 +435,11 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, c=c, l1_ratio=1, - constrained=False, + maxvar=maxvar, stochastic=stochastic, positive=positive, random_state=random_state, @@ -418,20 +447,33 @@ def __init__( class PMD(_Iterative): - """ + r""" Fits a Sparse CCA (Penalized Matrix Decomposition) model. - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ w_1^TX_1^TX_2w_2 \}\\ + + \text{subject to:} + + w_i^Tw_i=1 + + \|w_i\|<=c_i + + :Citation: + Witten, Daniela M., Robert Tibshirani, and Trevor Hastie. "A penalized matrix decomposition, with applications to sparse principal components and canonical correlation analysis." Biostatistics 10.3 (2009): 515-534. :Example: >>> from cca_zoo.models import PMD - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = PMD(c=[1,1]) - >>> model.fit(X1,X2) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = PMD(c=[1,1],random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.69792082]) """ def __init__( @@ -443,7 +485,6 @@ def __init__( random_state=None, c: Union[Iterable[float], float] = None, max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, positive: Union[Iterable[bool], bool] = None, @@ -458,7 +499,6 @@ def __init__( :param random_state: Pass for reproducible output across multiple function calls :param c: l1 regularisation parameter between 1 and sqrt(number of features) for each view :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param positive: constrain model weights to be positive @@ -471,7 +511,6 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -481,7 +520,6 @@ def _set_loop_params(self): self.loop = PMDInnerLoop( max_iter=self.max_iter, c=self.c, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, positive=self.positive, @@ -490,20 +528,31 @@ def _set_loop_params(self): class ParkhomenkoCCA(_Iterative): - """ + r""" Fits a sparse CCA (penalized CCA) model - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ w_1^TX_1^TX_2w_2 \} + c_i\|w_i\|\\ + + \text{subject to:} + + w_i^Tw_i=1 + + :Citation: + Parkhomenko, Elena, David Tritchler, and Joseph Beyene. "Sparse canonical correlation analysis with application to genomic data integration." Statistical applications in genetics and molecular biology 8.1 (2009). :Example: >>> from cca_zoo.models import ParkhomenkoCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = ParkhomenkoCCA(c=[0.001,0.001]) - >>> model.fit(X1,X2) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = ParkhomenkoCCA(c=[0.001,0.001],random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.81803543]) """ def __init__( @@ -515,7 +564,6 @@ def __init__( random_state=None, c: Union[Iterable[float], float] = None, max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, ): @@ -529,7 +577,6 @@ def __init__( :param random_state: Pass for reproducible output across multiple function calls :param c: l1 regularisation parameter :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping """ @@ -540,7 +587,6 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -550,7 +596,6 @@ def _set_loop_params(self): self.loop = ParkhomenkoInnerLoop( max_iter=self.max_iter, c=self.c, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, random_state=self.random_state, @@ -558,20 +603,31 @@ def _set_loop_params(self): class SCCA_ADMM(_Iterative): - """ + r""" Fits a sparse CCA model by alternating ADMM - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2 + \text{l1_ratio}\|w_i\|_1\}\\ + + \text{subject to:} + + w_i^TX_i^TX_iw_i=1 + + :Citation: + Suo, Xiaotong, et al. "Sparse canonical correlation analysis." arXiv preprint arXiv:1705.10865 (2017). :Example: >>> from cca_zoo.models import SCCA_ADMM - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = SCCA_ADMM() - >>> model.fit(X1,X2) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = SCCA_ADMM(random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.99999997]) """ def __init__( @@ -586,7 +642,6 @@ def __init__( lam: Union[Iterable[float], float] = None, eta: Union[Iterable[float], float] = None, max_iter: int = 100, - generalized: bool = False, initialization: str = "unregularized", tol: float = 1e-9, ): @@ -600,7 +655,6 @@ def __init__( :param random_state: Pass for reproducible output across multiple function calls :param c: l1 regularisation parameter :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param mu: @@ -617,7 +671,6 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -630,7 +683,6 @@ def _set_loop_params(self): mu=self.mu, lam=self.lam, eta=self.eta, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, random_state=self.random_state, @@ -638,13 +690,32 @@ def _set_loop_params(self): class SpanCCA(_Iterative): - """ + r""" Fits a Sparse CCA model using SpanCCA. - Citation - -------- + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \|X_iw_i-X_jw_j\|^2 + \text{l1_ratio}\|w_i\|_1\}\\ + + \text{subject to:} + + w_i^TX_i^TX_iw_i=1 + + :Citation: + Asteris, Megasthenis, et al. "A simple and provable algorithm for sparse diagonal CCA." International Conference on Machine Learning. PMLR, 2016. + + :Example: + + >>> from cca_zoo.models import SpanCCA + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = SpanCCA(regularisation="l0", c=[2, 2]) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.84556666]) """ def __init__( @@ -654,7 +725,6 @@ def __init__( centre=True, copy_data=True, max_iter: int = 100, - generalized: bool = False, initialization: str = "uniform", tol: float = 1e-9, regularisation="l0", @@ -671,7 +741,6 @@ def __init__( :param copy_data: If True, X will be copied; else, it may be overwritten :param random_state: Pass for reproducible output across multiple function calls :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param regularisation: @@ -685,7 +754,6 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -699,7 +767,6 @@ def _set_loop_params(self): self.loop = SpanCCAInnerLoop( max_iter=self.max_iter, c=self.c, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, regularisation=self.regularisation, @@ -710,14 +777,23 @@ def _set_loop_params(self): class SWCCA(_Iterative): - """ + r""" A class used to fit SWCCA model - Citation - -------- - Wenwen, M. I. N., L. I. U. Juan, and Shihua Zhang. "Sparse Weighted Canonical Correlation Analysis." Chinese Journal of Electronics 27.3 (2018): 459-466. + :Citation: + + .. Wenwen, M. I. N., L. I. U. Juan, and Shihua Zhang. "Sparse Weighted Canonical Correlation Analysis." Chinese Journal of Electronics 27.3 (2018): 459-466. + :Example: + >>> from cca_zoo.models import SWCCA + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = SWCCA(regularisation='l0',c=[2, 2], sample_support=5, random_state=0) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.61620969]) """ def __init__( @@ -728,7 +804,6 @@ def __init__( copy_data=True, random_state=None, max_iter: int = 500, - generalized: bool = False, initialization: str = "uniform", tol: float = 1e-9, regularisation="l0", @@ -744,7 +819,6 @@ def __init__( :param copy_data: If True, X will be copied; else, it may be overwritten :param random_state: Pass for reproducible output across multiple function calls :param max_iter: the maximum number of iterations to perform in the inner optimization loop - :param generalized: use auxiliary variables (required for >2 views) :param initialization: intialization for optimisation. 'unregularized' uses CCA or PLS solution,'random' uses random initialization,'uniform' uses uniform initialization of weights and scores :param tol: tolerance value used for early stopping :param regularisation: the type of regularisation on the weights either 'l0' or 'l1' @@ -763,7 +837,6 @@ def __init__( centre=centre, copy_data=copy_data, max_iter=max_iter, - generalized=generalized, initialization=initialization, tol=tol, random_state=random_state, @@ -772,7 +845,6 @@ def __init__( def _set_loop_params(self): self.loop = SWCCAInnerLoop( max_iter=self.max_iter, - generalized=self.generalized, initialization=self.initialization, tol=self.tol, regularisation=self.regularisation, diff --git a/cca_zoo/models/mcca.py b/cca_zoo/models/mcca.py index ba1df3cd..4d0294c1 100644 --- a/cca_zoo/models/mcca.py +++ b/cca_zoo/models/mcca.py @@ -6,23 +6,37 @@ from sklearn.utils.validation import check_is_fitted from cca_zoo.models import rCCA -from ..utils.check_values import _process_parameter, check_views +from cca_zoo.utils.check_values import _process_parameter, check_views class MCCA(rCCA): - """ + r""" A class used to fit MCCA model. For more than 2 views, MCCA optimizes the sum of pairwise correlations. - Citation - -------- + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} w_i^TX_i^TX_jw_j \}\\ + + \text{subject to:} + + (1-c_i)w_i^TX_i^TX_iw_i+c_iw_i^Tw_i=1 + + :Citation: + Kettenring, Jon R. "Canonical analysis of several sets of variables." Biometrika 58.3 (1971): 433-451. :Example: >>> from cca_zoo.models import MCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = MCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([0.97200847]) """ def __init__( @@ -87,16 +101,30 @@ def _solve_evp(self, views: Iterable[np.ndarray], C, D=None, **kwargs): class KCCA(MCCA): - """ + r""" A class used to fit KCCA model. + :Maths: + + .. math:: + + \alpha_{opt}=\underset{\alpha}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \alpha_i^TK_i^TK_j\alpha_j \}\\ + + \text{subject to:} + + c_i\alpha_i^TK_i\alpha_i + (1-c_i)\alpha_i^TK_i^TK_i\alpha_i=1 + :Example: >>> from cca_zoo.models import KCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = KCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([0.96893666]) """ def __init__( @@ -152,7 +180,7 @@ def _check_params(self): self.c = _process_parameter("c", self.c, 0, self.n_views) def _get_kernel(self, view, X, Y=None): - if callable(self.kernel): + if callable(self.kernel[view]): params = self.kernel_params[view] or {} else: params = { diff --git a/cca_zoo/models/ncca.py b/cca_zoo/models/ncca.py index ab6d1abe..2fe741c6 100644 --- a/cca_zoo/models/ncca.py +++ b/cca_zoo/models/ncca.py @@ -13,22 +13,30 @@ class NCCA(_CCA_Base): """ A class used to fit nonparametric (NCCA) model. - Citation - -------- + :Citation: + Michaeli, Tomer, Weiran Wang, and Karen Livescu. "Nonparametric canonical correlation analysis." International conference on machine learning. PMLR, 2016. :Example: - >>> from cca_zoo.experimental import NCCA + >>> from cca_zoo.models import NCCA >>> X1 = np.random.rand(10,5) >>> X2 = np.random.rand(10,5) >>> model = NCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2)).score((X1,X2)) + array([1.]) """ - def __init__(self, latent_dims: int = 1, scale=True, centre=True, copy_data=True, accept_sparse=False, - random_state: Union[int, np.random.RandomState] = None, nearest_neighbors=None, - gamma: Iterable[float] = None, - ): + def __init__( + self, + latent_dims: int = 1, + scale=True, + centre=True, + copy_data=True, + accept_sparse=False, + random_state: Union[int, np.random.RandomState] = None, + nearest_neighbors=None, + gamma: Iterable[float] = None, + ): """ Constructor for NCCA @@ -38,15 +46,19 @@ def __init__(self, latent_dims: int = 1, scale=True, centre=True, copy_data=True :param copy_data: If True, X will be copied; else, it may be overwritten :param accept_sparse: Whether model can take sparse data as input :param random_state: Pass for reproducible output across multiple function calls - :param nearest_neighbors: Number of nearest neighbors (l2 distance) to consider when constructing affinity + :param nearest_neighbors: Number of neaest neighbors (l2 distance) to consider when constructing affinity :param gamma: Bandwidth parameter for rbf kernel """ - super().__init__(latent_dims, scale, centre, copy_data, accept_sparse, random_state) + super().__init__( + latent_dims, scale, centre, copy_data, accept_sparse, random_state + ) self.nearest_neighbors = nearest_neighbors self.gamma = gamma def _check_params(self): - self.nearest_neighbors = _process_parameter("nearest_neighbors", self.nearest_neighbors, 1, self.n_views) + self.nearest_neighbors = _process_parameter( + "nearest_neighbors", self.nearest_neighbors, 1, self.n_views + ) self.gamma = _process_parameter("gamma", self.gamma, None, self.n_views) self.kernel = _process_parameter("kernel", None, "rbf", self.n_views) @@ -59,18 +71,25 @@ def fit(self, views: Iterable[np.ndarray], y=None, **kwargs): self.n = views[0].shape[0] self._check_params() self.train_views = views - self.KNs = [NearestNeighbors(n_neighbors=self.nearest_neighbors[i]).fit(view) for i, view in - enumerate(views)] - NNs = [self.KNs[i].kneighbors(view, self.nearest_neighbors[i]) for i, view in enumerate(views)] + self.KNs = [ + NearestNeighbors(n_neighbors=self.nearest_neighbors[i]).fit(view) + for i, view in enumerate(views) + ] + NNs = [ + self.KNs[i].kneighbors(view, self.nearest_neighbors[i]) + for i, view in enumerate(views) + ] kernels = [self._get_kernel(i, view) for i, view in enumerate(self.train_views)] self.Ws = [fill_W(kernel, inds) for kernel, (dists, inds) in zip(kernels, NNs)] - self.Ws = [self.Ws[0] / self.Ws[0].sum(axis=1, keepdims=True), - self.Ws[1] / self.Ws[1].sum(axis=0, keepdims=True)] + self.Ws = [ + self.Ws[0] / self.Ws[0].sum(axis=1, keepdims=True), + self.Ws[1] / self.Ws[1].sum(axis=0, keepdims=True), + ] S = self.Ws[0] @ self.Ws[1] U, S, Vt = np.linalg.svd(S) - self.f = U[:, 1:self.latent_dims + 1] * np.sqrt(self.n) - self.g = Vt[1:self.latent_dims + 1, :].T * np.sqrt(self.n) - self.S = S[1:self.latent_dims + 1] + self.f = U[:, 1: self.latent_dims + 1] * np.sqrt(self.n) + self.g = Vt[1: self.latent_dims + 1, :].T * np.sqrt(self.n) + self.S = S[1: self.latent_dims + 1] return self def transform(self, views: Iterable[np.ndarray], y=None, **kwargs): @@ -80,18 +99,24 @@ def transform(self, views: Iterable[np.ndarray], y=None, **kwargs): :param views: numpy arrays with the same number of rows (samples) separated by commas :param kwargs: any additional keyword arguments required by the given model """ - check_is_fitted(self, attributes=["U", "V", "f", "g"]) + check_is_fitted(self, attributes=["f", "g"]) views = check_views( *views, copy=self.copy_data, accept_sparse=self.accept_sparse ) views = self._centre_scale_transform(views) - NNs = [self.KNs[i].kneighbors(view, self.nearest_neighbors[i]) for i, view in enumerate(views)] + NNs = [ + self.KNs[i].kneighbors(view, self.nearest_neighbors[i]) + for i, view in enumerate(views) + ] kernels = [ self._get_kernel(i, self.train_views[i], Y=view) for i, view in enumerate(views) ] Wst = [fill_W(kernel, inds) for kernel, (dists, inds) in zip(kernels, NNs)] - Wst = [Wst[0] / Wst[0].sum(axis=1, keepdims=True), Wst[1] / Wst[1].sum(axis=1, keepdims=True)] + Wst = [ + Wst[0] / Wst[0].sum(axis=1, keepdims=True), + Wst[1] / Wst[1].sum(axis=1, keepdims=True), + ] St = [Wst[0] @ self.Ws[1], Wst[1] @ self.Ws[0]] return St[0] @ self.g * (1 / self.S), St[1] @ self.f * (1 / self.S) @@ -108,4 +133,4 @@ def fill_W(kernels, inds): W = np.zeros_like(kernels) for i, ind in enumerate(inds): W[ind, i] = kernels[ind, i] - return W.T \ No newline at end of file + return W.T diff --git a/cca_zoo/models/rcca.py b/cca_zoo/models/rcca.py index 2285ef6c..4247240b 100644 --- a/cca_zoo/models/rcca.py +++ b/cca_zoo/models/rcca.py @@ -4,28 +4,44 @@ import numpy as np from scipy.linalg import block_diag, eigh -from .cca_base import _CCA_Base -from ..utils.check_values import _process_parameter, check_views +from cca_zoo.models.cca_base import _CCA_Base +from cca_zoo.utils.check_values import _process_parameter, check_views # from hyperopt import fmin, tpe, Trials class rCCA(_CCA_Base): - """ + r""" A class used to fit Regularised CCA (canonical ridge) model. Uses PCA to perform the optimization efficiently for high dimensional data. - Citation - -------- + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ w_1^TX_1^TX_2w_2 \}\\ + + \text{subject to:} + + (1-c_1)w_1^TX_1^TX_1w_1+c_1w_1^Tw_1=1 + + (1-c_2)w_2^TX_2^TX_2w_2+c_2w_2^Tw_2=1 + + + :Citation: + Vinod, Hrishikesh D. "Canonical ridge and econometrics of joint production." Journal of econometrics 4.2 (1976): 147-166. :Example: >>> from cca_zoo.models import rCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) >>> model = rCCA(c=[0.1,0.1]) - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.95222128]) """ def __init__( @@ -157,22 +173,37 @@ def _multi_view_evp(self, Us, Ss): class CCA(rCCA): - """ + r""" A class used to fit a simple CCA model Implements CCA by inheriting regularised CCA with 0 regularisation - Citation - -------- + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ w_1^TX_1^TX_2w_2 \}\\ + + \text{subject to:} + + w_1^TX_1^TX_1w_1=1 + + w_2^TX_2^TX_2w_2=1 + + :Citation: + Hotelling, Harold. "Relations between two sets of variates." Breakthroughs in statistics. Springer, New York, NY, 1992. 162-190. :Example: >>> from cca_zoo.models import CCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) >>> model = CCA() - >>> model.fit(X1,X2) + >>> model.fit((X1,X2)).score((X1,X2)) + array([1.]) """ def __init__( @@ -185,6 +216,7 @@ def __init__( ): """ Constructor for CCA + :param latent_dims: number of latent dimensions to fit :param scale: normalize variance in each column before fitting :param centre: demean data by column before fitting (and before transforming out of sample @@ -202,18 +234,33 @@ def __init__( class PLS(rCCA): - """ + r""" A class used to fit a simple PLS model Implements PLS by inheriting regularised CCA with maximal regularisation + :Maths: + + .. math:: + + w_{opt}=\underset{w}{\mathrm{argmax}}\{ w_1^TX_1^TX_2w_2 \}\\ + + \text{subject to:} + + w_1^Tw_1=1 + + w_2^Tw_2=1 + :Example: >>> from cca_zoo.models import PLS - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) - >>> model = CCA() - >>> model.fit([X1,X2]) + >>> import numpy as np + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> model = PLS() + >>> model.fit((X1,X2)).score((X1,X2)) + array([0.81796873]) """ def __init__( @@ -225,7 +272,8 @@ def __init__( random_state=None, ): """ - Constructor for CCA + Constructor for PLS + :param latent_dims: number of latent dimensions to fit :param scale: normalize variance in each column before fitting :param centre: demean data by column before fitting (and before transforming out of sample diff --git a/cca_zoo/models/tcca.py b/cca_zoo/models/tcca.py index 8f7289aa..740ab737 100644 --- a/cca_zoo/models/tcca.py +++ b/cca_zoo/models/tcca.py @@ -7,27 +7,39 @@ from sklearn.utils.validation import check_is_fitted from tensorly.decomposition import parafac -from .cca_base import _CCA_Base -from ..utils.check_values import _process_parameter, check_views +from cca_zoo.models.cca_base import _CCA_Base +from cca_zoo.utils.check_values import _process_parameter, check_views class TCCA(_CCA_Base): - """ + r""" Fits a Tensor CCA model. Tensor CCA maximises higher order correlations - Citation - -------- - Kim, Tae-Kyun, Shu-Fai Wong, and Roberto Cipolla. "Tensor canonical correlation analysis for action classification." 2007 IEEE Conference on Computer Vision and Pattern Recognition. IEEE, 2007 + :Maths: + + .. math:: + + \alpha_{opt}=\underset{\alpha}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \alpha_i^TK_i^TK_j\alpha_j \}\\ - My own port from https://github.com/rciszek/mdr_tcca + \text{subject to:} + + \alpha_i^TK_i^TK_i\alpha_i=1 + + :Citation: + + Kim, Tae-Kyun, Shu-Fai Wong, and Roberto Cipolla. "Tensor canonical correlation analysis for action classification." 2007 IEEE Conference on Computer Vision and Pattern Recognition. IEEE, 2007 + https://github.com/rciszek/mdr_tcca :Example: >>> from cca_zoo.models import TCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = TCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([1.14595755]) """ def __init__( @@ -92,7 +104,7 @@ def fit(self, views: Iterable[np.ndarray], y=None, **kwargs): M = np.expand_dims(M, -1) @ el M = np.mean(M, 0) tl.set_backend("numpy") - M_parafac = parafac(M, self.latent_dims, verbose=True) + M_parafac = parafac(M, self.latent_dims, verbose=False) self.weights = [ cov_invsqrt @ fac for i, (view, cov_invsqrt, fac) in enumerate( @@ -148,20 +160,33 @@ def _setup_tensor(self, *views: np.ndarray, **kwargs): class KTCCA(TCCA): - """ + r""" Fits a Kernel Tensor CCA model. Tensor CCA maximises higher order correlations - Citation - -------- + :Maths: + + .. math:: + + \alpha_{opt}=\underset{\alpha}{\mathrm{argmax}}\{\sum_i\sum_{j\neq i} \alpha_i^TK_i^TK_j\alpha_j \}\\ + + \text{subject to:} + + \alpha_i^TK_i^TK_i\alpha_i=1 + + :Citation: + Kim, Tae-Kyun, Shu-Fai Wong, and Roberto Cipolla. "Tensor canonical correlation analysis for action classification." 2007 IEEE Conference on Computer Vision and Pattern Recognition. IEEE, 2007 :Example: >>> from cca_zoo.models import KTCCA - >>> X1 = np.random.rand(10,5) - >>> X2 = np.random.rand(10,5) + >>> rng=np.random.RandomState(0) + >>> X1 = rng.random((10,5)) + >>> X2 = rng.random((10,5)) + >>> X3 = rng.random((10,5)) >>> model = KTCCA() - >>> model.fit([X1,X2]) + >>> model.fit((X1,X2,X3)).score((X1,X2,X3)) + array([1.69896269]) """ def __init__( @@ -218,7 +243,7 @@ def _check_params(self): self.c = _process_parameter("c", self.c, 0, self.n_views) def _get_kernel(self, view, X, Y=None): - if callable(self.kernel): + if callable(self.kernel[view]): params = self.kernel_params[view] or {} else: params = { diff --git a/cca_zoo/probabilisticmodels/vcca.py b/cca_zoo/probabilisticmodels/vcca.py index 5f9696eb..eaf5ed3a 100644 --- a/cca_zoo/probabilisticmodels/vcca.py +++ b/cca_zoo/probabilisticmodels/vcca.py @@ -13,14 +13,11 @@ class VariationalCCA(_CCA_Base): """ - A class used to fit a variational bayesian CCA + A class used to fit a variational bayesian CCA. Not quite the same due to using VI methods rather than EM - Citation - -------- - Wang, Chong. "Variational Bayesian approach to canonical correlation analysis." IEEE Transactions on Neural Networks 18.3 (2007): 905-910. - - :Example: + :Citation: + Wang, Chong. "Variational Bayesian approach to canonical correlation analysis." IEEE Transactions on Neural Networks 18.3 (2007): 905-910. """ diff --git a/cca_zoo/test/test_data.py b/cca_zoo/test/test_data.py deleted file mode 100644 index 5a0ac1c0..00000000 --- a/cca_zoo/test/test_data.py +++ /dev/null @@ -1,56 +0,0 @@ -import numpy as np -from sklearn.utils.validation import check_random_state - -from cca_zoo.data import Noisy_MNIST_Dataset, Tangled_MNIST_Dataset, Split_MNIST_Dataset -from cca_zoo.data import generate_covariance_data -from cca_zoo.models import MCCA, CCA - -rng = check_random_state(0) -X = rng.rand(500, 20) -Y = rng.rand(500, 21) -Z = rng.rand(500, 22) - - -def test_data_rand(): - (x, y), true_feats = generate_covariance_data( - 1000, [10, 11], 1, [0.5, 0.5], correlation=0.5, structure="random" - ) - - -def test_data_gen(): - (x, y, z), true_feats = generate_covariance_data( - 1000, - [10, 11, 12], - 1, - [0.5, 0.5, 0.5], - correlation=0.5, - structure=["identity", "identity", "identity"], - ) - cca = CCA().fit((x[:500], y[:500])) - cca_pred = cca.score((x[500:], y[500:])) - mcca = MCCA().fit((x[:500], y[:500], z[:500])) - mcca_pred = mcca.score((x[500:], y[500:], z[500:])) - - (x, y), true_feats = generate_covariance_data( - 1000, - [10, 11], - 1, - [0.5, 0.5], - correlation=0.5, - structure=["gaussian", "toeplitz"], - ) - - -def test_deep_data(): - dataset = Noisy_MNIST_Dataset(mnist_type="FashionMNIST", train=True) - (train_view_1, train_view_2), (train_rotations, train_labels) = dataset.to_numpy( - np.arange(10) - ) - dataset = Tangled_MNIST_Dataset(mnist_type="FashionMNIST", train=True) - (train_view_1, train_view_2), ( - train_rotations_1, - train_rotations_2, - train_labels, - ) = dataset.to_numpy(np.arange(10)) - dataset = Split_MNIST_Dataset(mnist_type="FashionMNIST", train=True) - (train_view_1, train_view_2), (train_labels) = dataset.to_numpy(np.arange(10)) diff --git a/cca_zoo/test/test_deepmodels.py b/cca_zoo/test/test_deepmodels.py index 63080bba..dcad9bb9 100644 --- a/cca_zoo/test/test_deepmodels.py +++ b/cca_zoo/test/test_deepmodels.py @@ -1,11 +1,22 @@ import numpy as np +import pytorch_lightning as pl from sklearn.utils.validation import check_random_state from torch import optim, manual_seed -from torch.utils.data import Subset from cca_zoo import data -from cca_zoo.data import Noisy_MNIST_Dataset -from cca_zoo.deepmodels import DCCA, DCCAE, DVCCA, DCCA_NOI, DTCCA, SplitAE, DeepWrapper +from cca_zoo.deepmodels import ( + DCCA, + DCCAE, + DVCCA, + DCCA_NOI, + DTCCA, + SplitAE, + CCALightning, + get_dataloaders, + process_data, + BarlowTwins, + DCCA_SDL, +) from cca_zoo.deepmodels import objectives, architectures from cca_zoo.models import CCA @@ -16,290 +27,169 @@ Z = rng.rand(200, 14) X_conv = rng.rand(100, 1, 16, 16) Y_conv = rng.rand(100, 1, 16, 16) -train_dataset = data.CCA_Dataset([X, Y]) - - -def test_input_types(): +dataset = data.CCA_Dataset([X, Y, Z]) +train_dataset, val_dataset = process_data(dataset, val_split=0.2) +train_dataset_numpy, val_dataset_numpy = process_data((X, Y, Z), val_split=0.2) +loader = get_dataloaders(dataset) +train_loader, val_loader = get_dataloaders(train_dataset, val_dataset) +train_loader_numpy, val_loader_numpy = get_dataloaders(train_dataset, val_dataset) +conv_dataset = data.CCA_Dataset((X_conv, Y_conv)) +conv_loader = get_dataloaders(conv_dataset) + + +def test_DCCA_methods(): + N = len(train_dataset) latent_dims = 2 - device = "cpu" + epochs = 100 + cca = CCA(latent_dims=latent_dims).fit((X, Y)) + # DCCA_NOI encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - # DCCA - dcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.CCA, + dcca_noi = DCCA_NOI(latent_dims, N, encoders=[encoder_1, encoder_2], rho=0) + optimizer = optim.Adam(dcca_noi.parameters(), lr=1e-3) + dcca_noi = CCALightning(dcca_noi, optimizer=optimizer) + trainer = pl.Trainer( + max_epochs=epochs, log_every_n_steps=10, enable_checkpointing=False ) - - dcca_model = DeepWrapper(dcca_model, device=device) - dcca_model.fit(train_dataset, epochs=3) - dcca_model.fit(train_dataset, val_dataset=train_dataset, epochs=3) - dcca_model.fit((X, Y), val_dataset=(X, Y), epochs=3) - dcca_model.fit((X, Y), val_split=0.2, epochs=3) - - -def tutorial_test(): - # Load MNIST Data - N = 500 - latent_dims = 2 - dataset = Noisy_MNIST_Dataset(mnist_type="FashionMNIST", train=True) - ids = np.arange(min(2 * N, len(dataset))) - np.random.shuffle(ids) - train_ids, val_ids = np.array_split(ids, 2) - val_dataset = Subset(dataset, val_ids) - train_dataset = Subset(dataset, train_ids) - test_dataset = Noisy_MNIST_Dataset(mnist_type="FashionMNIST", train=False) - test_ids = np.arange(min(N, len(test_dataset))) - np.random.shuffle(test_ids) - test_dataset = Subset(test_dataset, test_ids) - print("DCCA") - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784) - dcca_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) - dcca_model = DeepWrapper(dcca_model) - dcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=2) - dcca_results = np.stack( - (dcca_model.score(train_dataset), dcca_model.correlations(test_dataset)[0, 1]) + trainer.fit(dcca_noi, train_loader) + assert ( + np.testing.assert_array_less( + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() + ) + is None ) - - -def test_large_p(): - large_p = 256 - X = rng.rand(2000, large_p) - Y = rng.rand(2000, large_p) - latent_dims = 32 - device = "cpu" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=large_p) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=large_p) - dcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.MCCA, - eps=1e-3, + # Soft Decorrelation (stochastic Decorrelation Loss) + encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) + encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) + sdl = DCCA_SDL(latent_dims, N, encoders=[encoder_1, encoder_2], lam=1e-3) + optimizer = optim.SGD(sdl.parameters(), lr=1e-1) + sdl = CCALightning(sdl, optimizer=optimizer) + trainer = pl.Trainer(max_epochs=epochs, log_every_n_steps=10) + trainer.fit(sdl, train_loader) + assert ( + np.testing.assert_array_less( + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() + ) + is None ) - optimizer = optim.Adam(dcca_model.parameters(), lr=1e-4) - dcca_model = DeepWrapper(dcca_model, device=device, optimizer=optimizer) - dcca_model.fit((X, Y), epochs=100) - - -def test_DCCA_methods_cpu(): - latent_dims = 4 - cca_model = CCA(latent_dims=latent_dims).fit((X, Y)) - device = "cpu" - epochs = 100 # DCCA encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - dcca_model = DCCA( + dcca = DCCA( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.CCA, ) - optimizer = optim.SGD(dcca_model.parameters(), lr=1e-2) - dcca_model = DeepWrapper(dcca_model, device=device, optimizer=optimizer) - dcca_model.fit((X, Y), epochs=epochs) + optimizer = optim.SGD(dcca.parameters(), lr=1e-2) + dcca = CCALightning(dcca, optimizer=optimizer) + trainer = pl.Trainer( + max_epochs=epochs, log_every_n_steps=10, enable_checkpointing=False + ) + trainer.fit(dcca, train_loader) assert ( np.testing.assert_array_less( - cca_model.score((X, Y)).sum(), dcca_model.score((X, Y)).sum() + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() ) is None ) # DGCCA encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - dgcca_model = DCCA( + dgcca = DCCA( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.GCCA, ) - optimizer = optim.SGD(dgcca_model.parameters(), lr=1e-2) - dgcca_model = DeepWrapper(dgcca_model, device=device, optimizer=optimizer) - dgcca_model.fit((X, Y), epochs=epochs) + optimizer = optim.SGD(dgcca.parameters(), lr=1e-2) + dgcca = CCALightning(dgcca, optimizer=optimizer) + trainer = pl.Trainer( + max_epochs=epochs, log_every_n_steps=10, enable_checkpointing=False + ) + trainer.fit(dgcca, train_loader) assert ( np.testing.assert_array_less( - cca_model.score((X, Y)).sum(), dgcca_model.score((X, Y)).sum() + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() ) is None ) # DMCCA encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - dmcca_model = DCCA( + dmcca = DCCA( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.MCCA, ) - optimizer = optim.SGD(dmcca_model.parameters(), lr=1e-2) - dmcca_model = DeepWrapper(dmcca_model, device=device, optimizer=optimizer) - dmcca_model.fit((X, Y), epochs=epochs) + optimizer = optim.SGD(dmcca.parameters(), lr=1e-2) + dmcca = CCALightning(dmcca, optimizer=optimizer) + trainer = pl.Trainer( + max_epochs=epochs, log_every_n_steps=10, enable_checkpointing=False + ) + trainer.fit(dmcca, train_loader) assert ( np.testing.assert_array_less( - cca_model.score((X, Y)).sum(), dmcca_model.score((X, Y)).sum() + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() ) is None ) - # DCCA_NOI + # Barlow Twins encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - dcca_noi_model = DCCA_NOI( - latent_dims, X.shape[0], encoders=[encoder_1, encoder_2], rho=0 + barlowtwins = BarlowTwins( + latent_dims=latent_dims, + encoders=[encoder_1, encoder_2], + ) + optimizer = optim.SGD(barlowtwins.parameters(), lr=1e-2) + barlowtwins = CCALightning(barlowtwins, optimizer=optimizer) + trainer = pl.Trainer( + max_epochs=epochs, log_every_n_steps=10, enable_checkpointing=False ) - optimizer = optim.Adam(dcca_noi_model.parameters(), lr=1e-3) - dcca_noi_model = DeepWrapper(dcca_noi_model, device=device, optimizer=optimizer) - dcca_noi_model.fit((X, Y), epochs=epochs) + trainer.fit(barlowtwins, train_loader) assert ( np.testing.assert_array_less( - cca_model.score((X, Y)).sum(), dcca_noi_model.score((X, Y)).sum() + cca.score((X, Y)).sum(), trainer.model.score(train_loader).sum() ) is None ) -def test_DTCCA_methods_cpu(): +def test_DTCCA_methods(): latent_dims = 2 - device = "cpu" + epochs = 5 encoder_1 = architectures.Encoder(latent_dims=10, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=10, feature_size=12) - encoder_3 = architectures.Encoder(latent_dims=10, feature_size=14) - # DTCCA - dtcca_model = DTCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) - - dtcca_model = DeepWrapper(dtcca_model, device=device) - dtcca_model.fit((X, Y), epochs=20) - # DCCA - dcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.GCCA, - ) - - dcca_model = DeepWrapper(dcca_model, device=device) - dcca_model.fit((X, Y), epochs=20) - - -def test_scheduler(): - latent_dims = 2 - device = "cpu" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - # DCCA - dcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.CCA, - ) - optimizer = optim.Adam(dcca_model.parameters(), lr=1e-4) - scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, 1) - - dcca_model = DeepWrapper( - dcca_model, device=device, optimizer=optimizer, scheduler=scheduler - ) - dcca_model.fit((X, Y), epochs=20) - - -def test_DGCCA_methods_cpu(): - latent_dims = 2 - device = "cpu" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - encoder_3 = architectures.Encoder(latent_dims=latent_dims, feature_size=14) - # DTCCA - dtcca_model = DTCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) - - dtcca_model = DeepWrapper(dtcca_model, device=device) - dtcca_model.fit((X, Y, Z)) - # DGCCA - dgcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2, encoder_3], - objective=objectives.GCCA, - ) - - dgcca_model = DeepWrapper(dgcca_model, device=device) - dgcca_model.fit((X, Y, Z)) - # DMCCA - dmcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2, encoder_3], - objective=objectives.MCCA, - ) - - dmcca_model = DeepWrapper(dmcca_model, device=device) - dmcca_model.fit((X, Y, Z)) + dtcca = DTCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) + dtcca = CCALightning(dtcca) + trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) + trainer.fit(dtcca, train_loader) -def test_DCCAE_methods_cpu(): +def test_DCCAE_methods(): latent_dims = 2 - device = "cpu" encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) decoder_1 = architectures.Decoder(latent_dims=latent_dims, feature_size=10) decoder_2 = architectures.Decoder(latent_dims=latent_dims, feature_size=12) - # DCCAE - dccae_model = DCCAE( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - decoders=[decoder_1, decoder_2], - ) - - dccae_model = DeepWrapper(dccae_model, device=device) - dccae_model.fit((X, Y), epochs=20) # SplitAE - splitae_model = SplitAE( + splitae = SplitAE( latent_dims=latent_dims, encoder=encoder_1, decoders=[decoder_1, decoder_2] ) - - splitae_model = DeepWrapper(splitae_model, device=device) - splitae_model.fit((X, Y), epochs=10) - - -def test_DCCAEconv_methods_cpu(): - latent_dims = 2 - device = "cpu" - encoder_1 = architectures.CNNEncoder(latent_dims=latent_dims, feature_size=[16, 16]) - encoder_2 = architectures.CNNEncoder(latent_dims=latent_dims, feature_size=[16, 16]) - decoder_1 = architectures.CNNDecoder(latent_dims=latent_dims, feature_size=[16, 16]) - decoder_2 = architectures.CNNDecoder(latent_dims=latent_dims, feature_size=[16, 16]) + splitae = CCALightning(splitae) + trainer = pl.Trainer(max_epochs=5, enable_checkpointing=False) + trainer.fit(splitae, train_loader) # DCCAE - dccae_model = DCCAE( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - decoders=[decoder_1, decoder_2], - ) - - dccae_model = DeepWrapper(dccae_model, device=device) - dccae_model.fit((X_conv, Y_conv)) - - -def test_DVCCA_methods_cpu(): - latent_dims = 2 - device = "cpu" - encoder_1 = architectures.Encoder( - latent_dims=latent_dims, feature_size=10, variational=True - ) - encoder_2 = architectures.Encoder( - latent_dims=latent_dims, feature_size=12, variational=True - ) - decoder_1 = architectures.Decoder( - latent_dims=latent_dims, feature_size=10, norm_output=True - ) - decoder_2 = architectures.Decoder( - latent_dims=latent_dims, feature_size=12, norm_output=True - ) - # DVCCA - dvcca_model = DVCCA( + dccae = DCCAE( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2], ) + dccae = CCALightning(dccae) + trainer = pl.Trainer(max_epochs=5, enable_checkpointing=False) + trainer.fit(dccae, train_loader) - dvcca_model = DeepWrapper(dvcca_model, device=device) - dvcca_model.fit((X, Y)) - -def test_DVCCA_p_methods_cpu(): +def test_DVCCA_p_methods(): latent_dims = 2 - device = "cpu" encoder_1 = architectures.Encoder( latent_dims=latent_dims, feature_size=10, variational=True ) @@ -319,105 +209,20 @@ def test_DVCCA_p_methods_cpu(): latent_dims=2 * latent_dims, feature_size=12, norm_output=True ) # DVCCA - dvcca_model = DVCCA( + dvcca = DVCCA( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2], private_encoders=[private_encoder_1, private_encoder_2], ) - dvcca_model = DeepWrapper(dvcca_model, device=device) - dvcca_model.fit((X, Y)) + dvcca = CCALightning(dvcca) + trainer = pl.Trainer(max_epochs=5, enable_checkpointing=False) + trainer.fit(dvcca, train_loader) -def test_DCCA_methods_gpu(): +def test_DVCCA_methods(): latent_dims = 2 - device = "cuda" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - # DCCA - dcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.CCA, - ) - - dcca_model = DeepWrapper(dcca_model, device=device) - dcca_model.fit((X, Y)) - # DGCCA - dgcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.GCCA, - ) - - dgcca_model = DeepWrapper(dgcca_model, device=device) - dgcca_model.fit((X, Y)) - # DMCCA - dmcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - objective=objectives.MCCA, - ) - - dmcca_model = DeepWrapper(dmcca_model, device=device) - dmcca_model.fit((X, Y)) - # DCCA_NOI - dcca_noi_model = DCCA_NOI( - latent_dims, X.shape[0], encoders=[encoder_1, encoder_2], rho=0 - ) - - dcca_noi_model = DeepWrapper(dcca_noi_model, device=device) - dcca_noi_model.fit((X, Y)) - - -def test_DGCCA_methods_gpu(): - latent_dims = 2 - device = "cuda" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - encoder_3 = architectures.Encoder(latent_dims=latent_dims, feature_size=14) - # DGCCA - dgcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2, encoder_3], - objective=objectives.GCCA, - ) - - dgcca_model = DeepWrapper(dgcca_model, device=device) - dgcca_model.fit((X, Y, Z)) - # DMCCA - dmcca_model = DCCA( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2, encoder_3], - objective=objectives.MCCA, - ) - - dmcca_model = DeepWrapper(dmcca_model, device=device) - dmcca_model.fit((X, Y, Z)) - - -def test_DCCAE_methods_gpu(): - latent_dims = 2 - device = "cuda" - encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=10) - encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=12) - decoder_1 = architectures.Decoder(latent_dims=latent_dims, feature_size=10) - decoder_2 = architectures.Decoder(latent_dims=latent_dims, feature_size=12) - # DCCAE - dccae_model = DCCAE( - latent_dims=latent_dims, - encoders=[encoder_1, encoder_2], - decoders=[decoder_1, decoder_2], - ) - - dccae_model = DeepWrapper(dccae_model, device=device) - dccae_model.fit((X, Y)) - - -def test_DVCCA_methods_gpu(): - latent_dims = 2 - device = "cuda" encoder_1 = architectures.Encoder( latent_dims=latent_dims, feature_size=10, variational=True ) @@ -430,27 +235,29 @@ def test_DVCCA_methods_gpu(): decoder_2 = architectures.Decoder( latent_dims=latent_dims, feature_size=12, norm_output=True ) - # DVCCA - dvcca_model = DVCCA( + dvcca = DVCCA( latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2], ) - dvcca_model = DeepWrapper(dvcca_model, device=device) - dvcca_model.fit((X, Y)) + dvcca = CCALightning(dvcca) + trainer = pl.Trainer(max_epochs=5, enable_checkpointing=False) + trainer.fit(dvcca, train_loader) def test_linear(): encoder_1 = architectures.LinearEncoder(latent_dims=1, feature_size=10) encoder_2 = architectures.LinearEncoder(latent_dims=1, feature_size=12) - dcca_model = DCCA(latent_dims=1, encoders=[encoder_1, encoder_2]) - dcca_model = DeepWrapper(dcca_model).fit((X, Y), epochs=40) + dcca = DCCA(latent_dims=1, encoders=[encoder_1, encoder_2]) + dcca = CCALightning(dcca, learning_rate=1e-1) + trainer = pl.Trainer(max_epochs=50, enable_checkpointing=False) + trainer.fit(dcca, loader) cca = CCA().fit((X, Y)) # check linear encoder with SGD matches vanilla linear CCA assert ( np.testing.assert_array_almost_equal( - cca.score((X, Y)), dcca_model.score((X, Y)), decimal=2 + cca.score((X, Y)), trainer.model.score(loader), decimal=2 ) is None ) diff --git a/cca_zoo/test/test_models.py b/cca_zoo/test/test_models.py index a95483e3..22f00b08 100644 --- a/cca_zoo/test/test_models.py +++ b/cca_zoo/test/test_models.py @@ -17,20 +17,23 @@ MCCA, GCCA, TCCA, - SCCA_ADMM, SpanCCA, SWCCA, PLS_ALS, KGCCA, + NCCA, + ParkhomenkoCCA, + SCCA_ADMM, ) from cca_zoo.utils.plotting import cv_plot +n = 50 rng = check_random_state(0) -X = rng.rand(500, 20) -Y = rng.rand(500, 21) -Z = rng.rand(500, 22) -X_sp = sp.random(500, 20, density=0.5, random_state=rng) -Y_sp = sp.random(500, 21, density=0.5, random_state=rng) +X = rng.rand(n, 4) +Y = rng.rand(n, 5) +Z = rng.rand(n, 6) +X_sp = sp.random(n, 4, density=0.5, random_state=rng) +Y_sp = sp.random(n, 5, density=0.5, random_state=rng) def test_unregularized_methods(): @@ -56,60 +59,60 @@ def test_unregularized_methods(): corr_kgcca = kgcca.score((X, Y)) corr_tcca = tcca.score((X, Y)) # Check the correlations from each unregularized method are the same - assert np.testing.assert_array_almost_equal(corr_cca, corr_iter, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_mcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_gcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_kcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_tcca, decimal=2) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_iter, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_mcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_gcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_kcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_tcca, decimal=1) is None assert ( - np.testing.assert_array_almost_equal(corr_kgcca, corr_gcca, decimal=2) is None + np.testing.assert_array_almost_equal(corr_kgcca, corr_gcca, decimal=1) is None ) # Check standardized models have standard outputs assert ( np.testing.assert_allclose( - np.linalg.norm(iter.transform((X, Y))[0], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(iter.transform((X, Y))[0], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(cca.transform((X, Y))[0], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(cca.transform((X, Y))[0], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(mcca.transform((X, Y))[0], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(mcca.transform((X, Y))[0], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(kcca.transform((X, Y))[0], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(kcca.transform((X, Y))[0], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(iter.transform((X, Y))[1], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(iter.transform((X, Y))[1], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(cca.transform((X, Y))[1], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(cca.transform((X, Y))[1], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(mcca.transform((X, Y))[1], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(mcca.transform((X, Y))[1], axis=0) ** 2, n, rtol=0.2 ) is None ) assert ( np.testing.assert_allclose( - np.linalg.norm(kcca.transform((X, Y))[1], axis=0) ** 2, 500, rtol=0.1 + np.linalg.norm(kcca.transform((X, Y))[1], axis=0) ** 2, n, rtol=0.2 ) is None ) @@ -135,10 +138,9 @@ def test_sparse_input(): corr_mcca = mcca.score((X, Y)) corr_kcca = kcca.score((X, Y)) # Check the correlations from each unregularized method are the same - assert np.testing.assert_array_almost_equal(corr_cca, corr_iter, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_iter, corr_mcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_iter, corr_gcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_iter, corr_kcca, decimal=2) is None + assert np.testing.assert_array_almost_equal(corr_iter, corr_mcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_iter, corr_gcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_iter, corr_kcca, decimal=1) is None def test_unregularized_multi(): @@ -156,9 +158,9 @@ def test_unregularized_multi(): corr_kcca = kcca.score((X, Y, Z)) # Check the correlations from each unregularized method are the same assert np.testing.assert_array_almost_equal(corr_cca, corr_iter, decimal=1) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_mcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_gcca, decimal=2) is None - assert np.testing.assert_array_almost_equal(corr_cca, corr_kcca, decimal=2) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_mcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_gcca, decimal=1) is None + assert np.testing.assert_array_almost_equal(corr_cca, corr_kcca, decimal=1) is None def test_regularized_methods(): @@ -178,7 +180,6 @@ def test_regularized_methods(): corr_pls = pls.score((X, Y)) corr_rcca = rcca.score((X, Y)) # Check the correlations from each unregularized method are the same - # assert np.testing.assert_array_almost_equal(corr_pls, corr_gcca, decimal=2)) assert np.testing.assert_array_almost_equal(corr_pls, corr_mcca, decimal=1) is None assert ( np.testing.assert_array_almost_equal(corr_pls, corr_kernel, decimal=1) is None @@ -188,49 +189,63 @@ def test_regularized_methods(): def test_non_negative_methods(): latent_dims = 2 - nnelasticca = ElasticCCA( + nnelastic = ElasticCCA( latent_dims=latent_dims, tol=1e-9, positive=True, l1_ratio=[0.5, 0.5], c=[1e-4, 1e-5], ).fit([X, Y]) - als = CCA_ALS(latent_dims=latent_dims, tol=1e-9).fit([X, Y]) nnals = CCA_ALS(latent_dims=latent_dims, tol=1e-9, positive=True).fit([X, Y]) nnscca = SCCA(latent_dims=latent_dims, tol=1e-9, positive=True, c=[1e-4, 1e-5]).fit( (X, Y) ) + assert np.testing.assert_array_less(-1e-9, nnelastic.weights[0]) is None + assert np.testing.assert_array_less(-1e-9, nnelastic.weights[1]) is None + assert np.testing.assert_array_less(-1e-9, nnals.weights[0]) is None + assert np.testing.assert_array_less(-1e-9, nnals.weights[1]) is None + assert np.testing.assert_array_less(-1e-9, nnscca.weights[0]) is None + assert np.testing.assert_array_less(-1e-9, nnscca.weights[1]) is None def test_sparse_methods(): - # Test sparsity inducing methods. At the moment just checks running. - latent_dims = 2 c1 = [1, 3] c2 = [1, 3] - param_grid = {"c": [c1, c2]} pmd_cv = GridSearchCV(PMD(random_state=rng), param_grid=param_grid).fit([X, Y]) cv_plot(pmd_cv.cv_results_) - c1 = [1e-4, 1e-5] - c2 = [1e-4, 1e-5] + c1 = [5e-1] + c2 = [1e-1] param_grid = {"c": [c1, c2]} scca_cv = GridSearchCV(SCCA(random_state=rng), param_grid=param_grid).fit([X, Y]) - - c1 = loguniform(1e-4, 1e0) - c2 = loguniform(1e-4, 1e0) + c1 = [1e-1] + c2 = [1e-1] + param_grid = {"c": [c1, c2]} + parkhomenko_cv = GridSearchCV(ParkhomenkoCCA(random_state=rng), param_grid=param_grid).fit([X, Y]) + c1 = [2e-2] + c2 = [1e-2] param_grid = {"c": [c1, c2]} + admm_cv = GridSearchCV(SCCA_ADMM(random_state=rng), param_grid=param_grid).fit([X, Y]) + c1 = loguniform(1e-1, 2e-1) + c2 = loguniform(1e-1, 2e-1) + param_grid = {"c": [c1, c2], "l1_ratio": [[0.9], [0.9]]} elastic_cv = RandomizedSearchCV( ElasticCCA(random_state=rng), param_distributions=param_grid, n_iter=4 ).fit([X, Y]) - corr_pmd = pmd_cv.score((X, Y)) - corr_scca = scca_cv.score((X, Y)) - corr_elastic = elastic_cv.score((X, Y)) - scca_admm = SCCA_ADMM(c=[1e-4, 1e-4]).fit([X, Y]) - scca = SCCA(c=[1e-4, 1e-4]).fit([X, Y]) + assert (pmd_cv.best_estimator_.weights[0] == 0).sum() > 0 + assert (pmd_cv.best_estimator_.weights[1] == 0).sum() > 0 + assert (scca_cv.best_estimator_.weights[0] == 0).sum() > 0 + assert (scca_cv.best_estimator_.weights[1] == 0).sum() > 0 + assert (admm_cv.best_estimator_.weights[0] == 0).sum() > 0 + assert (admm_cv.best_estimator_.weights[1] == 0).sum() > 0 + assert (parkhomenko_cv.best_estimator_.weights[0] == 0).sum() > 0 + assert (parkhomenko_cv.best_estimator_.weights[1] == 0).sum() > 0 + assert (elastic_cv.best_estimator_.weights[0] == 0).sum() > 0 + assert (elastic_cv.best_estimator_.weights[1] == 0).sum() > 0 def test_weighted_GCCA_methods(): - # Test the 'fancy' additions to GCCA i.e. the view weighting and observation weighting. + # TODO we have view weighted GCCA and missing observation GCCA latent_dims = 2 c = 0 unweighted_gcca = GCCA(latent_dims=latent_dims, c=[c, c]).fit([X, Y]) @@ -252,12 +267,20 @@ def test_weighted_GCCA_methods(): def test_TCCA(): - # Tests tensor CCA methods - latent_dims = 2 + latent_dims = 1 tcca = TCCA(latent_dims=latent_dims, c=[0.2, 0.2, 0.2]).fit([X, X, Y]) ktcca = KTCCA(latent_dims=latent_dims, c=[0.2, 0.2]).fit([X, Y]) corr_tcca = tcca.score((X, X, Y)) corr_ktcca = ktcca.score((X, Y)) + assert corr_tcca > 0.4 + assert corr_ktcca > 0.4 + + +def test_NCCA(): + latent_dims = 1 + ncca = NCCA(latent_dims=latent_dims).fit((X, Y)) + corr_ncca = ncca.score((X, Y)) + assert corr_ncca > 0.9 def test_l0(): @@ -276,7 +299,7 @@ def test_VCCA(): from cca_zoo.data import generate_simple_data # Tests tensor CCA methods - (X, Y), (_) = generate_simple_data(100, [10, 10], random_state=rng, eps=0.1) + (X, Y), (_) = generate_simple_data(20, [9, 9], random_state=rng, eps=0.1) latent_dims = 1 cca = CCA(latent_dims=latent_dims).fit([X, Y]) vcca = VariationalCCA( diff --git a/cca_zoo/utils/check_values.py b/cca_zoo/utils/check_values.py index 00839f28..451145cb 100644 --- a/cca_zoo/utils/check_values.py +++ b/cca_zoo/utils/check_values.py @@ -54,7 +54,7 @@ def _check_parameter_number(parameter_name: str, parameter, n_views: int): def _check_converged_weights(weights, view_index): """check the converged weights are not zero.""" if np.linalg.norm(weights) <= 0: - raise ValueError( + warnings.warn( f"All result weights are zero in view {view_index}. " "Try less regularisation or another initialisation" ) diff --git a/cca_zoo/utils/plotting.py b/cca_zoo/utils/plotting.py index 69abf2a9..2857fb7c 100644 --- a/cca_zoo/utils/plotting.py +++ b/cca_zoo/utils/plotting.py @@ -9,7 +9,7 @@ from matplotlib import cm -def post_process_cv_results(df): +def _post_process_cv_results(df): cols = [col for col in df.columns if "param_" in col] for col in cols: df = df.join( @@ -26,7 +26,7 @@ def cv_plot(cv_results_): """ if isinstance(cv_results_, dict): cv_results_ = pd.DataFrame(cv_results_) - cv_results_ = post_process_cv_results(cv_results_) + cv_results_ = _post_process_cv_results(cv_results_) param_cols = [col for col in cv_results_.columns if "param_" in col] n_params = len(param_cols) n_uniques = [cv_results_[col].nunique() for col in param_cols] diff --git a/docs/requirements.txt b/docs/requirements.txt index 894675cb..1a019f5d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,10 +1,13 @@ -jax -numpyro -arviz +sphinx==1.8.5 +sphinx-autodoc-typehints +sphinx-gallery +pandas numpy -scikit-learn +matplotlib +numpy +scikit-learn>=0.23 mvlearn -scipy>=1.5 +scipy>=1.7 matplotlib seaborn pandas @@ -13,4 +16,7 @@ joblib torch>=1.9.0 torchvision Pillow -sphinx-autodoc-typehints +jax~=0.2.20 +numpyro +arviz +pytorch-lightning \ No newline at end of file diff --git a/docs/source/api/deepmodels.rst b/docs/source/api/deepmodels.rst index a92d7a23..600e207a 100644 --- a/docs/source/api/deepmodels.rst +++ b/docs/source/api/deepmodels.rst @@ -43,10 +43,10 @@ Split Autoencoders :members: :undoc-members: -Deep Wrapper for Training --------------------------------------- +CCALightning Module for training with pytorch-lightning +--------------------------------------------------------- -.. automodule:: cca_zoo.deepmodels.deepwrapper +.. automodule:: cca_zoo.deepmodels.CCALightning :members: :inherited-members: :exclude-members: get_params, set_params diff --git a/docs/source/api/iterative.rst b/docs/source/api/iterative.rst new file mode 100644 index 00000000..e557d68e --- /dev/null +++ b/docs/source/api/iterative.rst @@ -0,0 +1,46 @@ +Normal CCA and PLS by alternating least squares +-------------------------------------------------- +Quicker and more memory efficient for very large data + +.. autoclass:: cca_zoo.models.CCA_ALS + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.PLS_ALS + :inherited-members: + :exclude-members: get_params, set_params + + +Sparsity Inducing Models +-------------------------------------------------- + +.. autoclass:: cca_zoo.models.PMD + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.SCCA + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.ElasticCCA + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.SpanCCA + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.ParkhomenkoCCA + :inherited-members: + :exclude-members: get_params, set_params + +.. autoclass:: cca_zoo.models.SCCA_ADMM + :inherited-members: + :exclude-members: get_params, set_params + +Miscellaneous +-------------------------------------------------- + +.. autoclass:: cca_zoo.models.SWCCA + :inherited-members: + :exclude-members: get_params, set_params \ No newline at end of file diff --git a/docs/source/api/models.rst b/docs/source/api/models.rst index 401c9ec0..2cfeaddc 100644 --- a/docs/source/api/models.rst +++ b/docs/source/api/models.rst @@ -1,54 +1,84 @@ Models ======================= -Base Class --------------------------------- -.. automodule:: cca_zoo.models.cca_base - :members: - :private-members: _CCA_Base +Regularized Canonical Correlation Analysis and Partial Least Squares +------------------------------------------------------------------------ + +Canonical Correlation Analysis +**************************************************** +.. autoclass:: cca_zoo.models.rcca.CCA + :inherited-members: :exclude-members: get_params, set_params -rCCA ---------------------------- +Partial Least Squares +**************************************************** +.. autoclass:: cca_zoo.models.rcca.PLS + :inherited-members: + :exclude-members: get_params, set_params -.. automodule:: cca_zoo.models.rcca +Ridge Regularized Canonical Correlation Analysis +**************************************************** +.. autoclass:: cca_zoo.models.rcca.rCCA :inherited-members: :exclude-members: get_params, set_params GCCA and KGCCA --------------------------- -.. automodule:: cca_zoo.models.gcca +Generalized (MAXVAR) Canonical Correlation Analysis +**************************************************** +.. autoclass:: cca_zoo.models.gcca.GCCA + :inherited-members: + :exclude-members: get_params, set_params + +Kernel Generalized (MAXVAR) Canonical Correlation Analysis +************************************************************ +.. autoclass:: cca_zoo.models.gcca.KGCCA :inherited-members: :exclude-members: get_params, set_params MCCA and KCCA --------------------------- -.. automodule:: cca_zoo.models.mcca +Multiset (SUMCOR) Canonical Correlation Analysis +************************************************** +.. autoclass:: cca_zoo.models.mcca.MCCA :inherited-members: :exclude-members: get_params, set_params -TCCA and KTCCA ---------------------------- - -.. automodule:: cca_zoo.models.tcca +Kernel Multiset (SUMCOR) Canonical Correlation Analysis +******************************************************** +.. autoclass:: cca_zoo.models.mcca.KCCA :inherited-members: :exclude-members: get_params, set_params -Iterative Models --------------------------------- +Tensor Canonical Correlation Analysis +---------------------------------------- -.. automodule:: cca_zoo.models.iterative +Tensor Canonical Correlation Analysis +************************************** +.. autoclass:: cca_zoo.models.tcca.TCCA :inherited-members: :exclude-members: get_params, set_params -Inner Loops --------------------------------- - -.. automodule:: cca_zoo.models.innerloop +Kernel Tensor Canonical Correlation Analysis +********************************************** +.. autoclass:: cca_zoo.models.tcca.KTCCA :inherited-members: :exclude-members: get_params, set_params +More Complex Regularisation using Iterative Models +----------------------------------------------------- + +.. toctree:: + :maxdepth: 4 + iterative.rst +Base Class +-------------------------------- + +.. automodule:: cca_zoo.models.cca_base + :members: + :private-members: _CCA_Base + :exclude-members: get_params, set_params diff --git a/docs/source/conf.py b/docs/source/conf.py index 7f0db98e..5321577f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,24 +12,48 @@ # import os import sys +import warnings -sys.path.insert(0, os.path.abspath('../..')) - +sys.path.insert(0, os.path.abspath("../..")) +warnings.filterwarnings( + "ignore", + category=UserWarning, + message="Matplotlib is currently using agg, which is a" + " non-GUI backend, so cannot show the figure.", +) # -- Project information ----------------------------------------------------- -project = 'cca-zoo' -copyright = '2021, James Chapman' -author = 'James Chapman' +project = "cca-zoo" +copyright = "2021, James Chapman" +author = "James Chapman" # -- General configuration --------------------------------------------------- extensions = [ - 'sphinx.ext.autodoc', + "sphinx.ext.autodoc", "sphinx.ext.autosummary", - 'sphinx_autodoc_typehints', - 'sphinx.ext.viewcode' + "sphinx_autodoc_typehints", + "sphinx.ext.viewcode", + "sphinx_gallery.gen_gallery", + "sphinx.ext.napoleon", + "sphinx.ext.githubpages", + "sphinx.ext.mathjax", ] +sphinx_gallery_conf = { + "doc_module": "cca-zoo", + "examples_dirs": "../../examples", # path to your example scripts + "gallery_dirs": "auto_examples", # path to where to save gallery generated output +} + +# -- sphinx.ext.intersphinx +intersphinx_mapping = { + "numpy": ("https://docs.scipy.org/doc/numpy", None), + "python": ("https://docs.python.org/3", None), + "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), + "sklearn": ("http://scikit-learn.org/dev", None), +} + # -- sphinx.ext.autosummary autosummary_generate = True @@ -37,10 +61,10 @@ autoclass_content = "both" autodoc_default_flags = ["members", "show-inheritance"] autodoc_member_order = "bysource" # default is alphabetical -special_members = '--init__' +special_members = "--init__" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -57,4 +81,6 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] + +master_doc = "index" diff --git a/docs/source/index.rst b/docs/source/index.rst index 69926a13..84857c44 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,11 +13,11 @@ Documentation documentation/install documentation/getting_started documentation/user_guide - documentation/tutorials + auto_examples/index .. toctree:: - :maxdepth: 1 + :maxdepth: 4 :caption: Reference api/data diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 00000000..864f95df --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,4 @@ +Tutorials and Examples Gallery +================================ + +Below is a gallery of examples \ No newline at end of file diff --git a/examples/plot_dcca.py b/examples/plot_dcca.py new file mode 100644 index 00000000..16da6ac0 --- /dev/null +++ b/examples/plot_dcca.py @@ -0,0 +1,69 @@ +""" +Deep CCA +=========================== + +This example demonstrates how to easily train Deep CCA models and variants +""" + +import numpy as np +import pytorch_lightning as pl +from torch.utils.data import Subset + +# %% +from cca_zoo.data import Split_MNIST_Dataset +from cca_zoo.deepmodels import ( + DCCA, + CCALightning, + get_dataloaders, + architectures, + DCCA_NOI, + DCCA_SDL, + BarlowTwins, +) + +n_train = 500 +n_val = 100 +train_dataset = Split_MNIST_Dataset(mnist_type="MNIST", train=True) +val_dataset = Subset(train_dataset, np.arange(n_train, n_train + n_val)) +train_dataset = Subset(train_dataset, np.arange(n_train)) +train_loader, val_loader = get_dataloaders(train_dataset, val_dataset) + +# The number of latent dimensions across models +latent_dims = 2 +# number of epochs for deep models +epochs = 10 + +encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) +encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) + +# %% +# Deep CCA +dcca = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) + +# %% +# Deep CCA by Non-Linear Orthogonal Iterations +dcca_noi = DCCA_NOI( + latent_dims=latent_dims, N=len(train_dataset), encoders=[encoder_1, encoder_2] +) +dcca_noi = CCALightning(dcca_noi) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca_noi, train_loader, val_loader) + +# %% +# Deep CCA by Stochastic Decorrelation Loss +dcca_sdl = DCCA_SDL( + latent_dims=latent_dims, N=len(train_dataset), encoders=[encoder_1, encoder_2] +) +dcca_sdl = CCALightning(dcca_sdl) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca_sdl, train_loader, val_loader) + +# %% +# Deep CCA by Barlow Twins +barlowtwins = BarlowTwins(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) +barlowtwins = CCALightning(barlowtwins) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) diff --git a/examples/plot_dcca_custom.py b/examples/plot_dcca_custom.py new file mode 100644 index 00000000..694943b7 --- /dev/null +++ b/examples/plot_dcca_custom.py @@ -0,0 +1,39 @@ +""" +Deep CCA with more customisation +================================== + +Showing some examples of more advanced functionality with DCCA and pytorch-lightning +""" + +import numpy as np +# %% +import pytorch_lightning as pl +from torch import optim +from torch.utils.data import Subset + +from cca_zoo.data import Split_MNIST_Dataset +from cca_zoo.deepmodels import DCCA, CCALightning, get_dataloaders, architectures + +n_train = 500 +n_val = 100 +train_dataset = Split_MNIST_Dataset(mnist_type="MNIST", train=True) +val_dataset = Subset(train_dataset, np.arange(n_train, n_train + n_val)) +train_dataset = Subset(train_dataset, np.arange(n_train)) +train_loader, val_loader = get_dataloaders(train_dataset, val_dataset) + +# The number of latent dimensions across models +latent_dims = 2 +# number of epochs for deep models +epochs = 10 + +# TODO add in custom architecture and schedulers and stuff to show it off +encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) +encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) + +# Deep CCA +dcca = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) +dcca = CCALightning(dcca) +optimizer = optim.Adam(dcca.parameters(), lr=1e-3) +scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, 1) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) diff --git a/examples/plot_dcca_multi.py b/examples/plot_dcca_multi.py new file mode 100644 index 00000000..b0e77a76 --- /dev/null +++ b/examples/plot_dcca_multi.py @@ -0,0 +1,61 @@ +""" +Deep CCA for more than 2 views +================================= + +This example demonstrates how to easily train Deep CCA models and variants +""" + +import numpy as np +import pytorch_lightning as pl +from torch.utils.data import Subset + +# %% +from cca_zoo.data import Split_MNIST_Dataset +from cca_zoo.deepmodels import ( + DCCA, + CCALightning, + get_dataloaders, + architectures, + objectives, + DTCCA, +) + +n_train = 500 +n_val = 100 +train_dataset = Split_MNIST_Dataset(mnist_type="MNIST", train=True) +val_dataset = Subset(train_dataset, np.arange(n_train, n_train + n_val)) +train_dataset = Subset(train_dataset, np.arange(n_train)) +train_loader, val_loader = get_dataloaders(train_dataset, val_dataset) + +# The number of latent dimensions across models +latent_dims = 2 +# number of epochs for deep models +epochs = 10 + +encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) +encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) + +# %% +# Deep MCCA +dcca = DCCA( + latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.MCCA +) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) + +# %% +# Deep GCCA +dcca = DCCA( + latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.GCCA +) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) + +# %% +# Deep TCCA +dcca = DTCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2]) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) diff --git a/examples/plot_dvcca.py b/examples/plot_dvcca.py new file mode 100644 index 00000000..f039c2e2 --- /dev/null +++ b/examples/plot_dvcca.py @@ -0,0 +1,87 @@ +""" +Deep Variational CCA and Deep Canonically Correlated Autoencoders +==================================================================== + +This example demonstrates multiview models which can reconstruct their inputs +""" + +import numpy as np +import pytorch_lightning as pl +from torch.utils.data import Subset + +# %% +from cca_zoo.data import Split_MNIST_Dataset +from cca_zoo.deepmodels import ( + CCALightning, + get_dataloaders, + architectures, + DCCAE, + DVCCA, +) + +n_train = 500 +n_val = 100 +train_dataset = Split_MNIST_Dataset(mnist_type="MNIST", train=True) +val_dataset = Subset(train_dataset, np.arange(n_train, n_train + n_val)) +train_dataset = Subset(train_dataset, np.arange(n_train)) +train_loader, val_loader = get_dataloaders(train_dataset, val_dataset) + +# The number of latent dimensions across models +latent_dims = 2 +# number of epochs for deep models +epochs = 10 + +encoder_1 = architectures.Encoder( + latent_dims=latent_dims, feature_size=392, variational=True +) +encoder_2 = architectures.Encoder( + latent_dims=latent_dims, feature_size=392, variational=True +) +decoder_1 = architectures.Decoder(latent_dims=latent_dims, feature_size=392) +decoder_2 = architectures.Decoder(latent_dims=latent_dims, feature_size=392) + +# %% +# Deep VCCA +dcca = DVCCA( + latent_dims=latent_dims, + encoders=[encoder_1, encoder_2], + decoders=[decoder_1, decoder_2], +) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) + +# %% +# Deep VCCA (private) +# We need to add additional private encoders and change (double) the dimensionality of the decoders. +private_encoder_1 = architectures.Encoder( + latent_dims=latent_dims, feature_size=392, variational=True +) +private_encoder_2 = architectures.Encoder( + latent_dims=latent_dims, feature_size=392, variational=True +) +private_decoder_1 = architectures.Decoder(latent_dims=2 * latent_dims, feature_size=392) +private_decoder_2 = architectures.Decoder(latent_dims=2 * latent_dims, feature_size=392) + +dcca = DVCCA( + latent_dims=latent_dims, + encoders=[encoder_1, encoder_2], + decoders=[private_decoder_1, private_decoder_2], + private_encoders=[private_encoder_1, private_encoder_2], +) +dcca = CCALightning(dcca) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dcca, train_loader, val_loader) + +# %% +# DCCAE +encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) +encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=392) +dccae_model = DCCAE( + latent_dims=latent_dims, + encoders=[encoder_1, encoder_2], + decoders=[decoder_1, decoder_2], +) +dccae_model = CCALightning(dccae_model) +trainer = pl.Trainer(max_epochs=epochs, enable_checkpointing=False) +trainer.fit(dccae_model, train_loader, val_loader) diff --git a/examples/plot_hyperparameter_selection.py b/examples/plot_hyperparameter_selection.py new file mode 100644 index 00000000..0d687e88 --- /dev/null +++ b/examples/plot_hyperparameter_selection.py @@ -0,0 +1,6 @@ +""" +Hyperparameter Selection +=========================== + +This script will show how to perform hyperparameter selection +""" diff --git a/examples/plot_kernel_cca.py b/examples/plot_kernel_cca.py new file mode 100644 index 00000000..581c4476 --- /dev/null +++ b/examples/plot_kernel_cca.py @@ -0,0 +1,100 @@ +""" +Kernel CCA and Nonparametric CCA +=================================== + +This script demonstrates how to use kernel and nonparametric methods +""" + +# %% +import numpy as np + +from cca_zoo.data import generate_covariance_data +from cca_zoo.model_selection import GridSearchCV +from cca_zoo.models import KCCA + +# %% +np.random.seed(42) +n = 200 +p = 100 +q = 100 +latent_dims = 1 +cv = 3 + +(X, Y), (tx, ty) = generate_covariance_data( + n, view_features=[p, q], latent_dims=latent_dims, correlation=[0.9] +) + + +# %% +# Custom Kernel +def my_kernel(X, Y, param=0): + """ + We create a custom kernel: + + """ + + return np.random.normal(0, param) + + +kernel_custom = KCCA( + latent_dims=latent_dims, + kernel=[my_kernel, my_kernel], + kernel_params=[{"param": 1}, {"param": 1}], +).fit((X, Y)) + +# %% +# Linear +c1 = [0.9, 0.99] +c2 = [0.9, 0.99] +param_grid = {"kernel": ["linear"], "c": [c1, c2]} +kernel_reg = ( + GridSearchCV( + KCCA(latent_dims=latent_dims), param_grid=param_grid, cv=cv, verbose=True + ) + .fit([X, Y]) + .best_estimator_ +) + +# %% +# Polynomial +degree1 = [2, 3] +degree2 = [2, 3] +param_grid = {"kernel": ["poly"], "degree": [degree1, degree2], "c": [c1, c2]} +kernel_poly = ( + GridSearchCV( + KCCA(latent_dims=latent_dims), param_grid=param_grid, cv=cv, verbose=True + ) + .fit([X, Y]) + .best_estimator_ +) + +# %% +# kernel cca (gaussian/rbf) +gamma1 = [1e-1, 1e-2] +gamma2 = [1e-1, 1e-2] +param_grid = {"kernel": ["rbf"], "gamma": [gamma1, gamma2], "c": [c1, c2]} +kernel_poly = ( + GridSearchCV( + KCCA(latent_dims=latent_dims), param_grid=param_grid, cv=cv, verbose=True + ) + .fit([X, Y]) + .best_estimator_ +) + + +# %% +# Custom Kernel +def my_kernel(X, Y, param=0): + """ + We create a custom kernel: + + """ + M = np.random.rand(X.shape[0], X.shape[0]) + param + return X @ M @ M.T @ Y.T + + +kernel_custom = KCCA( + latent_dims=latent_dims, + kernel=[my_kernel, my_kernel], + kernel_params=[{"param": 1}, {"param": 1}], +).fit((X, Y)) diff --git a/examples/plot_many_views.py b/examples/plot_many_views.py new file mode 100644 index 00000000..098e52c1 --- /dev/null +++ b/examples/plot_many_views.py @@ -0,0 +1,6 @@ +""" +More than 2 views +=========================== + +This will compare MCCA, GCCA, TCCA for linear models with more than 2 views +""" diff --git a/examples/plot_ridge_reg.py b/examples/plot_ridge_reg.py new file mode 100644 index 00000000..ad63399b --- /dev/null +++ b/examples/plot_ridge_reg.py @@ -0,0 +1,6 @@ +""" +Ridge Regularised CCA: From CCA to PLS +=========================== + +This script will show how CCA and PLS form opposite ends of a ridge regularisation spectrum +""" diff --git a/examples/plot_sparse_cca.py b/examples/plot_sparse_cca.py new file mode 100644 index 00000000..d73208a9 --- /dev/null +++ b/examples/plot_sparse_cca.py @@ -0,0 +1,137 @@ +""" +Sparse CCA Methods +=========================== + +This script shows how regularised methods can be used to extract sparse solutions to the CCA problem +""" + +# %% +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +from cca_zoo.data import generate_covariance_data +from cca_zoo.model_selection import GridSearchCV +from cca_zoo.models import PMD, SCCA, ElasticCCA, CCA, PLS, SCCA_ADMM, SpanCCA + +# %% +np.random.seed(42) +n = 200 +p = 100 +q = 100 +view_1_sparsity = 0.1 +view_2_sparsity = 0.1 +latent_dims = 1 + +(X, Y), (tx, ty) = generate_covariance_data( + n, + view_features=[p, q], + latent_dims=latent_dims, + view_sparsity=[view_1_sparsity, view_2_sparsity], + correlation=[0.9], +) +tx /= np.sqrt(n) +ty /= np.sqrt(n) + + +# %% +def plot_true_weights_coloured(ax, weights, true_weights, title=""): + ind = np.arange(len(true_weights)) + mask = np.squeeze(true_weights == 0) + ax.scatter(ind[~mask], weights[~mask], c="b") + ax.scatter(ind[mask], weights[mask], c="r") + ax.set_title(title) + + +def plot_model_weights(wx, wy, tx, ty): + fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) + plot_true_weights_coloured(axs[0, 0], tx, tx, title="true x weights") + plot_true_weights_coloured(axs[0, 1], ty, ty, title="true y weights") + plot_true_weights_coloured(axs[1, 0], wx, tx, title="model x weights") + plot_true_weights_coloured(axs[1, 1], wy, ty, title="model y weights") + plt.tight_layout() + plt.show() + + +# %% +cca = CCA().fit([X, Y]) +plot_model_weights(cca.weights[0], cca.weights[1], tx, ty) + +# %% +pls = PLS().fit([X, Y]) +plot_model_weights(pls.weights[0], pls.weights[1], tx, ty) + +# %% +pmd = PMD(c=[2, 2]).fit([X, Y]) +plot_model_weights(pmd.weights[0], pmd.weights[1], tx, ty) + +# %% +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(pmd.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") + +# %% +c1 = [1, 3, 7, 9] +c2 = [1, 3, 7, 9] +param_grid = {"c": [c1, c2]} +pmd = GridSearchCV(PMD(), param_grid=param_grid, cv=3, verbose=True).fit([X, Y]) + +# %% +pd.DataFrame(pmd.cv_results_) + +# %% +scca = SCCA(c=[1e-3, 1e-3]).fit([X, Y]) +plot_model_weights(scca.weights[0], scca.weights[1], tx, ty) + +# Convergence +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(scca.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") + +# %% +scca_pos = SCCA(c=[1e-3, 1e-3], positive=[True, True]).fit([X, Y]) +plot_model_weights(scca_pos.weights[0], scca_pos.weights[1], tx, ty) + +# Convergence +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(scca_pos.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") + +# %% +elasticcca = ElasticCCA(c=[10000, 10000], l1_ratio=[0.000001, 0.000001]).fit([X, Y]) +plot_model_weights(elasticcca.weights[0], elasticcca.weights[1], tx, ty) + +# Convergence +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(elasticcca.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") + +# %% +scca_admm = SCCA_ADMM(c=[1e-3, 1e-3]).fit([X, Y]) +plot_model_weights(scca_admm.weights[0], scca_admm.weights[1], tx, ty) + +# Convergence +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(scca_admm.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") + +# %% +spancca = SpanCCA(c=[10, 10], max_iter=2000, rank=20).fit([X, Y]) +plot_model_weights(spancca.weights[0], spancca.weights[1], tx, ty) + +# Convergence +plt.figure() +plt.title("Objective Convergence") +plt.plot(np.array(spancca.track[0]["objective"]).T) +plt.ylabel("Objective") +plt.xlabel("#iterations") diff --git a/requirements/deep.txt b/requirements/deep.txt index b4296f02..effe6a81 100644 --- a/requirements/deep.txt +++ b/requirements/deep.txt @@ -1,3 +1,4 @@ torch>=1.9.0 torchvision +pytorch-lightning Pillow \ No newline at end of file diff --git a/setup.py b/setup.py index fa47162f..6406732c 100644 --- a/setup.py +++ b/setup.py @@ -8,28 +8,28 @@ EXTRA_PACKAGES = {} with open("./requirements/deep.txt", "r") as f: - EXTRA_PACKAGES['deep'] = f.read() + EXTRA_PACKAGES["deep"] = f.read() with open("./requirements/probabilistic.txt", "r") as f: - EXTRA_PACKAGES['probabilistic'] = f.read() + EXTRA_PACKAGES["probabilistic"] = f.read() setup( - name='cca_zoo', - version='0.0.0', + name="cca_zoo", + version="0.0.0", include_package_data=True, - keywords='cca', + keywords="cca", packages=find_packages(), - url='https://github.com/jameschapman19/cca_zoo', - license='MIT', - author='jameschapman', + url="https://github.com/jameschapman19/cca_zoo", + license="MIT", + author="jameschapman", description=( - 'Canonical Correlation Analysis Zoo: CCA, GCCA, MCCA, DCCA, DGCCA, DVCCA, DCCAE, KCCA and regularised variants including sparse CCA , ridge CCA and elastic CCA' + "Canonical Correlation Analysis Zoo: CCA, GCCA, MCCA, DCCA, DGCCA, DVCCA, DCCAE, KCCA and regularised variants including sparse CCA , ridge CCA and elastic CCA" ), long_description=long_description, long_description_content_type="text/markdown", - author_email='james.chapman.19@ucl.ac.uk', - python_requires='>=3.6', + author_email="james.chapman.19@ucl.ac.uk", + python_requires=">=3.6", install_requires=REQUIRED_PACKAGES, extras_require=EXTRA_PACKAGES, - test_suite='test', + test_suite="test", tests_require=[], ) diff --git a/tutorial_notebooks/CCA_Tutorial.ipynb b/tutorial_notebooks/CCA_Tutorial.ipynb deleted file mode 100644 index 0d31c1c4..00000000 --- a/tutorial_notebooks/CCA_Tutorial.ipynb +++ /dev/null @@ -1,3214 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "CCA_Tutorial.ipynb", - "provenance": [], - "collapsed_sections": [], - "authorship_tag": "ABX9TyODnGHi5fJq92xQu05U70sT", - "include_colab_link": true - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "d5f617ab68ec42b0a81333040a03ce70": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_082d16684fce464e8ab300fd88db366e", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_42a064361141414d9735dd76a475268a", - "IPY_MODEL_00c063db01344e58b6f4155605f5036e" - ] - } - }, - "082d16684fce464e8ab300fd88db366e": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "42a064361141414d9735dd76a475268a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ToggleButtonsModel", - "model_module_version": "1.5.0", - "state": { - "_options_labels": [ - "CCA", - "PLS" - ], - "_view_name": "ToggleButtonsView", - "style": "IPY_MODEL_eb5dd98764d741b8ac1d86b5ac566ad2", - "_dom_classes": [], - "description": "Model:", - "_model_name": "ToggleButtonsModel", - "tooltips": [], - "index": 0, - "button_style": "", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "_view_module_version": "1.5.0", - "icons": [], - "description_tooltip": null, - "_model_module": "@jupyter-widgets/controls", - "layout": "IPY_MODEL_76bd49f092e24228924699e3fd9b735c" - } - }, - "00c063db01344e58b6f4155605f5036e": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "error", - "ename": "AttributeError", - "evalue": "ignored", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/ipywidgets/widgets/interaction.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_interact_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_kwarg\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0mshow_inline_matplotlib_plots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_display\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36minteractive_cca\u001b[0;34m(tog)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0mrcca\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrCCA\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlatent_dims\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mX_tr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mY_tr\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mtest_scores\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mX_te\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mY_te\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0mplot_latent_train_test\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtest_scores\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0mplot_widget\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwidgets\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minteractive\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minteractive_cca\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtog\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtog\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'rCCA' object has no attribute 'scores'" - ] - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_f2c9bdfd43f4436dab5dfcf0cf8e8b80", - "_model_module": "@jupyter-widgets/output" - } - }, - "eb5dd98764d741b8ac1d86b5ac566ad2": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ToggleButtonsStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "button_width": "", - "_model_name": "ToggleButtonsStyleModel", - "description_width": "", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "font_weight": "", - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "76bd49f092e24228924699e3fd9b735c": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "f2c9bdfd43f4436dab5dfcf0cf8e8b80": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "4186484e492348708679feb6fc1a5937": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_e9033710a974478088014f255d040332", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_a10e2175e99542f8984a5e1ac038d6c4", - "IPY_MODEL_07f1da8af80c4a33873945c3d206835d", - "IPY_MODEL_10adc5e3157243a5ba6c391d59254072" - ] - } - }, - "e9033710a974478088014f255d040332": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "a10e2175e99542f8984a5e1ac038d6c4": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_e17dc362cb1c46379c2ed85b398d9250", - "layout": "IPY_MODEL_098c336771204a41aa40df3c5a820c70", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "cx", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 1e-10, - "_view_module_version": "1.5.0" - } - }, - "07f1da8af80c4a33873945c3d206835d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_690cbdac87c94770afaa6fae82747ec4", - "layout": "IPY_MODEL_1b30b8c885004e3e8e4aaeb9c703de24", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "cy", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 1e-10, - "_view_module_version": "1.5.0" - } - }, - "10adc5e3157243a5ba6c391d59254072": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "error", - "ename": "ValueError", - "evalue": "ignored", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/ipywidgets/widgets/interaction.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_interact_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_kwarg\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0mshow_inline_matplotlib_plots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_display\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36minteractive_cca\u001b[0;34m(cx, cy)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minteractive_cca\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mrcca\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrCCA\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlatent_dims\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mcx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcy\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mX_tr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mY_tr\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mtest_scores\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_te\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mY_te\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0mplot_latent_train_test\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtest_scores\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mplot_train_test_corrs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtest_scores\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/cca_zoo/models/cca_base.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, views, y, **kwargs)\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0mcheck_is_fitted\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattributes\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"weights\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m views = check_views(\n\u001b[0;32m---> 70\u001b[0;31m \u001b[0;34m*\u001b[0m\u001b[0mviews\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maccept_sparse\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maccept_sparse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 71\u001b[0m )\n\u001b[1;32m 72\u001b[0m \u001b[0mviews\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_centre_scale_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mviews\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/cca_zoo/utils/check_values.py\u001b[0m in \u001b[0;36mcheck_views\u001b[0;34m(copy, accept_sparse, *views)\u001b[0m\n\u001b[1;32m 24\u001b[0m views = [\n\u001b[1;32m 25\u001b[0m \u001b[0mcheck_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mallow_nd\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maccept_sparse\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0maccept_sparse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mview\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mviews\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m ]\n\u001b[1;32m 28\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/cca_zoo/utils/check_values.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 24\u001b[0m views = [\n\u001b[1;32m 25\u001b[0m \u001b[0mcheck_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mallow_nd\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maccept_sparse\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0maccept_sparse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mview\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mviews\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m ]\n\u001b[1;32m 28\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py\u001b[0m in \u001b[0;36mcheck_array\u001b[0;34m(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator)\u001b[0m\n\u001b[1;32m 763\u001b[0m \u001b[0;34m\"Reshape your data either using array.reshape(-1, 1) if \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 764\u001b[0m \u001b[0;34m\"your data has a single feature or array.reshape(1, -1) \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 765\u001b[0;31m \u001b[0;34m\"if it contains a single sample.\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 766\u001b[0m )\n\u001b[1;32m 767\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Expected 2D array, got 1D array instead:\narray=[ 0.24873507 -1.10304645 -0.78789571 -0.2254131 -1.31878264 -0.4543386\n -1.07305892 1.42619916 -0.40343571 -0.94682405 0.67444959 -0.62848586\n -1.1024857 0.88752992 -0.32314154 0.8410213 -0.01505387 0.12919879\n 0.56317879 -0.63482736 -0.81021457 1.20183681 0.57891931 -0.06749798\n 0.79570091 0.54015971 0.94330287 -1.14283454 -0.06417904 1.129803\n 1.67722708 -0.53593825 0.15916538 0.01933582 -1.24418122 -0.28755981\n -1.57869268 -0.16732161 -1.03801888 0.16991478 -0.30989722 -0.32581508\n 1.55342149 0.07080917 1.87822239 0.44637153 0.81555286 -0.86952237\n -0.69367864 0.01726388 1.21307524 -0.36295402 -1.43145229 0.39799036\n 0.06397831 -0.55101008 -0.37306379 -0.87107367 0.92390675 0.0233241 ].\nReshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample." - ] - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_adc24ca24d0748edb2e13da7e26f635d", - "_model_module": "@jupyter-widgets/output" - } - }, - "e17dc362cb1c46379c2ed85b398d9250": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "098c336771204a41aa40df3c5a820c70": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "690cbdac87c94770afaa6fae82747ec4": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "1b30b8c885004e3e8e4aaeb9c703de24": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "adc24ca24d0748edb2e13da7e26f635d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "fd8e3e038fa446c48f29f3c719a88ee8": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_f5588cc41bfe4dfc99d34f0b8c7092ed", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_9032567411404f3d9e85862bff74b0be", - "IPY_MODEL_08ff7d25488b4b53ad841974fa97e8bd", - "IPY_MODEL_53f71b9ae00a4b4cb836d40358392790" - ] - } - }, - "f5588cc41bfe4dfc99d34f0b8c7092ed": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "9032567411404f3d9e85862bff74b0be": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatSliderView", - "style": "IPY_MODEL_1947b94e59c0410b875b21164719c876", - "_dom_classes": [], - "description": "c1", - "step": 0.1, - "_model_name": "FloatSliderModel", - "orientation": "horizontal", - "max": 7.745966692414834, - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "value": 3, - "_view_count": null, - "disabled": false, - "_view_module_version": "1.5.0", - "min": 1, - "continuous_update": false, - "readout_format": ".5f", - "description_tooltip": null, - "readout": true, - "_model_module": "@jupyter-widgets/controls", - "layout": "IPY_MODEL_b2afc9cdf727400487ce19f9f0ce7d53" - } - }, - "08ff7d25488b4b53ad841974fa97e8bd": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatSliderView", - "style": "IPY_MODEL_16b08cb41fc94870b7578021c7927d1a", - "_dom_classes": [], - "description": "c2", - "step": 0.1, - "_model_name": "FloatSliderModel", - "orientation": "horizontal", - "max": 7.745966692414834, - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "value": 3, - "_view_count": null, - "disabled": false, - "_view_module_version": "1.5.0", - "min": 1, - "continuous_update": false, - "readout_format": ".5f", - "description_tooltip": null, - "readout": true, - "_model_module": "@jupyter-widgets/controls", - "layout": "IPY_MODEL_654c62b8b9c3489c9f03fc76e81a2ed9" - } - }, - "53f71b9ae00a4b4cb836d40358392790": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "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XUvfvn1LbSsLk8nlDxyBiMjN6Vo/M6tEyDgqJSWFwMBA+/PAwEBSUlKu2VYW3t66Hb6ISHmpkJDp168fycnJJbbt2rULs9lcEWWIiEgFq5CQWbduXbmMY7FYioRVcnIyFovlmm0iIlI5qsSJf0f17NmThIQEsrKysNlsrF69ml69el2zTUREKkeVuNV/XFwc8+bN49y5c7i5ueHp6cmyZcu48847mT9/Pv7+/gwZMgSATz75hPffv/xdLmFhYcyYMcO+3FZam4iIVLwqETIiInJjqlbLZSIiUr0oZERExGkUMiIi4jQKGRERcRqFjEgVNnfuXLp160bTpk356aefKrsckTJTyIhUYd27d2flypU0bNiwsksR+UOq1b3LRG42ISEhlV2CyHXRTEZERJxGISMiIk6jkBEREadRyIiIiNPo3mUiVdicOXOIj48nIyMDb29v6tWrx2effVbZZYk4TCEjIiJOo+UyERFxGoWMiIg4jUJGREScRiEjIiJOo5ARERGnUciIXKeYmBjeeOMNvvvuO3r27FnZ5RQxduxY1q1bV9llyE1MN8gUKSchISFs2bKlssso4v3336/sEuQmp5mMiIg4jUJGpIwSExPp168fwcHBPPvss+Tl5QGwZ88eOnXqZO/XrVs33n//fXr37s19993H//3f/5GRkcHYsWMJDg5m5MiRnD171t7/4MGDDB48mJCQEPr06cOePXvsbY8++ihvvvkmgwcPJjg4mNGjR5OVlQVAXl4ezz//PG3atCEkJIT+/fuTkZFh32/16tUA2Gw2Fi1aRNeuXWnXrh1Tp07l/PnzAJw6dYqmTZuybt06unTpQps2bVi8eLFz30i5KShkRMogPz+fiRMnEh0dzd69e4mMjCQ+Pv6q/ePj4/nggw/YsmULW7duZdy4cUyZMoXdu3djs9n4+9//DkBaWhqPP/44EyZMYO/evUybNo1JkybZgwQgLi6Ov/zlL3zzzTcUFBSwbNkyANatW8eFCxf46quv2LNnD7Nnz8bDw6NYLWvXrmXdunWsWLGChIQEcnJyiI2NLdJn3759bN68mQ8//JCFCxdy7Nix8njb5CamkBEpg++//56CggIee+wx3NzciIyMpGXLllftP3z4cOrXr09AQAAhISG0atWK5s2bU6NGDSIiIkhMTARgw4YNdOrUic6dO2MymQgLC6NFixZs27bNPtbDDz/M7bffjoeHB5GRkRw9ehQAV1dXsrOzOXHiBGazmRYtWuDl5VWslo0bNzJy5EgaNWpErVq1mDJlCps2baKwsNDe56mnnsLDw4NmzZrRrFkzfvjhh/J66+QmpRP/ImWQnp5OQEAALi4u9m2BgYFX7V+/fn374xo1ahR57uHhQU5ODgDJycls3ryZrVu32tsLCwtp06aN/bmfn5/9saenp33f6OhoUlNTmTJlCufOnaNPnz5MnjwZNze3YrX//mucGzZsSGFhIZmZmSXW+/vXEPmjFDIiZeDn50daWhqGYdiDJjk5mUaNGl3XuBaLhejoaObMmVPmfd3c3Hjqqad46qmnOHXqFOPHj+f2229nwIABRfr5+/uTlJRkf56cnIyrqyu+vr6kpqZeV/0iV6PlMpEyuO+++3B1dWXFihUUFBQQHx/P4cOHr3vcPn36sHXrVr7++musVit5eXns2bPHoR/+u3fv5scff8RqteLl5YWrqysmU/H/2lFRUXz44YecPHmSixcv8sYbb9CrVy9cXfW7pjiPQkakDNzd3VmwYAHr1q0jNDSUTZs2ERERcd3jWiwWFi1axLvvvku7du3o3LkzS5cuxWazXXPfjIwMJk2axAMPPMCDDz5IaGgo0dHRxfr179+fPn36MHz4cLp37467uzsvvfTSddcuUhp9n4yIiDiNZjIiIuI0ChkREXEahYyIiDiNQkZERJxGISMiIk6jkBEREadRyIiIiNMoZERExGkUMiIi4jT/D3TepoMBMGzvAAAAAElFTkSuQmCC\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": "
" - }, - "metadata": {} - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_83245b18235e4bad8141bc399ea5cd94", - "_model_module": "@jupyter-widgets/output" - } - }, - "1947b94e59c0410b875b21164719c876": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "b2afc9cdf727400487ce19f9f0ce7d53": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "16b08cb41fc94870b7578021c7927d1a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "654c62b8b9c3489c9f03fc76e81a2ed9": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "83245b18235e4bad8141bc399ea5cd94": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "d841bfa912f54aec8f0b185e97f33c06": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_da249cda804d4ca9b5ff93c27bfb3a65", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_71bdb348f5804e45a2adeb256ee72c72", - "IPY_MODEL_116218d7741949e08f547ac9844aa7e1", - "IPY_MODEL_9a3e2484604b49fdbae2dad6d60f43b2" - ] - } - }, - "da249cda804d4ca9b5ff93c27bfb3a65": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "71bdb348f5804e45a2adeb256ee72c72": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_e93f9ba98fe34786a3064647deae6cfd", - "layout": "IPY_MODEL_f2174b7c3df64f278e748afaf20ff90d", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "c1", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 0.005, - "_view_module_version": "1.5.0" - } - }, - "116218d7741949e08f547ac9844aa7e1": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_94b31bb0dadf44eaa94e43614a626040", - "layout": "IPY_MODEL_6c7b054a3a7b4aa6bae9122dfcb83bde", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "c2", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 0.005, - "_view_module_version": "1.5.0" - } - }, - "9a3e2484604b49fdbae2dad6d60f43b2": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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hx/quq7r69ev17t166aXlZWFlJkxY4Y+bdo0Xdd1/b333tO7deumHz58uMa2fvfdd3paWpp+6NChkPTnnntOHz16dPD1FVdcoU+fPj0kz2uvvaZ3795d93g8uq7r+uTJk/WLLrooJM+cOXP03//+98HXvXv31t9///0a21KTe++9V7/00ktD0v7973/r6enpelZWlq7rVf2QlpZ2wmPU9V/7d926dcE0j8ejDxkyJORcHS8QCOh9+/bVV6xYEUzr3LlzrY7h8ccf16dMmXLSPCNHjtSff/75kNc333xzSJ7rr79ev+uuu05az7Hvi5rs2LFDT0tL03Nzc3VdP/F5HzlypP7222+HpG3cuFFPS0vTS0pKdF2v3XmeNWuWPnny5JO2Wdd1/ZZbbtEfeOCBE26fPHmyPnLkSN3v9wfT3n33Xb1bt2660+mssczx5+zbb7/V09LS9K1bt9aY//3339eHDh2q+3y+kPSrr75af+KJJ055DEKIxklGHEWTsnHjRnr16kUgEMDr9TJo0CAee+yxkDzdu3cP/v/oKEvfvn1D8vTt25ctW7bUuI9t27bh8/kYNmxYSLrP5yM1NRWAHTt20KFDB5KSkn7T8ezbty9khA2gf//+eDweDh06FByV6dSpU0iehISEkMvtU6dO5cEHH2T58uX079+fjIwMunbtetL9Dhw4sNp+dV1n3759tGzZstbtB+jVq1cwzWw2071795DLsIcOHeK5555jy5YtFBYWous6LpeLnJyck9avaRqvvPIKq1atIjc3F6/Xi8/nq3X7jtW5c+eQ1wkJCac9iWXDhg289NJL7Nu3j7KysuAl8+zsbBITE2ssU1RURHZ2NnPnzuWpp54Kph8te+DAgeDo7KnOc21deeWVTJ8+ne3btzNw4ECGDh3K0KFDUdVf707q3r07BoMh+Lp37954vV4OHjxIp06dTnnOtm/fTlRUVMjn7Vjbtm2joKCAfv36haR7vV6sVutpH5MQonGQwFE0KT169GDevHkYDAYSEhIwm80h2w0GAxaL5TftQ9M0IiIiWLZsWbVttZloUx+O36+iKCH3+d12222MHz+etWvXsmHDBl588UWuv/567rrrrjPd1BrdcsstREdHM2fOnOAl3CuvvBKfz3fScq+++iovvvgiM2fOpEuXLoSHh/Paa6+xZs2a027DqfrwVHJycrjp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0tAFJhgXERER6ay6xa1qwzCoD1pU1gf5/GyNtg1B29ElVqURERER6cy6fI+jjc27Byt47MV3qaoNMHJgKtdMH0pCTJc/NREREZFOpcv3OBZX+nng2Tepqg0A8PZ7JTz7yj7UvygiIiLSvrp8cDxWWtesbfueImoDmnZHREREpD11+eDYw+du1pae5MPt7PKnJiIiItKpdPl01TvVR+6w9Mi2wzS4YfYI3KYRxapEREREup8uP4LE4zT57+lDmJp7JrUNITKSfaTEu9FDjiIiIiLtq8sHRwC306Rvz7hPGxQaRURERNpdl79VLSIiIiIdQ8FRRERERFpFwVFEREREWkXBUURERERaRcFRRERERFpFwVFEREREWqXTTMdjdqEJu7tSrR1J16Vlui4t03Vpma5Ly3RdRDoHw7ZtzXooIiIiIl9Jt6pFREREpFUUHEVERESkVRQcRURERKRVFBxFREREpFUUHEVERESkVRQcRURERKRVFBxFREREpFUUHEVERESkVRQcRURERKRVFBxPwt133820adOYOXMm8+bNY9euXdEuqVNYtWoVM2bMYOjQoSxbtiza5UTVhx9+yBVXXMHUqVO54oorOHjwYLRLirpFixYxefJkBg0axHvvvRftcjqN8vJyvvvd7zJ16lRmzJjBwoULKSsri3ZZncJNN93EzJkzmT17Nt/+9rfZs2dPtEsSOe1pycGTUFBQwPjx43G5XBQUFPDb3/6W/Pz8aJcVde+99x6mafLoo4+SnZ3N/Pnzo11S1Fx99dXMnTuXWbNmsWrVKv7xj3/w9NNPR7usqNqxYwe9e/fmqquu4pFHHmHgwIHRLqlTqKioYN++feTm5gKNAbuyspJ77703ypVFX3V1NfHx8QDk5+ezZMkSVqxYEeWqRE5v6nE8CZMmTcLlcgEwcuRIjh8/jmVZUa4q+gYOHMiAAQMwzdP7a1VaWsru3bvJy8sDIC8vj927d5/2vUg5OTlkZmZGu4xOJzExMRIaofHflMLCwihW1Hl8EhoBampqMAwjitWICIAz2gV0dcuXL2fixImnfViSTx07doz09HQcDgcADoeDnj17cuzYMZKTk6NcnXRmlmXx7LPPMnny5GiX0mncddddvPHGG9i2zWOPPRbtckROewqOLZgzZ84X/sW/adOmSCBYs2YNL730EsuXL+/I8qKmtddFRE7O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\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZkAAAEcCAYAAAAV2MmlAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3deVyU5f7/8RczgKC4AAIOZllWLqlFIe47KCYKZu5m7mWWpZXi95QLeTpfPW1malmaeY6V+XU7kinRw/SoRy2Xo0dsOZamrALiBrLM3L8//J05EYhDMjDo+/l4+HjM3Nd1X/O5R+XNdd333ONmGIaBiIiIE5iqugAREbl5KWRERMRpFDIiIuI0ChkREXEahYyIiDiNQkZERJxGISM3jfHjx7Nhw4aqLuOmd+bMGZo2bUpRUdHv2v/dd9/lD3/4QwVXJa7KTZ+TkaoUEhJif5yXl4enpydmsxmAuXPn0r9//6oqrVxmzZrF5s2bASgsLMQwDDw9PQF46KGH+OCDD8o13vr161m7di2ffPJJhdd6o86cOUPPnj05duwY7u7uZfbdt28fL774Ijt37qyk6sTVlP0vRMTJDh06ZH/co0cP5s2bR4cOHUr0Kyoquu4PtKoUFxdHXFwcAIsWLeLUqVO89tprVVzV9ZX2vrr6ey3Vi5bLxCXt27ePLl26sGzZMjp27MjMmTM5f/48TzzxBO3ataNNmzY88cQTpKWl2fd57LHHWLt2LXB1JjBs2DDmz59PmzZt6NGjBzt27Cj1tZYtW8aUKVOKbZs3bx7z5s2zj9WzZ09CQkLo0aMHf/vb38p1LIcPH2bo0KGEhobSv39/9u3bZ28rbewTJ04we/ZsDh8+TEhICKGhoaWOm5OTw8yZM+nUqRNt2rThqaeesrd99tlnREREEBYWxpNPPkl6erq9rWnTpqxevZpevXrRq1evUt9rm83GsmXLCA8Pp23btjz77LPk5OSUWse6devo06cPISEh9OzZk08//RSA3NxcJkyYQEZGBiEhIYSEhJCens6iRYt44YUX7Pt/9dVX9O3bl9DQUB577DFOnDhhb+vRowfLly+nX79+PPTQQzz33HPk5+cDkJ2dzRNPPEFoaChhYWEMHz4cm81Wrr8bqQSGiIvo3r27sXv3bsMwDGPv3r1G8+bNjQULFhj5+flGXl6ekZ2dbWzdutXIzc01Ll68aDzzzDPGpEmT7PuPHDnS+OyzzwzDMIx169YZLVq0MNasWWMUFRUZq1evNjp27GjYbLYSr3vmzBmjdevWxsWLFw3DMIyioiKjY8eOxqFDh4zLly8bISEhxokTJwzDMIz09HTjhx9+KPM43n77beP55583DMMw0tLSjLCwMOPrr782rFarsWvXLiMsLMzIysoqc+x169YZQ4cOLfN1JkyYYDz77LNGTk6OUVBQYOzbt88wDMPYs2ePERYWZvzrX/8y8vPzjbi4OGP48OH2/e69915j9OjRxrlz54y8vLxS3+uVK1cagwYNMlJTU438/Hzj5ZdfNqZOnWoYhmGcPn3auPfee43CwkLDMAxj+/btxqlTpwybzWbs27fPaN26tfGvf/3L/vfYuXPna74/P/30k3H//fcbu3btMgoKCoxly5YZ4eHhRn5+vmEYV/9NDBw40EhLSzPOnTtnREZGGh9//LFhGIbx2muvGS+//LJRUFBgFBQUGN98802pf79StTSTEZdlMpmYMmUKnp6eeHl54evrS+/evfH29sbHx4dJkybxzTffXHP/4OBgBg8ejNlsZsCAAZw9e5bMzMwS/Ro2bEiLFi1ITEwEYO/evXh5efHAAw/Y6/jxxx+5cuUKgYGB3HPPPQ4fw6ZNm+jSpQtdu3bFZDLRsWNHWrZsaZ9V/d6xMzIy2LlzJ3PnzqVu3bp4eHgQFhYGwObNmxk4cCD33Xcfnp6eTJs2jcOHD3PmzBn7/hMnTqRevXp4eXnZ6/j1e/3pp58ydepUGjRogKenJ08//TTbtm0r9WR/t27duP3223FzcyMsLIyOHTvy7bffOnQcW7ZsoWvXrnTs2BEPDw/GjRvHlStXii2jPvbYYwQFBVGvXj26d+/O8ePHAXB3d+fs2bOkpKTg4eFBaGgobm5uDr2uVB6FjLgsX19fatSoYX+el5fHrFmz6N69Ow8++CAjRozgwoULWK3WUvevX7++/bG3tzdwdQmnNFFRUcTHxwMQHx9PVFQUADVr1uTNN9/k008/pVOnTkycOLHYcs71pKSksHXrVkJDQ+1/Dhw4wNmzZ29o7LS0NOrWrUvdunVLtGVkZNCwYUP781q1alGvXr1iS2YWi6XYPr99r1NSUpg8ebK95ocffhiTyURWVlaJ19uxYweDBw8mLCyM0NBQdu7cyblz5xw6joyMDIKDg+3PTSYTFoulWK0BAQH2x97e3va/w3HjxnHHHXcwduxYevbsybJlyxx6TalcChlxWb/9rXTFihX8/PPPfPbZZxw8eJDVq1cDYFTABZJ9+vRh//79pKWl8eWXX9KvXz97W+fOnfnwww/ZtWsXd911Fy+//LLD41osFqKjo/n222/tfw4fPszEiRPLHPt6v5E3aNCA8+fPc+HChRJtgYGBJCcn25/n5uaSk5NDUFCQfdtvx//t8wYNGvD+++8Xq/vo0aPFxgAoKChgypQpjB07lt27d/Ptt9/SpUsX+9/J9Y4jMDCQlJQU+3PDMEhNTS3xOqXx8fEhNjaWr776iqVLl/Lhhx/yj3/847r7SeVSyEi1cfnyZWrUqEGdOnXIycnhnXfeqbCx/fz8CAsLY+bMmdx22200adIEgMzMTBITE8nNzcXT05OaNWtiMjn+36Z///5s376dv//971itVvLz89m3bx9paWllju3v7096ejoFBQWljhsYGEiXLl2YO3cu58+fp7Cw0L50GBUVxfr16zl+/DgFBQW88cYbtG7dmttuu83huocNG8Zbb71lD6vs7Gz7cuKvFRQUUFBQgJ+fH+7u7uzYsYPdu3fb2/39/cnJyeHixYulvk6fPn3YsWMH//jHPygsLGTFihV4enoWu7T9WrZv386pU6cwDIPatWtjNpu1XOaCFDJSbTz++OPk5+fTrl07hgwZQufOnSt0/KioKPbs2WNfKgOw2WysXLmSzp07ExYWxjfffMOcOXMcHtNisbBkyRLee+892rdvT9euXVm+fDk2m63Msdu1a8fdd99Np06daNu2baljL1iwAHd3d/r06UOHDh346KOPAOjQoQPPPvsszzzzDJ06deL06dO8+eab5XovRo0aRY8ePRg7diwhISEMHjyYI0eOlOjn4+PDSy+9xHPPPUebNm2Ij4+nR48e9vYmTZrQt29fwsPDCQ0NLbYMBnDXXXfx5z//mVdeeYV27dqxfft23n33XftnjMpy6tQpxowZQ0hICEOGDGHYsGG0a9euXMcpzqcPY4qIiNNoJiMiIk7jUiEzf/58evToQdOmTfnhhx9K7WO1Wpk7dy7h4eFERETYP3x3vTYREal8LnXviJ49ezJq1ChGjBhxzT6bN2/ml19+ISEhgZycHGJiYmjfvj233XZbmW0i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dWtatGhBjRo1iIiIICkpCYBNmzbRpUsXunbtislkomPHjrRs2ZIdO3bYx3rkkUe488478fLyIjIykuPHjwPg7u5OTk4Op06dwmw207JlS3x8fErUsnnzZkaPHk2jRo2oVasW06ZNY8uWLRQVFdn7PP3003h5edGsWTOaNWvGd999V1FvndyidOJfpBwyMjIICgrCzc3Nvi04OPia/evXr29/XKNGjWLPvby8yM3NBSAlJYWtW7eyfft2e3tRURFt27a1Pw8ICLA/9vb2tu8bHR1NWloa06ZN48KFC/Tv35+pU6fi4eFRovZff41zw4YNKSoqIisrq9R6f/0aIr+XQkakHAICAkhPT8cwDHvQpKSk0KhRoxsa12KxEB0dzbx588q9r4eHB08//TRPP/00Z86cYeLEidx5550MGjSoWL/AwECSk5Ptz1NSUnB3d8ff35+0tLQbql/kWrRcJlIODzzwAO7u7qxatYrCwkISEhI4evToDY/bv39/tm/fzt///nesViv5+fns27fPoR/+e/fu5fvvv8dqteLj44O7uzsmU8n/2lFRUXz00UecPn2ay5cv8+abb9KnTx/c3fW7pjiPQkakHDw9PVm0aBEbNmwgLCyMLVu2EBERccPjWiwWlixZwnvvvUf79u3p2rUry5cvx2azXXffzMxMpkyZwkMPPcTDDz9MWFgY0dHRJfoNHDiQ/v37M3LkSHr27Imnpycvv/zyDdcuUhZ9n4yIiDiNZjIiIuI0ChkREXEahYyIiDiNQkZERJxGISMiIk6jkBEREadRyIiIiNMoZERExGkUMiIi4jT/D+Vglbjta6H5AAAAAElFTkSuQmCC\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": "
" - }, - "metadata": {} - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_f2be86b07cc34fec98d8a0ac03f6e410", - "_model_module": "@jupyter-widgets/output" - } - }, - "e93f9ba98fe34786a3064647deae6cfd": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "f2174b7c3df64f278e748afaf20ff90d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "94b31bb0dadf44eaa94e43614a626040": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "6c7b054a3a7b4aa6bae9122dfcb83bde": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "f2be86b07cc34fec98d8a0ac03f6e410": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "e7c5993c7704484db6390038d50e8e52": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_156c40392f954961b7c6cebee3912d7d", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_13fc9e6c26604f58a3a821c119bd268a", - "IPY_MODEL_73ae601863af45f6b0d97be47ab8149b", - "IPY_MODEL_04140cf3fa164a64930ec261b9f556f6" - ] - } - }, - "156c40392f954961b7c6cebee3912d7d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "13fc9e6c26604f58a3a821c119bd268a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_1172e605bb634b31ad0c9a437db7c047", - "layout": "IPY_MODEL_33780801a70f4d168ec7fd904ee99685", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "cx", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 1e-10, - "_view_module_version": "1.5.0" - } - }, - "73ae601863af45f6b0d97be47ab8149b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatLogSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatLogSliderView", - "orientation": "horizontal", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "disabled": false, - "readout_format": ".9f", - "_model_module": "@jupyter-widgets/controls", - "style": "IPY_MODEL_e7a68ca2f9794421b2f047ddadfc6741", - "layout": "IPY_MODEL_6aa67cbfb05b4ff9ba03eb365e94c458", - "min": -10, - "continuous_update": false, - "description_tooltip": null, - "_dom_classes": [], - "description": "cy", - "_model_name": "FloatLogSliderModel", - "max": 0, - "readout": true, - "step": 0.1, - "base": 10, - "value": 1e-10, - "_view_module_version": "1.5.0" - } - }, - "04140cf3fa164a64930ec261b9f556f6": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "error", - "ename": "AttributeError", - "evalue": "ignored", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/ipywidgets/widgets/interaction.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_interact_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mwidget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_kwarg\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0mshow_inline_matplotlib_plots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_display\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36minteractive_cca\u001b[0;34m(cx, cy)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mrcca\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrCCA\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlatent_dims\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mcx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcy\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mHX_tr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mHY_tr\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mtest_scores\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mHX_te\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mHY_te\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mplot_latent_train_test\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtest_scores\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0mplot_train_test_corrs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtest_scores\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mplot_model_weights\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweights\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mrcca\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweights\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtitle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Model weights'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'rCCA' object has no attribute 'scores'" - ] - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_d83a784c9575476e86292a2f795a439c", - "_model_module": "@jupyter-widgets/output" - } - }, - "1172e605bb634b31ad0c9a437db7c047": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "33780801a70f4d168ec7fd904ee99685": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "e7a68ca2f9794421b2f047ddadfc6741": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "6aa67cbfb05b4ff9ba03eb365e94c458": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "d83a784c9575476e86292a2f795a439c": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "2c3a73827f794598bb0bb9a749c4b70f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "VBoxModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "VBoxView", - "_dom_classes": [ - "widget-interact" - ], - "_model_name": "VBoxModel", - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.5.0", - "box_style": "", - "layout": "IPY_MODEL_f4a50fa01e66441090ebd78f4be413d4", - "_model_module": "@jupyter-widgets/controls", - "children": [ - "IPY_MODEL_6590606b34d643fb8c6c7e06599d5bca", - "IPY_MODEL_886e8a7d34ca460db623ee35abce05a6", - "IPY_MODEL_6097fdf1adb94dc9bf89bd5e0a101fc8" - ] - } - }, - "f4a50fa01e66441090ebd78f4be413d4": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "6590606b34d643fb8c6c7e06599d5bca": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatSliderView", - "style": "IPY_MODEL_75aa1b12f01448198ad356465332fffd", - "_dom_classes": [], - "description": "c1", - "step": 0.1, - "_model_name": "FloatSliderModel", - "orientation": "horizontal", - "max": 21.540659228538015, - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "value": 3, - "_view_count": null, - "disabled": false, - "_view_module_version": "1.5.0", - "min": 1, - "continuous_update": false, - "readout_format": ".5f", - "description_tooltip": null, - "readout": true, - "_model_module": "@jupyter-widgets/controls", - "layout": "IPY_MODEL_6049c8f1a0c54f11955956952181b242" - } - }, - "886e8a7d34ca460db623ee35abce05a6": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatSliderModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "FloatSliderView", - "style": "IPY_MODEL_ed894f48599f4920bf247584398d2e0e", - "_dom_classes": [], - "description": "c2", - "step": 0.1, - "_model_name": "FloatSliderModel", - "orientation": "horizontal", - "max": 4.898979485566356, - "_view_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "value": 3, - "_view_count": null, - "disabled": false, - "_view_module_version": "1.5.0", - "min": 1, - "continuous_update": false, - "readout_format": ".5f", - "description_tooltip": null, - "readout": true, - "_model_module": "@jupyter-widgets/controls", - "layout": "IPY_MODEL_ca3f1c4c5a804634a7f13b0b9ab0ebdd" - } - }, - "6097fdf1adb94dc9bf89bd5e0a101fc8": { - "model_module": "@jupyter-widgets/output", - "model_name": "OutputModel", - "model_module_version": "1.0.0", - "state": { - "_view_name": "OutputView", - "msg_id": "", - "_dom_classes": [], - "_model_name": "OutputModel", - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAo4AAAE7CAYAAABT4QC7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdd3hUVf748fedXpNJ7wRISAIkdEIThGBlrdgVG+pa2HXV3VWKYu/81NXVFdvirquugsh3RWVVFEVXqkjvJY30Ppl+z++PgYEhhYABgpzX8/g8zpl7z5w7dzJ85pTPUYQQAkmSJEmSJEk6DM2JboAkSZIkSZJ0cpCBoyRJkiRJktQhMnCUJEmSJEmSOkQGjpIkSZIkSVKHyMBRkiRJkiRJ6hAZOEqSJEmSJEkdIgNHSQKys7NZsGDBMX+dl156iTPPPPOYv05n+ec//8mYMWPIycnhpZde6vB5U6dO5YYbbjh2DTuMgoICXnnllV9cz/H6XEiSJJ0sZOAonTSmTp1KdnY22dnZ9OnTh3HjxjFz5kxqa2t/cd1Lly7lnHPO6YRWBq1cuZLs7GyKi4vDyidPnsy///3vTnudY6m8vJwnnniCW2+9lW+//ZbJkycf09fr06cPH330UafUNXfu3BMSuLZ13zvLjBkzuPbaa49J3ZIkSR2hO9ENkKQjMWTIEF544QUCgQDr16/n/vvvp6ysjNdee63FsUII/H4/er3+sPXGxcUdi+a2YLVasVqtx+W1fqmioiJUVaWgoID4+PgT3ZwjEh0dfaKbIEmS9Kskexylk4perycuLo7ExETOOOMMrr/+er777jvcbjcfffQRffr04ccff+Siiy4iLy+PH374gaamJmbOnMnw4cPJzc1l4sSJLF26NKzeQ4cknU4njz32GKNHj6Z///5cdNFF/Pe//w07p7q6mmnTpjFy5Ejy8vI4++yzmTt3LsXFxVxzzTUAjB8/nuzs7FAvUWtD1fPnz2fChAnk5uYyZswYnn/+efx+f+j5a6+9lhkzZvDyyy8zatQo8vPzuffee3E6naFjtm3bxk033cSQIUMYMGAA5557Lh9//HG77+WSJUuYOHEiubm5jBgxgoceeojm5uZQO/dfw9ixY9vtRaurq+Ouu+5iwIABjBw5kueff55DN6T6/vvvufbaa8nPz2fw4MFMmjSJtWvXhp4vKCggEAgwbdq0UK8yQH19PX/6058YO3Ys/fr14+yzz+att95qUf+hDh2qLigo4C9/+QuPPfYY+fn5jBw5kieeeCLsfe6It99+mwsvvJCBAwcyatQo7r77bioqKgDave8ACxcu5MILLyQvL4+CggKefPLJ0PsNh7/PL730EnPnzmX58uWh96itHtqmpiamTZvGqFGjyM3N5fTTT+fJJ58Me61p06Yxa9Yshg0bxqBBg3jggQfweDyhYw53zyD4d/L4449z+umnk5ubS0FBAa+++mro+aqqKqZOncrw4cMZOHAgV155JStWrDii91ySpK5F9jhKJzWTyYSqqqEAQFVVZs2axdSpU0lJScFqtTJ9+nTWr1/Ps88+S3JyMu+99x633XYbCxYsICMjo0WdQghuu+02AJ5//nkSEhL44YcfuOeee3j99dcZMWIEbrebSZMmYTKZmDVrFmlpaezZs4f6+nqSkpJ45ZVXuOOOO/jwww9JSkpqs9fzm2++Yfr06dx1112cddZZbNq0iQcffBBFUbjrrrtCxy1atIiJEyfyj3/8g71793LPPfeQnJwcOuaee+4hKyuL999/H6PRyM6dO1FVtc33bfPmzdx+++1MmjSJZ599luLiYh588EGcTifPPvsskydPJjs7m9///vfMnz+fuLi4NnvxZsyYwdatW/nb3/5GbGwss2fPZvHixfTr1y90THNzM1dddRU5OTkEAgHmzJnDzTffzKJFi4iKimLu3Lmcdtpp3HfffUyYMCF0ntfrJSsrixtvvJGIiAhWr17NQw89RGRkJJdcckmb19ead955h1tuuYUPPviAjRs38uc//5levXpx2WWXHVE99913H2lpaVRVVfH0009zzz338M4777R73z/66COefPJJZsyYweDBgykrK+ORRx6hpqaGZ599NlR3e/d58uTJ7N69m5KSktB8U7vd3mobX3jhBTZs2MArr7xCXFwcZWVlbN++PeyYRYsWMWHCBN5991327NnDjBkzMJvNTJ8+HTj8Pdv/d1JaWsoDDzxAdnY2ZWVl7Nq1CwC32811111HRkYGr7/+OhEREXz66afceOONbf7tSZJ0EhCSdJK47777xPXXXx96vG3bNjF+/Hhx2WWXCSGEmDdvnsjKyhIrVqwIHbN7926RlZUlvvnmm7C6LrroIjF16tTQ46ysLPHxxx8LIYT48ccfRW5urmhoaAg7Z+rUqeL2228XQgjxwQcfiNzcXLF3795W27pixQqRlZUlioqKwspffPFFccYZZ4QeX3XVVeLOO+8MO2bOnDkiLy9PeDweIYQQkyZNEueff37YMTNnzhSXX3556PGgQYPEvHnzWm1La/70pz+JSy65JKzsiy++ENnZ2aK4uFgIEXwfsrKy2rxGIQ68v0uXLg2VeTwecdppp4Xdq0MFAgExZMgQsWDBglBZ7969O3QNjz76qLjhhhvaPWbcuHHi5ZdfDnt86623hh1z0003ibvvvrvdeg7+XLRmw4YNIisrS5SVlQkh2r7v48aNE++++25Y2fLly0VWVpaoq6sTQnTsPk+fPl1MmjSp3TYLIcRtt90m7rvvvjafnzRpkhg3bpzw+/2hsvfff1/k5uYKp9PZ6jmH3rMffvhBZGVlibVr17Z6/Lx588To0aOFz+cLK7/22mvFY489dthrkCSpa5I9jtJJZfny5QwcOJBAIIDX62XEiBE88sgjYcfk5eWF/n9/L8uQIUPCjhkyZAhr1qxp9TXWrVuHz+djzJgxYeU+n4/09HQANmzYQGZmJomJib/oerZv3x7WwwaQn5+Px+OhqKgo1CuTk5MTdkx8fHzYcPvkyZO5//77mT9/Pvn5+RQUFNC3b992X3f48OEtXlcIwfbt20lJSelw+wEGDhwYKjMYDOTl5YUNwxYVFfHiiy+yZs0aqqurEULgcrkoLS1tt35VVXnjjTdYuHAhZWVleL1efD5fh9t3sN69e4c9jo+PP+JFLMuWLeO1115j+/btNDQ0hIbMS0pKSEhIaPWcmpoaSkpKeOqpp3jmmWdC5fvP3bNnT6h39nD3uaOuvvpq7rzzTtavX8/w4cMZPXo0o0ePRqM5MDspLy8PrVYbejxo0CC8Xi+FhYXk5OQc9p6tX7+eyMjIsL+3g61bt46qqiqGDh0aVu71ejGZTEd8TZIkdQ0ycJROKv369ePpp59Gq9USHx+PwWAIe16r1WI0Gn/Ra6iqit1uZ+7cuS2e68hCm2Ph0NdVFCVsnt+UKVO44IIL+Pbbb1m2bBmzZ8/mpptu4u677z7eTW3VbbfdRlRUFDNnzgwN4V599dX4fL52z3vrrbeYPXs206ZNo0+fPlitVubMmcOSJUuOuA2Hew8Pp7S0lN/+9rdceOGF3HHHHURFRVFeXs4NN9zQ7nXsnzIwY8YMhg0b1uL5g398/NI27jd69Gi+/vprli5dyvLly7n33nvJyspizpw5YcFie472nu2nqioZGRn89a9/bfGcDBwl6eQlF8dIJxWTyUR6ejqpqaktgsbW9OrVCwimSTnYypUrQ88dKi8vj4aGBjweD+np6WH/JScnA9C3b1+2b99OWVlZq3Xsb1t78wwBMjMzWywWWL58OSaTibS0tMNe38HS0tK45pprePHFF7nzzjt5//33j/h1FUVp831pqx6An376KVTm9XpZt25d6HFtbS3bt2/nlltuYfTo0WRmZmI0Gqmurg6rS6/XEwgEwspWrlzJ6NGjufTSS+nTpw/p6ens2bOnw+3rTOvWrcPtdjN9+nQGDx5Mz549qaqqCjumtfseGxtLUlISu3btavF5Sk9PP6IfOq29R21xOBycd955PPLII8yePZvly5eHzXNct25dWF0//fQTBoOBbt26deie5ebmUl9fH3avD5abm0tRURE2m63FNbfVOytJUtfXZXocq6ubUNUj/2UNEBVloba2+fAH/kqdKtfvdvvwegNUVjaGle+//sZGN0DY82ZzFOPGncHMmQ/y5z9PJzExifnz57J161ZmzHgk7NjGRjeVlY1kZPRlyJB8br99CnfccScZGZk0Njayfv3PGAxGLrjgYoYPH0tCwmvccstvuf32O0lJSaW0tIT6+jrGjz8LozECjUbDwoWLGD/+LPR6AzabDafTQyCghl73yiuv5b777uG5517i9NPHsW3bVl588SWuuOIa6us9gAev14/b7Qtr6/56/P4AJSVV/O1vLzF2bAFJSck0NTXy1Vdf061b9xbv1X4TJ17FTTdN4oEHHuLCCy9h795Snnnmcc466xz0ejuVlY3U1QU/U9XVTWi1rddjsURz2mljmDnzIe69dzpRUdG8884cmpqcoXulqhocjijeeec9bLYYGhrqeeWVFzEajTidnlAbExOT+fbb78nNHYxOp8fhcJCQkMKiRZ+yaNHXxMbG8fnnC1mzZg12ewSVlY1tfvYDATWs7kMfH/g8+dt8jw79XERExKEoCi+99DfOOutctm/fxquvBhep1NU1U1nZ2OZ9v+mm23jqqUfRao2MHn06Op2O3bt38+OP33PvvTMA2r3P+8uiouLYseMzli1bQ3R0DCkpsTidLVeGz579Mjk5venRoyeKomHu3HmYzRYMhuD75vX6qa2tY9q0+7nssqsoLS3h+edf4IILJuJ0Bjp0zzIy+tK//0DuvPMP/P73d5OR0Yuqqkr27NnN+edfxIgR40hMfIvJk2/mt7+9g7S0btTU1LB69QrS03swZszYdt/3w2nt3sfFtb5YSJKkztNlAsdfQqfr2NDLr5W8/vavf+rU+3n55b/w6KMP4HQ66dkzk2eeeYH09O6tHq8oCk8//RxvvfU6L730HJWVFURERJKZmcU111wHBHs+//rX13jllRd56KHpuFwuEhOTmDTpBgCio2O49dYpvPPO27z44nP06zeAv/61Za7JESNOY9q0mbzzzhzefPNVHI4oLr74Um688ZYOX7tWq6WxsYGnnnqU6uoqLBYrgwYNYcqUP7R5XmZmL5566v/x+uuvMn/+XKxWK2PHjmfKlLvaPKct06bNZNasp7j33rswmUycd95FjBkzlsrKSgA0Gg2PPvoUL7wwixtuuIqEhERuvXUKf/tb+E40v/vdXbz00nNceun5+P1+li5dyQ033Ex5eRlTp/4RnU7H+PFncemlV7Jo0aeh6z9eMjN7cdddf+Zf/3qbf/zj72Rn53DnnX/kT3+6M3RMW/f9nHN+g8Vi3XfuW2i1OpKTUzj99HFH1IbzzruQ1atXcvvtk3E6nTz55JOMHt1yJyKj0cgbb7xKWdleNBoNvXplM2vWi9hsttAxY8cWYLFYueOOm/H5fIwffya33/47oGP3TFEUnn32BWbPfplZs56kvr6euLh4LrhgYqgNf/3ra7z++t944omHqaurxeGIonfvvgwbNuKIrrs1p/r3niSdKIo4mgk0x8Av6XGMi7Mfttfg10xe/y+7fq/XS0HBSJ54YtYv7gU53uS9l9d/NNf/u9/9ltTUNKZOfeAYtOr4aO3aZY+jJB17v4oeR0k6Wo2NjXz77dcoikLPnjKvnCRJkiS1RwaO0intpZee48cff+C2235HauqRLUaRJEmSpFONDBylU9r06Q+e6CZI0nHX2nxbSZKkjpDpeCRJkiRJkqQOkYGjJEknFUUJruiVJEmSjj85VC1J0klBUcDoKsdXvBHh82BM64OIyTn8iZIkSVKnkYGjJEknBWNzGVX/fhjhdQULFA26qx8Ea/qJbZgkSdIpRA5VS5LU5SmKgnfP2gNBI4BQqfvfx+g07W/rKEmSJHUe2eMoSVKXpyqAu6lFeaC5AYUusYeBJEnSKUH2OEqS1KVVNXp47v2fKDO3TNAemX8ePlVuPSdJknS8yB5HSZK6rGZfgCf+sZK6Rg9/d1m4deydWDZ/huJ3Y88/H0vGINxO2eMoSZJ0vMjAUZKkLquqzk1doweA7XubmbbATX72eVx1TiZ+qxWtxQbOU3evakmSpONNDlVLktRlmYzhv239AZUfN1XjEXqE7GiUJEk67mTgKElSlxVrN3D+aT3Dyi4p6EW01XCCWiRJknRqk0PVkiR1WRpF4Tcj0hmYFUd1g5s4h4mkKMuJbpYkSdIpSwaOkiR1aQatQvd4K93jrSe6KZIkSac8OVQtSZIkSZIkdYgMHCVJkiRJkqQO6bSh6jvuuIPi4mI0Gg0Wi4UHHniA3r17d1b1kiRJkiRJ0gnWaYHj008/jd1uB+DLL79k+vTpzJ8/v7OqlyRJkiRJkk6wThuq3h80AjQ1NaEoSmdVLUmSJEmSJHUBnbqqesaMGXz//fcIIXjjjTc6s2pJkiRJkiTpBFOE6Pz9Fz7++GMWLlzI66+/3tlVS5IkSZIkSSfIMQkcAfr168eSJUuIiorq0PHV1U2o6tE1JS7OTmXlqbtfrbz+U/f6T+VrB3n9p/L1t3btcXH2No6WJKmzdMocR6fTyd69e0OPFy9eTGRkJA6HozOqlyRJkiRJkrqATpnj6HK5+MMf/oDL5UKj0RAZGcmrr74qF8hIkiRJkiT9inRK4BgbG8sHH3zQGVVJkiRJkiRJXZTcOUaSJEmSJEnqEBk4SseVnL4gSZIkSSevTs3jKElt0asulJpd+Ct2oY9OgbgMvFq5AlKSJEmSTiYycJSOOa2i4lv3OY3LFoTKTJmDsYy7BZ9iOoEtkyRJkiTpSMihaumY07mraVz+n7Ay9/ZVaBrLTlCLJEmSJEk6GjJwlI69gA+E2qJY+L0noDGSJEmSJB0tGThKx5xqicGYkh1WprU5UCITT1CLJEmSJEk6GnKOo3TM+TASceatuNYuwrVtJcbUHKxDL8CtjQgdoygKBl8tNNeDOQKvIYpjsxmmJEmSJElHSwaO0nHhNkSjHXoljsEXEdAYcYsDnd2KomCo2kT1Jy8iPM0oBhPRE6bgS8hFCJm+R5IkSZK6CjlULR03qlDwKmYCIvxjZ/DWhoJGAOF1U/PJSxg9NSeimZIkSZIktUEGjtIJJ5rrQkFjqMzvRXXWnqAWSZIkSZLUGhk4SiecYo5A0RvDCzU6FEvkiWmQJB0HWq38+pUk6eQjv7mkE85rjCb63DtAu2/KrUZH9Dm34jPFntiGScdVRYOH5VsqWburhka3/0Q355hp9gZYtb2av3+2mRVbq3B6Aye6SZIkSR0mF8dIJ5wQ4EvsR+ykpxDOWhSLA585BlUujDllFFU38/Cby1DV4FL62EgT99+QT4T51/UVFRDw7hdb+WHdXgC+WV3MoOw4brswF51Gft4lSer6ZI+j1CUIFDzGWLzRvfCY4lDFyfvRVDQKTZ5AsNesjVhAq1XQaH+dgYKigE8VuH0qSgeCIRX495dbQ0EjQFW9m63Fda3UrRAANCfpx6O60RMKGvdbvaWSygbPCWqRJEnSkfl1/ZyXpBNAqwh07irwe/GZYli0qop5X28noArOGZHOucPTsei1wYMV2Fvr5vMf91DX6OGc4d3ITIlE/yvpbVKBrUX1zFm4kcZmH+eOSKdgcOqB629FQBVU17tblNc3eVAUQvk865p9/Hd5Iet3VpPfO4ExA1OIO0bXcawE1JY7KAGobZRLkiR1NTJwlE4Izb5A6eBeppORTnUT2PQVVf/7CNQA+oSepGZejtcfDAQWfr+bpFgro/okIARU1Ht48PX/4Q8Er3vdjiruumIA/XtG/yoSnpfWuHjmnVWhxx99swODXss5Q9Navdc6nQYdcM7wdN7+dFPYc9npB5LAu/0qz/xrFWXVwdX3xRVNbCmq5f4bhx2zazkW4iLM9O4exabdBzIG9EyOIM5hPoGtkiRJ6jgZOErHlRBQXN3M8k1lWE16BufEEx9hQpykUZO2rpDa7z8MPfaV76RbxHfkpA1kc1EDAEtWl3Ba30RAsGlPDTazgdEDUzAbdWzYWc28b3bQt3s0J/vItaLAjlaGl/+7rJCxA5IxHLSK2BsQbCup55vVJaTGWRmRl8T1v+nDx0u2YzcbmHRODilRltDx5bWuUNC434adNeytduIwnTxfYzoN3HZRHj+sL2Pl5nIGZsVxWl4yhl9Jj7MkSb9+J883rvSrsLO8icfnLA89XvDdTh69ZQSxdsMJbNXR89eUtiws+pkBPUeEAsfM1ANphSJtRiaM6s5/vttJk8vHoOx4hvXtdsSvq9EoXa63VgiIsLa8j/FRZnQHBY0ajcKKjeW8+X8bAFi1Gb5cUchjt45geO94tFoNukPiKF0rUbWiEFbvycJu0nFufhrn5KehcPL3ukuSdGo5+b51pZOWUGDe19vCyjzeAOt3VaOcpB0u2siWs+yUhAy2l/uAYCBVMCQ4TCsERNmMvLtoC43NPoSAVZsrKCxroKPxj8lbjW7XUtSfP8ZYuw3V23Ju4ImUmRJJUow19FirUbjqrOywL5pmb4APvtwadp7T7aewvAmjLhg0Kgo0uPxsLq5n+95GHHYjA7PC3+sz87uRHGvlZKSqAqEKGTRKknTSkT2O0nHl9bXMWefxBgguPz7x/4gqCtQ0+SiqaESj0dAtwUZEe0Oh0d2x5IykefMPAGgsEThGX8FZdTbGDxMkx1mxG3UYA42Ixkri0dInzcbGoqZQFd+v3cvE0zMw6dqPHo2+WmrmPUmgsQqARuajXPgHlMSBXWZ+pN2kY9p1Qygsb8TjC5AabyMuwtihW3vwdIXKBg8Pv7UcpysYgHdLsHPXlQMYPSCFnSV1ZKdHk5EcgdEgv8IkSZKOJ/mtKx03ioALx2Tw/Ps/hco0GoV+mTFdZo5jRb2Hh95chssTTEDtsBl5YPJQoiytD6V7tVZMo6/DMuBshN+DEpGARxdJ5oHpeZhcpdR89AwBZx2gcGu/3/C+pQf/21IPQEqcFf1hgkYAUV0YChr3q1n8T2yXZePChMWg7RI9WDajlj7dHAcKDmmSxaDlsvFZvPWfDaEyq0lHtwQ7EExn9Mn3uzirn4P+sR60wk+p18Ku0noGZsQyKDOmS1ynJEnSqUgGjtJxldPNwZ+uHsQnP+zCbjFw/qgeJEWZu0JnIxqtwufL9oSCRoC6Jg9rtlZRMDClzeDWp5ggIr3V5/R4qf9qzr6gEUDA2k84Z9Qf+N+W4By96yb0pu1kNQcIv7dFmepxsfTnQt5bWsnlBb0Y1ic+bBFKV6SqgvyceKLsRr5eVUxqvJXT+qcQZdUjBPgDgrxEhew9CxDbtgPgMFnhrD+h0cQdk6BRo1Fo9gZQFDDptF3mh4wkSVJXIwNH6bjSaxT6dHPQt/sgAIQqukTQCMHFHSWVTuKiTFwyOBKbzsfueh0Vdc1oNAqBwJE3VOtvxlu6tUV5mtXLM78dSITNjNFo6tBQszamG4pWjwj4DhT2OZOFq+twunz8feFGIm0G+vWIPuJ2Hm8GrULfbg7694xG3TfXb/97oNcq5Nlr8ZRvDx2vup2YNn+OJulW1E6emu31qyzbXMHcxdvQajRceWYWPZMjaHb7iXOYMeu7diAuSZJ0PMnAUeo0ihLc2aMjPUKiKw41CsHFp3UjsXED2uUvInweekTGYz7n9wQCR5egOaC3YkzJxlOyJaxcbzJiW/wMqs6IZsSliIQc1MP8OXosCcRefj+Nyz4mUFeOkjOWLysT2Vt9YPj661XFDM4K9sopCvj9h2+3oijUOL2UVjkx6rWkxFqPOFjaX0dJpROjXkNqnK1DdbTWPiHA0FzJoXup+Mt3ohFewHREbTucTYV1/P2TjaHHr85fx7Xn9uZfizYTbTcy9bohRLeyWlySJOlU1CmBY21tLffeey+FhYUYDAbS09N55JFHiI7u+j0fUueodXpZu7OamnoPg7LiSI2zoD3JlkoLAXkxHqo/fyvUCeqvr8C9+HUiLpqOVznygMUn9EQU3EDN/GcINNWCosGefz6udYvx1QS3nqtbMAvT+dPQJGa1+54JASVKEj87LsJldKFptqJYAQ4Ejr27O9DV7qRp7WKE142133j80T0JtPOnvrfOxcNvLMOzb+FSz5RI7r5iAFZDRwbQ99VR6+LhNw/UkZ5o549XDcRmPPKvGCEExuReNB5Sbs4ZiV9jCm5P00m0WoUvVxS2KN9aWEu3BDu79zbwweJt3HZ+Ll2ma1ySJOkE6pQxGEVRuPnmm1m0aBH/+c9/SEtLY9asWZ1RtXQSqHf5ePit5by9cBP/WbqTh99axpbi+pMyxY5orGxR5qssRPEcGsZ0nNucRNQVjxBzxYPYr3iUgLMG1661Ycd4dqygvtnX6vkaAhibS9GWraOhcCurtpQz7/tSPly8jR3FdQzMDqapsZh0nNtLUPHewzSvX4Jr6zKq5j6Brnp7q/VCMBT69xdbQwEfwM6SenaWNnT4+oQCHyzeFlbHnrJGthUHF/8oCIyeKgy12zF5qlGUwwdggajuRI65CkWrB8DcKx9jn7F0/s58CslxthalUXYjDc7gnNKNO2tw+1tmA5AkSToVdUqPo8PhYNiwA1t/DRgwgPfee68zqpZOAjtLG0L/yO737qItPDQ5H91JtiOGxupoUaa1RyMMB5ZJq0JQWuOiuLKJSKuB9AQ7lsP0znm0doi0U17XTJSrucXzqtFOTaOnxZCoRgHtnhVUfT4bEMQCtw69gpf80ezY6+SnrZXMvGkYYwem0iMpgsCmBSDCo6umlQsxT8jGH2h5L3wBlaKKphblVXUuFCWqQ3Mv/QFBSWt11LvRaEBX8jPVn76MCPhQdAaiJ0zBl9QPIdr+bPg1JjS9zyYmYyioAQKmKDyi82fWBAIqZwxJY+nPpaFFUXaLnvhoCzUNwRyZ/XvFYtJrZIejJEkSx2COo6qqvPfeexQUFHR21VIX5WtlnprbG6ArTmM8nIAtCfuIi2n833yAYKBzzu14tFYQwd71tTtrePGDNaFzsrtF8aShCmYAACAASURBVIfL+mHSH35oN8JqRPQ9G3atATXYi6UYLZRZMnG0Mo/O4Kmm6os3OThqUVZ+yMXD7mHWXic6rYZom5HucdbgbjJtvnLrQZpRr+X0gSnMX7IjrLxnSmSHc0MatApjB6fy4Vfhyd17pTowuKup+uyV0IIe4fdS8+nLxE56Erchpt16VQEefTSKAo1uP5V1TVhMOmIjjG0O6SsKNLkD+FUVu1nfoSGV+Egjj/12OIUVTSgoaLUKr8z7GQimSpo4NlMGjZIkSft0euD46KOPYrFYmDRp0hGdFxPTcrjoSMTF2X/R+Se7E3n92W4/Oq2C/6BVx5eMyyQlMeK4taEzr1897VLs2cMIuBrRO+LRx6Rg3xeoVNW5mLNwY9jxWwprKat3M7R3YofqL9dnYbhkJk27NxBAR4UhFa8tiV5pUegPCT5dRcXhq6gBhIpJuAC4pCCT7imRaPel4HH3Hk7jyoWhoBQgcvgFWKPbfn8mjOpBvdPLN6uKMBt13HBeX/pkxGI5gvmJZw9Pp7HZy3+XFQbr+E0f+mbGouzd3CKNkPB7Ee4G4lK6d6juTbtrePiNH0PJwC8c05MrzszGvi+35v5773L7WLyqiLcXbsLl8TMiL4nJ5/UlsQO7y8TGQlb3YCDr8flJT4rA7fWTFGPFYe/cxTid7VT+7juVr12SThRFdGLCsqeffpotW7bw6quvYjAc2SrE6uqmo87PFhdnp7Ly6OegnexO9PUrCpTUuFjw7U6q6l2cM7w7/XpGY+xAUuvOcDyvv8kT4K4XlrToTb3nqoHkpke1e25ds4/v1+1l0+4aCgankp0eRWOTD7NJi8Oib7WHzxhooOa9+1GbD8w5VAwmGgqmsaXWwK7Sekb1SyY7NSLYI4rA2FSEa/03qD43ltwC/FHd210cE6wUGl1+tFoFm1GHqgo0GgW3T0Wrod1FO4pGQYhgn2aj24dWo8FqDCYjN3mrqXpnWljwqOiNiPMeJCI++bC9mn4heOqd1RSXN9KnZwxur58te2qZOXkY6XHWsHu/p9LJw28uA0Cv03Dn2fFkit3om8oxZQ5Fjc0MLq7pArwBwd6aZmoa3MQ5zCRGm9EdxaTgX/LZ9wYEu8oaWb+zitQ4O326R2Fvb5ekLqa1a5eBpCQde532LfHcc8+xfv16XnvttSMOGqWTmxCQHGXmjotyUYVAo3BMt8DTaDqW8ueXMqhOqCtB+NxoHUl4TPHYTFpOG5DCtz+VhI7TaTWH3TO52RfgyX+upLI22FO4cVcNZwxN46rxvYKbLbZxOV5dBDEX/pGahS8RaKhCa4vCnX8Dry2uoKi8ESFgxcZynr5jFBFmHQIFt60bulE3oCjg6UA6HgAEoaBBVQUuX4BlGyv45Ptd2Cx6rjk7h8wkG8pBQ94C2FPhZMF3O/F4/Zx/Wk96pUSgO+j++C2x6Apuw//1awifG0VvwjdyMu8trWbKJSmH/aB4fCqRNgMj87JYubkci1HPTefn4nS3XEi0p+xAEHHz2Dh6rHsNb1MdXsC5fgmOM25Ekzm2zc+OAOqcXpo9AWIijC22gNRoFBRFOerUTPsFhOCTH3bxyfe7Q2XXTejN2P5Jx21IXNEoLF1Tyjufbw6VdUuwc9+kQZg7MOVCkqRTV6cEjtu2bWP27Nl0796dK6+8EoDU1FRefvnlzqhe6kSKAnq1GSXgw2+wE1A7r1dQCNFuEPRLmbw1+Es34W+swpjWl4AjHT/6Y/JahkAjTV++hmfPOiA41zH2shm47elcOjYDm0nPkjUlJMdaufbcHGJshjavW1EUymqaQ0HjfotXFjFhRHcclravQQhw29NxXP4wiqeRBtXIg6+vw+k+sLuNxxegtslDhPnAn/MvCW4URWHllkr+8dkmAGoa3Dz59goe/e0IUqLNoeNKa5p59O/LQtddUbuBu68aSILDjF4TbHsgAGtcKWjzfo9D56bOb+LfX9UxOCe6Q7uTm41a+mfGhU0PWLOtkgcm57c4NibyQG9id1M9gaa6sOcbvvs30d0H49G0nBbjVwVLft7Le19sQVUFcQ4zf75mMLH24I/gWqeXlVsqKa1sYkReEj0S7eiPcuFXVYMnLGgEeHfRZgZkxrb7WehMjS5/izmpheWNlFY1k5Eke+0kSWpbpwSOvXr1YsuWLYc/UDqhtATQlm2g7qu/E3DWY809HfPQC/HoWq4k7mqMvjpq5z+Jvz6YLqfxf/OJ/s0UlNSWAURnENV78FcV4jjtUgAUrQ7Xpu8wDEvGZtRz2bienDeqO3qtBu2+Hla/KvD4Vcx6LRoFdMKDtnY33tKt9LTFc9O4eN76puJAgKkobSxZacmrsYLZisflC5tLCsEez8gjSFCtKOD0BCivdWE26oiLDF9s4guofPrD7hbnbSmsJTXGjBDB3rflG8tD13L6wBQcdhPPv/cTZoOWq8/OITs1Eg2C3t2jeXzODqrq3UA9VrOeM4d262CiePh6VVFYmaoKthXV0f2QNDo9Eu307hHFpl21KK2EpCLgb/NXTVmti38tOtD7Vlnn4s1PNvCnKwfg9AR4+K3locwBS34q4daLchneO+GotiZsrbfUHxC4vQE4ToGjKgS+VlIM+X9hb6okSb9+J8+EFukX0zWWULXgudBj57qvUXQGtMOuRFWVfb2RLhTVh19nIyC6zlZrorowFDTuV//NOziu7gv8soVVrfE6G3CMnEjNN+8ifME9TGy5p6PzN+PTRiJUQkOZiqJQVOXktQXrKa5oYlB2PDdMyCGy+H9UfTUnVOeAuHSuGHkZ739fAcC5I9KDAd8RBB8RFj1TLu3Pix+swR9Q0WkVplzaL7TPc0dUNHh4fM6KUCB0xtA0Ljk9IzQnVatRiLIbKa8JTxtkP2Qepmnf4hm7RU+sw8y8rw/ki3z2X6t4+ObhpMVaiDTrmTk5n+JKJ6oqSI23EWnWdai9GiW48vtQep2GQ/srLQYtv7+kP3urm4kwNuI1WhCeA9cQMexCfHp7qwnEq+rcLcq27KnF5VPZU9bYIt3Ue19spX9mbIvh7I6Id5ixmvWhxT4AyTFWou3GI67raEWYdRQMSePLFQeCcptZf9gpF5IkSTJwPIUEaopblDk3LCFm0Hn4dFb0VVup+/JN/A3VWPuchiX/Ytz69hd8HC8i4G1RpnpcKOoxSMysKPjtSbgXvxoKGgGa1i/BnDsWIiLDDq9r9vL4nBWhBNirt1QwLttE+rL3w9tbuYfThwh21SWS3yeBnG5RrQaNigIarQaxbw/nMALyujt4Zsooahs9JMRaseiUDgeNKvDPzzeHBUJfrihiWN9EMhKDQ5QKcOWZ2Tzy1rLQ68dEmMhKO9AzraqCIdnxLFiyg7zMWJZvLG/xWmt3VJEen46qCmxGHTmpB943IUAowcHq9vKBK8AlBb148u0VoTKjQUvfHtGtXrNJp6FHgg1FsRFx+QO41n6Jr7IQS7/xaFLy8LbRoRYd0TJoy0iJxKTTtNqr6A+oRz0lw27SMeOGobz1nw1sL64nLyOG6yf0xqA9jjlPBVw0uidJMVaW/FRCz5RIJozsHpwnK1MPSZLUDhk4nkI05pZzl3SOBFStAUNTGZUfPRNKHu3c8C0A+tNu6BI9j9roNBSdIWx1rn3IbwgYOz/lj6JArVvBXNcyGAo01UJEj7Cy8prmsF1TADweT4s0NAB2I0y5OLfNPaSNnipcu9fSXFOBN6E3geieRDnsYUPaQoDDosdh0R/xqlqPT2VbYV2L8pp6dyhwBOgWZ+HxW0ews7QBs1FHz+QIIg5ZcRsfaeLR346gqLIJZ7OPovLwdkTZja0GXQFVsK20gflLdqAoChPHZpCRZG9z5XZGkp2Hbx7Ojxv2YrcYGJITT5zd2G6AI0Rwxx7tiOvQKyoBVcHfzvFJMRYmjs3ko2+CvaYRVgM3X5CLTqOQlmDHbNSFEoRDMN3U/pXjR0oISIw0cd81g3D7VEx6LcczZtzPYtAyflAKpw9IRrNvwqkMGiVJOhwZOJ5ClOh0DMm98JbumxSv0eIYey1ujFBX2mLHEefGpcQOu4SALrKV2o4vjyWB2Mvvp3HZxwRqy7ANnYA+OhXXig+pQmDKzMcbkYbaCUGuUAXGyBi0CRkEysMTY2sj4jh0hprFFJyXZjRoSYmzUVHTzP92+ujfZzTNG5aEjlMMZjSOZLxtBY2+WmrmPY7aVAuAZt3niFE3Uc4QEh3mVs85Uma9lv5ZsSzfEB4Ux0dbwg8UkBBpIiGy7fQ1QgjiIozEO0xER5jYsKs6NP/SYTPSp3vrvYI7y5t45p1VocdP/WMl99+YT8+E1qccaIC0WAvdCzIRItjb2eEeVlWgdmAmqV6jMGF4GsP7JuB0+4iNNGMzahECoq16Hrp5GF+tKKK4oonxQ9Pokx71i1f2axXliPYDPxZUVQSTpMuAUZKkDpKB4ynEo4vAfu6dUFOE8LrQRCfjMQeTVitGS4vjtfZo0HaN1EpCgNvWDfOZv0NRfWibyql4/+EDia5XfkbcFTNxR3TvlNeLsNvQFkzG+flf8dfuRdEZiBp/Az5bUotjExxmbrkol7pGD7tLG8jLiKVvz2g2VFro3j8C/e5l6OK6ETH8YtzGmDb/kRbVe0JB436GtfOpsvREG5P2i9PA7HsVrhifxd4qJ0XlTWg1Cpef0YvkQwPHI1DT5GX2/HVcfXYOzfuSwSfFWGlo9rGjpB6H3UhKrBWTToNOp+GL5YUt6vhmdTFZ5/dpsycWIHDQoiCfKvD4VCyu8DBeo1Fw7cs9eaR5ETUoxNqNxO6ba7g/OBUC4uxGrj6jFyigBmSUJUnSqUsGjqcYr9YOcX1aPhHVDWNabzxFwRQsKBqizpiMV2PuUr0RflWDVmvGu/HbsN1RECpNa77APXwyVoO2w6uV26KgoNpTiLrsQXBWIwxmvIbWe9B0WoUdRfUs3r/6d1M5a7dVkpHm4N1tyQzpeS21TYJr9IlY2hteDfhblKleN0YdfPNzKd0S7KTGWNBqFFy+AMWVTtburiUhykx8pKnD1xxl0TPj+qHUNHow6bU4rPqjuseCYJqaqno3Q3on8P5/t6AKQUK0hfy+icz/5kBv7ZgBKVxzZhZabXAxzaGCC28O3whFUSiubmb2x+sormiid/dorjorG6/XT3KslVWbK5i3ZAd2i55J5+SQmWSn42vX23c8codKkiR1dTJwlADwam3YzpqCrbYQ4XaijUrGa0vqcnOeFAWM3lp83parYP1eN6/MW8uwvomclpfY7m4nHeXGBNaU4IM23ovaJi/f/FSE0aDF4w0Gs7v2NjA8L4mKWhefrgrmb/zNaC+WqPAhZ0URGNxViMYqdI64FvM46Xs2FW4Df/8kmE9yyqX9ye0Zzd/mr2f9zup9dcCfrxlM77Tw/aWVdhKxGzQKifuHoY/iHvsCgq9+KmHuV1tRBcQ5zFz/mz58/O12RvVLbrH39bdrSjgzP42UaAtnDO3Gd2tKQsPaOq2GMQNSwnoU29Lg8vHE2ytC8w23FdXirSoipWEtyqpt9E0eiKtfCu9+X8mTb6/k4VuGkxZz9L2pR6K991uSJOnXQgaOUohXa4PYYG+kD7pUT+N+RlcFlR88imPUxTg3Lg17rjFtFFtX1bJ5Ty056VHtzs/rTEKBq87MocnlxW4xsKWwlhUby8PePpNB26KnTVHAULmZqgX/DwJ+tFYHcef/nob13xKoK0fTexwVEb15d/7W0Dn/+HQjU68bEgoaIRisvPF/G3j81uEYtRpUISirdeP1eUmxeDDpdQhzFKoIBtIajUKzN4CigEmnPapchKU1zXzw5YF2Vda5+H5tKeeflkFKnBVfK0PO+4O9pKjgopq1O6pRgH6ZsSREtr/YZb/yWlfYIpVrRseTsOp1vI3BVE3aki2MyBrNuh4DWLernnU7q+kebyFwDBbf72fy1eIv24rqrMOYnI0vIvXwWzxKkiSdpOS3m3TSUBQF7+6fUN2NNK1bQvT463Ht+AkhVNy9zmDOD97QHtLVDe7jEjj6VMGCb3ey9OfSUNnZw9PJ75tAQ1MwlY9Go3D7Jf2IMIWnOjH4G6n5/G+wb4g64KyjYv7/I/76Z1AtMWwscfH4nOVhr+fy+PG1sjy4rtGNLyAwaqGoqhmjv5GEXV/h3rgYt6JgG3I+xrwzcSoWdhXXs724jlWbKzh3RHcGZMSgO8JdUKrrw3t8c7pHMSg7HptZT2Ozj+F5ify4riz0vNmoIyHKEko+7vUFyO8dj8NqQBzBYhebOTz47ml1ojaG5/dUty6lIH8463bVowBOt4pJ3/mZAQQQaKqiZuEzBOorQuUxF96DmthP9j5KkvSrJANH6aShKBBorAHAW7GHmsX/xJSajS4mhffXKWwqbgodGxtpDjuvstFLcUUTRr2GtHg7NmPnrGatrHeHBY0AXywv5InbR6IB+mfGEhNpIsp6YEtCRVGoa/ZhcdWhNje0qDNQX4HXGE+03YBRrw1L9XPWsHQiLPoW+3WP6p+MzaDFpwoq61xkONfhX/9F6PmmZfNRolN4Y7WJFZsqsJr1XDC6J18sL8RhM5J5hNvMxUQEg/Lx/aIZl6nDatTy7c4a3l1UgQBuuiAXk17L0p/30j0pghvP60OEWcfeWjdPv7OS+iYvWo3CdRN6M7JvQpvTCpyeAIUVjbi9AVLibMRFGjlrWDf+u2z/AptWzlOCeS2jI0zB5OHHKNXN7oom7JVbMR4UNEIwMX3k5Q/jVTpnJbwkSVJXIgNHqcvzBlSKqpyUVDgZ3W0g/LQo+IRQcRdtwpZzOjvXBHcI0Wk13HxB39DKWICSGhcPvbEstJ1aYoyFqdcOaZGXsCMOnce2f07jwVRVIFRBbKQprB37FVc389ic5fxmUAzjIhNQ6w9KjaNo0NhjAYi2GXnwpmF8+PU2SiudjBucyqi8JKxGDTOuH8qb/7eB8tpmTh+UwrVjktDXbEWvBsiIjsW6bSWeQ17Xs30F1Q3BLRqdLh/v/XcL157bmxWbyshOjejQHMP9kmPMPHxVL6I3zUX95mcACmK60e2cK/nLZ2X889NNXDehN1ERZpqavcTYjXj9gpfn/Ux9U3AOZ0AV/P2TjfRKdZDoaNk73OQJ8Oy/VlFUEfxBoNEoPHBjPqMHpBAXZcHnV9HH6tBEp6DWlITOM/Y7k4A1ljOGRhJhNWDWH91wfLsU+Ojr7dzUp2WuzoC7KZiY/sRm2pEkSTomZOAodW0KLF5dwgdfBXNPru5u57bxdyBWz0f4PegHns9nhRGMGWAnMdZKSpyV+AhjaH6mAP791bawPXjLqpvZVlzH4MzYDjfD6KtFLd9GoLEGQ1IvAo50/OhIiDbjsBmpazoQpmWkRLa5fZxfFbz5nw14vAEWrqpm0AXXE7N5Hsb47qhqAFOPgXjN8cG2C0Giw8TvJubh9wuMek2wl1FAjwQbD96Uj06vw+SupP6TZ2moDPbCWVJz0Mb3hOLw/eM99lSq613hZb4ACdFW1CPM9KPXauihKaW28OdQmVpdSM/kDSTGpFJW7aLe6WX+N9u5+YK+GHUaapxeSiqdLeqqbnC3GjjuLmsIBY0QDMj/tWgzf7x6IDuK6vhxQxlzFZhy1jX071uCWr4dY8YQijSp7Nwe3PoxPd7W+UEjwc+Vy+OnUokhUaMNW+FvH3QuPr2t1a0NJUmSTnYycJS6tDqnj7kH7YG8dncjf9yrY9atMxCqylMfbWVXaRkQnE9395UDiT8oaAuogopD9lwGqGnwoChKh4IKo7+BugWz8B/UqxV17h2UReShBgT3XTeEj77ezpbCWgZlx3Ph6J7o25gz6PWr7CkLDk97fAFWFPmZ0DOf5rWL0JismHvloxyyKkkRoNcqLdLB6DUKcdEWan5ch6/yQG5Eb/FmbAPPxr3lf6jO4C4xWkcCpfZs6pvCt500GbTkpEezsbAWhy2YzHv/sLFPFVTWu3G6fMRHmXFYDAgh8PhViqucdCvc1OL6DBUb6RGfhdcn0GoUbp/Yj34Z0QghsJp0JMdYKa0ODx7bCrIbmn3otAoZKQ6cbh/FFU1U1LoQKlw/IYcz87tRVNFEjV/liRXNWM2juH5oDik2A1ektXy/OqLG6aW6wU203US0zdDmKLcGOH90T97872buKfgDti2fojRVY+p3BvqskW1ubShJknSy0z700EMPnehGALhc3qOeTG61GmlubjlkdKroKtevUQRGTyWa2j3o8ILBwr59KVplDDSgrdqGpmY3Bp1ANdgQh/xT3eDy8cXyorCyxCgTZ/dSCGxZQn50PcMGdGddqR+XJ4DT7WN0v+RQ0KDTakBRWLejOqyOS8f1IrKVfIKt0VZtw7l/eHwfb+kWPGn5vLZwO6s2l3PxuAwuHpPBkKw4TLq2r1mr1VBS5WRvlZMIq4Ebc2oJ/PgewutCbW6gedP32HsNwm90tFnHwaxWI/UrP8NXuSesPNBcBwW/R5uWi63vaLbYh+E2xbB6S0XovenfK5Yxg1J55M1lfLumlMWrijGb9PRIjsCvwkdLdvD6/21g6c+lfLO6hP5ZcThsBr5bV4bL4yfSqKIp+insdf2Zo/lfVRS3XZxHYrSVrcV11Dd5ibQbsei15PSMYfnGcrx+FY0Ck87JoU96FJpW5jiajDoSYqzUNrpJjLEyfmg3slIjyY1X0VdsxuYsRBUq85dVUljhpKK2mUaXj0FZcUf8XaIosHp7NU/8YyXfrSnlqxWFpCZGkNxOKp+YSBPJcXY++LEGX9pgup12Dsa0XALK8Uua31X+9k+E1q7dam39R4gkSZ1H9jhKnUKjUdDtXUvlf/4SGraLHHMV2pzxraYmMQYaaFj4At6ynftKFGIu/jO++D5h/+hH20xkpkayvbg+eJQCd46z0jDv0dAWidHaRUyd8Gfufb+ElFhb2PmqKhjRN5Fmt59Pf9iN1aznugm9SYnp+MKF1vacVt3N7CmppXDf/syP/30F064fui/hdDvvE3DNWdnUN3noEavHuP3TFlsYeos2oOT26PAQq6nnQJr37S2+nzuhP099uJukGAtTJvYh2e7jy+WFXDa+F4GAIMJmIKdbFP9dvoeLx2aiACWVTXzw1TZ6pUXR5PJhsxrQaRX8AYHHF2D2x+uYet0QvltTQs+USBp1Dkb3HIa6cxkA+pRsTHmnc1O/SNbvqOatTzYCwa0YLx+VyJn9Y4gxW7jqrCyaXH7iosxkpkSibaV3VlGgpMrJPz870Ku5bH0Zr9zWh5p5TxCoD66kjlM03DX2D0z/2I0/INhWVIvPL3D7AhRWNCGEIDXORpRV324wWev08er8daGgWhUwe/5anplyGo42fmDoNQp53aPI7RGNlgCGpr0ESnaisTgI2JPxa2QQI0nSr48MHKVOoffUUvP5q2Fzveq/fY/YtFwC+xNoH0RUFx4UNAII6r5+G+3592Oy2EP/yOs0MOWSfiz4bhert1QwbnAKkYWf4j54X+2AH1vFz1w+fiSDc+JbbM1nMWi5YFR3zhiahlZRMOqUI+qR0kantEjMrc8t4MsNjWHHLV5ZxM+RJs4cmkakue3eTIdFz73XDMLn8aD5Ogpqwldla8wR+I+ggUp8Fvb882lc+SmoKppeI1npTqO8ppLymmbqnF6KKpx8vix8q78rz8yirLqZRT8Gy7O7RXHR6RlsK67jgy+3EhdlZuK4XqF8jSWVTgKqwGEzUlnn4qttVRRmD2PM8FEoQqVWiSSyRs/WwlK+WB7sAY20GZh+dgSW1W9R+3M52rjupPa9gscXB3sdczNi+MOl/Vqsqvap8OHibWFlAVUlULY9FDQCIFSsm/7DzMsnsausGZc2Ardf5cE3loXmnVpNOh68aTix9rZ7AhubvWHzYAH8AUGD0xsKHBUF9KoLTcCDX2/HL4KrXzQItEWrqPz05dC5tsHnoh90MQGNAejYlIiw11YFZbUuahrcxDrMJESaWg2wJUmSjrfOT24mnZq8TlRPy7mEanN9q4eLVo4NNFazfutemg+ZIBZp1nPd2dk8dftIJo7uAb5D1wtDwNNMt0Q7UTY9JTXNbN/bQKPHz/54RKgCs06DQXtkQSOAx5xI7OX3Y+reD11EHPZRl7E7egQ79zaFHWcx6Vm9pYLXFqynvQXKigIGrQazyUTEyEtAOfBnqLVGokvpfUTt82qtaAdOxHLFk+wc+kfeqBzMP78LBld2ix6LSQ8K9OkRHXbeik3lwaH8fbYU1qJRlFDOw8paF4GAiskQDJDyMmKIMOsZ0ieBvj1iAPhxSx3PfFrF05/VYI1w8PKHP6PXafDuSyE0aVQ05qWvhFaOByp3E/3THM4fElyYtH5HNXXOQ/tcAdEyi45ep0XxNrU81FlDatm3DNz8V86N3cPuwoqwxUpOt58vVxahaSfwirIbMRvDf0ebjTqi9s2/VBAYqrdQ/8FDVL51F+6vXsXkDU5/MHhrqf3yzbBzm1Z9hlq/l6/X7GXRyiLK6twdTgskBHyxqpiZr//IC/9ew/2z/8d368uOWVohSZKkIyF7HKVOIcwOtBGxBBqq0EUlYozvjremFG1EXIuhWAj24qFoQsPNAErWaD7/uZ6kNA/m6EOGkoXApNMQCAR7c9y714Y93Rg/kL1VTSxbv5fv9uVVtJp03H9jfruJwNtaIKMTHrT1RQTqytDYolGj0jGffSca1YdPa8FY1oROuyfUS2XUa+mRHMHXq4ooq26mvtlLtLVlD1e9y8+GXdWU1TTTPzOWHgk9iLv6UXzlO9DoTWgTMqgQkewtacDl8ZMUY+nQPtQBoaBa41hdXsPPOysw6DSM6p/EuMHdmPX/2TvPMDnOKm3fVZ1zmJxzHuWco2XLli0b5wC2MV7C4sUYWGDJu7As7JI/dtfsGiecsy1ZyZKVg5WzZjQ55w7Tubuqvh897lFrJNuAAS/0/WOua2p6EqYeHwAAIABJREFUqt+3q7rq1HnP85ynj1CQZWHxtHyuWVDC/3vhOMFwjPJ8O8fPJ5tn9wz6iMkKohBfrh31RzDqNWSnmbj7mlqQFWZWpNMzEuKm5RW8ubcVgFtXVJKTbiQQjnHwTB+Lp+Wz7VAn6aIPJZpsFi57+imtipuea9Ui2otqQkf8EQbdQT5xdQ1v7mvjxPmh+P8pCrqcCi5+5DBVzcHfcAAlHMC9+X8oWvXlCZ9PR793QuClIoYm0I80OoxRY+Mfbqzlv147h9cfwWrS8oVbpmIZs2zSBfsZfPnHiYx6sOkgihTBuOoBiAZRLtECs7Ojlyc2xEf7/NbzfPdTc8h3vn/7wyFfmBcvyrY+tfEcU8rTcXzAutwUKVKk+FORChxT/N4oAnj8UQTAZtKCohARTTjXfIFYbwOxkR5CXQ3oC2ouG/BEzDk4rv8K3refQPYNI1Qs5KR+Bl2DQ5gN73FaCgo9qnzMVz6IcnoTiqjGV7qC/90b4prFBp7aNG5B4w/FeGLDOb5029Tx1LoAwYiMKELvcIDGTjdZDiNl+TbMY5k1UQClcSdD259K7MtYswD9wo/HTZ1lhbJsE//8d3M50zrCaCCKzazlpbfjN3ujXh03nr4If1jih08eZGAkbomzbncrn7txMhX5mXSIRpSYQkbAyHNbznCiOR4sXa4P9aXQiAK3r6zkitmFDLpDbNrfxgtbG7nzymp+t/EcO492k+EwcN91dbz1TgfTqzLYtL8drVpk5ZwiijP0TEvzE+5r45YyA2d8NvKKs1kxswCHWZvoLqNRiRRlGCnPtbBiRj6KomDUqghFJXLTTbT3jVJT7GTt4lJUhomZZUGtxS/FA6DbV1VhNWpQZAVBgMZuLz/+3WGksVrDa+YXU1XgIBCOsWhKLiNqBWnp59Edfxkl7MNcMx8lFkkYwwOYg70YdLqk1oQrZhSgXKCylmQZpWkX7u2PJ7blzrmDGxbVEoxBcY6V8lxLwttScvcllWEAhFqPYwl7kA0ONOl5RIfGVfeCSoMpM49rF0ls2NtGTJJZt7uNz15flzSOSxEIRScca0lWCIRiqcAxRYoUf3FSquq/Av6c8w9EJJ5/u5mHXz3J5oOdRGWZ0lwrapWIoNET2P8SgfOHkIOjRPpaCHecxFQzb4LSVEEgZsqgzz6ZBsMUnmuysfXkCJ+5YTLF2ebLvv+IL8pX//sA9rxCDgSL2eUp4Jn9bgx6LWX5Ng6fS+7iMeqPMG9yLjqNSCAi8equVh5+9RSHzw2SmWbkrXc6eetQJ01dHmbVZqEWBXSRYUbe+HlSNjQ61ImpchbSmNpZUcCsV1OQaeZo4wCv7mhJmIF/9Y5JFGndCCNtaIRYQl3e3DfKlovqDCeVp/Or54+x81gP+0/3kZdpQokEWDPFTFmumdaBCEcbB1kyPf+SbQGTjr0Q/zHgCvLTZ44w7AkxpSIeHHaN+SEGQjGOnx/iS3dMp7nbQ0ySWbOwlBPnh7i+Mopy/A2ip7eh7jxMgdxN3rR56A2mS6qeZVlBLQqoxfjyv0oUmFKZSdeAj/2n+hAFgSWzy7EYVIS7xwN687J78NvLuWZhCVX59kRQH4zK/Oh3h5MCvvOdbu5YVcX82kwsBjXrD3Tym7eHiBbORluzGOvIaYKndyWNyzxlOZbcEho7XKhEgVtWVDKrOjNRIyiKAr6BTiKbf5Xs5t57hnDedB7Z3EFDxwiLp+Unai/VsVGCZ5LfRzRaMUy5kohowlJSR3SgFcnnQmVNJzj3U/zr+iG6B33cvKKCY42DWE0aFkzKed8e8BqNit3He4hEx8+/NJuea+YXTzgH/pavfSlVdYoUfxlSGccUHxhBgGPNQ2w7FLfHURSF9XvaKMuzM7XUiRgYTAoQAGKuPvD2gb1swv5EIC83A53ZSnpumDSbPi5geI8b66AniCwrPLO5kavnl5CZbqWuPJMZVZn4ghNvoPVlaTy2/gyzarLoGfKz5Z144NY16OPRdWe488pqntxwlvOdbnqG/JRmW+IiGCk2YV+xUIAzbjfZZoVMhhEiPnS2bG5ZVsa8Sbl4/REKMwxkDLzD4LpHE/9nX3kvYtkiYrHk2s1Mh4GO/lH8ofh7qUSBOTkxaHkV6WALlSY7Sz52L/+xI0IkJl/W5kcQoM8T4uXtzRh0Kka84/V9Dque3ot8E8NRiZYeL0+8eZbbV1UR87l5cJoH6cBbqAwW0lbejeeddcQG2xFcnZBRd/kDQtxiSJYVFEUhzazloVunEIhI6DUqNKKAevI1ZBRNQvK58Kls/HqPnyNNRyjJtfLgrVOxjNUWBiOxpNpEiNspxWSF/WcHUKlEBl1BwhGJjUcG2Xx0kO9/bBHm1qMoY3Wv2twKVBllLM6zMbMyAxkw68Qkg/NARGKob5D0izKIyBIGJZ4NTrMaksUotjz0FbMInT/47qeOY+V9RFQWUBSC+mzM134V78gQ206M8Ob6kUQAfLbNRVm+jWvml7xvthHArFPx9U/M4uFXT9LeN0p5vo3719a/p81TihQpUvy5SAWOKT4woiiy53jvhO0Hz/YzszIdQbz06SSoLn+aqQSBLJv+PesQL8RqjGcUJFnhjd1xVbbFqGF+fTZWg5FPXF3DM5sbiMZkSvNs1Jel8/j6M9SWpHG2dZi7FmfjCUq8dcJFMBxLiDggbs4NIBudaHPKifSOG4+LehMHe6Cxt5UCzX5cYxY0CCLpN3yZsuy4jZAuNMDQtvjypyYtD0PJZKI9jRjy6sjPtKHXqgiNZSatJh2eCwKllVPSEff8luhAW3yOfjfSxl/w4A3fQ29QXzagdvmjfO9/DxCKSFQVOjBfsJwpywo6rWpCa8R3bWdUosBUsY3QjqcBiAKhrgacS25n5O3fJWr3RBE0YTeCIuETrQyMxjDp1Qy4g+w61kNxjpXplRnYjRpUgpAIBgVBgJEOhtf/Ejk4iqg3c8fce2kb0NHa4+Vsm4s51XHfRatBQ1G2NWGQDvCxpeX86rljDHtDqESBO66sZv+puNm7rMCPNrn40V3fxRQaQNDowJ5PWGUGWUmIfDyBGCpRxKxXJTrvDEaNZOrNyKFxsY2oN9MfMSKKo9y+qiqpY2BENCLO+wTqssVIAS+69DwCjnzUF2QsY4KOt5skXtqfnPVu7fHw2Y9NIucy9Y0qlYiiKIljoiiQbdfzj3fNoHc4gCTJGHWpS3WKFCk+GqSWqv8K+HPNXxBgZDTM2TZX0vYVMwsoyrSAWo8q6ifa35r4m750GpqapcjCh3PjM+pVqNUqzrXHxyCKAl+8bTq5DgOiACXZVuZOzsVp1SOKAut2t6IosHamk9WWc+Q1PEdltIHFCyfT6tVQkG3nZPMQBp2aG5aUoVOLyKgxlU1GCnqRPYNocivQr/g0P93Qz82TVehPvHzBiBTCXWeRS+YyEgRrdJjg6e3YZq9BNFrxn92LEotgzClBa89iZl0OXn8ElUpkakUmFQV2Do0tr187xYT1/IaLZqzgtVUgWbLRX6Ju0mTScbJ5iF3H4oKgEW+Iq+eXcKxxAAUYcge5en4xp1rGDdCvWVCCyajmaMMgi6qtZJ95NlkRryhos4qJDHVhnrkG1Bpo3IFr3c/xH9kIATcxUyZD3V2kW9TsOedm26EuDjcMMH9yLipRoHskgMsfZbinC/VbP0EOxbOeSiyCuvs42TOXcrDZR6bTSH2JE0UBURCoL0/nbNtIvBWiw8jUqozE3BQFEGDVnCIGXAFMBg13XVVDZmY6WLORjOlj9jdxfGGJ595u4uFXT7L9aBdOu5HsNCMgMCpryaish74GlLAflS0T45V/z4gqg+sXlZF7kTjLF47xrd8e5cVDXjack9h43MPkQgPZai8aosgaI4oSD2b3nEh+uFo2PY95tdlcXGkgKQqt/T7W7W2na9iPw2bApIsfY39Y4tcvneDl7c3sPt7DvpN9zKzNxqhNPgf+lq99qaXqFCn+MqQeY1N8YGRZYeHkXHYd72HQFV/SK8g0M60yg5gs0zoYJpKxlJJVNaiGmtHllBKwl7Hu8CAalciksjQyrPo/qnewShBYPaeQmdWZePwRMu0GygsdjIyMBSaKgt2k5WjjAI0d8XZ7GXYDOa5jyMfeACAWDqDe+Z98asWXORsQWD2vmCXT8rAZ1GgjbqTes4z2thHKnspw7gq2nvZy6ncdXDWvBJ3cOmFM0ugI5xq7+H+b+vnWLWXk5VcjBbz4TsVNueWQn4GXf0LGnd8n05rLHauqeG1XC1sPdTCzJot71tTy2o5mArIG0WBBDib7Q4ZFIwFv6PJG1BcsYSoKrNvTwr3X1jHkCaIWRdJsBu5dU4s/FEOrFvH6I+Q4TcyqyaKxy0utdmK2V9AaSL/hy4SNWWiHGhja/mR8u0qDrbAcZc//w+AdQtDo+OKSe/hJ1ERzX4DuIT9yNEqGMog+NIjJYmH0IuslJRrGqYofr/rStKTWgOlmLQ/cMpUzLcN4/VEcVh33r60nEI4RjkhsPtDOkCvIt+6djVoUyM2yMjiY/HlBXLD/9pEu3j4cb7HoD8V4+JWTfPPe2by6o5lTLcPotSqun3MPS6+wIRqtiAYLUy/Tvrx7yM+w993sq8A3rssm48AvGXL1Iqi12Jd+HKFkLsXZFtYsLGH9nvgDS1WRg5WzCiec84IAZ9rc/OzZ8c476/e08i/3z8Vh0tLQ5eZs+/gDmtsXZv3eNu6+supP0ns7RYoUKT4oqcAxxe+F3ajhO5+cTe9wAEEQyEkzYtSInGxz8dNn4jdBtUqgMLucT1XU8c1f70sEBjqNin++fy4Z1j8uK6AWBXIcBnIc8ayQSpVc+6UW4P7r6vnZM0fpGfazpMaKeP4FLqpoIyPWxwG/hUhUIhSJoY7F8G74ZcKYXAAyplxLr6sArz+KShRwi3YMCFy4bqzOr+NIRzzz8bM3OvjNp+5i6NnvJb+ZIiONdIMxF6tezQ2LS1k2I59j54fYuK+N5TMLyC/JwJD5SfwbfzUuzKlayrZmmeuKLm9enZ9hoiDLTGd/fNm1bziAxxehpdtDVaGTX794POn1TquewiwLOp2KzGwH2oybiK3/WeLvosGCrmwGAW0mAgKR3nFrGFPtAryHNyB546pvJRom+NZv+Pza73B0yIDNrMXQexphx8PIgGrBjRPM0xFVBAQjN6/IpjTbkjS2cDRKS2MrbT0h+n0K0/IEKqV21DE3QWseFWur6PVImLSq9+xFHY4p7DjaPWH7+U43rb3xpfBQROLZXX00Dcp89vq891zxuPBvC2sdZDS+jOSKZxaVWATXW4+QfkcxVoOd2+plbppciUflxGA0oL6EsCgqKTw7Zqz+LoFQjOYeL7OrMujsnxgMN3S4iMnyBLP0FClSpPhz8qEFjj/60Y/YtGkT3d3dvPHGG1RWVn5Yu07xEcOoUVF2wQ0/Iik8vXlcFBOTFFq6PTR0uJPuuOGoxMFzA6yZW/ieN/0PgzSzlm9/chZuXwSnEeTN6UmWLQAjEQ2v72ohJilsPdTJbz+Ze1E3G1BObuDq6Q/x6z4fVpOWZ/cF+NY1DxLc/iiS340qr5bQtFuwNkqAC18wytkhFdkmOzFvskeioB1f/rQaNBw8O8DLb8frKF96u4mX3m7iS7dNofi67zDa30VYNLGtWaasJAen5fLBtlGj4st3TKex003vUHwJ93jTICebhplVkzXh9TOqM9lzoofCbAvBUJRoehXpt36HcPsJVEYbmoI6AtoMAAKRGBrjeBpObU3Dd/LiOlcFT183v90Q5u5lOcw8/wzvalF8p3Zhn/8xXLuejwfDgohhyT1YrIWsyjKhuqAHgT4yTHT/S9Q1HWJSeiHWq+/Gs+1R5IH4MTEB5vmfIL926fuePxpRoDDLzIh33F9Rp1ExuUBP9hIn7S6Iqo2YDBo0apGYDDq1cNn95meYcFp0jIyGqc/VIh1KDvoQRFSxEEPPfgvJH890m2esRjvtOqLCxPaWisKEDkcQr92VZYXqIievkXwuLpiUk1Cwp0iRIsVfig9NprdixQqeeuop8vImtpdL8dFE+JAyF7LCBPEFQDQmTXgPfzDCe72tRoiik0ZRCRNvqr8vWpVIpk2PWqPHuvA2EMfrw1S2LJrC6cTGfPoyHQbCoUvUiskxNKKCzaylrtjBP941i32ebPYW30/TrK/wrLSKrzzRTE6aMdFhJaQyYF9+d/K8MgoRnIWJ34Woj3LtAF+/2smySeMdXXpGQmw+L9OmrcBtKuTqpbUsmpTznl/UsCSjUYnMqsxgdk0WT244mzDN7h3yc/c1tRj1akQB5tbnYDZqsZl1XL+ohGvnFWHQ6wnbShCnXo9csZSQLh40iqLA3lP97BuwIGaUxD+OkA+VxTlhDDGtlTSbnoI0HXJgPFsW8wzgO7UD69ovE17yAOGrvskvDxv53qMH6RgIjn8+RPC+9T+EzsVrQqW+Jug9nQgaExx+EX3k0t2ILkQU4OYVlejGjonZoOGHN2Vg2/kTSt/5d64YeZrrqhVikoxOLbL9WDfPb2+mtd+HdInITKdR8dCd07lhaRkxlQF1WvJ1zlA6Fff+VxJBI8S7xwjuzkuOT6sWuXFZedI2jVqkLNcGQEm2mZuWl6NWxb8ss2uzWDz1vbOiKVKkSPHn4EPLOM6cOfPD2lWKPzEaKYAw0kpssA1NWgFKeilR1eW9E98Pg0bk+iVlPLruTGKbWiVSnm9PGDm/y9y6HCRJQVIURkYjaNQidpMGAQGdtw3PjieJDnZhrJqDceZaQtrxIEUUL58RuhhBiP8QRIjGFCL2Emy3fJeR9mYUtQ63IZf/eX48KFk2o4Djg6PUGa3IgXFVr65yHrIpjW9/sgKzTk1UUThwup+TYwbd73KscYiibCuZTiPFWVaimhrSb/8e0nAXot6MkF5MWG0F4pk197pfYB+KWwPdUDCFnIUreXr3IE6rjlUz8+ML4ZeYqyAISIqCKAjEJJnN+9v53cazSLLCjUvLmVGdSUmOldZeL9cuKsXjC6MoMh9bWk4kJnOiaZBTzUN8+745aEMjyP1NyAEPmuxyotZ85AsuCYGIxOu7mvEFo/jn3Uh9RYiIRiBn1VRGXvtZYvnZNPt6dKKbb0/tw2TLJDLlCgwaFSqjFUWWQRCQDA68SiHHzw9SmOdkZn0eXn+YeB4RxKCLcNe5pLkq0sSHESUSQisql+xG9C4RSeFE8zBbD3Vy99U16LRqatNixF77HtKYSCc23I2y+ZekVX8WQWNg3bbzjAaivLm3jS/cOpWppc5EkBaISDy24RyHzvaT4TBQVWhn8cr7GXnl3xKqc1PdQobW/eeEsci+YZgYZ6MoClPK0vnibdPYdKCdTIeBVXOKyLBqUZT4Q8/VcwpZMCkXSZaxm7Sp/rApUqT4SJCqcfwbQy1IRI6+ge/IuHrXWLMA3cJPEBP+sNpDWVaYXZ2JQatm44F20u0G1i4sJdOu46Hbp7H/dB+TytIxGTSYDGo8wRgPv3qSc+1xg+ablldwbb2OoRd+kAhG/Kd2IPndGFd9HmJRhKFmIr3n45m77EoiKutlxxOMSBxtHmL74W7yM83UFDtw+yJkOUwEw5kUCx6yxRH+bmU2j2zrJxKTUYBHtw/wlas/S67vNJa0NCRRhzq9gJnWLGKykJhrXqZpQuBYlm8jJ81IfWkaGgFkIGwpImwooGvIj6s1TKbDT366idDpt4kOjRuBK53HmTZ3OjszTVQXOkBREAB3MEb3kA+VIJCfYUYUBY40DrDlYCd5GSZWzS7i8TfP4PXHP7NH15/BYtLyhVuncvBsH25fhJ1Hu9l9DK6aX4LNpGX13GKKsy2kq324X/lR3GdzjLS1DyFnT04ETCpRwKBTMxqI8vzeAZ4fe919a/IoWvZ1CoxB1Fot/v0vE+48M3bcNpD2sX/Eu+MpokPxbJug1WPJqefHTx5KWB6pRIF/und24mFAEjUTaiEFQUDQ6BIejQCGmgUoenuixNTlDTHii2A2qNGqxLjopH2E/3w53pKyoSMuMHnkk/moC2rQ5ZShyBLIEt4jm0lT+fnVxgFWzSnile3xsoGnNp2j6v55uEfDgEIoKnPobLzXttcXoaLAwZNH/Uye/SWKTEGsDgfYMtEX1RFqTa4nVVkzmOgIGkerEphU7GBKWTyylCUlOaOogO29uiilSJEixV+Aj8xVKS3tD894AWRkWN7/RX/FfND5h/ta6T6yMWlb4Owe7HPWoM/74+pS83NsLJtZgFoloFbHlwjTnGa0GhU/f+YIoahMeb6NgixLwk5HkhWee6uRK3PTkwUUxFu6pRPAe3IL3nfWJbbrS6biWPMArcMSI54QrmCMomwLOq2amCTz2rozvLazGYCmLjeHzvZz9fwSyi1+5L2/QfG7CQF1Jhvfu/kfePaQn7I8G4qi8PAOF99ensfItkdAjoEgknbV3+GYtBS1Jq5qnlGdxeGzAwy640utmQ4DxTlWHl13mp9/cSlWk5YBV4CYJPPW4U5e29nCLfMzSQ8GEdRZE/psAzgUFz++dSaxli0IKjVKbi0/erqTvjHz7kyHgXvW1PHIG/EAraNvlCMNg9y0rIKnNo1n6t462MEPPruANLuRh36+A4gnLt/cE1eD37W6muWzCvGdPZsUNAJ43n6S3E/+CLXJltj2uZumsOd4D6IgYLPoiEYlQlGJf3utk39/YBG6028kgkYAFBn/0Y2oDOZEVlCJhAgefo3qgvmcaPUkjvu2Qx205VjJy7RgNRkwTr8J4Z2nE7vyD/Viu+Fr+Pe/jDzShaF2EY6ZV6JxxMd3snmInz2zj0FXkOw0I1+8fTqluTbW7WlLmldFgR2VwYwky7h2PgeAqDPiWHI7LR4d/qA7SZkejkgcbhjgkddPA1BXmsYNS8t4ZXsz1yws4eXtTXh8ETaNvX7BZC1fvN2Bbvkn6H/xx8Q8/SCI2ObfgCm/Aqvhj7u2vR9/6LUvEpVo6nLT0u3BatJSWeggO830IY/uT8vf+nU/RYq/BB+ZwHF42PcHCyYyMiyXtOT4KCMIoI24UTxxOw/FmktETC6ilxSF7uEgjZ0uHBYdlfl2LPqJh+z3mb8uGORSTtLhQIDRD/kzdPkj7DnZS0uPl+uXltM16MOs17D3AnGFRi2yoMaBYIxboly4TCxo9UQDPrwH30zab6j1GP3NTXz1d+Oq2fvX1jOvNhNfKMb6Pcl1cb5gFLNJg6pjb1INmuT3kD5ynHRbPb994xS3rarCKrlQdv88HjQCKDLDm/6HbrLQpOdjN2goTDfxwC1T6R70oVaJGHQqfvHcUW5cVo7fH2b9nhZe39nC8pkFFOVY+eV9lUhbfons6Sd0LgtD6VSigxe2HhQw5Vcy8PS3E2pqQa3l/oVf4F9eiweOA64gDe0jWIwaRgPxkCwckSbU42U6jQwN+zDG3Hx9uZpAxEyD18RTuweQZQWnRcf59mGso8ndZACk0CiB0QDhQDyIGvFH2Ly/nepiJ0cbB9h0oB2nVc/tq6r45/vn8uSGc9zhmHjOKOEg4kUWP8pwF7kOLScucDPy+qIcbhjkyY0NPHjbNHaed7J2wRewSC5CKjPb+vSYWrQ0BZYzo97GrKmlDIRl+s72oVaLNLS7WDajAI1a5EzLMD949B3+9TPzsJvGFehz63MwGTSM9Paiaj6S2C6HA4ye3kWb5WOoVSJpJhVfW5OJSRrFkpGFR2Mg02FgwBXkdMswZXk2rCYtWrWIx5f8gLPnRA9rFpaQZU3Ddsu3EUaHQKsnZkhnxKeA7093bfpDr32CAGc6PPz7U4cT2wqzLHzlzumYtBP9Qj+KXGruqUAyRYo/PR+ZwPFvDZ2/l+EXf5DoXKEvmYpp2X1E1PELnyDA6TY3P7/A5y0nzcTXPzED80VdJNp6PHhGw6RZdejepy2ZYspAk1mc6E4CoLZlIlgmqm//GEZDMf7l0YOJFnLHGge5en4xgVCU4lwrJ84PUZpt4nPz1ejPvEpgcwDrjNXxzFDAiyCKqNLykRGTeka/y9CIN+n3R9edobbIgVotolKJxC6qjzPq1Cjd7RP2Iw+2MXfyFQQjEpIkMzlfR/DgRSIZWWJ0eJBHNvXzrXtmo1eL5DoNhKMxnlh/lkF3kDULSlg0KYdzHS6e3dLIx1fX8NY7HZxoGqCq4jyKJ77UGXP3ozJa0eZWEOmJ29zY5t/I6MntSfNUYhHyg43cu2YeNgNIfi+yKu7J+G7gCCT1Li7KtrBsej7SSBcjb/4YVXAUCzDbnoNlxV2sOxEgL8PCNx7ez4OrnGSLKrig7Z55+lVENWaQIRCV+NfHD5GfZebwuQGOn48rxEe8If7rpeM8dMd09p/q5dobJmHmLS58GLFMu4LhDQ8nfYSaqoUc3udL2ja1MoNnNsezpe+c7kWtN/KdN/oQBFAUH4un2Th/fpCeoQBXzK8gHFV4eksjR84NcP/1k3hmc0NC3LR4Wh6VhXY0YTcPzFUYqtQwLDgRHHkcOz+EKjoxuIr2t2FLl/n8TfVUR04T3vVYYh45i+/m1uV1PLWliRFviJYeDwWZZnTaiZdMtUrANRom26YnggmsY1m7j7CQJSwpPLb+TNK2jv5ROgd8VOfbLvNfKVKkSPEhBo7f//732bx5M0NDQ9x7773Y7XbWr1//Ye3+rwq1IDO678Wkdmeh1mOYJrVA9pT47zGZxy+6sPcO++kc8FNTEL+wR2WFbUe6eWFrI7IC2U4jX75zOk7T5T3/IoIe+9WfJ3BkA6HWo+gL6zDOvJbQHyGOuRRdg74JfYe3HuxkzcISqoqctPV4uWeuAc32nyb8FUWNFs+B14l54kGKoXoekckfQ5VXi9Q9/lmIlnTUzjwE4VyiJiwmyfhCUQrSjdy8vILfbRxfvs1wGOgd8lNXMhdajyWNyVCzkLJsC1XX1eHz+TEGOgjrTYlOJxDP/nkUM71DfQxVraIoAAAgAElEQVS4gxSmmxCB8mwL37h7FpKsYNSpUIkCe0/1cuvKSkwGNUtn5GPWSKib1iWJOdy7X8S8/F6Miz+OWq0BSwahdT+d8BlG/F5KnCGc515G6jiBOqOQggW38MONITy+COl2PeX5dj55bS1ZThPHGgc5fLafa8RdSSbisruXGdZB6j++mPV72giGYzyy28uXln8B87l1KL4hjFNWIhXPQ5bjVgsDriAj3hArZxXy/NZk6xlZgf6RuKn3I/uCfGbpP2Bu2oIgRbDMvhYhqwLb0rvw7HoWJRpGrFzIabGaG5baePtIF7KkcPX8YsIxmRuXVXC0cZB9p/r48ecXMrs2i7ZeL5kOI1aTlt4hP+X5dn76zBG+fvdM3jndz4pZBTzx5tlE0FiUZWRpUZSqLC2eTT/FO9yNFshVaYiu+CJ7T45w7Q3ZEwQ12pLppGWkU2AKEt76JBdGesFdT1J05df5lzUW/O4YxkwLoiOPsKJi55j46F1WzCqksX2EukI7kvSHRYsKMOILE40ppFl1aC5uM/MhoFbCqHx9KCE/aksGgjBxrKFLuCOkSJEixYV8aIHjN7/5Tb75zW9+WLv7q0YlhYj2NU/YHvP0I+SMebzJCv7gRO1oJDZ+Ye8ZDvDcBSbCfSMBntx4jgdunJykwIzKCj3DAYY8IdKsOnLTnKjm3EG0eg0nukMYutWU50t/8iUqRVHQalSgwPWLS8kKH+Zdlz1tThmh7oZE0AgQPLePkHMSrrIbKMosQW47gpxZQU/GPJ7b1sOVc4vZuK8NiJtaG/UaZBkWTsohN93EoXMD5GWYKM2zI0syeqcTw4Kb8R54DQDL7OsQc+sAiMVkTKPtuDb+GvvCm3HveRE56EPUGYnN/yTPvBWvyZRlUEQBQVHG1K8CqAQUWUEmrs5+4s2z9A7FA88Fk7KZXjQdBi+0ZVEIoaPFY6Ui14pKEjFPvyq5VhAQimbgPPwY0mB8jrHBDoy7fsU/3fANznt0FGZbaexwkek08pOnDhOJycyvSUNmYncbRjqx1mnpHnp36TvEP70SZtWM61kwK519vVGe+/VxpldncNeV1fHjBHj9YdJseoY9oaTdWcdau7X0+fnWuhCfXXsvVqOa5rCAZRgGqccw7yvkpxmQzWloh4IcOtGDzaTjyjlF7DrWRZouSn1ahNlzdYSX1+MaDfHC1kbuvqaW/3jqSFLpSnGOlXBU5uoFxaTZDCydkc+rO5ox6dV8Yb6AatcviM1fS3R4vHxBkaLoTq+jJv8KOmJOsmfegnj0FRQpiiqzlNjkaxkdgIjiQSdfJGFRZDKi3bi3PoYOkBCwX/cgnvRJ3HlVNefaXQy6g0wuTyMmKagEgX53EKtRgyAIaFUf3G8xHJN5Y28bG/a1oShQmmvlgZunfqjCGLUcInroJVzHtgDxh6HvXftFHnx2IGGlpRIF8tL/b9U4pkiR4s9Paqn6L0BUbcBQORvf0c1J29XpRUTGbjYmrZqr55fw6s7xAFOtEsnPGM8MDl10Mwc42TxMKCpj1MRDRwXYcqiTF7c1JV6zdnEpGXYD/ztW/A8wuSKdz11fj1b14Zh+5GeasZm1SfVgN6+oID/TQufAKAOuIAGnLhHgatPzCbafnrAfnb+Pbn0JT58qojqniub2MM374jWSi6blJd5r9bxifvPqKR66bSo6tUBBppnWXi+v72wmJinctboGq8lOozCD0iunkO4wIlicRKR45xlBFPC1HEMOjuLe8yKWScsQNFoEjZ4Xui0MebxMqUhnNBjhxe3NOK16ppSnk24ez+4qApxqHk4EjQB7TvaxdvosjPlNSF2nAQFN3TI2tul5/dAR1i4uo2/Yz5wKJ5Ou/DzRY+tApcVfsQpfWINzLGhMvEc0TJ7OR0Z9IW5/jHS7gdZuT0KtfLpzlND82Yj9ycGjrngqkUiM5TPzOTa27LxiViGiWuSnLzdht+j49PWTOHCml2ONA8yty2bB5Bx2HevmlpWVPL7+TMIdaFJZOmk2PeX5Npq6PNy8vIL+/mGsuSba+mVe3tECisLCqXkc75GpLtRSkmuluduD1x+hKNtCbbpEXefLyOfj49RnFDJSeSf9I0G8/ghmgyahFjfo1CyfWcAPHn0nMZ/sNCNrF5eij/nQHvoNikaTlCVO4O2nrsrKlqMD9A87WTv1QfQqiZP9EDvq5+OrqhCDWrx6c9IKgKA1oAQu9ItUcL31CAfLPk13QIvDokclCpxsGmb7kXhbQ0GAT6yuZe/JbtYsKKW20I7qA2QO2/t9vH24i/xMC/3Dflp6vGzY38Zty8sTSVBZgX5PkIGRIDazltw04+/1XVV5uxNBI8RLIeTdj/GVG7/AL19vIdNh4K6rqv/ork4pUqT460f13e9+97t/6UEABIORP9jc9lLN7j/KKIqAITMP2dVDzDMAKjW2RbdA/jRkYTyWL8iyYLfq6R8JUJpv5/M3TSbbPi46iMTkCW3VaoqdLJw8bhg94ovwq+ePJZVbNXa4qC52crJ5OLGtfyTArNpsbJfph/z7olOLzK7LxmTQYNCp+cRVNRj0ah5+5SSHzw0gSTILZ5YitB1MqKl12aVEB5MNk4Ply+iNWNh7spfmvgCu0fHjPLc+h7I8O3qdmtd3tTDgCjJvUi7nOtwcOz/EK9ubCUdlojGZw+cGyEk38ei6s2w9Mcz6d/qYPzmPjgEfb+xpAwFskgu6T6LEIoS7Gwl1nkVWG/BmTqW6JI3a4jR+8dwxmjrdnGwa4mzbCNNrstCP1ZX6wxKv7WqZsEQfRIeufCaGqnmEy5awvi+LjYfjwVtTp5tF0/LwRQV+uqEfd8Y0zJOX8v2Xu6nKMZA5dDipDhFAP/kKntozhNsX5rktDRRkWxJ9ucMRibScHIozDMhD7QhqDbb5NyEUTUcWNNgtenIyzHh9YTKdRtbvaUWSZO5ZaKPYd4w5mkbS7CbSnBamZcVYMclBVNQyb2oBFQV26srSiUkyFQV2Vs4sYNm0bKpVneSdehxD42aKnSJZJWUcbfPT3jfK0mm52FV+yjL1VJfnUpZv42z7CEut7dC0JzEnOeDBmZXJ/kErGpWKFbMK6er34QtGuWZBMduPdOG7IAPvC0aZU5eDTR3C2bYNRYpiqppL8CLFulJ7BVu67QiiwLl2Nwdb/OxrCtDcF6Ao24Jep8FkseAoqyHcfhIlEkRlduC86u/w7HkxyQpIiYYJFi3g2Z09uEbDXLuwlCc3nE16v6YuN3Prc3hs/RmmVmXieI+yEYhbDsVGh7gis5/FhkYW16djcTjZ3+hiybR8VKKAIMDh80P86MnDHDjdx46j3QQjErXFTizmD3btE0faCDYeSNomh/wUL17DwlkVLJ6S975j/ahxqeu+yZQKfFOk+FOTyjj+hQhpnBiv/AfMwREElYaIzknsosDZqFWxakY+S6bkohaFeIfkC16Tm2bi2oUlvLE7nrWxm3XcfXUNFy44B8OxCT7S8XZnE6P0aOyP79ZyIU6TlrULihFFkV5XgK/9557E+Nv7Rvmvt9R845Zvx4O0mIIpO5fIYBfRwXZAwDD1CjZ162l3DzO7NpsDp8ftY+xmHUPuIC+9PZ5JNerVHDs/yLAnxMmmIS5myBNKqJJn1mbT0Onmf187BcDp1mH+8boqDGYnii/emlBQa1FPXs3rL7WxdHo+D79yEogrwefXOLDoRfpHAthy456SoYhEXWkabb3Jwp2akjR2nOylrXeU65eWsfXIuEhHkhUy7AbW7WklHJHYeWqYXo/E9UvKeON4D1WzbkHc/+T4HOuXcHpEj8c/QnO3h+piJ9VFDsxGDaGwxIZ9bTy9e5CMm1ejWGZRkGVFyMsnOna8tSqB+bVZTC5P4xv/vQ+A+5ZlUnziYRQphqZiJprBk4SlQUaPbkEaHaFi6kr81WvYc8LNogojK3Mk1NFmRDkfi+hj6M3xPtfK6S1Mq1exJbOEBeVGZgR2EzuwmeH9enKX3MWeTht1xU50fVsn1Bxq+s+Rk76K/ad7yckwUVuaxqJpeWQ5jGx5p4OL0ahFsrPy0A1PI9xyFH/DARyLb8VzaANyKICqdhkHopUcOtfPvWvqOHS2P3H+iaJATUkaP3ryELesrOCaPB/GilmoDGbkkB/J55qQwVTl13GoIz7q3iF/wirpQvzBaGKZ/2jDAGXZlvd0i9DIAWxHnyTafQ4Z0LGLpVWLMc1ajlYtgqLgDcX47RvJ2fithzpZOj2PnKzL+5kmjd2WOWGbrqAGSWNGn7IWT5Eixe9BKnD8CxJFA4YxNfNl7i2yrFy2UF6rEli7oITlswpxe4Nk2o0YtWJScJlu05NhNyQ8ByFeD2gyJGcWnVY92WnGP2o+lxu/LEsMuIITMspn2lz0SXVE02eg6TtF9LnvY65fhKlqNoKoQiyaxtygFfXZfsoL7BRmWzh4pp/8TDNLZ+TzzunknskfX13DC1vPk59lJivNmDRnAItBQzAcr2VbNDWXpzeN99ce9oTY064wY8E/YBjtQSXIqNML6JDTGHC1YtBrsJm1GLQCn1+gxXDmVQSfD93oarTyXCKiEYtBQ2WBnaYiBw1jPpULp+RSnGMmzVpMpsPADx8/lDSmSWXptPeNJh2P851u/MEod1xVDXY1zuJy8A6A0cppj4UXd3djMmqYXZvF+U43//FU3GbGbtbxmRsm4/GF2Xqkh9Mtwzx4Ww7RkSDZhih6OYissxDBgE4tYjNr8fojFOvcaDOL0eVV4ju1EwCzMwvr7OtwbX0M37G3SCuZxuevyMX1+k/xjhl7iwYLjtWfm3DMhaZdXDl5GvXCeaKH4wI5JRjFvfG/uPlj/8TJkJGwUo/YeiTp/3Tls5geyGJgJIg/EKW+1MnmA+2M5lhZNaeIV3eMl22IokCG3UCPO4Rj+s3oVWpC5w8hxaLor/oCbW6RjLw80vv9/N3aLEryrDx0+3QOnO5DFAXK8+1EoxIZdgO5ZgXfrqeIufsT+1fbMnBc8wCeHU8hewdRF08jNOkG9j0x/qDisOhQq4SESAfiZRODrvh5l2YzoLzPMorg7SPafVHHnIZdrLhjNfLY/4Yj0iVFK/7QeF2mSiUCymXFOVFTNs7Vn8W19VGUSAhNRiG25fcQ5MNZYUiRIsXfDqnA8f84ogDl+XYGB+NZjovvUzq1yBdvn8bTmxo40zZCTbGDu1fXoteKXL+4lP2n+6gucrJmQXGiLvLDRBgLes2XWAK3W3QYtGrSjDKB7RuIKjK+kzvG/75UQ07Fcpo6XWzc14bZqKWiwE7XgI+eIT8ub4Q7r6rGadGTl2GitcfDiDeEazTEp66r53ynO1H4HxdXSImbvCwzIRP0+s4Wyu+Yhq08H0EAnVpFmU7ku5+ay5GGfmZUZ3FdtUzktR8gjUX6sR1PoNGoEcqW4AtF+MVzR5k7KYfbVlUBcLp5mDMtLoY8QW5cVMJXPz6DN3a30tDhYk5dNjqNmhe2NXLftfWcah5OjMntC6PTqBj2KyiWXGz5RXQM+nlq+xnqSpw0dLjISTfx7JZxcZTbF2bP8W6sRg1mLXzqujoOnOrmhsoogYOPM+pzoXbm4Fj993j0uaxZUMp/v3ICQVDQF9Xh2j5uvu3e+Tzpqz8d7+8tS0jufqLe4UQ3GAA5OEqocR/arGIi/W3jx9ychtmgQXd2z4SsorflBI8dzuXvV1eQXTmPSGM862monIWmZDortHYW1mejVgmEohKZjmpCYSnuoahRsflAO2lWPctmFPDoutP0jwT40h3TKV1wP+dsy+nzSGx8sptgOEZp7jBfu2sGalGgeyTIT54+Qn6mGUWBnUe7MejUPHDLVMyKHzkcSBpnzDNIJBQisOKreNxeNp/wUN2vpjzfzvSqTHRaFZIsc9/aep7f0ohrNExxjpWVswp5bP0ZqgrtTKlIJyIp7ymUUeRLqZgVNCK8u0huN+kozbPR0j1ec6nTqMhyGAhFYrQN+Nh+pBujXs3iqXlkO/QTHkQl1ChFc0m7swqiQRSDk+Af2CkqRYoUf9ukahz/Crjc/AUBjjQN84vnj3LD0nKWzSigqtCByaDBbtRQXeRgydQ8plWkY9BcXlEtKQqdQwGONw/jD0uYDRo0lynMFwTQxTyo3B1Egz42Hhngtb2dlOfbUKsE2nrjNjEqUeCBW6aS49CjFiSiZ3ckmXMD6ArrEDPLKS1wsPdkLx5/hO5BHzNqshhyBxn1Rzh4pp/rF5cRCEXHVNUKHX2jNHW5Wbu4jKXT8rhyTiGrZhViNGhwjYZwWg3MqcvCoNdwumW8zlOrFrl2YSmjwSj//copXny7iaIcG//+u8OcbXPRO+xnia0LoTdZ/Rxz9WKsXcSQT2br4S46+32cah7mVPMwA64AdaVOXtvZwozabLLteqZVZLBkah41xQ58wSgOix6NRuSqucVkOAzMqMniqrlFPPzKSTbtb2froU5yMiyYDGpKTEGWWNqYo20g02YgKBpo7Y+LpNQqgY/Pt7NIfZzJvt1kmxVqynNRb/tZYtlVDvoItx1DKprN45tbuH5JOcUFGShntiZl3CCughd1BiSfC6X+KoKdZ2DwIsW2oqDNqyHaN5aJE0T0Kz7NsOAkS+3BXDETfUENupxyokNdRPKns6ERth4fonrGdAqnzMBUWI3KnI7a4iSqMqESBQIRiV+8cJwXtjWx81g32w51ctvKSlbNKSQckXlhawMefzwsPdUyzJSqLP79hXOc6/YTk+IlF67RMEun56NSi/SNBNh7ohevP8Lo2HclJslUFNpJT7ejVQO9F9Qrimo6sxfznWdbcGakodGoqSy0E47KvLG7lRPnB7GYtCyenMfKmQUsn5XP3PpsRgMRVs0tQlHgN6+eYs/JXvKzrNjNOvrdIYZHw2i1qsT3R6XTEW07lmSfpC+Zirp6KfJY0YkowKSKDPpHAvSPBMjLMPHF26aRZTPQ0Onmn3/7Du19ozR1edhxtIvZdTmYL9EoQFFAUumRNBYk4f9+ziBV45gixV+G//tXj79yVIKMOjQMsoxsTCOqfPBD5g7Ee0LfdkUVT7x5Nkml+t375pBh1cUNpN8jYBcEeOfsAP/z2niNVU2JgwdunJIQhVyIzt/N8Ev/lrgRLqxaxohpMj98/BAP3jaN/CwLkaiMRiUiyzJqJYoYGMK+9A48u18gPGaKjahCW1BHSIEcu54ffmY+g54gbn+EaEymtcfLsCfE526czM+eOULXYFwRO6UinbuvqWXP8R7K8+0Me4J0DvjRa9WUZpl56Jap8ekqCkqJE/3VNRw5N4DNrGPl7AKGvSEa2l2093nJsBs4dn4oEYjEYjKSxjjhS6MyWlEEFWk2LbctyKbe6Ucv+xkVHWxpUhLL4+7RMOFIDJNBQyAUQyUKFOdaMRvUvLmvnUFXkNwMM15fhF+/eDyhSI9JCg+/coKHH5hK+tlHCY7GhTVSwx6um/4xzqZl0jMc4Nb5meSe+C3BMbNx+lpxLL8b10WtHKXREdRBFx39ozy+/gwHS5x8sThjwrFUGS1ER3owTF7Ba6fCLMqrxMC2pNeEC2bjyp1BRnY1ciiAZM3hicNBhkeamTw7D/fOp0GOIRqtOJbdxYERB+FIL7WFVooHd+HZtT2xL9FoxXLzd5F1TjoHfJzvHH+QyHbqMbhbMJzYzcpImDnXLuSxgzHOdvnw+iNjS7XJ2M06fKEYP3vuKNctKkOrFhPqc4CSXCvdAz6iUQmfu4ils29H17wTzGmIU66lsU2LUT/Cazub+fKdM9BpVWzaP16fuvlAB2V5NmZXZaDXxIUlMyrS2XSoi1fGltWD4RjvnOln36ledh3rAeJL3N+4ZxZOk5aIaMJx7UOEzu4g3HEKQ/kcNBVzCV+0hOwwanjgxskEwhI6jYhGFFCAF7edT3pdTFI4dn6I1bML/uBOXO9F3Jz9Q99tihQp/g+RChw/wmgkH5Ejb+A+tgUUGUPVHEzz7yCs+WCdHXyBCCaDhmFPMBE0QvxmtvlgBx+/ovJ9by6+kMSTG8ZrsOoLLdw8SUTfuQ+t2Q7OIiJj5uEaoni3P5GUPaHhbZYuqGfrSXj7cBfRmMSZ1rj45Nefriew5TFCrcdAELFMWYG+dBrR4W5M01YRNueBEr9RmXQqsBvQqFU8/MoJugb95KabOHC6PxE0Ahw/P8TCKbn8/Y2T+f6j7zDsjWfjBAG+cc9sSrPG7YwK0o2k2/RMLk+ne2CUDXtbOXRuMG7vM7+E442DRKLjS4nhqEQnOZRd2BpRELEuuJmQosZAkOXsJbhnNwBO4L7ln+SglIcggNsfpqHdT1v/KLNrsznf4aK528O0qkw+uaaWYW+YjbsauHOujTqDjq6gg2f2DhGKSMgKCK4u5NFxn0sA4cQ61kx9kN9sDVCXFkE+nZw1JJas8AYQNDq0pvHWbGdaR+ieOg2nZldCRSxo9Jjql6CfciWtfiPrHzlCMOJg7fQbEE6sR5FiiJULGU6bzJNvddPa864gqJVbV1ayolTBu/2/Eu8hB7y497+GZsrfA7C8xoB8aGfSuOSAF09XG6YyZyIr+C6fXWRG/dZPCI1119G3HuHeJQ/wtR6BqkIH6VYdaxaUsG6sJ7dKFPjU2joOnO5j3qRcjpzr56E7pvP4mMdmbYmTufU5vL6zmcXT8njt4DBvm0z88z1fY9+ZQV58og2HVc+tKyvZtL+dEW+IU83JnW8A9pzoZX5dNpIk4wlEcfsjbD4wLuQRBMjLNPPUBYb0rtEwG/a1cfX8EgZcAcwGEzlTb8Q8/QYkVIQv850UAbMueWXgUi99tzZSI8RQhTwoai0RtfV96y3fC0GAPneIhg43Oo1IZaEdp0mbCiJTpPgbJBU4fpTpa8B3dFPi12DDAbTZFQjVV3ygm4DdoiMnzYhrdDx4MBs0fP6qHMrsEjpvG5IhLRH4XYqYLCcK851WPfdN9iPu/jXvhoa64smYV36GiGhEJYUI9zRN2Mf/Z++8A+Mo7/T/mdnZ3le9y7IsS3KXeze2MTYYMJheEloI5VIgl54jhUshl8txKSQcgSQk9Gpsim3cce9NtmX13rW9z8zvj5VXXssh5ciFH9Hzl72aefedd2Z2nvmW5zHJCVKhk0SC4UR6saLYibFtH75zTi6qgu/IRtKu+iLdBUvYXtuHyzbA6DwbFr2ELxznsT8eZEFVPq09ibTr6Dw7bRfx6a1r8yAKQpI0QoJ8Prf+DN+4fSoaYeizmJyQNNpzogOXzcBdV45jzbY6HBYdbT1+LptVxI4jQ5JHv3q/h8du/wpKZw1CPIytuIKos4iYrBJsa0Q89UHKXAI7niNt/lf58T0Toa+JObkBIiUu1p3uYdfxBAl8b3cjda0DfPOmCu7NP01g3QbSgUyTjVEr7+c7b3QMLtHwrndVURhTYOe7nymmQNNJ/wV/D9YexDTrWoJ7Xh/8RMC+9C7Clgz+5bqJPL22mlAkzsZ6kc/e8F3oqQNBQMoaTciYhUSM7Fg3Dy3PZEN1mP/ozmblxC8yvsRBV8TEQFA+jzQmsG5nA0uuMRO6YC6yu4t0Q4zPXjOBXHMIQdSgyqnH5A3GCXojWExD0jA5aWbsvUdRLrCetDVt46u33UW6w4xeI3LVvGJmjc/GG4iS5TRR3+EhElN4Z1ciKiersGxGIaGoTF2rm9+uO8nYIif5WVYEIeHV/PyWZvZVJ8h3rzvM79+u5tblFTitei7WolZW6GT7sQ76PGFcdgOtXT7S7Ab6vWFEUWDJtALS7UZuurSM/ae6qGv1YNRLlOQ7+Oovdyaj2UunF3DdolJ0mr+Ciakqqy8pTdG31IgCU8ZkoAv34tvyO8JNxxNNTEvvJp4zIZn+/mvR0hvkO0/vTb5omo1avnvPzA91qRrBCEbwycQ/BXEUBNCHu4l31qLKMbTZY4iYc1DVj97W66OCRiMSuUCTDiBUsxtL5WJi8p9vZLHoNdx86Vjq273sPt6BIMDjt+Uj1GwldPgkamYh5rKZ6HMqiGguLuthNWqZNT6bPSc6ubrKjubgL1My25HGY1g8beAcgywZMRRNSEQQz4Nf40AUBlg+u5j6NjeXzSxkUrGV8NrXuRCR/g6au83094Z5eVM/mS4jX7l1Kmda3LT3BhBIENgr55fgC0YpyLYyc1wOb26rTXo452VYLuq6YzFKdHvCdPYHSLcbyUszsf1IO2t31APQMxCirtXNzcvKiQ069Gw+0MKXb5/K+t1NRGIyMyqz+Z8tXZxtEdFprfzbmFwyVZEedwjF7eHCWLAai2CWFKzVa4ic2pEkUyumXMOZ7GwaOxMk+GyLB/qbCRwZEoVXgl5cZ99ibuVyJpbn0U0Am9GCEjpPqLpyKU+sb+OGpRbiWVloc8cQax9KX0oZxfTlzcdxzXg0EQ/dUTP/vWkAX2QPn101gR8/OI9YXMZm0hIHMGcDIAtgjPTg2f484frDFAsi91VcwobQRNZXBykYU8ITbx7i0hmFw9Y5FI4R1Q2Piku2DA41hnjnUBuPPzQfuWol0f1vJP+ucebi02fS0zzA0bM9fO1T02ju8mE169C5G7lQ7l4UBPIkL9965gSLpxcyviQNXyBKQaYFp1nLgNXAtkNDzTz7q7tw2QwU59iwmnTcvqICty/C61tquevKcYiikJRnApgwys6iMgOjclU0VjP5GRZy0sx0DMrwZLqMGPUSTw3uIwhw91XjyM+yUt/m4bYVFWw52MLGfc2IAiydUUia3YjDomPNtrokaQR4f38L8ybmUphxceeWuKrS4w4TCMfIdJqwG7WoqsrkMRl8847pbNzXjMmgZen0AvKdEv5NzxNuSshHKSEffWsfJ+PWfydszr/o+B8KQeD1bXUp2YlAKMbxuj4WTcoZiTqOYAT/ZPinII76YCd9L39vqHNSlMi48RHC1uEPvY8LFEVBlzOawMltKZ/r8yuQ1b+s+1lVE+lYp+0TYsIAACAASURBVE2PVhoPET/y/lcINyeaO0LeXiLttaRf+XlUu+2iERVJFPjUinLSHUZc5jDqBd2nAGo0QYdiqoRtwS3EPV3E+ztA1GCYcS29mgy+d285uS4jJVlmVBVEEcSCyhTBb8eca4h6uiirfp1yazqLrrye/9oWobXHT487xOg8O5lOE59dNYHq083kOQSOtUY5VOvhhkvH8rt11SyYkofFqMVpNSAKQ6m8DIeRWRNy+NaTu5IPumsXjSY3w8yNS8vQSiLNXT5yMyxkp5lwWfU4rXryMizkOIyUXjeBk/X9PPHa8eQD/8alZQmnDRW8wSgh2YpDq08RjdZklpBuUgmd2pGyZsLRtVw19SF+NkgcBQEIDgxbW6WzlrtuK0Y12GnuMaG74qtEq7eidTcTzJ/JzoFMGjp6sZp07KzxIeVcS2l+G3p3E6G0Mpq0Bbz5Tj3NnT5uX1HOu7sb6exLnMMf/H4/P3pgLhnW1JSjTg6gNB0g2N9OuP7w4ElWUKo3cfWKccS0Ca/xPk8iqnZh7eC8yXm0x+wUz7uV0M4XQFUQ9Sa0l3yGpr0xIjGZPm+UzZ2FLJnzWcx9p4hZ8zij5iOrJvo8QWZPyOU//ngQefAE/uJTVXBsQ4oYuqm0ClOggRvn5vDk+lpEQeDdXQ2owHfumYnDokcQBM4v4l2/p4lv3DGde1ZW8Pv3TrNxX+L6++26am5fUYHLlrBXvHNRFlWhXSj7dyEc1mKbsxpx7EK+dcd02geJY0xW+c/nDg7dByrsOd6Jw6rngdUTOVzTQ3NnIiKuqImayNtXVKCVhJR09jl4/FG4CHGMyipvflDPe7sT9ZUGnYZv3jGDPJcRg15KeK1fO2FQn1VBirkJ1R4cNo7s7oS/gTiqqLh9w0sevIEogiD8r1LgIxjBCP7/wye+q1oQBJSaHalRMFVBjQXRjp7+sXpbVkk4vfT7ImglDSa7nXjnWWR/glBIjkys828hpkl9uPy5rnKdRqQw00KpLYB3x0up3xmLoM2v4Nc7fORnW7EO6gkKgkC3N8Kmg60crullankmxflp6HxtxAeGhLg1ZgfWGVehSAZUVSAumTGXz8E8dhamquVoCiaRk+EY1uWpqmBMyybScAQ1EkSfMxqA4MkdifMT9qNp2k/p7IUERAvlhQ5EScPB6k4W5voZXfNHXPXrmWj3MGHmNJrdAnMn5VLX6uH9/c0snJLL3Il5tPcG0IgCd105jv9583iK5t6pxgHGFDp5YcMZCrKsBMMx3t3VyO7jHciKysLJebgsOgQBNAjYLXqmjM1ifEkaK+YUU17oQCsOdsdKGn65ro4lK5cgDLSgBr1IxVNQZ97KQE8/uuZ9pC6AQrBgJrtqE6R76YxC8h0iYv3ulM20RRPpTa/CpldJMyl0RM206UfxZnsOLx+Nc7TRz+VziinJs+P2R3jynXp2tGg5FMrjrRNR9pweYMn0gmSX9+Vzijkx2Eme7jAyYXQ6Jxr66ewPYjbpErWkzQeINB0j2tWIkmK7B4rWjKZoEt0DIaob+mnp8nPLZeUEwjFkWeXSmYVcOaeYYAx+vcNL9tSFRPOq6MhdQLXHgigIzJyQTYbdyP+sO8uWWpkj4XzePSuwq8bHpTOKsJp1/P7t6hTtwoPNUa694bKED7QzB+vkxfiObsF/8gMKps1j3REvcVklJ81MS5cfjSgyqzKTnAwL5cUuxha5mDQmHVlWuKQqHw2g12v54Gh78nqsbRng01dU0tDm5vrcFqgetOhTZCJNJzAXVSJaM3FZ9KTbDOw60Ul1Yz+imLj2Zo/PoSjbhj8UwxuIcuBUF5FYqtzO5LIMZo3Lpr7dO0xndNWC0Zgv0g3d2hfk6fPsQeOySk3zAPMn5yWdYxRFTRI4UVCJ1R1AiaQKlJsmLUU2pg0b/89BFMBi1rO/OrV+9uZlY7EZ/3E6kCNd1SMYwT8Gn/iIoyhCPHCRSI5vABEV+aJxtv97xBSFzYfaeWVTDYqaSMd+9fap5K38VwRvB6oiI9hzCX9IPeKHQVVVkIxJXb7zIWvN7D3ZyZGzPfzo/jnYjVp6vBG+/dSe5INv84EWPn1FJQVFV1BkshE6vRvbtBVorGkMvP0zNPZsLNMuJ2IpJCqawDooJq7An2rbDuszcF7/CKqnE8lgpOv5b18waYU0dYCIVIA/HOet7XV8aWUu6sYfowx2Csttp8iOBOgfezfr9jUnIzxPrzvF12+r4qu3VhGXVVr7AhcVUY7FFSxGLTqtyK5jg7WEaqKRp6LYxfSy9OTLhVGroSTLTGmOFfmC2jynWcuDqyfx6FvHuW7ePYxeoEOj11Jf20Jupgtteh6x3qFaSW1mEY7sfFbMseKyGkizG3j1SAM3Tr4Kjq4DVUHjyMY95gp0vTUEtr6BGvZjLV9CTbyMsUXplOS7sJm1FGXZ6OoPoZUS9WuhSJzW7kQ6O6E3OKg5KSucK5MUBLjtsnIaO71EYwoxSeSpNcf5wurxcGwTajQ8aAGZGhkL2/Lp84TIz0xch25/hNpWN8tnFaEoKiaDhCoI/OG9UzR1BPhp+zny4uWmZWPZuLeR798/F6dZT1mhg5pmN32eENGYTIbDgE4nUt8eGIxmQUGmlVA0TtdAiBhaoh11IGoInNkzNKlgovs602VMnv+2Hj+d7jADvgjPrz89GOUW+Nz1k7HoNagqlGRbefC6Sbyw4QyyorB0eiG1rW4eXDUW3c63h2lQRlurEbPGDYraq1SNzWTNjjruuKKSrYda+eBIO0a9xKqFoynINNPrDrH/VCrZyk03oxt8kXnitWPUt3kwGSTuuWo86baL1wteLNrX1hMgHLuYBiREBSOOZXfT+9pjyXvdWDYDHH9DmprE/TBhlJP7rpnAmu31mAwSNywtI9/10RsGjGAEI/j44xNPHGVZxVA6Hf/hDSmfm6csIyZ/PEgjQOdAiJfeHxJz7veG+e26ar500xQ0jtEfyXfEDC5sM6/Cu3uorkxfMI79XYm39EhUpq03gKPQwcmGvmHRkg+OtJHmMDKu8FKW3XUj0Zqd9G98ZvAAGgjXHST95u8RNmb/xXOKaKzgsiKoCY9g2ZtqFWh12Hn27Wo+fXklwXAcXagv6W19DnJvM2PnqPyqa6hRpqEt0Rhh1mnQaQQcFj1ZLhNd/UOpdq0kIgpQlGPjbPOQ9Etxjg1RFNhX3cWsikzig2lYjUYAQUC+mDWjCiXZFr5z9ywiMZmMWAf9a39Moa8PUW/CtORuQrUHiDUcQlM4kXDllfxsTQ2RmII3EOWLN02hsDCLGmseY66cRkdnH2fdWiZHwpi3/4KkR8jB15ky9Xq+t9tJ/2Dzz+0ryjHqtZgM0rBjXDGrGJ0c4uHl6QRliZg+ccsvmVZAQ4c3xY3lpkvLqO8KMNaZS6B6B5YJi5BsGcS9iSYeMXssJ0OZtPoCxGSFe6+ZwMn6PmJxhf9+aSiiP60ik5JcO00dqY1LsZgMgkgkJiOJ8NC1FcS666GvGdGahiGvjJ+/U0e6w8TCCRmsGB3D3HkIRW+lzzkBt2xEDHpSajwBvIoRpzVKWYGTDwYlb+ZOzKXfG+GFQdIICcH3X79xjB8/MBerQUISBaaNSScvw8KAL0xTh5dJYzI40dTHQlch9KX6wEtpecTPq/PLdRn5wX1z+eP600lx7lAkzgsbzvCD+2Zzw5Ix1La6k81pCybnUThIuF0mLV+7tQpPMIZeq8FmlP6kwkGG0zjss3ElLkz6P/3zHXWNIeO2HyC7OxH1ZhR7HlFx+Dh/KXQakZnlGVSVZSBAssFsBCMYwT8fPvHEESDuKCbt6ofx7nwFNRbBOmsV5Iz/R08rBb3uC0v/4XTTAOGYjFn3t3VCXghZEdCOu4z0nDFEO2uJmzPZ22vnN+uHHpD6QSHwNLuBWy4bi6KohCIy7+5qIC4raESB9/a1sXi8A+/et1LGV+NR5L4WyP9w4qgCnlCccDSOPxgj02HEYTbiXHInvW/8hHMRSk1WKduaRHrdYWRFoazQQRiRC9t4BK0BRdJTmGWlaTDiNGF0GhqNgCAKqIqK0yRx7zUT+O3ak7R2+3HZDKxaOJo3t9UhCDC9MpsBb5AH5puxd+xFUGSkikWochwVkba+IO/saSISjbNiVjGjsi1ohMGnpwDuYAxPIEZnnx8rITSHfobsS6SElUgQz3tP4F/yVbx5l3GsJUL3Xi8Wk46uVg8LpuSx9WArh850o5NE7r5qHL96bwCTQWL+fC8XQnt2K3PHfoq1+8OIQqLW7v0P6rn1snKuWVSKPxilsz/I+JI0CrRu9Hv+iNzXgqA1YFv8aXI/PQWt3sC3n0pNi7+xtY4HrpuIuWo5wbP7Gdj+EraqZWjMdsKWPNaeVli7tZuv3jYVnVbDgTPdXDI1n39/JjUNf+BUNw/dPIUtB1uHzpEAFpMOh1VHus2AIAiITXtRt/0ucW0C0bxyVky+lZ+va+S/V9sIvPMzzlH0dO1mvIu+gmPWbbDlyeQ1YiqfjS3ayk9WutjU1ofZILF0RhHHanvJz7IOk6uJRGV8wRjWwZRwXFF5Z3cDHxxp54alZfzs5SMEQjEmXr8IR8txlHCCpEqZxYg55anXHWDSS5yo6+NCdPWHmFTi4tHPzKbbHUSv1ZDhMCAJQ4xLEgXSLIko44WkURAEBgJROvoC2C067l01gTe21jJ7Qg4um55JYzI+tD9aVQXChizIzvqQrf46qCpII4RxBCP4p8c/BXGUBS1y1kSs145FQCUmGD5WtY2Q8JS+EGWFDgwf4ujytyAmGiC9EiFzHA1tHryCl5uW2di0rxmnTU9euplARGbzwVaO1CQiTS6bgdtWVBCJyry25SyXTC1AFDUI0kVSa+KHz9cbjvP8xjPsO9mFw6LnyvklvLr5LHetHEd2RjnOm75LX3MDYUHPsX4zL3zQQ+UoF7tPdHD3leM5crKJzLEL4MyQBqAy/UYee6ORqRXZOK160h0m5kzMYe0HDRRmWZk4Og2jVsOoTDNfvnUqvmAUVVVp7PShKCpuf4TibCuLC0LoN/0n8qDsS7x2D9Yb/o0WNZfv/GZPkoQcPpPo+B2Ta0NWVHYc7+CFDWeIyyqFWVb+dbkrSRqHJikj+/rplUbx7r56HrxuEv3eMNcsLGXroQRpBIjGFWQFzAYp8W+tmQtboVSTg4FgIhp8ybQCDp7qwqDToNeJ7DjazYnaXmwWPQVOibEdLxPtSzSAqLEwnvVPUnzdd/igPTLsHojEZEwGLWFzHum3PIrc14wqaOhU0/ney/UowJ0rKynJsRKMymzc20R5keui51mv1XDV/BK2HW7FbtazdEYhda0DfOW2aRgkESXQR2T3iyn7xNpOUzTJy9wKJ5H9r6YecyyCw1/Pz485uHbOw5SYg+jlAJG2M8m63UuqLse5YgEvbamn1x3mxizrsMYdmzlBXs+h1xtORilFUUh24zdFHXRNeJACU5CYInCiT0fZgJbizAuPUyQ3w0x7T2o9oW2QEJp0IsWZf315SWtfkEef2Zuc+9ULRnHHykqefOM43kCU/MwWPnf9JDIyrH9mpBGMYAQj+GjxiW+OOR8KEvLHlCubDFoMeolTjQklPodFz+evn3xR67AL8ddaLiqqytaj7fzy1WOcrO/jdOMA914zgRWzijBpRUKePjJFNxOL7TQNyPR5wpiNWmRFIRSJc8tlY9Hp9JicLkJn9yfHFU02zNOvJq7507VPz79fw+7jieaacFTmWG0vl88dxZZDLUyvyAaDHZ8xl9cO+tlf62XGuGzGFjmpLE5jVLaF/qCCOXc0mRWT0RWMI16xjKf2Q11HgH5vONmo0esOcfRsL25/hHSniQx74mVBL4nYTDrqOrwMeMNMKctkTIGDzr4AkyP7kbvqU+arxkKcUEo4dCZVeLvfG2bAH8VqMfDrN45RVuBk+exi5k7KxWnWQu1O1HhqlZyp6nKefK+FFXOKmTwmg9+/W43LZmDjvtQ6wuZOH3dfPQGnVU+ay46590Sycx1BxLz0XjpidqrKMwlH48ybnMf0iizauv2s+6CBWFzBH4wxt1SP4/SaYedAn1tKVOfgRLMvpe5zZkU6yyalIUoSMY0ZxZqLYs3GbLezcEoBy2YUMibPhigIxGSVDfuamVyWkVgL71AdXnaaidkTcjDoNCyZXsilMwvQSSI97jAHz3STl2nFKoaIHHlv2Nx8GRNw5RXgaN8zJLI+CGvpJAwFlfgxYxWCRDc/Sey8dHKsqx5/zlQ2HU/UNHf1B7hu8RhqW9xE4woOq56Hb64iwzrUQDHgj7L1UCIyOmF0Oifq+nBa9ZRkmSkyBTB56tEqUUSTk43HB5hRmZUs2RWERGSwtMCZbKgCmDkum3Gj0rAa/7bfmrgKv3ztGL2eoSzE1PIsnlpzInm+vIEopxr7mT85Dzl+8VrHTzpGmmNGMIJ/DD6eLOqfEFpRYMX0QmZUZBOMxEi3GzDrNH+XyGivL5riBhOXFX6z5gQ/vH82el8T0rs/JS/gQdDq+cb82/mvPUZqW9w8fMsUVs0bhU4joqqg5k4i/bpvEK4/hMaWjq5oEmFdGrIKPZ4w4ahMptOAaTBqGozJ7BxsQDkf0ZhMY7uXqKxi1IlkOfTcv2ocgahMJCKj04rYjAkf6om5OmL71zBQk5C3Ea3prJpyD8ebfKyYPYrfvHWC6ZXZSW3Gxg4vR8/28sP75yTTgr2+CFsOtnGqsY+yQidzJuSy72QnlA1fbFUBjThc/kgjitQ0D7D9cCvfuWcW9W0e3t3dREevn5XzSrh0/l0om55INifYF95C2JXHv91ViM0goarwwOrJ9AwMlzeaXJbBq5tqGFPgIK1gNGc8d1Ok7UOjRBjQZvHC3hhlhRqKcmyUFzn5/dun0Eoi2vMsIEUBynIsSI6sYR7UUsxPYcdaHlh5FR0+gX3Vndw41Ux2+zYCr/wOY0kVxikrCOvSBtdAxaRLjH0upWozSVw5r4Tfravm4Zur2H2ig1MN/YzOt7NkWiFxXz9GTzcDHj3rGmWWzBpFY4ePk/V9dPUFueGSYkaVTidaO5TmFrR6OuN2/ue10/xoxQrY/j+Jz3VGLOMXoM8ppfNQBxnZWYTCUYbF2lSVSQX6pAxTrzvM61treeiWKkQg02HEpBNT7qlMh4GcdBMdvUH8oRiVo1zMn5THZHMn0Xf/K/H9QKHBwso5X0juG44rHKvrY/3eJi6bVcS1l5QmUrkakZqWAZ5ee5Kvnyc2fw5xRaXPF0EriTjNuou25sXiSrK5KbmfrA5LZ7f1BOjzhLHq/jJ5rhGMYAQj+CgwQhw/RhBFSLfqYDCV9vdKp/8pTTZtzE//2p8hBxKF/mosgv7Ue/zbFbcje7qxBM4gGIqJDFYZxgUdcVcZmvSxqKpKWE08UF/dWsfmA4n0qMtm4OufmkaaRYdWI5LtMtHem5rWkySReZNyae7ysWZHPZlOI5fNLCLbYcQ8SDrPPTQtwRb6aoY0EQUURut7eWR1HmFJpKo8i22HWlPGj8sKJxr6BgWcTfzw2QN4A1HmTcoj02XC7Q9z5YISVKMdTmxNsMVBaMYtYZTBhqQRUqR8rpiZjcnfhjUWwDRwCl+vRGm+nRmVWby2pZaMqyuxzXgYm+pDZ3OiyytGkHTYB8mdOxTjsWf3U5xr48alZazb2UAwHGPmuGwyXSY27mvGbNDS1OnlFxs6EQUQRYm4nIh8jspzkuU08pPnD9PY4cVh1TNvUi7HahPNRXMqXEhHX8U+/XL6tzyXbCgyj19IpL2W6NkD6NKn8dt1Hn5893hMm/6D0GATjP/o+0Q76rCu+ipRhpdQQGKJLp1eQEGmlc0HWpg3OZf5k/No7vSSI3QT2/0zZF8/iBqKqlazp8nE2EIn2WkmMp1GBgIK8cyllKYVoo94kGUZoWw+v32+jVAkTrO2lCmrvkSoegemsukMbHsR36H1XGLPJFBwB6KzCI3VlfiOQZjLZxGv3c0NC2fy4tYmbGYdNywpw6jTkGU3IDD8ntJLIl++ZSpv72rk0Okubl9Rwd4jjUyKvZmynRL2U0AHqGMSjVOnuvjd26eQNCI9AyFe25JwTJoy2sHcUj3RPDOKqg7VwQLeUJxfvXmMM01uBAGumDuKlbOL0EuaFC1Eo05k1vhsth0+rwv/Ir7wVpMWi1EC+SLNWiMYwQhG8HfCCHH8GEArBxD6m4gPtKN15aG6ioiJfz+pi3S7AVEUUiIYGU4jRiVA6LzaPEFrwDppMf1rfgSoRABtZjH2lQ8TkYZaVM4fp6XHnySNkEjpvrzpLPddPQ6tKHDPVeP5/u/2JdN6laNcGHUaxk/M5bu/2UM4KlPdALuOdfD9+2aTZhlKPQkCxAeGIpa67BLMY6bh2f48aZEg+oJxlM+5CbuUzuu7UjX0ojGF7/9uP1+5fSr+YJQ7V1by3p4mth5qxWUz8JlV4/n9vhBXLXwIW/seBEVGU76IoH0UUX+cB6+bxJlmN/5glClj0sn3HEbe/RwAYWBW/kTek5bQ3itTVujkdJOH2lY/Hb1B5kyw42xt52R9HzcvG8vZlgHq271cvWA07b0BNu5rZun0AtLsBoLheNLXeOW8UVhNWq6YOwqzUYsoJLyRfcEolcUuwjGZ3kEtQLcvgkmvpSg70SA0qUBHfP8x3H3N2GddlVg/UUI02+l790kA9EoiFRrsakXvTU3FR7sbEPw9YCn4k9eRQRKZPNrFpNI03t3bzKubz7Jyehbxs68METpFRjjwMpULHmZ7e0KIPctl5q0ddayaZEbxuwm3HkPIq0TSGAhH41w2s4jigiyiulwsrnx6/viNZKpe9nRj2vVrTo1/kNlL7iLWcIhoXzuG/LEokSDBswe4dPVVzJ5ShKqCySChFT78Jcxh0nLrpWOIxBV+/NxhxmbrUAPDI8EaJYoqQCgmU9fm4ZbLxiLLKiV5dixGiU8vyKC8dzPmqA3JkYnY7kGTOZqwPh0Q2LC/mTNNie59o17CYdGz60QXcVlhXImLbIcxEYFU4ZqFo/EGoxw+04PJIJGXYeaahaN5Y7ALXhQF7rtmIpkuMz0Xsd0cwQhGMIK/F0aI4z8YEjEiB14ncHRT8jNL1XK0064j/nc6PU6Lji/eOJlfvX6cUCSOy2bgizdOQTTEEE22ZG2ZuXwW3kPrOV+HMdbdiNrfBJkTLjp2d38QjSggigKxwcL+6sZ+InEFgyRSnGnme/fOpqHdg8mgJTvdxNlmN69vqWXlvISN4Po9TUTjCvVtXtLGZiTHVlXQuvKS/zeXz2Jg6/PJ/0daTiLuf43FBgtzb5zDYbcLfziOQT/UsOP1R1lYlc/aD+qTDir93jA/ff4Q/3rrVL757H6+cMNqLOaELVw0Xs3CKXl4AzGC4Rh5mRai7i6UY6+kHnjrMWbMWcSjb3dw3eIy4orCgVNdiAJUlqTxmzUnuG7xGH79+rGkPMuh092smF2MySCxZns9YwodTBqdzuSyDK5dVIokCkiSyPHaXpq7fEgagZXzSpg2Np0SfR+RxhP8+7w43YZifrnFzSuba1g6vZBrFpWSYYihsaYh+/pwfzDUaOJceFPiH4LIgGAHuomqF2toEi7e/ATDXjoEVeXKmblcOsGBpITxP9c4bB+XJkhzZ5xwJE5xjo3KXD2j6l5C7hnc1tON0n6aX3/+mwg6WzICJ/v6huo7B6EEvcws0qGoGkJNJ5HsGfiOvI8SDiBMvIJHXzjFZ64eT44jIT/zl0TuFRWicZWu/gC97iCXLVyKuPeP5y2HiDa/AoUYkpB4WTp2qp0rJ+gx97by33eWEq3bhyEtnXBbDb7B+1nQGUi//ltELLkosQhGvUQoEufaRaW8sbWWQDghtCQK8G93z6QoPSHubzNIPHjNBLzBGFpJxGqQKM2xMa08E08gSqbDiMs64hM9ghGM4P8eI8TxHwxNoDuFNAL4D71HRuVC4qacv8t3CsDEUS5+9MAcAqEYDoseo1YkigHXigfoW/NT1HgUjcmWdK05H2pk6EHuCcURBbANeufOLBCoWCGikaP063J46gMvYwpcGAZTbcGYwn8+f4h+b5gl0wtYt7MBm05lWYUerdpN1J5Gb3kmB093I2qGp+dUVzGmqssJHno3xdrvHEINx3DMXkVow+NYq77I0+93ImlE7lxZiV6rQRQFygqdwxpSYnGFfm+YL92aiEg+fp7EzJmmAe5cWUl+hoVYXGZiiY34oeHNSJISxWLSIUkiWXYTi6rymTI2g9+uTVg86rRikjSew6b9zVy1YDSvbj7L9ZeMoSzfTn2Hj/98/iAefxS7Rcd1i8fw6uazePxR3txWxzUVCj0v/HsimgdkSzq+sOQLfG9NF7KikuEw4gtqMEy/DWnrL5J1lqaymUQ6G9BYXYSn3softiZeEPpwUFA2i2jNHjRWF5aKOWizSlBMaXBeFjQUU6jv8NLW42dxCeg9TYiSDq09Hc+eN4n1tmCefxORjMJU4XBRg8Oq565JUYI2J640E/ocFaWuMWUtZHcnumAPUe1Q9aJosIIgppQPoJHoDIgENXYKpl1N6IPnUSIhxLELOCxU0tjRxdNrq/n6bVXEZJWWngD+YJScNDNZDj3CBZWFPb4Iz757mmAoyuzxOWw60MI77WmsmPVpDHWbEY12HPNvINrbjH/9k4hmJ9dXXork9yLv+gMA/j0izoW3gGpMiJSfm6rRhupuJ7rnNa4KeLj00kVsaEvDG4gmSSMkiOvrW+p46IZJoKoIAviCMXo9YexmHUatiCQK5DiN5JzTdfyYKUOMYAQj+OfACHH8B+NCMes/9/lHBUVRseolrIMiwqoKokYgnllO+m0/RPH1obG6MMdj+A++O7SjIKJx5eGJyryxo54tB1qQNCKrF5dyRaWe4NofoB2skUwXNXxl2cOQOSRg3tUfTApXZzpNqCE/qxwnUfe8D4DG6uKuw7ooDwAAIABJREFUpV/gTFM/JbkXKjZCq1fghdpRLJv5JSy24WukdeUQ9/SgxqOkiYl5xGWFdTsbmDspF4tRhycwFPk5Hy6bnl5PmJrzxMDPYe/JTiRJZMLodDad8nFp4XiizSeGlkVnoFu2cevyTGxmHb3uEI0dHtLsBjr6AozOt+MY7OYVhERzTVxWUIH8TAvfuXsm+RlmvIEo//HcweTcPP4oz68/w5XzRuEPxynOMhM++nqK+48aj5IbOMVnr1mAQSex92Q7siKwvzrIp2Y/jAsPYdFAi+SiarSNWFTD8xuasVkEVswZxUBYpj53BeUVC5G87Xh3v44SeQfzhEUYp11NSLITiSu8uOksO4608e1VWYTf/BmhWATH/Ovp3vg0aixxTvvXP0X6FffTt/EZlFAA+4wr0FjTiHXWk2uyglKLYsjElp/GxRKsgibVwi5mTMex6BbcW4aif9LsW+lVbLy9s4n2XpWrqu5nTJ6VftnIMy8dB6Ch3UMwKvO7d05x+LyO+C/dUsX4ImcyohmKyfzo2QNJQl9VnsXcSTlsOt5JdbuJz1/zZXLTrUTqduA+J3bf04zQdBz7oltIVliqCp4D72CduChl/raqZfS+8+sk8ZU667l87h28P2Afduy+YHSwLhLqO/089ocDSTmemy4tY8mUPDTiiJDiCEbw/zP27t3Ll7/8ZbZv3/7nN/6YYoQ4/oMhWDOR7FnEPUOdr5IrBywZH7LXRzwHQaDTHWb93kY6+4JcOqOQyqJS9JKIYdJyBAT8xzcj2dKwL/oUMWsuew93sGl/opYxGld4Y2sdS6wkG2sAUGRMZ97BXFJObDBgJJ0XRVSBxaNk1J3vJz+Tff3oDr/GY/c9gMmgHZZmrG33cKzRx7FGH1dOS+PSoqmoTQcTx6HRYqtaRv/mBMmICgYgER3t6A1w58pxNLR7OFbby7WLSnl+w5CryNULSshPNyMrKnrt8EinXqshHJU5UtPDZbMKqfWupMToQmk8gCajCM3068nUZPPc+jPE4gozxmUzsTSDUbl2clwmFkzOo9cd5oYlY9BKGkLROFaTDkVRKC+woxtcl35fZBihDUXijM538Js1J2jrNDLeMlwUPBbw8vaxRlq7/Xz1U9OIxmTe3tnAT945d12FuXFpJpkRCxlWHZ9blkm4ejtC03pCeVPxmcbR2R/CsuMPyTEDxzaDycHLPeVkZ1jZcaSNvAwL6b0HUc5Fe1WSpBFAlWP0rX+a9JseQYgG6N/0LLHuxuTfHXOuIebrIyilYRi/mPCJzUNrXDaLuDlVsFpGg6Z0Iem55Sj+fkKSnTM+E0fO9lHdkKBtz27rArq4f/XEZBp9SlkmPe5QCmkE+M1bJ/jhfXOSEfCeQVvCc3htSy1jChz84P45OEw6ZEXF5/ej7luXuuCqghz0IGgNyeNX/P1oXUNZAkFvIu4fSI2WAtpTG1hy2ddZt7M+5fq+ct4oFFWl3x/jF68eTdGffHFjDRNK0slxXrxZaQQjGMEI/q8wQhz/FxCEhKB1NKbgMOsuasN1vvyGyzy8JikimnGu+hL+PW8QaTmJoWgClhlXE9aY8IXitPcF0Usi2Wmm5MPuo0afL8J3fjPkS316MDW7cGIOEcmOOO0G0qasQBV0RAQ9qgrbD6fasRl0EjH/cAcNxTcAShwGfS6ynAYmlqZzrLYXtzdMVm6AC9sQYq2nSZPiRC6SijvfZm3tgT6ik+ezdMkisiQ/BAdw734TVY5B6Vy2Nw7tN3lMOu/sauBkfR93XFHJ3pMd3HpZObG4QnGOjZIcCzqNSH6GBY0osvVga/LBLQpQVZ7JM2tPMndiLpkOI43hNIKTrsUycTl7agOIXSamjtVjMWrJdJl4aeOZhOe43cC3PjUFV7QDoa8JWRVoJZufvNeLNxDl3lXj6fdFMBm0aEQBm0U3rIN78pgM1u9poscdYsAXIbByAbqW6pR18WdPoXV3oqwgEIqh0Qh8+opKth1qJRKVmT85D08gwnt7GrltXgaBt36MMtgIJbadIWPycmTJxIWKgOHq7SxYNJuT7YnobmmeDWtGNuHcMUTazyJcRKpIVWXcEQ22oCeFNAJ4Dq6H9Ins7YjgC05i7pwxGPytRCy57Pc6qQqLOE2JtLhGFNBLAhpZAVMasrWArt4gihChumH4tdbU4cVp1WMx6rjtsrG09viHbePxR4kN1tsCGC+ik1rX6kYvibT2Bnj8xcPMLLNzrXa4XZ+g0aVEfjVWF35DDvaFt+Ld9RooCqJ+eJOboDOQZjPwrTtm8MrmswTCca6eP4riHBs/fu4QVeVZw0oaANz+8CeOOAqCQExR0AgXFhCMYAQj+LhihDj+jZBVlf2ne/j9O6eIRGUqil3ce/V47OeJ/npCMX7x6jHq2jyIosCqBSUsm16I7gKGGdZnYlj0GUxyCFljJKSK9HgjfO+ZfUkni7GFTv5l9UTM+o/GSUYUBbQxD4IiM+AThvlSv761lukVmRg0IooKEfG8ujMBRuc7kvZ+AG5/BDE31ZINwFy1nLhgSHYo6DQi9141jrOtHroHgpgslmHEUV9QgSwZLlrDVZJrx2nVJx+sG472U1lRRVM4TLkjg9hkG6rBRsCYzYGXzwBQUexi/pQ8fv7yUQBeer+G79wzk0hUxqSXsA2es4auAD975Qio8OkrKnH7I3h8EfKzrLy7qxGNKDJ7Qg4/evYA312VgbT1RaLdjUwvnIBu1k3o9BJXLyjhv148knSZMWoFMkMN9K/9WTIy5dDq+foVD/H1l9t5cWMNtywby5NvHsegk7h31XjuXDmOp986gaIm1vqKecX89PnDQCLt/mq1xA3zPoOxZiNoJAJly3l2/5DY+IAvQqbTSE3TALnpZnRaDRv3N7H6kjH8/u1qbq6MJknjOURPbMa08NPDzgX2HHad6iM328nXrykgp2cP/sNH0WcW4VryKWLubnRZxUS7GpO7KFOupdmvY7xwEV3MaJg+n0xcVll7oI+3RQGbORdfIIqsdDJufAnbj/fy8qazVI22c+dkGe+el1FiEQxTriCzaAaKqmN0np2OC2SdRufZWTQlD4dZhyQKyGkmNKKQ7OAHmFGZlSKqn2bRceW8EtZ+MCT8vvqSUkRB4D+fP0QoEueD6gFWrFyJdtsvk9uIRgu6wkqEwxtQQzEkWwaeqXfw6O/rsZktfG7FNyl1qSj+hFe5EhlaWcvs64ipOkZlafnyzVNQVNBq4IXNddS2eijKsZOTbk45PkGAdMff7jX9cUQ4rnCwpod3dzXitOm5fnEZRRmmj52r1whG8Ldg8eLF3HjjjaxZs4aenh6WLl3K+X4rzzzzDE899RQajYaHHnqI1atXA7B161Yef/xxmpubsVqtXHfddXzuc58DIBKJ8M1vfpMdO3YgyzJFRUU8+eSTpKen4/P5+OEPf8j27dsRBIFrr72Wz3/+82g0H637HHyExLGhoYGvfe1ruN1uHA4Hjz32GMXFxR/V8H8XiKKANh5AFTTERP1f9YPV0R/if94cqnE71djPK5vPcvfKCgQ18Sa9dmcDdW2J1K2iqLy+tY6KYhejs4fbhMVVEURzgiwJ8Ma2uiRpBDjTPEBtm4dJJRe3ePtrIKlRqNtL3/YXUKNhisYv4tMLq/j9tu6hbTQi4p+IAagKrJhVxP7qTnzBxBwLsiwoziKybv420c46UOJo0wuRncXIgwsbU1Q6+4MM+KJkpRmZOsZJdP9OrJMW4zu6BVCR7BlYJ15CTNRdVJ/ObpR45K4Z1LS48YdiFGZZeXHjGbRaDVtFkYpRRaRJBl558yzLZxeR6TTR2RcgGlOYUpbO6HwHGQ4Tz6ytJj/TwiVT87EZJTyhOD/+44GkM8dTa05QXuzk9hUVHDrdzczxOVhNWt7YUss3VhcjbPgB8UEyIDcfw1AyAbXzOBl1B3lkWgVntWN5YkMnt8/LIFKzIzWdG4uQ1n2A/Mwy2nv89Hsj5KWZuGeBjUJjB3oLzLy/nO2NAjazjmA4zrhRaRw4nUg77z3r5UiThn+94V8QBIGfvHSScDSR3l4wJQ+dJPLzl4/w/fvmcqK+l2hMYfmsYl7fUkssrlz8h0SRCZlzEdMKUfoSjS2CVo+ndDnb3+nmvgIbBadfJdZ+BhkIenuJdDVgnbwU/awb0Ib8BPu7CFkKeP14nNUlegRdIYJWn9LEZKqcyz6vFrtZ4qsrs3AqA8REHac8LsZmqGQ0bcIYDFN8WRlGvQbfOz9N7hvY/gcMi3SsrXayZHoh1Q39yXrZiaXplBU4Unzd06x6vnXnDH7z1gk6+oLMnZjDdYtKU65qjSiydHo+40pcyW7l3DQTnf3BZMlAJCbzm30KD131DcKNR1F0Flq0hfzk9X4eufFRwj43W0/7eWttL5GoTCgSx+320b3xcdBIOGavQvYPoMZjRPOn8dOtQbSGE1x/yRjSLDrCMZlOdzRpO7nraBu3rajgzW119LhDGHQa7l01gXTrJ8cV5XwtTID23gCnGvfy/c/OJusi9qsjGMH/j1i7di1PP/00RqOR++67jyeeeII5c+bQ29uLz+dj+/bt7Nq1i89//vMsXboUu92O0WjkscceY8yYMdTU1HDXXXdRUVHB0qVLeeONN/D7/WzduhWdTsepU6cwGBL3y9e+9jXS0tLYsGEDoVCIz372s+Tk5HDTTTd95Mf1kRHHb3/729xyyy1cffXVrFmzhkceeYRnn332oxr+I4dODiDX7GZg/zoEnRH7wpuRM8ch85ex867+4Tpv+6q7uGVZGSathnBc5uDp7mHbtHT7Kc2xfihJjcpqknCej87+AJNHp6WIBX8YRFEgFJURRQGdRkh+p2agid6NTye3Cx3fzNTJVt5xZtMzkKgJvPnSsegl4U/OM82i47v3zKa524cgQF6GGTs++t79ddKpRNCbyLj+W2AyIasqa3c2sm5nQ3KMf715ChWKSqynBef861FVBTnoJeb3oCh/WtTYbtQyvSyDYFTh8ZcPs2hqAYfOdBOLK2Q4jTR3+Bhb5EQjimzYkxCCzsuwMGVsJr5AjF+9fgyAE/V9bD/Sxr/fOxu3P5JivwcgCgKvb6njRF0vCBAZ/LshokU9L4JkLp9FsPYAkdZEhFNoq6Eyq4RHbr0XKwHktuFtIKq/F7NhHLPG53Cyvo+HFpvIopuB91/HF/QiGiwsWHwPX3krwLxJeaxePJqWbh9d/UEEAeZMyGXTkW7Otri5ZtFoTAYJu0VPOBLHYtKxdEYh0ZhMcY6d371dTUtXYg7zJuYipeUhuXKJ97cn52OpWk40rYDwos9hCHQgSRpikhF3d4TrFo7CGB8g1n4m5RhkXz8D+lxqfVlYTPn0GorQCRquXGwh3a4nohpIv+Fb+Ha9Sqy3BbVkDnuECo7UufnCfB2ht/8LlDhG4PLL76dv/VME5DgC4BBE0lb+CxcmpJXq9zFKq/npC4e4dlEpYwod6DQiWQ4j2gtrRVQoyjDzyJ0ziMQUVFQM0tD9LQhQ2+Hl8ZeOEAjFMBu1fOHGyeg0AjaTDq0kJiWlJIOBX+wI09aTRyQaJxBOXOPrT/opyXXxyq5Uq8ocyZNocItHGdj6PKLJhqg1cFJTxeG6RPNVbYuHb9wxjZ88dxiLUcuSSZlUWL3YYr1E1XruuTQPTE6ynUZsRukTFYkLx2TWftCQ8pmiJH73RojjCD4puPXWW8nJSdQ933///Tz66KPMmTMHSZJ48MEHkSSJhQsXYjKZaGhoYPLkycycOTO5f3l5OVdccQX79u1j6dKlSJKE2+2mqamJ8vJyxo8fD0Bvby/btm3jwIEDGAwGTCYTd9xxBy+99NLHlzj29fVRXV3Nb3/7WwBWrlzJo48+Sn9/Py7X/z5C9lFDEATk5kO4tw52agbc9L35U9Jv/DayfdRfNIbdMvztvzjHin7wwaSTRMYWOdl3MtXuLdv151Mxeklg3sTcpNjvOZTmO/5i0hiOyew80cXaD+ox6iU+taKCsfl2wnGZWPOpYdtrGnZz3+VfYndNwh+6ONOSnKeiQrcnTI8nhNOiJ9tpJCYrPL32JCcHa82sJi2Pr5BT7O3USBD/4XfRzb+bzv5ICmkE+Pmrx3jq/kWEzuxmYMfLAEiuXJwTlxP+kMMUBAFFVQnHZBZMyeeZtSeTc23r9rN8dhFarYaX369J7nO8ro8v3zaVFzceThkrGI7T0u0nL92ctKo7hzS7gYZ277A0fkzQpdw42owCAqf3pGwT76rHhYeQaMFQWEmo7lDqepfNZ5TWRkGmlabmTtJjHQzsW4sSStTlKWE/wQ1PcMvsL/DuiT4WVeWzsCqfHJcRo1HL1oOt7DmR8Px+a0c9X7xpCs+8dZKOvgA6SeTaS0r5w3unqW/zcN3iUkpyy7HqgXiEHTUBZq94CLHtKLHOOgyjpyJkVyBoJHSuDPoRMNRuQTn+HvmKQmn5HAwly3FfKIsD6E1mRE8ETX8rxYofn2zjyBkLhgn5uMxawpYCtjtX4df42XcsQEdfFyuqMojt/ONg7StIjiwiLdUgn9cUpCoEa/ahyy4h2jlEyhSTC/dAnFhc4aX3a3jk7pnkp/1psXxBgF5vmCdeO0ZbT4C8DDMPrJ5ErtOANxznpy8cTkYWA6EYP33+EI89MBeHWcv9107kF68eRVFUzAYt3QNDigDn0OcJM7Myi/Gj0zhRl7gXNKKA02lL6RpXgl4wKPijQxdYOBrnRH0/nX0BMhxGlqS1EdyS6Nw2AHn54zBfej+q9pNFGiGxRmajlj5P6noa9SPVUyP45OAcaQTIzc2luzsRTHI4HEjS0LVuNBoJBhPBiKNHj/KTn/yEs2fPEovFiEajLF++HICrr76azs5OHn74YbxeL1dddRUPPfQQ7e3txONx5s2blxxTUZSU7/8o8ZHcpR0dHWRlZSVTYBqNhszMTDo6Ov5i4piWZvlfzSEjY3j6909BDgdoP/TesM/jrSfIKJ34F43hiypMGZuR7NrU6zTcfnkF2ZlD87hlWTk1ze6kxd+8SblUlqSRZv/ztUor5hTTPRBk57EOdJLIzcvGUpBlpa7LTzyuUJRjoyBr6LsuPP412+t4bn3CgeScxMuP/2UeL75/hltKbKSKniTIz7SJhf+PvfeOj6u+0/3f50zvM5pR79WWZMu994IbtsE4gE0xYAJsCEkI7KZs9hJISDZkk3vvJmwgCSSETmgxNjbNuOHeJVfJ6r2ONL2ec/8YeeSxTELL70Xy0/N6+Q8dn/Od7+nP+ZTnYerExAaeqCTz7oEGnni9Mr7sKwuLqShyxEkjgEGnwt/VOGw/It1NJJtU1HUNj9AGw1F6FQ5yb/tPQt1NhKPg1qTQHDKSY9Ni1F8+S+hy+thzspUdR1qYMCoZnz+S8FJ1uoMUZdn4zesnE+cRlWjpcqNWKvCR2LWsUilITtJzy/JSnt0aI9WiEKuZS7bqaem6kLB+NzbyyhcQPL0DYJgu4EUYlVEuuJTkpBdhm7cOd+VOkGXM01ZTp87jQksDRVk2Us1KBEmKk8aLkCMhbKKP0nwHdrmXWaG9aM7W4cuYxJIxZeRnWEgya5GBTbtqae+N1cSFIhIvv1/NhhWllBfYUatEirR9BA6/idTbzJi8qRw6V8Go8vmoCxbS4w8TcEZItspkphjR91TRdfLt+DwC5/YSTRmFOHYpUuWQNJOYOwFMKRQ0bkc4+RYAJgRGL7kLqaMVjUaJIr2EHSc6aesZOv8ZNiWRpo7434JKjRwa3gwih/yIBuvQAlGJq2ARxvMKZo7VxVO7DoeRSFSibyBIbWs/wVCU3HQz+Rlmup1+fvNaJVfPzicYkQiFopys6SZteh7e4PAO9kAoiicYoSDbxgKbgeIcG70DfhxWHWfqevndX6pYWGFnVIpIu0vAmmImFJH43k3jCHU2ILk6UZts6JPTCaYXEWofunYiE7/C5l39WI0aVkywYTGo6PWFmDchk2VjjfjfezRhLtGW0+gDXegz/vrD/9M8+75M2LC8lEf/OKSXajVqKCuwf6r9+Ufd9xH8/wPt7UNOZ21tbaSkpPzNbR588EFuueUWnnrqKTQaDT/5yU9wOmONjyqVivvuu4/77ruPlpYW7r77bvLz85k3bx5qtZoDBw4kENK/F740n3e9vZ4EN4pPg+Rk06ey3VIKUkxbrjexMxidhZ4e99/8uhcEgaNnO9BplNy8dDSRqIQgwJEzneQ6DPH9MGsUPHrXDLr6fahVClIsWqRQ5BPNVQncsaKUtfOLYk4sCoFHnjoYJwc6jZKHvzqNZJMmvv8qQigifsIKLe/saxg2Znuvl2PnupmYkcK4hFo2LcZpa+h1BoHEF/iAP8xTm04lLHvtwxqKs60Jy7r6fHiSRqHjg4TlujHzcboi2IxqNGpFPN0LkOEwoFMp6I/oaSeXR587jD8YS58unJTF9QtikkCXHHhe3XGBdw7ECKovEKaiOJmZFemMyrGRZNHh9YXodHrRqIaXHIQjMounZvPah0Mvc7NBTYZdz7sHGjl4uoNblo1Gp1ESjkp8eLiZUbk2lkzLZdexFrQaBTctGU1aqhVDzg0YS2cQ8TjpVSShLphEpO5ofFxNZgly3UGmZpfR/+E2LDPXYl7xDbyShheODlDX1sSMsRmolAJaqx3BFkRQqhP1OwWRsNLIdZOtuN98FNy9BAFF63mSR81lb3AGaUkGwlKUU3XDu4y1aiWbdtdy2+wkPJt+hRwajO5UbaO80Elju4OIoOSJNyoJhqIoRIFvr5tAafuJYWMJ57ZzOu8Wcmfno3O3oE7OppU0Mr19cdIYg4x757OYJy2jZ/tfEPVmvrXsAb77/FDUcH+NlyklMwie+yi2RTiMftRUvOcTo7bKMYtR2dJQF00jGgriN2VR7zTQ3tOMJMvcsLgEm0XD7/9ShcOq490DjXEfdKVC5Ed3TUeriPKjVRZ8Ne8S0Vho1xXx0mEP44od6DXKhHT0xe10KkX8HjUoBQyDEc2yXBu/uj0fae8zRA83MMrswFx6B1L3GSJNLlRaAwNHtuF19RAaMw/r8q8T7W4k6ulDmZLHR61qRqV3cmNRP8qTTyBHwximXMMuOZ/2zn4KL3PIAQj5PHj/yvPi0z77vkzISzHw47unc6bBiVmvYlSODY3AJ96fK+37CJEcwZcJL774IgsWLECr1fLkk0+yYsWKv7mN1+vFYrGg0WiorKxky5YtzJo1C4ADBw5gs9koKirCaDSiVCoRRZGUlBRmzZrFz372M+6//370ej0tLS10dHQwderUL3y/vhDimJ6eTmdnJ9FoFIVCQTQapaur6+8WJv28iMgipunXEWg6HZfTUBgsqDLL/mqK9CJEUaCl28u+ynb2VQ59URRnW4c1AuvVInkpny2aKhLz0RUE2H+2K04aIabtt2VvPRtXlAKg9bUx8OEfCbXVoM4o4f4lN/CDV/0JRE09mEZ/ekcXN82+gdJRPhRymJT8IoLG1Ct2MfuDkYQX60VEL2tckWRoE9KomHcrvv2vIUfCiGWLqFUWkR2VsOhU/OC2KTz5ZhVtPV5KcmzcdU05alFAEuCjyvaED4cPj7YwsyKDgtShY+cJhHH5Qswel8HRc110Of1MGpVCTYsTlzfEpt11qJQi1y8qZu2CIn796lDU0TR4HBvb3dy6fDS1rQNkJRuZWpaKUiHw+s4LBEMxD+Jr5hby3sFG/MEITZ1uMpONrJiVT0WRg7xkA7IsE5AVdInZBLQZ/PgPh/jemuWUpucT7KxDbc8CWWbg0GaUehOR/i563vwl2jEL+E3LWCrrYjVu1U1OVs0p4EBVO2U3FGKffzPO7X+KpYMFEeOC20nNKEDtqcN9WRe0VP0R161dxqEWP6IocPvKMpo73XFtTYhdpwOeEGmK/iHSeHH72oPkT7yG7z57Ln6NRCWZJ96o5PHVecPOd9heyNbjTpq7PJj0Kcyb6MBqVJJCz7B15VAApSUFTXohwfZaHIEmJo9Ood/t45pxBjJtSqK6pajCQeTuBkzjFzFweCtJCzfgrT4IsoxxyirclmJ+8eY5zjf50KpEvrY2id9tOkZuqp4bJ5kwyE0onCHO1nUzsSwjThoBVEqRrn4fReFqPDuejB0PIFtrZO2Er1HX5mLOmFTuXVvB46+eJCrJKESBe9dWYDMM1w8FsCqD9O94kmh/LFoadfXQv+W/sUxfTf++N0BUYF94K70fPIP31C50YxYQSh8fWxcYZ5SZYuvHvfm5uCGPZ+8rjJu2gU3NDkpyxxNtHCLtglKNaM0YPpF/EoiCQGaSniy74ROX4IxgBP9IWLlyJRs3bqSrq4tFixbxta99jcrKyr+6zQ9/+EMee+wxfvSjHzF16lSWL1+OyxXT7u3p6eGHP/whnZ2d6PV6VqxYwTXXXAPAz3/+c37xi1+wYsUKvF4v2dnZ3HXXXX+X/fpCiKPdbqe0tJQtW7ZwzTXXsGXLFkpLS7+U9Y0XEbTkknzTj4l01SMo1ShSCgio7Z9o22hUYkppCh8eaU5Yvmhy9iczxv2UEARhmPQIxEhQVJKJDHTT9+bPiXpjhCTUVo3W/RtumHEnz+2K1RzazVoyHHrGlyRzorqbF/bE0nxjCu18ozwF1eC0ZSGW6tQoFSDL2Ewakm26eNMMxKKdGQ49/3bzJJ566xQDniCzxmWQl5PGj//cwcLSb6BWwHunPdTvruEn9zhIt+nIsuv54R1TCUejWOUBCLTg85h4YW8v7b1+vrKwmLMNfRw730V+hhmFIOAPS+g1CiJRiaYuD61dHhQKga8sLOJ8o5PmTjdqlZKX3xtq3Hj81ZP82y2T+NaNE6hpduKw6CjMsrDreCu56SYqCh0snpRJ0DNAr9ePL6hGFIbSzQdPt7NgUhZbB6O2bT0enL395I3VoA5HCKnN7DnRwbNbzzK20MHU8jQO1vrIiZ5AkKK4m8/F5VdkSUIZDwduAAAgAElEQVQQBGQgcHoXsydOofKSPooPDzexcEoODQMqnjtuZsOKf0fwD9Ae0PLOiRArTWFSPka/0yp4efdANx19MVI4psDOvAmZ7DreypJpOVQ3xgSyI8LwlL+g1iAoVHgGO/enlliZW6hCQEZOTUOVkk+4K1aTKhqTaLNPoWlv7CPJFwgzaXQqClGgsdlJnkIV084chNKcTGSgG4UpiaSy2fiDAa6bnYm9dS+Bg2+ALCFZU9EuvZ9QwE//2z9DjoTo62xAl1sOChWiNZNjtS4qihxUFDlo7nSz52QbWQ4990/2Ix74HcgSsiDy/avuZo8r8RjNnZBJe1sXxa1/SVguBTxkKbrZ0atDkmPWmz//+iz63EGSTFqsVyCNErFSB03AGSeNFyFHQkP3vBTF33QGdWo+oc565GDiPatTK4g0JdbYAhia9hKIrqY6eylFWjM0HEHtyMY872aC2uS/yzPly4QR0jiCf1aMHTuWe+65J2HZtGnThrnGfPjhkBHCsmXL4jWNl2PlypWsXLnyiv9nMpl45JFHeOSRRz7nrP82vrBU9cMPP8z3vvc9fvOb32A2m3nssce+qKH/LpBlgYA+A/JiX/Thv7H+5ShIM7FxVRkvv19NJCKxek4BFQVJX8gzXkUQ0d2JLElgSiGEnrGFjmFdiAsnZ6MQBML9XXHSeBFRdx8LR+kwOSrQqBTkpZkwaZRsvLqUk6NTOFHdzbjiZMYX2VEN2pj1eUO8tqOWM/W9TChJ5pq5BVh1Kv7tpok8+ZdT1LUOkG7X87W1FShFkRSbjp/8ywyikkwgGMEXjNDY4eaPHYnpI5cvFPfX1ShB31lJ3ztPIkdCCGotq2bexY9r/Lzw7jnWLxmFw6IlK9VETWs/2482k51qIi/DzJ+2nuHmpaV4/CH63SEWTc2hrcvDgdOJL3OIWQSa9WoWTMrCoFHS2OGmPD+JDIeRdK0f78G3CJ/7CLMllci4NVwzJ5+XP6gBoKM3Rvo2rirjwyMt3LXAgf3s67heqELU6DEtuI2jp2LHrKq2hxWz8jFoVWBbTOjDJ4cmISpRmpKQAjECIag02G2GxHOtVBCJSPiDYY7VODlWc9EbfACFKHDdQgVYM4Z3QY+Zi2vPK6yftob/sy1GHE/V9fKtdePJTTNTXpjEnhOx9at6tcxJKUDuuqTJZPwa9tUFMRvUrBhvZlZwL9LBgwCEuycizLsLZ0sbIhIBfSqi3sbEURG0GiWleUk8vekU91xXQUDrIDTna2iPPEvU048qKR3zpOX07XgeORIi2FqDfsX9WHwdBA6+NvT7/Z2ED7yEO38RuovpeSmCvz4WJVaNW8YHh/upb3OhVAgsn5mH2aBmZraMePCpoSYdWcK7/WnGr/phwnG1GDWEPf4rWneKUhSHRUePK0iSURMXeg9LEp39AWwmNRqFiCBA50CQV7ZX09Dm4t9WZ2O8TF4IQFAOEXMp5EdQaRCUagRzoguOJMkorGnD5iNYM9C41fxqWztFmWO5f821GMwmAqj+6UnjCEYwgn88fGHEsbCwkFdfffWLGu5LD5VCZO7YdCaVpCDLYNAqkD9jjeal0EQG8Ox8hkBdLDKhSsnFuuKb5KYkcefqcl589zzhSJSl0/OYPCoZWZYH3SkEEnLNgohGb2BykiNhfKNGyezyVOZVpKMIDoC3FYXCSESC/ce6OHK2k0hUYtfxVurbXPz7bZNxmDR89+aJ+IJRtGqRytpefvLMYYKhKHeuKudETTdHz3WxaHI2WSlGWrqGmjyUCoEk85C8hirQQ8/W38Q7auVQAM3+p7l+6td56sNO9lW2sX7pKE5W97BtfwMA08pTsZrU3LlqDO8ebEQhiowrdvDqB7EGkHONTi6HSa9mb2Ubo3JtbNpdx4WWGLEenWPmgYIzBCoHv/C8/Qidv2TOtT8k5cbx7D3ZhsOqo6IomdFZJuaNSSa46yn8jTEPZCnoY+CdJ1g769ucbort59a99Rh0KqbeVoFi3r3oG/Yg6s3oc8vp33sJWRq/huo+kfL8JPpcQXoH/Nx4VQktXW4iUXmYWPW8iVmEw1FqnApS52zE3HaciLMDdVo+kf5uQh0XsOQmNtR09Ph45YNqHlg/gasmZ9Pa7eHV/d0Y569l5lQZZTRId1jL6ycCnG6u477rx5HScwTpQIw0IirRJGchuBoxqFUYLHZCbafRucKsH1/Mm1URnn7rNADPbT3Lt28cT6/LhLDkB6SqvQTP7IqTRoCot5+uzl6SVIHLWpIg3Hwazdg1CbZ9ECPYjS4F9W2x9EwkKrN5Tz3fXj8RQ397/Nq5CDkaRgx6uGNlGZt21+H2hbAa1RxokJhbugzh0ItDKyuUKFIKOLm/hwyHgfPN/XxwqImZFRm8MViukGLT8eD6iWhVCh55+mC8gea/t7XyowUbCOz4fXw449j5+OuHUk+6vAo8p3fjWPs9glpHwi0pyzKavPEojrwd/9ATVFo6UqZTZLOxp7KdjGQzot5KmL+PS9QIRjCCEXxefGmaY/4RIUkyukFf4y+CNAJE287GSSNAuKuRwNndqMevYc6YNCYUJyNJMkatIv5SUtkzMM9Yg2v/G/HtLDOuI6x1XD58bK4yKAcacb79P1imrMD54XZCXY0sSM5l4rU38uiWnnh9X68rQLpVh0oUsOiUtPT6eOKNGIlKtulo7fbE9Spbuj1cv6iE13fU0NThxmbSsGZ+ER1OP8mD4sWyxznsxS8FfSSpYlEcjVqJKAi8f6gJrVrBPWvG0tzp5r0DTYwpdJCaZOCd/Q0cPdfJHSvLOVnTw7wJmRw/3xWPHBl0KuwWLU53kO5+P00dQ97O80cZ8B/bmXg8omE8bQ3suWChKMvK7uMtzJ+QhSSBNuqlv/oQl8MqJ0Z4FaJApxs+OK0lElmE5JS5pyydcPkqlL4eyKrAqFOxtLeaq8ZDvz6XxpCNQCiCxxdmwUQbP7hjKn/aeoZup595EzLRa1U8u+0smSlGyhwS4xuPYSydDqICpdmO0paOX9QDsYimSikiigJJJg25qSbMOiX/ujKLyCwlaq2GgSPbCHU3oMmZzPicMRysHkCtFtH3nou3RNmXfRU54EPy9JCVmkfP279ECPoIAGoErp3/TQ6eixHcurYBvMEIORYQXS4kTz8qWyoKvYWIa8gj2hcWUKmMwx42+tJZ9Cn06Bd/E7b/GjnkR1DrMC69l59tHa6B2tnnY2FJHr7j2oSaTVGjx6cy89qHNcwal4leq+S9g02sXVDEnnoFs2begb5+N4LJgVy2hJ+/00tDu4sbFhXz2PNHuXnpaJ7bNiRR1eX08z+vV3LXtWPipHFKkZmvlMsw0IBj9TdBiiIYHQhylIE9r6BKLUAes4xa0YE4bRxKowPllWqGtck0TbwXR7QbEYl2KYnfvdvHoilGfnbvTGxGTTwDMIIRjOAfF5emn//ZMEIcv0RQKERCreeHLQ82VGIcv5qwpEA/SFQvjWSIKg2KMUtx5IxBcvcimuxEzZlEPkbMXC0H6H/nSfTFk+jf90Y8+hHtbsR88ElunHEPz+zsRBCGGmouovOSWseiLCtn6vvif+emmXltezUFmRamj0nH4w/zygfVLJ6cTVmOlbONTgwBcIiKBI9fUaPHGYlFJb+ysAhJkslN0fGvS6xI59+gWBCZNG0K/7OrjeKcJAqzLNS2DFDd5CQj2Uh7r5fv3TaF1i4PkahEKCLx6vZY2jnFpo8TSgB3UEbUGpD8ien0iKDi+PluZoxJ59ZlpZh0ShQKAVnUorJnEu5uSljf6nCQZnfT0esjO9XIncsK0CsCrF9cxMNPH+HqWfkcapZ49QOBG2aWsVTpx7npl8iREJqs0aQURUnSWOlSWEkek8K+Ux0smpjFf2yYjD8s8cyWMyRZdaycnc+ek228ftjN/JvvxL3zOUJdjQhKNdaZ1xHEikrZS1aKievmF+ILhPmPO6YSliQkZzN9b/0M88Ql9Bx7HykwGJ2s2sa4wh7+89ZryDT4kLNLCdYcQpNdRsTZwcD+vyBo9JjHLUqwygMZQ/U7TClZzoFzfYwpsJOkDhP46Hl85/YNnkwFSQtvxbn7ZeRQACGjlGOdSryBKOvHL8V/4l0QRGzzbybkbMe48xcoMkrRXffvOF1+eoIqXjsZwmaSaOseqhHUaZRMyjeiM2rRr/42vW//GsnvQdSbCc24k711Idy+MO/sb4hv85ddtTx40wTc3jChwum8ubuOHX9oRKNScN/145EkGUmSh+l0AjR1uuPNWgatknWlfpR7niYABI7F/KZNa3/IEzv7sOrWUJBpwemUeWVQO/QHt0+hOMM8TClCQODdM0FO1lyk6rEaZItBTapFO5KZHsEIRvClxwhx/BJBkmTU2WV4q3YkLNcWTiQqKP9qvVNE0BCxFICl4G/+jhB0E+5txVA6Y1htpORzkamLRXNWziogyZio62gxDP3d2u0hJ91Ez4Cf6xeV4PUFGVecPEzouyw/iQutA/zvl46T6dDx4IzbUR54FjkaRlBpUC24B2e7if/YmINGqcBkUHH/QiOhzf8ZH8NY/RH3zv02D21q4ysLiqltidUAJtt0vPDOOVbNLgBkTlR3UVXbx/IJSawuV6KLnuWRVTa2nItyuMbFlmN9zFm2nuCHv4uPLTpyOd1vALzUtbk4U9/DvXNN6HpPEAr5sc1bT/eb/zveAKItmkxTOIkxhRqWTjMw1eFCPvA4krMNdfF0fnrzUuq9Opo7PaTaDUxL6idUXxMjjRlFaFLzcO6MpU91QNKiu9ncqGdaWSqpFi2+UJSJo1PZ/FEdO4+2cNvVpRQ4VPiOvE1oUCtTjoRw7n6Z6Tc9TNG35qBRKlArBQRfD76OKgSFGtHfgxTwgagcIo2DCNceJiOzAOfbr2BddBuq9GIMJVPo+/C52DWiUCJdoT5QCPnQ60RyUk1ct6AItasZ10XSCCBFGTj8Nsb5d+DyhwkmFdF1qI9pZVkoC8pxjJmHIEv0f/AHwp2xmsvIQDfh1rMcyr4dWWfgo5P13HnNGJo6XLh9YaYUmbljfAS2P8YAoJl2Paz4DyS/m2MtId58u4/Vc5PjU9BrlXx9kZ0CZSe62g+xZowipM/iugUlLJyci1GvwqJX4QlEmFCSTM4VLEDtFi1JJg2l+Tbyk5Roz7yQkGqXQwH6689y7LxIJCrB0Q4ml6YytshOcbaNli4vZxv6KM1LIifZOORqI8tcN6+QqtqeIWKqUzG20P53I42CIIw0oIxgBCP4wjBCHL8k8IWiHD7XhSZsZnzxdMI1MU07TdZoNKNmEfgMqXDFYIF/5DI5najagMKaGrPVuNwNRBBJTXPw8FdLUatEzjT1x6Ihtli6OivZwKyKdPZWttPU4WbR5GyKsqy8+kE13kCENfOLmDQ6hagkU55vx2HVkptu4rdvxrQgW3v8/Nc+HddPvp9MUxQ3Bt4/GeCqqTbqWgeIRGQ6+jzcot7JxdimoNFjLJuFzRBgxZQMJFlGFAVmVWTQ5w6yeEoOr7x/nnBU4n9tnMq/rBTRnHwD79YPCAFWYMOU63H6UijKToLcLBw3PoSruQY3Bs56bLx0sfvcouX2aXp0O36JZ9DJJNhYRcr13yPs9yJq9MiWDOqrBvjgUBMPrEgluu3/xkll8OxuVH43ulE3seWjOh64YSy65jeQBgWu9cWTce56JXZ+jFaMZbMRgv1cMzGNvZXt6LQKxhen8NJ75/EGYmMePdfFt5fY6X8pUdQcINLXSpNoo7HDxZJCGe+m/0Qe1AOUUnKxTL0aQRxeLycoVDBIDPu3/wnT/FsJG1Pi14Lkc6Gypgy7PnQTr2aWvoBVZg0WnQqpZbjmXnSgmyq3lS1VXu642sp182zYjGoQBHy6DHrqz6LvTLTok1zdFJj81EasSDJs3lPHj+6eQe9AgEK5Htdbj8fXDW/9v0gL7mP/QCoKtZ5lMy0kmTWsml3A2/vq+eZiB5lHHycU9EFqPkI4gDY3gmjKxqAedJmRwaxTMbU8jXf2N7BkWi7vHYyRcrVS5Otrx2HUKLh3TQUBjwveHy5JpRBkblhcTCQqEwxF2X64kX+5roKX36++pM63lo0ry5gzNi1ODLOT9Tx69wzONTpRq0VG59iwG9XIcuyeheFyV58F6qgHeuoI9zShTs4DRwEh8eNddkYwghGM4JNghDh+GSDA1gONcfmX6aOmsmDmbEZlm1FYUgkw3N7wrw4nCHQM+NlxtIU+V5DFk7MpSDehHKydcobU+CfegrJ6C+ZJS3EdGXIDscy6HjEti5oL/Qk6iKvnFLBqZi5apciGpaNZOi0XXyBCWpKe6pZ+vIEYyXpz5wXWXTWKtl5P3Llm8ugUTJdEKtt7/fzqXT/LZuSi16gYX2Jmb2UbO462oFEreHDdBORzsbes0paGeeISBg69TfT4+1ydPwF/7nXkrptAXbsrQYZHqRAx6dRYo130VCYKkUtH3+Shm/+TiD4ZZAhaCghqcnjq9Uqqm2Np+QWTsujs9THFXJtgfxf1OOnf/QqGVd8lKMdS9+UFShxWLSniQIIUDUC04TjW0mvJcBh4dWcdD+QqsGSNxldzBDkSBmQ0GcXoCsbjOrIVKeAju3gyctZy3jzWz7Z9jVw9K5+399WzbHouSoVIOBJBnZJDqLMhcb9URs7WOslP0xE6+kqcNAKEuhrRl0wh4upFnZZPqGMoEmyacBXe80O1m56PXkG6+iGU1lSig7aR7hPbSVp8O97zB5D8HhQVy3nxrJaPzh3jO7dOwqpTxT5ALmvMUmSPZe8FH9PL05FlmRSLFkGIfRy19/mx63TDtgHQ6fU0nI/Vo65bXIJGKeL2hQjWJ0pXAGga9pFScDOPD16jJTlWvn3DBBZNzsLcsAN30IehdCaiWkv//jdh72voR81AP2sdQaUFAG8wyovvnsPtCxOJSty8dDThqMSYAjvZdl2s6U2twGi3oZy5lr5tT8R/X1CqkRwF/Pn5aiJRGbNBzU1LRxMMRxOawwBeer+aiSXJ6NWDZR8ypFm1pNvS42RSlqG938/7h5px+8IsmZZDQaoRxWesd1QRJnDgFbyn98SXGccvRjV1HZGRx/4IRjCCz4GRJ8iXAJ5AhHcPDNn1HTjfz4Hz8K83Z1Fm+XSkEaDbFeSHvzsQr+07craT+9dNYFy+DVmO2SP+an+IhaNXU2wF26qxeJ19mNMyEG1ZOAMyTw12zl7EW3vqmF6ehtWoJhCOkp6kQ3FR+3Dw5bdorI3ZuTJp+nbq1Vqqk7R09AU4cq6L+9dNYF9lG5FobGW1UiTDYeQPm2O/M29CFitm5WHQqujs95M9agFcOIKpYgF9Hz4fj3qF6o+jiUrs8c9nzaIyopEo24+0kJqkY/1Vo3CY1Mi9w+0NkSIopCC+qIwsg1YpYtYqeXD9BHrdQZzuIG/trqOjz8uqacOju7IcRRn1ERZNIMR8QBdPzcVodnJ5hZyoNXCu1cOMinRsJi2SyYGvahO2eesRlCoElQZ9yVScO1+IbxOoOUy21gJyGS5vCJVS5KYlo3jxvfN4/WHwp7Jq4lL6tj8bJ4f6kil49WnsOXEB+/QUpJ5mLkc06MdbfQjjmLkYy+cSGehCnZqP9/yBBOckVXIOZnoRlv0Lrn2vEmw6i6DVozQ7UFqSIX8Sf25MpsPjwx+M8Os/n+TRu6YjGNKxr/oWzg+eQvJ7UGaWMlB2HaMHtBw910VmsoF0q47O/iA/e/4I/e4g4wss3DN2EaGqIXKvLZmGIiOX2doI18zJJ9ms5eC5Lt7e28BD5dbh+6WzYjNq2LiqHJtRM+gTL6BTq5ACbhAVqJOzce5+Jb6N7/x+VCm5iOXLkSSZqCTFP3jONTg51xDrzs9aP4GspCFbUFkGOWs89msewFu5HaU5GUXpPL71bGP8er4oQP+164ZblgbD0YRu+UvHvYiOgQAP/e5AfL2j5zr5zi2TGJ1lGbbdJ4Ho7UwgjQCeEx/gGLOIiP7LacwwghGM4K/j17/+Nffccw9qtfpvr3wJqqqqeOaZZ/jlL3/5hcxjhDh+CSAIAmqVYphvrvIKKcZPgupmZ0JDCMRsAsvumIJSEFCLAretKOWnfzpMYNA1ZM28QpaNzgFBwB8MDJsLQPeAn1+8eIw+V4Cy/CTuXFWOTa8iP8PMiol2lor7Yd8BQkCmIPKv87/OQ1sj+AIRNu2u5aE7p3P4TCcmvQqlUuT1HbEGlrkTMkm166lu7CMr1YRWreTVUwJrlv0bBDsSU+lApOkka69ei82oZsW0HFbPLSQYiAmYyDII5lRErTGhrk/lyKTVr+O/nj9ANCqzdkERk0qS0ahE6loHaO/1kWbXc6GlH599NHpRmdD9bRg1He+hv6CacTMtzmD8Ja+Yl8GsnLGEm6qG5jfpBrbsHuCrq7MZnWVGFJNRJd1KtL8NtGasV9+P1FUz7PiGq/exbvEimivSmZopI7vambouhS5XmJPtUBt0ULDwFiRPP6LejJhWzM82t7N2YRHNHS4onAbHtySMOWAuRrFkOrIYxHdmB9JAJ+qU/JjVzyAElRbT2Ln0bP4VSBJpNz1E2NmJv+44Pdt+i3niEvwRCa1agdMdq3/tcwXwBMJoVRrCGROwrv8pZ2raOd8DZ/b3U9PUj0IUSLWVEZFknvxLVdyz/UTdAJstY1i9tJxAey2yNYsLinS2bqlhank6PX0eMgtkxkh1FExXI6ZMRzizKy7xIyjVDKRNIdmqY+qY9ATbOUmS0eRPwF9ziFBPy7Bj7D+/H2PZYiSUGLUqFk3OYm9lO3npMV/rAU+QTIdh2HYRQQOpFeiWj0eWoaq+jwFPYg1oZ58PURTQaZQJ98/8iVmYdB+vySgIApUXeoaRy7/sruO7N09A+AzlibHI9hUQHV63OoIRjOAfA48//jgbN24cRhwjkchf9ageO3bsF0YaYYQ4filg1ChYd1UJf9xyJr4sw2EgK3n4C+yzQhj8dxHZDgOP3TuLrn4/Rp2KZLOWi1kxq1FDhsOQYOGmVop09ProcwVYOj0Xu0VLZW0v44ocKBQCa8Zp8G26xGtYltAefYGrJ27k1X1d5KaZybTrEctSaO/18z+vn0QU4J6lWZTZAgy4B9BnGnl+TyNGvYrFU3I46lIxVjc8saYwWslIsxMWBWQZrCYt3YGhF2VIZcG+9vu4dj5LsP0C2vxxKKZ8hX//bWU8QvT7TacwrptAXrqJZ7eeJRSRmDQ6hQdumogourEv2UigvhIpHEBXOAFBUOCt2oGpYgXb9ncTlWTUShGtxcaO0BImzpqNJurDr3UgJufx7XVK0m2xLtloVCaqtkOKHRnoEYKkJgW4HIqkTCrr3MxK6sT35z/GrQezZq/FFrjAGfdioullYPByusVPpqRialk6f9x8GkmGkuVjKC9xEqreh6DSII2/lldORun0dJJmN7CqYgmpzdvp2/k8htHTsS+7CxCJenpx7nkVpCgqeyaeU7vxVA41aPV/9Crm1f+KXdbF07DJVh1GXUz4WpZlwkozjhwdm0+doaPHR3G2lfVLRmHVqxjwR2hodyXs69bjfdjTRuMTHSi9An/+oBpZBrNBw/qyIH0v/jcgowOCWWMwXPt9Qp31hMNRgrZ8tPZcjJorqwZELDlYF91BpH04OVdnjCJ68YqSZVbMyCfdbuTY+S6mlqcyqyKTJKPmY5tJLtYLX6pNehGpNh31bQPcvHQ0x6u7aO/xMr4kmZw0c0yjMyoTisq09XpxuoM4rDrSbTpUCgFBGJ6SFoXEe/aTQhBAMqagtKURcQ6J46uSc5CNKZ9hxBGMYASfFDuPNvPstrP0OP04bDo2LC9l/qTszz3uRUeYdevWIYoimZmZ2Gw26uvr8Xq9bNq0iQcffJD6+nrC4TA5OTn89Kc/xWKxcPDgQR577DHeeOMNWlpaWLt2LevWrWPXrl34/X5+8pOfMHny5E88F8XDDz/88Ofeoy8Afn/oM3cVGgwafL5/7C/pDIeBsUXJWI1q5k/K4tq5hRg1n4zXX77/Op2K3cfbEiIYX11dHndvuQiNUiTJqMGoVXLpe0shCFSUpFDbOoDTHSTZquPeteP48/Yarpqaw9mGPvacaONkTTe7T7SSmWxE62pC1ZJopyaH/ATyZ9Puhg0rSpEkifoONyqlSHOnm1tn2ymvex751LtoWw6T6TtH2aw5fHhqgHHFybzyfjUVpVnYRRdR50XXFAH7ivsIm7MAEBUCGo0Kvz/x/EfUZrSjpmMctwh1yWye2dlOXVsieXH7QuRlWEhzGMhOM1OUZeXF986z0FxPYM9zCAoFSBKeyh2obClIIT+nFWWcbfXR5fQzY2w6x6u72X/OyfbqMO9dgO1nfYwrSaUwzYRwhde+ABg0SlQ6I1J3PVFXzOtZUKgITLsDWY5iP/zbS+SKZAKt1ZjLZ6IP9uKxFBKOChSYA1jlAVIdZmo7A/S5gpxpDZBcMQ1X6kRa7dN4+pjA+RYPOakmguEoE/IMSPueQw75Cfe2okrKQBAVOHe9FI/mGUZPx1dzeJg7iiqtkCc+8uL1RzAb1DywfiL2yzrudRoFBVlWjDo1uelmKlIk1O5W9IoIfQGRhs5EC75Vcwp4a3cdB04NkZtJeXqKLryIfEm0WHZ1MWAr5clKI2kl5RxvCpKTaiIclbGYtMPOvYxIVOdAa3MQ7rxA1BNLQYtGG5rZt4BmqIu6rrENIeCixx1m54kOztT3Mn1sOirFX4/26zQK7FYdlbW9gwYASh64aRKn6/t4dXsNFqOazBQjJ2u6SbHqKMmyEJFkNn1Uz1NvnebQmU52HmvBatbGXJ0MGnYea0W65CF41zVjsJv+eqnK5fd+VJY50zTAr944R/HUGdj0IgS96EfNwDT3lnh95z8DrvTcNxg+fWnPCEbwRWHn0WYef/UkLm/suvQFIhw710WKTUdexue79+bPn3upGCsAACAASURBVM/jjz/O+++/z4YNG9izZw/nz5/nmWeeYcOGDQBMmTKF22+/nZtuuonq6mqOHDnCzJkzaW1tZe/evdx44424XC5+97vf8dWvfpXvf//7WCwWfvvb3/KVr3zlE89lJOL4JYFKFChONzEq04wsy59LmiPZpOWRu6az52QbfQMB5k/MIj/N+KnGdBjVfPfmiXgDEXRqBd2uIC5vEL1WGXf0APAHIxw600neuGQub3hQ5YxBZ7VTlq+mt9/PS++fp6HdjUIUuH1lGeXRE4T7htKJkruXLFclafY8AMIRid+8Xc/9165Fmz4DgxjEkpFD2JJBWJKpa3OzdX8Deq2SFTPyyEszIssy0mAdY1hWEVaoEGWBZNvwblKLUcOzW8/S0O4iJ9VE3ux8Umw6tH01hCAufQMQ6m5GMW09Z6sllk3LpbZlALNBHU+/wlAm0usfSlPKQL8vjABYDar44QmpzSgW3gvt9RAOYEjP4aFna/naLN2wZhuiEZBltL01OErmoqh9F/n0+wCIllQeWP4N7ntqAI8/jD8s8MLuvlhdJCCKAgsnZ9M3EGDHOTfLypdA1daYgLYso7SlISjVyJEQot6MNqecUFcjQV8iyVYbLfzXujTcCguo9RjUimHXU4czwEO/349KqeDhlRa8rz6BJxQABG6ddSPBUBr7z/YhCrBsRh7vH2xk4ZRsXnjnXHyMvBQtUkMfl8OmkVg4KXsw4inQ1Onm9R0XWDO/iAmF9lh0ThDo9QRp7faiUAjkpVrwTf8a3o4GFEjU+4y8/PsaHv5qEilmDcreC+Qe+z1SfyfluRXccucN7KqVaOhwU5huRvMxHuEQ+7iaMzadMQV23L4wdrMGk1bJ/AlZ7DzaMlgv6USvVTK1PA1JkulxBeMNcBfx0nvnmVCcTKpFw4/vjt2zHl+Y+RMzyfkMGYeWHh+/fPEYAD9+w0NJ1hjuWLIKU5qdgDQiLD6CEfw98ey2s8N0YYPhKM9uO/uFRB0vx7Jly9Drh95tmzZtYvPmzYTDYXw+H3l5eVfcTq/Xs2DBAgDGjx//qS2iR4jjlwyXCwZ/FsiyTIpZww3zC2IabtEoyogHGRUhhqfYPg4qUcCqj6UjUyxarpqWM6yuC2J6jse605k9/19QHHoByedCkTGa5txV/PLPp2PNBTIEw7E0X1SS2X64mUk59cPG0vTVUpRVQVaykf+1fjRGrUhlWwR/KJmapn6K+yXmjpNo6/Xy8+ePAjCp0EJqqBHfvlOE1WbILEc0p6Mb7GKVJJmppals3dcQJ1QalYLSvCT2VbYDMcFnfzBCe48XX+kYlA2J0jeaggmcl9M511jLuQYnt11dSmefj1kVGXFrRIilCXPTTCAIBCNRXtl+gV3HWxCAJdNzWT0rn9YeL2/uqqU828CCAgPBcIhgMMyoLDN9ESXpal1Cd7Sg0oIsoSmaTKS/meggaQSQBjoRT2zi6ulXsXl/C699WMN3bpnE+SYnGrWC0Tk2/OEoda0D7D/djVAyivkzM7D2nWUgqkWvtmO/6g4GDm3BOHY+PVufJGn+TYQ6G+JRSHVqPkq1mq5XHkLpyCJpxTfwy0O6iRCTkdl5vJVIVGbNtCQMR/5INO7uIuPZ+zK3rP5f5GQ6EEWBQ2c6qG0ZIC/DEq8JVCtFlEYbQsls5DOXuC4IIkFjGocOdBAKS0wcnczZxj5uXZCFSR+l3xciyaCmo9/Pw08fJDhYt3v1rDyOne+mPV5yEYtiVjf3k10o0v3mz+Ld85HGSpShAD3i1by24wJLpuVww/xCxCukkOPTApIMapIGFQPkwW7pR++ZQX27C0EQKMgwk2RQIcvEG3EuRSQqEwhFkfUqUi1ablxQCAifSY5HFAWOnU903KlucfP7dxv5wa3JCHz+Z8sIRjCCj0fPJQYZn2T558WlpPHIkSO89NJLvPzyyyQlJbF582b+/Oc/X3G7S2skRVEkEhn+bPprGCGO/wAQBJCIuU58mrBhNCqjCTsJHNvCwKldKMwOrItuJ2wvQZKvUFMlCkiyHIsbXvYzaoXAtbMKqOtwJRAlgPHFyeyt6iRlQSHKcd/EbhD54LSLvW+1xsdJTdKzv6o9vk1btwfvxDLUNQcSxqJgGjcVFmDqO8PAjueQAl5mj72K3aHRVNX2UFXbw4FTHSyeGvt6M2iV3D0pSmDz0BeTwmClf8630eXmxiN8DpOaH981nYYON5GohD8YibvLXEQwFEWSJE5405haMAWp7nBs34um4E0dx8+eOBHfnyffqOL+dRPw+MJcPSufnUebsZq03LxsNO19Xn7/1ilG5SaRnWpCFGIWfe/sb6SiKJkn36jkB9ek4+g6guetbYhAWBC5e/k3+dWhCEUz70Z78OmYM4rOiHXmdQS6mvGmz0DTcXKoi1uhRGVJRuqqY+o8E72edEbnJdHZ5+PNnbU8fNd0XvngPOcb+9GoRFbOLuCjk228XxnhO7feiMcbouqjNpaX2rDlloMURQ4H6N/3BtaZa5AlCVGlQVCp6d/3JsayWXhO78G992W0i+4lIg/VGAqCgGcwZZxhkom6e4ddX76+bl75ILHjPRqV+O6tk3H7Qhj1Kjp6fajHXY1Sq8JbuQOFyY5ixk1sq5U519DHtfOKqK1rZ23BAKpDm2PXwLTrUORPobKmk7F5Fk7U9hOJSgx4Q1eUs1GIAtH+jgTJJYBIezXjp65mZxW8d7CJBROzSLVoEQQBdyDMgCeE2aDGrFN+7G0oyzJ2oxp7cczuUxRBKQUYCClo7/Vh1Knw+Iciyhl2A0mmoYd4NCpzuUzRJ4Usy1esvXRYdYif7tExghGM4DPAYdPRfQWS6LisTOyzwmAw4PF4MBiGZyNcLhdGoxGr1UooFOL111//Qn7zShghjl9yhCISpxqcbNlbj9mg5rr5ReQk6z/Ru0UhSPiPbIo70USc7fS8/hjJNz9KQJ8RX08QoGsgyLYDDTR3eVg8OZuKQjs6VWLzgUohUJBu5o6VZbz43nlC4SizKzJYPCWHDIeBA1UdHDvfxZ2ryzlyoSXeuOuwaKkocvDmzgsJ+9WuymPMlKtxH9kGsoxh7HxC2RNwN1UT3vGroXWPv820CWretybT0x+grccTr0G7alwS4UPPJMwz6u1H425mwJeJRRe7xGUZjDoluWlGohLsP9Uer0O5iMIsCw/cNImt++opmnYTgfT5hCMSH9VHaHu9ju/cMon6djfhSJT3DjSy53gr08em4Rzws3ByDi5fiI+O1LF+upX1E5RUdnn54HAXS2fksXVvPdmpJg5UtfOtxTbSA7U4jw3pZyJL+HY8xZq538VjSMJx/Y/RRNwolCqiKFAXzmHr/lZW5WcQAozlc1AmpRHuaUWVnE13IEBrtwedVonTFaQkx8aBqjbGFaeQk2pGr1Xi8Ye5aloO/mAUW6ib7JYPGWMS0OimoSqbixgNYZ21FgD3yR1EBrpQ2TNRp+YR6qxHXxwrnvbXnUQ924OgtcQb3vu9QUbnxiK4DU4otqbFyNklsKakYtC1xKO+EPvoyLLrkOXYl3PuYEezOHU99okrQVTRF1Ji7u3i+kXFKBQi15dFYcef4gQ6UPUBFqOJOd3vMkcdxbtyEc9XChw508ntK8t48o2hjnedRklJthUxkqi1CDHrS1dwKD0dCEUQBIEL7S7+z8vH8QUiaNUKvnH9eEpzLH/zHtSG+gic2s5AzSFILiI7ex63LBvN+4eaqGsbYEyhg9uWj0b9N+opPylkGSqKHJgN6vi1rVSIrJ6d/4U4x4iigCcYIRqVMemUn5XfjmAE/7TYsLyUx189mZCu1qgUbFhe+oWMv3HjRjZs2IBWqyUzMzPh/+bMmcNbb73F0qVLsdlsTJ48maqqqo8Z6fNBkL8kXlS9vZ7PnKY1mnXUNjvxBSIkW7WYtB8fEfhHgiDA4eoefvN6ZXyZKMAjd80gyaRBr1YgyzLJyaYESZKL0ERd9Pzh/mFyNkkr7iOcNdRB1e8L84Pf7k+QELlhUTHLp+UgX+GcCAK4AxGiEph1ylgLiELgR384TEO7C6NOxao5BcjELArL85MwaRR0u0K8taeO+nYX8yZkMqM8DbNWRBXsQ5ZlOsN6Hnr6KI/OdCIcfyPhN5XmZN5J3sDmw90AfOfWSfz8uaOsn5PGzLrfIPkTiYBh0Z14Mmdg1saIYyAiUVXXx6m6XvZWtnHt3AL63EF2H29Fr1Fy3YIiqi50M74khRSbjpM1vbxzoCFhzDXzi3jvQAMKhciNi0to6XIzrTyNh586CMA1U+wsVhxBqo7p54nJedQV3kB7xMLL750nNUnPnPEZzJaPYDOq6N87/IuwYfK3OesysH5RybB05YUONzU1zSxzNIOzFc+pIWFsZXoJ3RM20u4VeX7bOdYuLCYSlXjx3SGB9KwUIxtXlYGri+S9v8QwejpS0I/37P7YMRs1DVFvwlO1C+uMa/Gc2YtxzBz6929CDgVIWnx7LIUuKujU5NCNneIcOwpBoN3p50/bzjFjbDpHz3ZyxzQtmr1PEPX0IyhUWGZeR7C9FnLGs6XZyumWAMtn5nPwdDu5aWbmVmRgHDxXDV0eXt95AZ8/wvolJZy80MPbe2PnojjbyoNpewnWHo3vV9KiDfRtfzbhWAXn3sf333Lz8FenEwpH2VvVTpJZw9SyVJJNGpRSIOaxffaj+DbRWXfy6C4FLm+QzGQj37l5IlFJ5ntP7MN3SZpZpRT5+ddnYRnsKr8SVITxvvsrgo1DD26Fwcrx4rs52y2QmWJkTEES2fbPp5xwpXu/3xemvt1FOCKRn2Emxaz53M/DiCxz9HwPz71zlkAwwpzxmayd/8kb+P4euNK+JycPt48cwQj+v8Tfq6v6y4R/+IhjOCrxwrvn2LSrFoj5vv7g9imkWT55Ld+XFRFJ5q09l1mzyXDwTAeHz3SydkGsMeBjIapQmJLinbsXIWgSw+bNXZ5huo2bdtcxd3wG+kuijhIQjspolcKwF4YgySybnsuTb1bh8Yd5adDR5aGN0zAONlI4TGo2Xl1KVJJRKQUGvGGc/ihmnQMBOHK6hWA4SkRjZtgr2eSgyxWbY1aKkdxUI4/cNZ0Lzf2YJixlYN8QCRMUKrRGC0q9Kv4x0uH00dbjYc+JmOj1GztrKc628vBd0wmHo9S3uxhfksIrH1RTmpeU0PRyEZ19XmxmLS1dHt7eW88DN02gsT324lKIArPSfPw/9s47QK6yXv+fU6b32d5rNrub3jvpgZAACSFUqaIIgu16vQgWioo/y/WCiogoSJNeQyCE0EJ62/RsymZ7r9PbOef3x2xmM9mAKEEB9/lvTnlPmTlnnvdbnkfdMCC6rHbUUppZhaP0PC5dWIY3GGFyZSa6QwL69EKcMy9C8Xbj27ceTYkiu7MZWZqK269j59FOCjNtCXIS0+Bva6o53uJhxIpCXPufTjq3WMth1MImjrbbKcqxM7o0lbv+vCVpm8Z2H95AlAp9HyFVQTLb8VatS6z3V2/GMfUCBEmm54PnSF/+bTw71qBFgljHn42mKvSsfwY0FT0C2ZMvpc89C7fNjMNioLXLzxNvHGLMsFSe26dQVvJVFhSD2n4M37734tIwR7dz0blfZ9LYURxv6aM018Xb2xuobfHwtQtG0toT5O6HtyaITl2rL0EaId4JHzGlJj7rUnMJNw2W3bHWvc+tV15PhtOILAqUZg+PN06p/Y1TghHjjMsxjZxNX2cnencWe3pMfG+ZgjNYjxxow+Q9SoecjV6WWHhWPga9TEunn417mun2hj+SOIrBriTSCPFIeJ7Rx5/3xic5I4unfuj+nwROsy7pvXAmJtFNnQH++NLA9by3qwm33cj5MwpPO7kcwhD+UzFnQt4XjiieijOTI/k3oqUnmCCNAP5glIde3kfsC/AyEwQBs3Ewt9fJIh5/hPuf30PT6VxS+hERTbjmX8vJanD67DJw5ydtJ0uD68B0sjjQGCBAQ1eAXzy5k9se2MjqLfUEIsmdY5oGo0tSuHZJJU6bgQy3mf+6fDx5aad0M2saUUXllQ21/Ndv1/Od+9bz3LvHCERi2Mw6xhbbMaXn9VvZ9UOSMUxeQVSQueLscr572XhMskReiplzpuQhyDoc05ajTy/AVDwW94KribQcQTxJQD0UVthzNJlAH2no5d0djTz0yn5Wb6glHFUIhGIca+qjsngwIS/ItNPaf7+bO/30eMJkuM0IApx/VgnpFnDOWolr1sWYivodRBr3smnXcZ5ae5jaZi9WvYAjO4/O1x+g94PnCNYfxD3vSnRpBTgmnoP/2TtwbbgXR6Sdux7eSm8gntZVFJXufvFtbyAyKIoMkO4wMHlEJtctGYFBFpNSwiegqho9PgV9al48AngKws1H0aXlg6YSC4UIOwqxLP4mpoqZ9K5/+qTjagjbnsYSjtcyWgwit1w8FqNeYsehdnZVd2B0pNF7bC+9G55P0hP07VzNrv2NNLfHswyLphRQ2+LBG4xSdaQjiejETom6tnYFaHeNRtDHJz9aLIqgG+yioDNZGJ7nTNhsKoo6KKMRFc1E3GVsCRbylT/V0NrUjnnLQ4TXPYB/03N0PncPzrZtXLaojLe3N/DMW4dpaPNy7dJKXFYDqqbR1hfiYEMv7Z4wScOLMoiDdSYlfVwuZu6EXDKcZ6bu6dOGIEBNU9+g5e/tahrUQTqEIQzhi4/PfcSx2zM4MlTT7CEcVZE/RCD48wIRWDmvjJ8+MuApbDHKmI26RITwSGMvEyozT7u/pkE0rYK0K+5G6W5GMJjBXUBESk7n5KVZSbEb6fIMiFJffvZwzHopISNy91+2JMSzn337KKGowoUzi5Nqp4yyyOwxWUyuTEcUBPSScNpox+GGPl54d4C0rN5US26GlSmFRka27CD26iYck5cgWlxgsCCnFtAYc1GS3UZdq4e8dCuqy4RBJyGKoLM46H7nCYz5lRiyS4l5ulA9nXBsPYackYRlOzaLjqxUS5KUEECq00ifL4xBJ5FmEbh2bibbjgdAg6kjM9myvxVREJg7MS4Fc4LI5KZb8YdjBCMKP/vadIKdzQT3vUOk+TAA5mETsY2ZTygGBs0KdLK/potAez3R1+9PELBYTwueXWuwjTsnbieoRFG6m0g//AIj885lb00XZ43KxKgTOXtqIc+8dZhNdQpFBeNQ6gZ0M0VbKlubRbz0sL47wJWLy1k4OZ83TrKy1Mki2akWxGAu6jEJgzub4PGBMgiIO+wEjmwHBGqDNn69IwvP+7385gornKLtiKZiVT3EBAGifux9R/i/ZUYChhz2dBpZs6WW8hGWQbNTzWDjWLOX3TU9DMtzkpduZc74PAQh7rpyMmKKOqih5MltIb65+Ps4I/HvRrKn4DuwYaDZRRCxjDub0CnuSYIA+lAnancDgighpeThl2y88n48qj82I4YjfQKUT0BAQA0H8Gx4jtiITLz9BL62xcN7u5qYXJnBB3tbeOS1g4nxb1g2iikVcS/0mDEF66Tz8W15ceD4OSOwZhVwx5dLyXKb0Z1mwvZZhKZBimMwyc3PsP5dvcshDGEIXzx87olj2mlm7RWFboz6z84LTdWg2xcmElVJdRj+oWL44kwLd391KlVH4tEyk0HmhZOaTFJP80JPOjYiIUsuWHI/dBurUeb2ayax+1gnLZ1+JgxPpzDTlojQNHb4E6TxBN7YVMfZk/OTUtnQb/nWf32nI42SJLLtYOug5Rv3tjDLHsN3ZAMAfVviHbP26StodY3j1j9sSJzPB7ubuXJxBW9squX680cwsXgcxrr9EA0RrN1LqDaeUvMf2oSxeDzm+TeQZjcxa0wOe492JkhAdqoFNAFZEvnvRQ6ch/5CrKOe8fnjqHPOpaVL5c6vTEOWBJra/fzhxTjJclj1rJg3jD88txtZEvnOZeNI7diZII0AgSPbcZ11CXVqEfbIQNlErK8d3SnRwmhnE5LRjGPaBUTa6wgc2YHScphRE5fR7AkhCAKqqnHW6Cw0TeONTbUcnbeYUTmlRI5uJZo6jEbXBJ5c3UpMaeXKxRV0e0IsmR73/n57RwNZqRYuWzScNIeB37zRxoyi85iaC3LNbmI98W532ZmBbHGi+HpxzLmC+zd7Ein7g+0qI8x21JP0HQVZj9pdj1Gvw7t/A8b97xEXvhEYNfcmnu4L4XWV4TKYUcP9kXFBxFu8gL0vxH/PRxp6mVCRQYbLhMuqZ3RJapJl37s7G/ivKybw+OsHOdbUR0Whi3OnFxEwGunTpfLrJ3ciiS18Y963yAoeJRqN0m0vpzuQQqFdSJrYGPzNdD5zd0LqSLK6cM5Ywe0LDDxWpScn1UTfqlfjGpf998MxaTHGSPIP+UhDL92eMI+uPpi0/M+v7md4/oy4hJUostcwjowZmZi8DUQsmVT1Omjc1Mb1SysSDjSfF5RkxwXyjzb2AnG/+4sXlP1TzjZDGMIQPt/43BPHTKeRa5dW8tjrB4kpGpluM9ctrUT6CP21fyXCsXha9oSETV66jW9fNhbnR9RHnQwBgRy3mbxpBbT2hvjRg5sSPtQFmTZKcs6ME4TTrGPO6CzEfmu0k2HUD47c2i36j/TSFkSIxjR0kpj0562qGsU5DjbsacGolzhvYgq5NpX07EzCx9cMGid0dDvtlimD0oyb97VQkGXnTy/vI+2aSaijLyU91kLPcz9N3r9mJ9apHeituQzPsXPXV6agC/UgCyp7WzT+8Mohvr4oE+sH9xGNxgmDVrOFolAfWXNv4kd/2YY/FKMg08a3Lh1HMBxDAP700l7CUZVwVCUUDKJv3cupCpfh3g6ePJDCtNFWRFHg4ulpZKYJnJr0E812Iu119G15BUNOGe45l9NX9Tb1PQojK1JQVI2YqtHnjzC5IoPZY7PRSxJhtZQnj+dSdzhEQ/uA1JFRL2EyyBhkkfOm57NwUh4WIYjgaUHrhvIMmT+ua+NJs55bL/ombrULq05FZ7aieHtIufTH3PVSG4ebBhqONtWEmbz4GwTW/B7F14NosuGctpy+batAW4OlfBoDIhQa0tbH+Z+LbkUzOWmefAtpoQZENYKcXcELWwNJaV2dJGIz63lsTTXjh6dxx/VTqK7vJdUZd4Wxhjv43tlOgvpCIrKVmKJhNur4ySNbE7qid7zUjtuezoq5pfzppX3YLX387GvTEhMbSYLA7rVJ+piKr4doVzPGAx9w/ewbCe17J0EaAWK9bSDKbK5Nrv91243xeslTJkYxRcUXjPZrnwpU1Yd4f5cfuyULXzBCTOlgQnnG57Jxz2KQ+PalY2nq8BOJKmSnWnBZ9GekW3sIQxjC5wufe+IoiQLnn1XCqOIUguEYbpvhIx0f/tWoa/cl6R42tHt59YPjXLVo+D/00lVVjQyHkXtunE5Tpx+9LJGbbhkU8fskOOGrfCry0q3kpVtpaB8gEtcsqcQgJ6eiJUlAEES6fCHe2FzP7iMdjCtLZ9HkvEQjgaZpjC9LY31VM1+dbsSx46/EPB1I9lQMU87Hf8qxDYVj8IQGn5MoCkiiwLLZpRys7eHhVQf47iInWae9sHgdlqyFcTesp++DZwjHoowqm8pX588iXfKgRZO9o2PNh+hubk6INte1evnfJ3dy29WTuO+ZqqQu24auCPmZlXCS0wxAxFlEe28Qg07ilkUZlB5+jLA/BduYeXh39wtcizLOqcvo3RTvIg83HUbQGZBmf4XsLgveQJi9x2MEIzHe3t7AkYZexpalcf15IwhFFRq6IjS0D9y1FXNLqW/z8tjrB0l3m7lmSSXlzjC9q39LtC0uuD47vYjUBSvxiE5+/VINXX3xa093+bh2aSXdjWF6AgO1a26bgeUzctndF6Ni7tewdR9Gi4bp3fxSIgIpGpJrWRV/H+lWkR89tZvW7gBGvQFJMuEP1vGlc8rZejDeHT+pIoNh+Q5++dgOvIEob21rYOnMIqaPyuL5tQe4srQT4d1nCKkxZJubyPQbuOflDq5ZUjlIL63bE6KvX4bG44/Q64tgT7egKhqiphLrahj001CCXiSTDZOnhmB306D1Ub+H/OxKtlXHazklUeBry0dhNeqSIqMQn0y5+i0CFUVl9rgc3t3ZSK9vIMV/ztT85G55UcAY86D2NCOIEpojm4g42OXoswCTLFKaNVDmMkQahzCE/0x87okjxLXKnGZdwuXkswJBiDuSnIqqwx1cMq/0H9Zv0zQNl0WPyzK4GeDThEUv8d9XjKem2UufP0xRlp3sFHOCNAoCdHgivFfVRGO7l1GlqXj8EVq7Ary+qZZDtV389xUTMPYTeodJxx2Xl+F79kfEfPHUl+LpJNbbhqloDMHjcdcWXWoexsrZFAWt6GSR6Enpvemjsmjp8lN1uB1REokpKrvbRHIyh6G2DnTZ6rNL0azxRhupu5bOdx9PrAsf3sSkKRmIOSPxAJLNjblkPGokSPD4Hjp9gwv/FU0lJ81CdX1vYtlzbx9hwY1zCTXtQ+knJ/qiscScufxuZR96RxSl/jje3hZCvS2gVeKafSmi0YpoS6Vn7Z+T5IRCtXsxTLsaXyjCky8P2PEtn1NCJKpQdbiD1u4A9z1bxSXzy3hqbTXeQJTSXCedfSHe2xm3caxv9fK753bzqzneBGkEUNqPU1lYx7sRa4I0ArT3BKk60snkygwuP7ucSFQh0xQhx3+IyMb/JdORgX7M2fRueIGTRfwkmxs1lEz5xdyReLDQ2h1PUYciCvQrL5qNMtNGZZGbbiUYirH78ED5ABCXSDLqOKdcRtj45MB5e7ux7HicGeUrOdrYi9tupNuTTPj1/b+xrBQL3Z4wz71zlGF5DmaMzCZlxCzCzUeTtjfmlBHraUX192AaOYfoSb8PgHB6BZNdmYwuTcMXiJLhNuG26BEEgVuvmshvn62iszdEptvMzSvHYDUMWDHmpVn4wTWTeHl9DYqqcf6sYgozCx/pTgAAIABJREFUrEBcDeJgQx9upRPX1j+geuM2i/qsEuzn3EJY52QIQxjCED6L+EIQx88qNC3eeHIqRpakxP/gPmTCLgjgjyhx0WqjdLoG2n85rAaZ0UWu067r8Ue588+bE9G53Uc6WTa7hHSXibwMG1aTjuauAMUZA/dCF+pD8fUmjePZ9hqGC26nJ28BLouMMTOPkM6KS1a5ZkklNc19xBSNkhwH7+1sZEJFBs0dfmL9UZ83dnVStGA5ZZnVGNv3Yywah37YNEJC3P0j0jJYtiV0aAOuUfNxzv0Sqq8H/8ENiAYL7gVX03J08ETEpJM5e2ohx5v3JEoGhuU5CelTcF34fVRPG6IkI2gqHU/fRUBVUPMq4rn7E8esP0Co/gCmotGYSsYT6+tIOobozKC5T+HptdVJy19+v4arz63g4VUHaOny4/FFeHLNIRZNLcCgkyjLd/HTh7cm7ZPhMhNp2DToOnQd1UjOEaQ6jXR7wqiqht2ipzwNtL429tWEmD4un/TGD/Bvfym+U2cjqr8H54Lr6H3nr6DEEI1WwlOvI6LG0Nu2oXi7kPLH0FexjEM1XlKdRjp7k8ldKKLQ2O5jZ3U7I4rcgxQQdLJIIBjFqhvcyRvrbGBYkcSf32vk8kXlPPnmIQKhGKIosOysYjbvayXDbeaKxeXc99QuIjGVqsMdvLO9kXtXpmIbvwjfnncRJBnbuIUEavdhzC1HXzyBHmwII9rQDr6DoNNjnHIRT+/XWLd7AwWZNr6+YsxJ9oIa+alm7r5+Kr5QDKtJh0ESkIkihnpA0hE1uCjOtPGdi8eiCXAity0IsK+2h5feO8p/lx1JkEaASMsxlKb9CEUzPpcp7SEMYQifHn77299yww03JFkG/iv3P4Eh4vgpoyDDxrwJuby9Ix4FynSbWTar5ENJY1RR2XywnafWHiYaU1gyvYhFk/ISvsv/Dpxo7glHVdIcRsyyApEgbX6B+q4wGoN9eNduredbl4zjuXeO0NzpJz/TjtWsw2XRoxMFMFoQdAa0pE5dgZq2ML9YHSdSM0cLXHduBUZZIhyJse9YF7npViJRhYkVGYwdlsa+Y52MK0vnwPFuNA3uX9tKiiOD711xNkaXlVB/WlDTNHSuwd3nuvQiFNkKCHi2vQaA4uul89XfsWLlj9m4P058ZEngqnMrKE4VKTUHGXvjCI57DWgIZKdYMOpEwpjBUYRe9dPz9A9BjUfYIh312MYuIFS/P+nY+sxiwm21mErHEzy6M75QkgmOu5yjHTGUUwhVnNwZcNuNCWs5fyjGB1XNXDC7hCP1vTisBhRV5ZqZbtKlPhRJw+xeSLg2WVPQUDSWBXIbM0bVEXQWsa/PznhXL7od96IGPCwvHIfFtBLv7jeS9ou21qBOXAbn3UFrcxttESPPvNKNqmmcO+46pg538sSmdpw1CnuP1bFsdilPrukndwJcvGA4O6vbaWjzkuY0ccmC4fzy8e1JxyjMspOfaSMYtnFq0lZyZlHXoxIIxXhrWx1fvWAUzV1+THqJvEwbFpOONKeJ+5+tShB7gM6+EKGIjdDxvdgnLgZVwbd/PZLVhXHacoLGDISoxtM9Yxg5dhxFOS5+8kot7T3xFHVdf/T2B1dPTEj8aBoYZBGDNf4SNka78Kz7C+H6/QiyHsfsy5GLpxNDn/S8x1SNF987Rn6qEV3X0UG1sdG2GuSSmactGxnCEIbwn4vf/e53XHfddf808fuk+5/AEHH8mBAEgVBUQdXAbJA+tuitUSdy2YJhLJycTySqkOY0JVK2p8PxNh8PrzqQ+Pzy+hpSnSZmjvz3FNWHYyqvbqxl9cZa9LLI3SvzEI6sItxwAGN2OemlS6gJDW7QEQXYc6yT6roeAB57/SAXnFWMxx9hxewSBIMb16Kv0L16QJpGm7CCl/cM6FJu2NvMijklOMw6po3IxGzU8cbmOg439HDBrBIynQauW1rJm1vquXRhGe/takKWRBZOzueR1dUUZtlZMr0wUQcqZgxDn1VKpCWerhQNZqxTlqEqEXy73hx0DVrbYX7ylUV0ekLIkkgq3XhfuodYRz1IMqWzLkUYdhYxIfn7FGMhlJOiSGrIjxYNYa6YQeDgBkDAPHwyasiPf++72Caei7LwvzAR4ajXyCNv9TJrrBObWZeUwrWadOh1IlcvqSDDbWL2uBze29XEOdMKeWTVfgw6iZULyhhu6MTywb0JUh4sHo/trCvwvv8EAKayKYhqlOCbv49/BuZMOJfokYNE+msWldpdRAwmjAUjCB4dcGkB6Ojx0WbPZ0Ojh837Bjrk9zZH6Ir42VrdQ3ZqhBHFqTzz1mHOnV6EKArIkkBloZuxw1IJRxXSHEaMssj3r5rEuh0NHKztZlRpKkadzOqNtVw6Jx9p/DKUnS8DGqLBjG/cFax5JU7mZo/P46FX9rJispsRJi/mzghOZybVPpJI4wn4TFkYnRn0bX4ZiHeFu8/7JgFDJmhglAVWLihn28E2DnbGU/cno77NS18gQorVMGhsSVAJbH2ZcP/kQItF6F33CKmp+cQcxUnbCoKAQSdxqNFPeNI4hLbapPX6/FFEh0jjEIbwuYR33/v0vPMEMU8Xsj0F19wrsI086xOPe+eddwJw6aWXIooif/jDH/j9739PdXU14XCYKVOm8P3vfx9Jkvjd737HqlWrMBgMCILAo48+ym9+85uk/R977DHsdvs/dS5fCMvBD7PcO1OIqRpVx7p4Ys0hwhGFC2YVM3tczkcSwH8GkiTw5LqjrNmc3GRRnOPgB1dN/FALiE/z+o80e7jn0XhE6NaVw8jddm+SE41kTyM4/3vc+eShJK29Ly0uZ/WG2qQaNLfdyMSKdMoL3IwtdiOioA92oHo7Cck2/vf1Ng40DNT6GXQSv7x5JtZ+PU5BiLvXSKKANxiPcFqNMnqDjuYOHzFVY8u+FrYdbKOpI15zd+OFo5g8PC1x6/SKH/qaIBZBdGUT1ruRieF/7VeEGwfqCQHcZ3+VaMF02j1hXn7nEJcb30at2520TeolPyJ8CjHQEcG36ldJ8jwgED73h6Too8hdRwnV7ifUEJ8gqNOuZq9YwaHaHjbujXdHL51RxPjydP744l7augNkuM1cu7SSt7bWcdVkI+ZQJ7LJis+Sw54WhQdfjEcUL5qZy7zuZ4i2JtfyCfNuQbCl0NjuI92hQ37jnuQvWhBJWXwDqq8bTVVRwwG8VeuwL/02fS8ObCva3BypvIE/vtXC3TdM53hzH/tquqkocFGW7+L+53dT06+Vef0FIznW2Mt7OxsxGXVcfW4F40pSkMTBigeCKKCP9BBuqsbb3orXmscrB+GCs0rJM/morW1BtKdzrE9HIBQjJ93K828fYXaZiUktz6B21Sfus7jgFl5rcPLGptrE+Cl2I3dePwWnHEHoa0KNBJFc2YQNqYOaPERRoKkryO1/3Ji03G7R87MbpmE+TfTfoPjoeuy7SV3ZAI4F19OaOhGnxYD+JN3GIy0e7vnrdr48N52xnndRj28HQcQ2/hz0Y5cQkT6eFeGn/e77LGPIcnAInzV4971P52sPoMUGMmmCbCB1ydfOCHkcPnw4O3fuxGKxcPvttzNp0iSWLVuGqqp897vfZerUqSxatIgFCxbwwQcfYDQa8fl8GI1GZFlO2v+T4BNHHF9++WUeeughjh07xm233caXvvSlTzrkZw717b4kv+in1x3BZtEzY8SZjQKqqkbOaWoiCzJtCIL2qUQcAxGFgN+PDR8GkxnR7EoQeEEg0Umd5jSRLnsH2Rcqng76WhtZMW8YXX1BOnqCzBqbjSyJgxoXLCYdgXCMIw29jC9NQVUlQqZMMGUSialE6Ura/pIFw7CZ5ER0V9PiEdD3dzfz4rvH0DSN5XNLWTqjiEyXka3VHcRUjYqiFBZNKeDt7Q2sr2pmWmVGQjcvIlnAXTZwEA2iyNinr6Tj2Z8mop+S1QVomCKdrNrQycgcPWrVvkH3T/V0wCnEMYoe58Lr6X3jASJtNYhGK/YFX+beTR4CwTDXT0rDGPAg2VMxjl+CUDiJs4xWxpSkMmd8LhajjjSHAV84xtiyVBwWIylOI+/tqOOb06D71Z/j70+D67NLGTXjhsSxR2bLxI4M7h42E+SPm/1sO9jOnefaONF6IRrMiAYLxvxKgsd2EqiO2xRKNjfu+VexvkmkYNY3sPQcQTG5EPNH4m+VuOmidEx6iUllaUytyEBVVTQNvnfFeNp7Q4iigM2sw2rSMbYsjRSHiQyHkRPcSRBA0eKRaTTQR/voe+XXRDvj524Dblp0A1qaAwU7zbKOB57Ym9j3+vNHku4yU2n3oO6Lk0ZBb8Q2ajZSrIvLpxYwPH8s71c1UZrrjEes9RIRTOAq7f+eOO1kTFU10hwGzp9VnLD8FPu7qS0G+bTdxKpkQJ9eOGjy0RbWc+v9G8lKMfOtS8aRZo9HK0sy7dxx/RS2HWyjtvRiRk69CINeJmp0E9E+O6oQQxjCED4+et55Iok0AmixMD3vPHFGiOPJePvtt9mzZw8PP/wwAKFQiIyMDGw2G/n5+Xzve99j5syZzJkzB6t1MK/4JPjExLGiooLf/OY3PPjgg2fifD5zEEWB3adY1QG8ta2BaSMyzqgArqbBqOIU0t0m2rvjaTKLUeacqQWfSoOMNxSjq6GW9CMvEGk8QMxoxTbvGqTccSjEu0Nz0uIzkwkVGXT5g6QPGkUgio6/vnaAnFQLP/ryFHQC9AVjOG2GJM/n+ZPy+Nuaam5aMXpwdFnTuGzhcBo7vLR3BykvdDEsxzGoJKC6vpen1g5E8p5ee5icVCs5aWYeenl/kjzKdeeNwOuLJI7lC8eoa/MRDMXIz7Si10m0dPoxGWVy3YWkX3I74bq9CJIMmkbPW49gyC0n1bKUI21hxqQVobSdEsmznL77NWhIx3rB/yCG+kBnpM6rZ+JILw++uJfb2yXOqrwYl1XH1LxSnHodqgo2o4ztJLkTp0nH+LIMfv/8bs6ZWshF4614N/4lUTsJEGk+ijvSQkGmjUy7RKb3AFLRaAKHtyXfXnsG7T3xZqTaoIUJjlTso+ehRoIoAS+motF4d61NbK94uwkc28WBwFn8cW8vqc488tJtZPnCrN4Q79CuKHRx84rRnCxBr5dEclPM+MMKdz2yNUky5ysXjGTGiAwCYYWqY528sbmOdKeJZbNLKIw1JEjjCXjXP4E7bxRhycqEslTu+upUWrsCOKx68tKsTC5PQ9+wjV7iBNh11iX0bn4ZxduNZFnF5MVfZ8LKMaiq9g9nM2RRYOm0AiZXZtDnj5DhNOG2DdYtFAQBnRoAQcB+1hV0PvfTRNRRyB/H5hYD4KOlK8Bvn9vND6+ZhE4UEAXIT7VQOLsETYvX4IbgQ2ufhzCEIXz2EfN0/UPLPwk0TeP+++8nL2+wL/YzzzzDzp072bx5MxdeeCEPPfQQ5eXlZ+zYn5g4lpXFozfiR4hBf56haRpprsG6atmpFkRBOG304ZPAYZL54TWTaezwoSgauWlWHGb5H4o2iqLwsf4oO7o9ZB5fjdLYnzIN+ehb/TvSLr8bxRr/MRZk2Jg/KY9gKMqmOo0LKhfCgQGCYRh3Dm8ciP9RdvQGCUdi6AwyDpPM3V+ZypHGPmpbPLjsRt7d0cj00VmU5jiQ+oXBVVWLpwa7g7y5uY7aVg+jS9Po6A3hshnIOskZSJZF1u8erLX37s4G5ozPSyKNAOurmvjGyjFomoY3HOPnj+6gpSuewhYFuPa8Efz1tYPEFJUplRncOCGMZ+traKqSiDyG6vYyd/mlfOfhZhYuvxB73+8T0jOW0fPQnMkPrQb0+iOEIgopdiN6YzqeYIz7nt2O227k6iWV1Db34UoxYzPrsZ7Gi/xklOXY+dnXphGOqjh9NfhP6cAGIOjhu5dNQ+epJ/zifbhmX4Ya9BNqOICgN2KZcSnHI06Wz07h/56u4on17Uy5+ut41t6b6Gz37X0X11mXEOloQA3Fo8zhpmrmzV7OmPJsrCYdVUc6EqQR4GBtD81dAUoyB6cHGzp8g3QWn1xziIllaWw60Mpjr8cjc/WtXqqOdPLQZYNrbdRwMKHBKQkCuW4zue7kZ9GcVUCvIGIbM4+eDc8ndCUVfx9dL/+alCvuIaZ3f+Q9/jDIokC2y0S2q/83eMojpVODaHXb6d38UtwvffYVpF3+E5S+doKazHM7A6zZOvCH0djuwxOIkmIdKEz/Z8tzhjCEIXz2INtTiHkGB5pke8oZGd9iseDz+bBYLMybN48HH3yQO+64A0mS6O7uxu/343K5CAQCTJ48mcmTJ1NVVcWRI0coLy9P2v+TYKg55u9A02BkkTvJy9mglzhvZtHHbpD5R2HRSww/yRHm45LGUFThWIuXmqZeirIdlGQ7MOlOT+hFUcCiBVBqdw1aF+tthX7iaJRFLp1XSo83TFtfiJd2R5g/YziWWC9hg5smXSa718VJwLLZJdhMOjRVo7U3xPZDbQBMGZGJLAqMKHThtOhp7Arw0t5mHFYDkysysZhkHnxpb4JorNtWT266layU8iTiqKoaxdkOdlYnk6fiHCd9/sGe5eGoQqcnxPZmD1azPkEaId4pvm5bA5MrM9i4t4UtB9q4ZkImmhJNGkOyurA6HXzjwgq84W6ci76DWQcGvR7VlkGUARIQVTX21/bQ3hsgFtM4XNfNVedWcOB4D61dAVq7Ahys7SYzxYJBLzGqJAWdeHo/7xPQNA2zTsKsk5CiRsylE/Ef+CBpGzklB4tBQq9TCQM97z2FuXQ8zpkXIRqtKJZU8nY/hmgwc//Vc7lndQ++jhZ0p8oh7VqLpXwq3qq3ANAXjsPscJLrMLPtYMeg2luAUHgg+ikI8QmkqqoJT++TEYmphBSVl/u9oU8gpqj4jWkIsh4tNtBjbB0zn5jeHi9s/RAYMotIvehWlM76JDtEAC0aRvN1gfsfJ44yMeRgJ5oSQ7Wm0RfVoZdF5JNTDM376Fn758THrpd+TepFtxFJq6S9J8iaqmQZJKfVgNkw9ModwhC+qHDNveK0NY6uuVeckfGvu+46rrrqKoxGIw888AAPPPAAF1xwQTzzodNx2223odPpuOWWWwiFQmiaRmVlJYsWLRq0/ydpjvm7b7Hly5fT3Nx82nUbN25Eks6MTExKyifLwX+aRdFpwD1fn8Gxpj5iMZXCbDuFWWfG6u9MwWo38fgLu3l7e2Ni2exxOdx00RjMxtMLo3s8NkRnBkpvsne0we7EddL99AYidPmipDtNzJtRQXV9D05rEflZduoOtjF+eBpzxucxbngaTpuRA8e7+OGDmxJyMi+9X8PPbpxBZXEKG/c2c88j8TTqoikF7DnWidthZNHkAl7beDyR2m5s96FpA99rIBSluq6HklwnTqsh4cbhtBqYPS6HYERBFEiygZs1NodfPLYDRVFZMnOgDtFq0jGiOIWYomI/SUy9Q0ghpWQKsWPxOj8EEdv8L2MziVQ2vkDg8FZiCAjjFmCZdTGyLZmQ7KvpZN32BvbXdKGXRRZPL2JHdTtF2Q5WzC1l875Wmjp8tHT6MRtlrlxcgdNm5ONCMefh845EU6IEDm9DMttxzb0Cc145Vp2emCGPXps7nmY+uoPg8T04Z19G38u/Ghjk8FZ+fvmdqL1tdJ8yvhYJIejiNXhSagFHXdP45f2buXRBGRoqEysy2HYgPhkozLIzZWQm6Slm7A4zYqArXiNZdwBz0WhGZ1cMclY5/6wSXHYTJoOMxz9AEIsyrah6C1lX/oTu9/9GtKMBafgsfAXTsEsyaSkf7aRiL5tA2JFC7/pnkogngojO5qQromLUS2SmWBBP05hzKmLebnree4qe3esAkLPKOJy3nBd3evnqhaMZXZIKapSWVWsH7Rs+to204ROw2oysnD+MZ9fFtUNlSeAbl4wlP/vMvzf+kxtC/pOvfQifPZyoY/w0uqoBbr75Zm6++ebE5xOd1qfi2Wef/Vj7/7M4Y13Vt956KyNHjvynm2P+FV3VMVVDUTVMeukLlSJKS7Ox/1gnt96/YdC6e26cTobj9OQkpmoI7dUEVv0K1PgfvKlsMuazrknYnvkjCn9edQBNi9e0GQ0yE4anYTkhcSMKiKKQaD6RJJFH11Szbntyvdq8iXlcuWg4dz2ylePNHs6dUUR1XTfHGuMizzpZ5Jollfzl1f0oqoYgwE+/Np1MR1y8e9vhDu5/fg96WeSC2SWY9DJpLhP5GVaKc120d3hp7Azw0vvH8PgjnD2tgK3729h+ME50rl5SyaOrDzBnfC5uu5Edh9pJd5mYUJ7By+8f4yuzbGR1bsVotSOkFBBTVLz6VER3Dvba9+h774mk67EsuolVLRmkucyU5ztxWXQ88dYR1m1Lvu5vXDKWh17eRyymMm9SPoqisHZrA8tml1BZ6GZYtv1jlzuIIhhCHQiRAIIsoYl6jKmZ+BuOEuusR7KmINvdeDa/RLjxIPbJ5xE8uoNw0+GkcezTLkQtnIz/2R8lES375KXIzgx6pFTePBxj9Y7OxHNy11emsv94Ny1dfiRRQK+TeGtrPaqmceOSEkY3PUe4bkAr0lQ2heiUq3n6vXrq23zMn5jH+LJUzHqJvbU9/O/f4pHuGxdmMiK0E+3oRmR3NtLklaw7qvHOvh7ae4JMG5nJtUsqkD/Ee/7Esy8IGnLDNrpffyBRZmCfezWPH89g3c5W9LLIlxZXMLUy/UPHOgFdyy66X703eeGoxfx8XwEdvUF+ftMM0mx6Iu89SOBgcue1Y+YlaCPORdM0FBU6PSH6AhHSnaZPxdlqqKt6qKt6CEP4V+M/Im+iAUdbvDz++kE8gQhLphcxfVTmGfV5/ncjdhrdOuC0KcMTkEUBObcc3WU/QelpQZFNqI4copI5Uc9VXd9LfoaNpg4fT609jChAfVseF80uwSiLgxoPBAGCkdigY4UiMRAgGlMRBHBa9QnSCPHl7+xoZGJFBlv2t3LO1ALSHfHoVzCq8NjrB4F4uvNEFOd7X5qArT/1JwB5qWZuWTEagP213QnSOHGYgwJ9D/dfV0RzxMYvn95PKKJQ2+Kh6nAHv7iqFPH1nxEOBziRYBDHL+f/7VIozfZzhbp58PUcr2Jn3TjqW72U5Di4ZeWYRDTuZNS1eFEUjUhM5Y1NtVy5uJybV44h1+DF0rUVoTuEIbeSqCMP5SMeR1kLo1V/QMf6p9FiEYxFY7DPvQb/gU10rv5DYjt99nBsi2+htztIVXOACULVoLFUQeK+NW1cOe/b2GreQunrwFwyDiXoRbWmc/uT7Un6kRAn9uOHp9HWbSGmatz39MC4TrUniTQCBA9vIXXSBVx7bgWqGq8p1bS4MkBFvpMff3kK7Z0exnSsInwoPuGJtByFVb8kb9K3ExqKm/a1ct7MYjKdHx2Z1TQBJXcCaVf+DNXThWB18dLuEOt21sbHjqn85dX9FGRYyUv98PoeURROkVHqv/7mPZTnVNDSFaCl04/boscybjHBw9sS5Q2i0YK+eDyh/omAJEKG00jG3zn3IQxhCEP4POETE8dVq1bxi1/8Ao/Hw7p163jwwQf5y1/+Qmlp6Zk4vzOClp4g9/x1oMv0yTerEUWBBeNzvjCRx1SnkcIsO7UtA3Veeek20hymj9gLGjpD/PhPB4kpGtCDUd/GnV+ZSprNgCgKtHT50YAdh9qBeCr47W0NjCpKYUzx4NqxWExl/oQ8Nu5pSVo+f0IeqCoXzinh/uf39nsXJ6Oly8+KuaXMm5BLfroVsb9nXdU0guHB20dPR5b7/7SzUizIksj5E9zMEbZi7TMixRwUdLfw2/PKeb89ncfWtxOJqZgDLQTDgeRh9q5m8ZhvsXp3L5GRZdByLGl92FlI1+44uTnW1EdLV4DCLDt7TunAt5p0RGID575xbws3LUjHsO7/UPy9nKDOKcu+i5I+cvD19EPqrafznccSn0PHd6NzrCbcUZ+0XaS5Gl9LLd97vB1ZEii/YCH6xoOJ9YKko14uZFx5Cu8dbWNh8Sxsgg/V5EJ2ZRHSu3Fa+5KIY3GOA7fNgC8Uw2ExsH5PcoOS8GFFiJqKpmroBBU56kWV9EQEE5IgUJBmYbgrQsf65IgdSoxUIbn28qMmPyegV/zQcYxQ+3H06YVEZBuvbBxsMdnaHfhI4qiqGvqM4kHLo+nlHDsa/77NpnjkMGzLI/Xyu4i11SBIMlJ6MWFj2lBn9BCGMIQvND4xcVy6dClLly49E+fyqUAQ4GjjYM/bNzbXMnNUJnrpi9ENrhcFvnnxWNZuq2dndTtjh6Vx9pT8JNHhmKrR3B2gpTOA224gP8PKms11/aQxjlBEYcv+Ns6fXoCqaowuTeHxN6oHHa/qSAfjh6WinOZPvSDdyu3XTEpo4J0/q5j8dAuaBiMK3HzrkrH4QtFB+80el0N5nn2Q9JDVILN4eiGvrh9oqjDoJHLTP5wAWE0yNywbRZlSjbHRh+ILJpo+OLiRyXmjaBmzgLd2d6Ey+DcgSDJRRaW1K0iddTRFzp0ovfGIopBezKFoDv7QQIQxGImxeFohRxt7CfTbL44qTaWjN5jU/JLhNiP31KL4k8lR3/q/oT//diSdGUnUENUoMUGf2DfWmUwQAfyHt2AZPoVIU/L34+mLp+9iisa7LRbmL/ouuvptqLKRbvdo7lvXw7UL7IxX30d7awcnphopK/4Hq9DJTxYbaFXS+fP73ZTlOVkwKZ+GDj8/f3QbOlli8fTCpOPt69SxKL2IWPtAx7UhbwSqNQ1jtAvfhmcIHt6K7EzHufB6Iu5SNE1AE3SIJgtq0Jc0XlTQQX/styDTRvqHROxEUSASVdALMUJbn8O/953EOvOIWZw3dRbPrW9M2sdpG+z6ciqEzOEYS8YTOha3gRSdWdS5JlLf1sK44Wlk93d2axqETFlQmBU/b/i3kUZRFBAE4bTP4xCGMIQhnEncqxiSAAAgAElEQVR84VPVmhaP+pwKt82I/AUhjSfgMMmsnF3CsplF6GQhiYAJgsAH+1p4dPVA9GnG6GwyTmk8SHEYyUyxEFY09CJkusyU5Do40pBMdIbluVDV0/9JiQKUZNr4ziVjAZK6z3WSQGW+E00AeeUYHll1AH8oyswx2ZwzJX8QaYypGruPdMajh7OK2XawjZxUCxfMLsFtMSTqAzU0Or0ROvtlfCQRXlp/jNuGH8WYO5ye959OGldt2MvkafPZUWOgT5+ByeJEO4nMKaMv4LWN8QnHfW+08odbbsMYbAdBZHOjyJ9WDZAki0mHxShz3zO7WTqjGFGEzBQLhZk2fvLw1sR2JoPMiCI3aqRpEFXVwn5qGnsYle0jsOsNIk3VGIdNxlBxFmGdC8mWNug+69KLEC2upGWCrKdXcgPxqGBrT5QHmqIEwyOJRBXauvubW0wetNoBK0H7hLPxbnyOSH9k1STruePiHxC2FRCMKtz//B5iikZMiZPi/Ewb9a1xgrr+kIdl13wd4fgWQserMJZMQFc6BVWQ8L73OKGaeD1jrLeNzud/TtoVPyVkyiQi23DOvZru1b8fuKbsMiLWbGaP8zO2LJ2SHPtpJ3d9wSgf7Gmh6kgHt8xzIZxEGgEC+9dz7sULeW3rQIPOtJGZ5J1GYP9URGQ75vk3YJ3chqbE8OlTiLTG+P5VOeSmWTGcYceoT4oef5Qd1e00d/qYMiKT4ix73A9+CEMYwhA+BXzhiSNAaa6DNKeJjt54qkkUBS5bNPw0caZ/D1QgGFEw6iSkT/i+1zQNWRQGEbC+QIQn1yRHpjbsaebWqyYmPs8ck02Kw8jjbxzkKUnk8kXDGV3sZtGkfHZVt9PWL0penONgVLH778oEfZRckaDBxGGpVNw0HUUFu0WHeprUc22bj9/3u/a47UZGFKcwelgqj60+wDdWjsXc3+i080gX97+wJ3FOV5xTjttuIGDLw6oFBo0L8fqzxdPTuOuZY3zrnBspoRa1uwWhYBx/3anS7elBFOCapSMQjA4ixnhHbLE+ynkzYeuBNvIzbJx3VjHHG/uYNzGPNVtq6fNFMBlkfnXzTG6/djJVhzsIx1QE4Mk3D3PruTnYRSlJyDtWvohgKEL3C/+L4o1r/0W7XiTaVoN54c2QWoShcDTh2vi9EAxmhAnLqemDjFGLUI9uQk7JITxqGe0+FyeI456jnVy5uJy/vXk4QaAkUcAqxRhoKxCQLM4EaYS417Ln/b9hWfJfBCNakgvQi+8e5ewpBVy6cDhKf2d6tyaTOnop5tFLUDWBsKphDHclSGMCqkK4q4XdET1Om4G87LGkXnoHsa4GJLMTIaWAYMREdP9RHnplHyOK3Vw8bxjuk7rfI4rGvc/sTpRltHWIZJ7m+zWICvfcOJ3W7gBmg0yG04TuYz5gUQxgywdAD4wtjuuIahqfqaheU7uPux7eQp8v3uT07s6mhND6F6UMZwhDGMJnC/8RxNFulPnBNZM43uIhGFYozLLFC9Y/A+/VTl+Ev64+wP6absrynVy3dAQZDsMZtxcMx06vrScI8I2Lx7J643HyM21J5PJ3z+3m9msmUZJp40fXTqG1O4AoCmS6TImoi0ELgLcDZD2KOY3YP/CTUlSNLk+Yt7Y1oAHzJ+aSl2pJuPFIksimfQO1kt2eEOurmujsDWI06mjs8FOWY6el08+fXt6XdM+eXHOI266ZxJ6jInOc9RiySwk3D7i+6NLyseXkU2kVKMt1ku4yIejK0YkCiqpycWqUy2d1YZAFRJubk7L5OM06VpxVzJLphaga/P753USiKpMqM/jahaN59f0aapr70ACHUWbssFTWbmtgV3UHU0dm0qg4KJv/bSzVb6D6urGMmkuvcxhFQU+CNJ5A6PhurIFOwuYsrAtvhNY6lHCIkCmNHz9VS68vzMJJk5g6ew46o4nfPH+ANEeQq5dU0tDmZdEIC+lKE1MXBfGastnWYSY11YnoUhK6iYKsi4ttn4JoVyOiGkYU9Fx73gjWbqlLyCSpGry2oYb9NXFRH1kS+OG1U8hLNXPiwdIkPaLZPkhfsb5X4d5V8eaac6cXsmxmEbK9EAUIKxq/fnIrzZ1xzc2t+9s43uThzuunJLzh23qDSbW8B7t15KTkoXQNdLTrUnLAmoFdlLFn/3NaZScQUzVqWry8tb0Bt93A3PG5ZDqNn4oF6D+Kmua+BGk8gb+9Wc34YamfucjoEIYwhC8G/iOII8Tt3EYXndTM8Rl46YdiKr94fDudvfFozuH6Xu55dBs//eo0zPoz2/Htsuopy3Ny+KSUs8WkI8ttxmaUGVPi5ueP7xy037aDbZTl2DEhUpSRnOYzhjvoXfV/RLvi0S3r2EXoJywjKpkRhL8vXN7UHeDHD21ObPfB7iZ+/OWp5KeeqCHTyHAP1vBz2Q00d/iJROMRO08gTOSUaKWmgYDAqNFleNR8UvNGItfsJFq3ByF3FB0p49h3LEBloSvRXa9pGoqixRstDryOd/tqwpqGZdQcTJOWE5YHCIiqxiO797+4j7J8F+3dQZ5aexirSceli4YzZ0IuFn08QuUw6Vg5u4TFUws4WNfNH17Yy0+Xp2GTZOSMQjxbX0HUVFKWfoNBvjCCCFL8MY0IJo4pWazf10x+hoYvGOW680awZX8rP9/ZRGmeky+fN5L7nq6iur6H6+ZnY9/2Z7yt8UiiDMyYeQ0/XtNG9mXjyFl+K753HyHW1YTkyhp0nw1jF9PcHeKvaw9R3ehl0ZR8CjLtVB1up6LQzZtb6kh3GVkx0YFNitDW1Ex2agkScYkmr2rFMf9aek6SthHyx7K1eaB0ZPXGWmaOzk50TXf2hRKk8QQ6eoO09wbJ729qkU5Jw76yrYPScy+nPLCL8PEqDAWjMI8/h5D40fqPEJebqm/zEQxHyUmzku4wJtmICgIcrO/lN08NRE7f3dHI3TdMI+1j1Et+2jhdVFFRtc/C620IQxjCFxT/McTxs4guTzhBGk+gzxehsy9EftonswQ6FbIgcOOFo3nh3aNsO9hGSa6TK88ux2aM2xlKokBWqmVQLePIPAtydw2ipwPB4kRz5hEVTUiCSmD7qwnSCOCrehN34RiOqvk0tnkpyLRjNsp0e0I4rQbSnMaEhp4kibyzozGJXGoarN1axw3njyAWU1FVjYnlGaz64Di+YLyZxqCXqCh0s/1AG9n99yjVYcJlM9Bzki+2US+R6jBiN8r0BSWu+/0ulswYD+6RbNrRSVt3vEZxzvhcvrSoLKlsQWs9hHfbqsRn/9530KUXIJTOTdJcDIQV2nsCuOwGNuyJi+R7/BEefHEvd14/FU2Ll0WEoyqSFHcEspn0VBY4cNS9Q/B4slSOFglgyB9BuH5/YpltwmJiRndiopObZiEjxYTVrGPBpHxeXV+TkK45cLyb1q4Ai6YU8OoHNRSbPERbk7vB2f4s542/CU8wimzLoXnEDQzPNnKoO0bq1KuQdj2HFgnjWvxVIo1HML35M25IH0bb2Dn8v1X13LB8FJcuGMbBuh4KMsx8a3IUacu9aLEIktWJqfg7BC15bD3cweOvHyLbbeCmJT/ApfWg6S28sCfKa9uTo6q+YBT6iaNRL5120mE8aSKV5jAyZlgqu4/EO9hVVWNzg8CIJZfhmLQCRdITUj8iJS1ApzdCc6cfjz9CdV0PG/Y0Iwpw+zWTkyZIMRWeeyfZnzwSU6mu7yF9ZOa/PepYlG0fJLS+Ym4pJp34bz+3IQxhCF9MDBHHfyNMBmmQ2wmA6e/4F/+zcJhkrl1czqULyjDoREQG/qBVVWPx1AK27Gsl3B/JK893MEo7SOfTjybGsIyej37qJUhKDG9/vd3J8LfWce/bXZiNOhZOyedvb1YnjnHx/GEsnJiL9BECzMIp61KsOu76/+zdZ2Bc1bXw/f8p05tmpFHvktXcu42NwRiDKcYYApgWakL6Jc+Tm14gjZv+JHnvTblJSCghgdBjIIBxAGNj3HuTLVuyem/T55z3w9gjjUfuluWyf5+sPXNmzpkZS2v23mutT8xgf1Mv/YEImq6xfV8733lwOqn2WHKMIks8vGQiv31hM43tPjxOM5+/ZTwuSywo7vWFCEc0LCaVZ95NDKT+vf4g115SQJr9UMcURSawL3nm1b/jA6xllxGJDpyfUZWZMSaTN1cnZzzXNvfgdZn4aGcLS1fuJ8Vu4vb55WSn27jpsmKkNa8lHROs34PjyoewNe0i3LIfY04FureEiKTS2R9id20nrV1+Kgs9+AMRAk5TPGg8rKMngPXQ58dlSu7WpwX9uCwKGR4rP39mPU6bife2KZTkulha62VixecYlW1FXfcEkeZDWey97aQ372bJJQ+wdmcLE0pTyU61sWSKHfmDn3F4fiva10X3a/+Ndu3X+O0LsbqOu+vDPPxkHx+/ppLZ47JYX/tRwvlYTCrph3pBy7JEhtvCzZeP4h/LB0rpXDU9n7RBs3sGWeLB60ezbX8Hu2o7GV2USkV+CromEZKGuOhBJAl2N/TyoyfXxmfrJlekM2tcNh9sbuAvr+/gmx+fgjpoVnOoT2sgFCUQ0TCNcIJdfqaTRx6czttrajnYEiu0XlV4/P3HgiAIp0oEjsNAUWL9eo/3y9ttM3LrlWX87a2BgsOLLysm1WE8xlGn72j9qzNTLPzgoZnUNveiKDLjM6L0/PVrCffp37wMy+jLiDhzMeWPxrcjsVtNl+LBF/RxzSVFPL+8OuE1eHbZHiaVe0l3molGNa6YnMu/1w/MOkoSzJ+Wn1DMXNdjewonFHuQJAhGdGaPzkQitrTc5Y/w0bqDbK/p4IZLS0hxmshwW3BbDfHHTbEbsR0jGK9p7MVaoB5KtNEwZpYkXZcxpxxNlxm8x8GkSkwuT2fj7raEFnoAKQ4zq3e08N76OpZMc2KUotQcaGbdTol31tXx/flzUI+YDTSVTIoth+dORc6fRkjT8YWi1Bzs5MnXdyQEiUvml1GU7Rpydq4sL4VHHpyO2+EnOvtWQEOSVfz7txC1uCktLyKi6dQ197FwdjrL1tayYXcLV07NJ2CwYJX9A0Hj4fehr4NphQam50UwNmykKCUTv1em/4hF0Uh3M+GuxBlFgGVr67h0XCYP3zaBP/1zO9trOshNt/PQjWNxWQx0+8J8tKOZLXvbmFaVyfcemsn+hm4yPDbyvDbkI75Q2EwK0yu8XDe7+KQ6pwTCGr97cUvCEu+6nS3ceXUFH2xuoL3LT+TQNgQAVYZbrijlZ88MLFWbDAqqIlN9sIfRBSkn/NzDxeswceeVZSDFEtJE0CgIwnASgeMZFIrq7GvsYf3uVvLS7YwrScNlOfpLLAFXTMyhstBDW5efVJeZLI81Xvj6bNN1HY/diMeeCoChvyHeFWOw/t4eWiJBiqctItRUTaQzVuLFOmYub9WbMRtDFGQ5mDs5l4+2NSdk5Pb0h0l3xpYlsz1WHn1wBsvW1qHrMG9qHrnH6Eus6yTUpZRlWL6ujldXxJadN1e34bQZuX/haNzFnnhE5TAb+NIdk2nq9HHVtHze3VAfn1WdUOblo21NyJLE5NJUdB0MhRMwpL5DuD1WA1BxpGKuuozA4A45skSPL4zRoPDADaP51u9WxZOPinOc5GXY2b67jodH18O6paBFKMmpYE/eIpYGIyzd72bhjLsx7vgXGEy4Zt9KJKUw/vjaobaL729uRFGkpJnF11buZ/HlJVw5LZ+3Bs14XjerEEWRMCgSUqCH7tUvo4djS/iOifNxTrwWv2rCH47idpiwqlEevi6XLbX9vHnodcm6Jp0CSWZwar6tYibannfw7/qQztgLgPfGL+I3WdEGFU9XbCkETclt33LSbTR3BWho7efWeaNwO0xYDAqqLOEPR/nF3zdw4FB5ny1725k9Ppv7rqk46v+EYFSjqcNPTUs/aU4TdtOJ/SoLRrSEz+Nh4UPv3fxpBViMSrwigK5DRb6bBxeNYePuVuwWA8U5Lp5fvofJ5RmMK/acE1nWIoNaEISzRQSOZ4gswwebGnny9Z3xsUyPlW/cOxXbMRJdVFkiL9VK3jECppGi2zwYvPmEB3UnkUxWemQ3y9YeZKvbzPU3fxt6W5BUIyFLOvY1dfzsdiuR5tXkpNm49IYCtrdKRKJgNEiUucMYfE1oVjdhTOSmWrn/2krg5Muc9AaivL7qQMJYT3+Izp4Amj6wxKjpGjo6Kzc3EAhHuW9hFQcae1BVBVmSePm9vbjsJqaVe4lGNQIGD84bvwrdDaBpSO5sAspAYkw4qvPBpgb+9tZuQhGNKZXp/PDTl9DY1o/ZqJDrtWE0KMwriMDylweOq99JkSOTkuzRvLO1k7U1JuZU3c2CmUWErMn1BQNhjX+tPsCCmYVJt/mDEfp8YTp7gtx9TSWqIpHhsfLehnoe+cNqFk9Px9X6ZDxoBOjd8BamskvA5cZqVPjJ3SUEVv6V6ObNZKdkMfuG2/nRW71EbGkYxl1NeNPr8WONeVV0vvXHgRPQNTrefhzPtZ+m7cWfAzqSasQ/9eNsagiT6bHS1BELKE1GhVnjcvjm71bFD7/3uipmj8sEHZo7/fGg8bAVmxpYdGkRqfbkBBRfKMr/vLiF7TWxjG6nzci37ptGqv3YM/WKFMUrdfLoLXm8sqGXddWx/bySFNtDufDSIuZNyU0qI2VQJJra++jqDdLQ2se/18e+UBTnuI5ay1QQBOFCJQLHM6Q3EOXZZYktzpo6fDS09TPqNMuBjJQQZtzXfp6eFc8Q2LcRxVtIz+ib+eHf95Gf6UBRJOr7VLyuIiD2B/jqvB56Xvl/ACiAy5NN3rhPsHRdKw+O6aHnmefQw0GMWSWkXPUp/CbvKc/Y6LrOUHWO7VYjgxeV6zv8fPdPA3vr9tR28blbxtPTH2R/Yy83XFpEQaaLD3e0kJtuIzPFTEixg6dsyOc92NbPE4O+IKzd0UJWqo2bLi2OJ8/0+MM4Qy0cWeRGrtvAxPwp7G3opac/xNq9/cyfbRlyyVlVZNJcZhRZwmRQ4rOkAAtmFtLc4aOuuZccr41Ul5k125tZuSVWvignRSK6J7l3ttbfCa4iVD1EdNsb0BkLgqJdjVje+zU/uft7SClZrFo3heLZRVj66glaM+iLJL/Q0d4OZFc27jt+QE1NPW0RK0+/3UVX324Wzi6mJCf2ufc4zfz4qXUJx/71XzsxGRWqCt1Ih+7jshupbeolGg/chp5v3HOwOx40QuzLwsvv74t9ATnKOq0x0kdw3Ut0bVpGCjr3j5pJVdYlvLqxh08uGkNRlgOzqiQkP8VfM01n7sQ8Vm1uov3QbGVhluOEapkKgiBcaETgeIboup6wN++wE+mzey7zm7yY5n2G3VkH2Higj3+/2EJU09le08HE8nR6fCG8ztiskFH30/neUwnHRzsaSAk1cdM4K3zwVDyYCzXupfeDv2Ge9xki+omVHgppOoFQFJtZRQEcFgM3zS1N2COa6jJTkuOKBwCyLPHR9uQA6u01tViMKl19QcaVFvE/z2+KByxfvmsylXmuhL2XxlAnek8LkslK0JfcieiDzY1cO7MgnizR0RtEU90ceWWGzFICkhFJgsoCD5eMy+Zbv1/FbVeOYmp5ekJShiLBnVdX8KtnN3L3tZWs3dFMa6efuVNyaev0EwhFqSpK5f2NDVQVeZhUnk5qigWTQaG1v5eK9GKiLYl7FRVXBmh+5KZtaL5urKWTUV1eulb8Az0SwuRvI+TIJCs7k68/fgC7JY3+QJCvXJvGoZ2l8ccyF44lYnLSFNL4xbs15GfF+ptrms7L7+3lK3dPpjzHxZ6GnqQ9oKGIRldvkNXbmri20sCj05qRe5rpmzKBf+4xIpttuIeYQZQkifojyvUA7KnrIhzVjtoxRWvcTt+mt+M/R/as4oq55cz61GWYVAldJ/6ZCWk6bV0BJFnC6zShyhJum4FHH5xOQ7sPVZbITLXG60oKgiBcTJRHHnnkkZE+CQC/P3TK395tNhM+X+j4dxwGkgQRPbacJSkyOw90xm+zWwwsnlMy7IV4h/v6+8M6331qC9WNvoT3qKrQQ3m+O359Bi1A/5pXEzqiAJA3DpPuRzmYWH4m0tmMffw8ovLAcqQkxfavBcIaqhrb7SlJEgfbffzsrxt4bnk1u2o7qSxKxWJQyE23U1Uc25M5e3w2S64sI8VqGPR4EjVNvQkzVAA5XgehSJQ9dV20dvmpKk6Nt9DbeaCTkjw3DR0+rGYVp7+e9mcfxbdlOb4ty0m36fhdBdQ0D+yVG13sYWblQP8SWZb5cHc3+VY/dDfFxiwOnFc9RMmoQsoKPBxs7eP1lTUEQlHW72plUkUGKbbEYCnFbmBiWQb9/jCTytO5ano+KTYTv3tpKw1t/exv7GFimZdAKMpL7+5l2752Nle3MbEqG3veKKxde9EDfUiqEfvc+5AyKtD3vEfHm38k0tlEqGkfoZYDuCYvIFC3g47smdT2mynMtDOpPIMeX4iCTCfjxxSRMaqCUN129HAQU/5oXHPvJag4iGix17m5w0dVYSpTqjLZfaCTG+eUYDYoGA0K726oT/gSVZLrQgfGeiMY3v4pWt1mtI6DqLVrmTy1CnveKFRVxmwY4kuFLLNiU0PC0FXT8qkqdA9Zn1VVZQLrXyHSnti3mrAfa9UcBq829wYi/Oq5zTz3zh7eWVtHfVs/o0vSMCoyBkXG4zCRYjcmBPgjaSR/9420oa7dZhv52pqCcKETgeNpCGs6G/d28D8vbmHF5kZmjctm+uhMWrv8TK5I5/7rR+OxJ89OnWnDff0GVaazL5TQrQNg8eUlZLrM8Z91xYSqBwk1DMwAIiu05l6BLMuY6hJLsRjSCzBWXY4uGfCHNfpDEepafPzkr+t54d97aWz3UV7gIRjR+PYfPqSrL7Zfr707wPaadmZPyMGkyFSVeBlTkEJxphPzERnjuh7LcH5/Y0N8RlGW4LpZRbz9US060NUbZMaYLLbti2UDh8JRvG4rf3xlGw5jhKytTxHtbok/ZqSlhpKpl/Dm9thCtM2s8pmbxmEzDQQ5JqNMVDWzssNDSsU0IvlTsE9fhOLMJBLVeGN17aGanQOL2fkZDoqzjkwskbAfKtSeYjNiVGSMRoVt+zrir8ecibn8a3XiXs999d2MH1/CXkMZ4bwpNHgvodNeTI5Do/OfvwRtoO6fHg5iKRxLwOBkaVMWTy/bR2VhKqXZDqZXZTC53IvFqKLZM7FXzcY2bh5qxWUEFQdRXeePS3ewfN1B2roCVB/spqs3yJfvmkKaMxYEmw0ykyozqGuJLc9PLPMypTKTpSv2cftYjejuD2KFzg9FfVrLPrYxir+8VcOscdkYFBlZllAUGV3XsVuM2KxGdh3oQNNhSmU6iy4tHrKn9eHPgCHUQ2D/poRx6+g5kFkR/70jyxL/3tjAuxsGapM2tvsoyHSSe4brqp4pInAUgaMgnG1iqfo07D7Yzf/3j4E/Rj9/Zj3fum8a3/j4ZGQptmR3QeyB0uHGOcX0+UOs3dGC3WLgvoVVFKUPJHSENZ0NezsI6mMYO1FG2v0esjMN6yVL+PFLbYzNtXBNyUzYG0uQkAwmUubdRwATu+u7+c0Lm7nh0hKefmMHmh5bci506+ysPkh+XibBUOIsZn1rP529QTIOBa5DbRM4LN1l5rufmMGm6jb8wQguh4nXV+6P189MsZvo9w9kj88cm8WG3bFA0W3SCTXXJD2m1xjkO/dPj2Uhp1pxHqoZOfg1q8xzkZ1aSa8vhNNmxGw2EAxH2VHXTWNbP3aLgQcWjuGV9/fS0uknLcWS8BiqHkLpOkC4YTeKIw0lq4ygwY1RlviPWyfw7sZ6NuxuxWZN/nLS5w/T0ObjybdibfgUWeIHnyoFggw1LRe1elgazmb51tjM7Csr9lF5x8QjSiPpBBU7KAPve3tvkPU7WxIe62BLH6FwBHTToeMg02XmC7dMYMOeVlZtaeQvS7eT7rHgcruIXnY7ejiIZDTjr9lMqLkGWZJo7fRT3+4jPcXCqq2NbN3XzsyxWUwoTWPB1Fxmjs5AMShYVPmYfed1XUctmIDB++94opfqzsRcPishUx4k1u9K6t0Te96qjHMie1oQBGGkicDxFMmKlDTLA/DBlkZKs8svuD8yTrPKpxaNofeqCEZVwmpUExIJ9jX28uvnYkF0hieL2WUPMm18PhaPky8uyaep3UfUWYh3ylVIIT+yK4OgKY2OviA/eWodUU0nGI6i6bBwcirzPAdRd/4dmiw40m6nMs/Ojrq++POZDAqWI0qwSDJ0+yIEQlHcdmN8v5uu63idJuZPzqU3GObZd6rjbe1kWeKe66t488P9WEwq00ZnkuYyx5dCN9X5mVAwltCBxGLnitNLgWtgFmqoLwiapuMwqzjiNSR1tu7vjL9OECshdNc1lXy4pYGSbCdd/jCapuNxmJD2r6ftjd/G72tIzcW16MsEVSdOi8qiWQXccXUFtY3dqIpEZFBD7aoiD16XGa/bQqrTzJL55aQ7TYRlE87pN9K94u/x+8oWO+s6HLy5cWAvqNmowhF7GiG2JD34fT+yYPugG5KGrAaZaeVeRuW56fOFyHVJ+FY/gW/X6vh9XNMXEs2fxhvvxjKebWaVnzy9noa22Hu/vaaDSydkc9dV5QTCGi6zIZYgdZwvaEGjB+eiryB1N6LrOpIrk4By5OyuzpSKdHbXdiaMji1JFdnTgiAIh4jA8TR4HObkMadpyMzMC4EM8bqUg69RVeV4iRKA5g4/z3/oZ3+XxH98bCwemxHPob17YVwDD6jrtHT640vIBlXGaTMy19uM9NFzxOYYu+l69Rd89rqv8vBfffF9cvddX0WKVY3vT4tqOmt3tvLnpTsIhqPkZdj5j1sn4rEZBj2djtWokuO1c+fVFbFkCkXmzdX7+ezN45AlmQ+3N/HEazvix6zZ3cmnP7ulsDsAACAASURBVHsHen8H4baDSIoB12W3E3XmnPTrF9HgxSM610Q1nXA4yudvmcBba2p5dUUNmqbzlZtLyF2VmGgUbj+I3lEH6aMB0DQwm1TcdiNfv2cav39pC00dPiaVp3P3gnLcNiPff3AGqiINlCbSwFBxGR5nGr5t72LwFiCXzeKJPw2clyTBokuLE6Jhc7CVcP0OdH8fprwqQs48NBRS7UYuHZ/N+4P2HFYUuMlIsQz5GnT5wvzs6fW0dvn5zvUePIOCRoCedf+iZ+6XaeuqJc1lxh+MxIPGw97f2EB5vps/vLINVZG546oyZo/NOu6+w5BsA3fpUW/XNJ3pVRls2N3Cjv2x4HFqVYboxCIIgjCICBxPkRbVWTCjgJVbGuPBjMWkMq0y44IoxitJ4A9rBMJRHGYDyjH+Juu6TtoQgUKG23LcINo1KBnkYEsfiy/Jxrz3jxxeOJatTlzTrof+On7/MSshVwG9pnQiEZ1/b2pCUSTGlWr09gf53Utb449V19zH40u38/At4xPOXQZmjcli1bYmPtzSSFmBmweuH431UBLG1Ip0evtDvPlRLWkuCx+/toKo3Y5z8TeQfB0EdAMfNUqwt4cJeRasJpmQZDmhwEKSYjOlR7KYVWqbe3n5vYEM6K7ufrJDRxbzAT0yxH42HQrTbTzy4HRCYQ2bSUEiFggZhnjjwrIVcqdiKZwe307x9XtTWberBX8wwvSqDLI9A3VFzaF22p/7HppvYI9r2s1fIZhaiSxJ3DZvFGNL09i4p5WqQg/jSlITCrUfvvb+kMY7aw/Sfmhfp6wlF+LWIyGiUY1brihl5pgsuvuTr1eSiPcuj0Q1nnh9J8U5LvLTTn8fosOs8vCtE2jrDiDLEqlOU7y/uiAIgiACx9OS5Tbzg0/NZHdtF4oiUZaXQqrdeN7OTgQiGi2dfgwGBU3X+cUzG+joCVBekMInF43FPcReOoBoVGfO+GyWraklcGgvosmoMGdiDtHosV8Mr8vMojnFvPzePt5df5CiBaXIjjRojyUopMxcTOe7z8QDJsloxnvTN3nw93viS7MWk8r9C0cnPfa2fe34Q1HspsRgzW5SuHpKLvMm5aAkNkjBZlS4YVYh86fmYVCkWF9tHUKShVq/m+/+8UOml7m4ZVQ/oZdeJQQ4Z95MOGcCtZ1R+v1hMj02PHZD0udABm69chSP/WVtfMxiUqkscPPWmrr46za+KIW9HRLjKucS2TZQQkZSjSieHJJ7+cQYZQmj6cRKG0HivtB0p4nrpucDyV1IIk17EoJGgO4Vz2Jd+FU02YTVqDC1LI3pFelDttrsDUb41+paVmxqIMdr54EbxvC3t3bRFHbiNlnRB3WeMeWUM6qsmAIMaJqO2ahQkOlIKBA+c2wWm6vbEp6jucN3RgJHiPXCznIPPWMqCIJwsROB42nQ9VifWO/ojISx81GnL8xPnl5HU3vsj/ik8nQmVaTz9ke17DrQxW9e2MxX7pwUC6SGkO4y8f2HZrK3vhsdKMl2keY4/rK9KktcN6OAqZUZdPeFSHdbcBYvprV2G4bUbIL1uxNm2fRQAN+uVXicufE2fP5gBGWIZcrcdHtSlvVhmqbHioQPsXVN1/TkGn2SxPPLq5EkicXlEZQVf+JwXnLHG79Fmfc5vv9CL5GojqrIfPO+aeSnJXcDKsl08siDM1izowmXzcSEUV7SHEaKspxcVpXCDcU+LDX/RJOcWMsXIGfk0rfuNdTUHBwzbiJoyRi2D9nRZsr1oWY+A32s2tLA2Mp8rEYFXR+6848OPP3mLj7aFts/2dPfQU1DNzdeVsITK2r4+nWfw7P7FcLNNVhKp2CbvpiApnJ406JZlfnikols2N3Kjv0dTKnMoKsvyF8370p4Ho8zeduIIAiCcOaJwFEACZau3B8PGgHW72rh9qvKUWSJqKZTfbCbbl84vlfxSLpObC9jmXfQ2NCBiCRBIBJLhrGbVVRZItttIfvQLE9QKsB7x3fR+zvp2/BG8gP0dWCzFMGg/s29vhALZhTwxoexhCWzUeGhG8eesXp7mq7T3R+kOMeFveGDpFk/Zd8HlOTOYdeBTiJRjT+8spVv3Ts1qSC1LEF+mpXCy0riRac1DcYUeRit7UB7/0/xx+45sAnvHY/iKvkummwkoMvHDRp1oLM/hBrx44k0I/k6kZ1eos5cItKxW/IdjZpZGss8GhRlh8qv5O/vN2B3uxmdn3LUY7t94XjQeFggFCXFqvDZuW6cSpCUK+9HV82EFXvsGo/gNKtcPj6beZNyiEY1mruDvGzZF8+Ev2xiDjlnaLZREARBODYROAqEIzpb97Yljbd3B3BYjXT1BXFYDZiP0XP7hEmwr6mP37y4mbauAJPKvdx9TSUu88BHUdclAtZssGZjGxchsH9LwkOYKmZx4KPEEjB56Q4uGZ3BpeNz6A+ESHdbcVkMZyxRSZHg+kuKePHdvUTMnqTbjU43ZaqJXYcS7etb+whFNAxHec2OnN1LMUbo3vo6CXN2WoRw/S60sitOaJIxqum8t6WRXdWN3OndRufOd+K3uebcjlw1H007+WL0YUcuqbd8ne4V/0AK9BAsnctbzV66+jpo6fAxtsiNdpQtCaoiYTYq8S0MAFmpFqaa9tP3/u/R0GlBwn3l/egls456DrHOTLHnyHCZeOyhmTR3+XHaTaRYDUftGCMIgiCcWaJnloBRlZhUng7EytMsmFnIkqvKGVOcyrhRaUgSPHTjWGzG0/+e0dkf4rEn1uAPRrn7mkryMpwsX3eQxk7/kK2JtfQKPAs+hepKR3Vn4rn+80jpo/jcLRPITbdTlO3k6/dOJc9rRZEkstxmSrOcOM3qGc1u13UYV+zhhkuLCeRNRTIMLI1KBjPG1FxmZAzM2E6tysB6EoG2joRkGKLFnsFIbyBCly/M8QrCNHcHePL1ncwrM8CgoBFiexKV/vYTPp/BNF0i7C5la8nHeclxO99+z8xrG2L1Hs1mlZ//fSP7mnsZaqXbYVa5a0FFwth9c9LoX/4nBmro6HQuexyjP/nLy1B0HexmlZJMB1VFqSJoFARBOIvEjKOArsPV0/PZXdfFJWOzWLa2jsZDdQ7HlKTyX5+ZjddhPG4gJsmxsi+6fvTC580dfiJRnTuvGMWzb+/GH4ztFPznBzU88sB0clMT9wVGZBNSwQxc+RNAlwjLJtBhYomJsUVTAYnMdAetrb1DPNuZZVRlZlSmY8CJPuc2tECsTIykGula9QKOiitx2tLIz3Rw+5XlQ8XBRxXGiGvWLbS9+NP4mGSy0mHJ40u/WkEkqjFrXDa3zSvFbhr6v217TyxL2aAFk2/UovT19mKyeJNvOwG6DhVF6aza0U6vL4xBlVkwo5B1O1rYXN3O5up2HnlgOvlHdFjRdZhWkU6u187ehh6imobH1I0ePWKxX9fQ/D1gTj+l8xMEQRDODhE4niXS4WLU52ipHpfFwNfunswrK/bHg0aArXvbOdDUg9eRdtRjJQnaekO8taaWmoYerpicy7iSNCxDJKbYLQZcdiNtXf540AixpduX39vH524em/Qa6TqEMCfUo9Z1jpqoM5zaeoNs3t3MjPYtRA5sAqR4+76U7Dy+c/84XDbDKU3lR7wVpN3yTQJ716BYUwhnj+XhP++Ll3v6YHMDOV4b107PHzKRJfVQgkhD0IrXYkfzD9Q/VFKy2NmuMDlbPmaXnWNxmFU+u3gs3f4w1XVd/PODGg62DDzHexsbuGdBcvF7VZbI99rITbOyuaaT6o5eRpvt8cAbYtnykj31lM5LEARBOHvEUvUwC0V1NtV08NiT6/jNy9toOMqS7LlAkSS27kteLtxdFys3dDRdvgiP/HE1b66uZU9dF797aSvL1tUd0VVEIhDRSE+xcP/C0WR4rNy1oIKyfHf8Hj39IbQRTEuXJAlTuBNjZzVmfxMKiW0O+4IRvvf4Rzz5ZjXtBfORjeZ40GjMqUDNGIX7FINGgCgqQXcpyrQ7kMZexz+3R+gPxB4/M9VKSY6LVVuaCB+lK1Ga08zNc0v524cddM/4DMqhpBY1byzN4++hK2Q8bnmk41Ek8DqMbN3XTlN7f8JtFrPCsVq4yJLExJJUJowvx33D/0Gxx957xeYiddH/JWhwH/XYkaDKGgYpMlQTHEEQhIvWac84Pvroo6xatQqj0YjVauUb3/gGY8eOPRPndt6TJNi0t53fvDDQrm79zma+/8mZpLvOvfIhsgTTqjKpaUis2TemyHPMgKO+tS+h1zPAqytqmDspF5tRoaM/zHPv7KanP0RVUSrPL6+O32/x5SUEQhFqm3q5bnYR0gjFjZIEpu69tL3wk1j5GUnGNed2lPLLiRKrX9nY7qPnUEHq/3qtlXvnfJZstYfcbA+qJ5egnFx+51Romk5fMEyay4LTZuTh+W7S2jeiBHvQi2diliKESa6p2dodYN3OFhbOLmZLj0521QME8ntIz0gj0NHO3DIDElGip/l9MRDWmD0+hwyPFbNJpaG1j/c31jN7XPZxA1Nd1zEqEmF3Me7bvgfBHjA5CKmO47YNPFskScfcXUPP6peJ9nXiGD8PuWAiQcU50qcmCIIw4k47cJwzZw5f//rXMRgMLF++nC9+8Yu8/fbbxz/wIhDW4MV3qxPGIlGd3XVdZKRknnM1HzVN55KxmWyvaWfL3lgixRVTchmV6zrmcfIQyQnKof2OgYjGj59aS0unn9uvKufvbyXW33vlvX3ce10VVrNKZd6xn2c4GaM+Ol/7zUDNQl2j+92nScupIGrPA2ItEQ/zBSL8z5tNqIrMTz9XhFMeujj68RzZ+xlis9T//fwWKgo9fOkaLynv/xw9HIzVjaxeDQv/Ayl7YtLnJxrV2N/Yw/7GgcD/xqmpTIq8jW/Dv+heCfaJ8zFNWEhItZ/auaLz/uZGnnpjZ3x82ugMHvv0LFIdxhMO/nQdgoodrIfO4xz6v2DuO0jLsz8ALTbj3PH247jn+FFGX000KqYfBUG4uJ124Dh37tz4vydMmEBTUxOapiHLYhVcItYJ5EgGVT7ngsbDHCaVL3xsPO29ARRZxm03Hnd+KtdrI81lpq17oIXcrVeWYTMp1Lb64oW6o5qelHkb1XTS3BaKMuwYlBH8zIT6iPS0Jg1rfR1wKHDMSrVSnu9mV21n/PabLy/BZU3uEnM8UV2nrrWfXbWdeFOslOW54kkvLV1+dtV2sr+phwULooTCickuvSufx3FzFWFMCePpbgvZafZ4b2ezUeGK7G58778ev0/f+n9hSC9CKphxwuccjGgcaO5jf1MP2V4bzR2JS9QfbWtm4awi0E+tTuSRZFnCF4qi62AzyfF+5GdLuGlPPGg8rGfDW6SVTMFnOPpeX0EQhIvBGU2Oefrpp7n88stF0HiIIsGSK8v40ZPr4mNWs8qovBQURUaSOOVEheGkSJB+Ep047CaVb9w7lQ172jjY3MeUynSKs5zoemLgHI5o2C2GeJ9hAJfdyM79Heyr7+aaaXkjNvOkmxyo7iwinY0J44ojLV6Q26TIfPbmceyu66KupZfKQg+F6faTChpVPYTa30x3dx/7azVe/KCVUEQjL8POV+6ajNWgxF8CCfAHwyR99dA1pCE2yppVmf+8cyLvrDvI5uo2rr2kCHPt0xzZ98W/cyWW4llEItGkx0h6KuCfq/az9IP98bHJFelMrcxgzY6Bwt6D6zSejnBU46PtrfztzV1ENJ3Fl5UwZ3x2ciefYSQpybPHstEM+pm5RkEQhPPZcQPHxYsX09DQMORtK1euRFFif9aWLl3Kq6++ytNPP31KJ5KaevJLZ4N5vY7TOn64OFwWvvfQTFZvbSLFaWJqZQaRqM4z7+yhtz/EldMKGFOcivkoJVZO1Ilcf09fkK6+EC67EZfddNz7n9TzA6X5yYWxHU4L111SyNKV+3lz9QHuuKqcpStqaGjvJzvNxnWzinj6XzsJhaNcMSWP3PRTex9P//13oFz3Wdpf/AnR/m5QVPSpS6iLuKga9NheoCg3sVOKFgkRbq9Hj4RQ3Zmo1qGX3CO9HbQve5LObe8DMM3hoeD6T/C9lxqpa+6juTPAtNGZGMxGSnJc7GvoJuQuxqoaE9oupsz+GA7vQAay94jzK851EwxHMBtVurRy/Ps2JJyHObcct/vE9mPuq+/mtZX7E8bW7WzhjqvL44Gj3WKgMMuF9wx0b1m1pZE/vrIt/vPf3tqN123lqukFRz3mTP/fD4TLkC0ONP9AiSfHxPmYPRnYLOfe75lz9Xff2XAxX7sgjBRJPwNVkt966y1+9KMf8ec//5nc3NxTeoz29r6j9so9Hq/37NTxOx2yLKHr0NQV4Fu/XxUvsQLw1Y9PoSz71DfeH+/6JQkauwL8z/ObOdjSR1aqlc/cPJ7cVMtZWTIPRjQOtvXT1O4jO82Gyaiwubqdtm4/KzbWEzo06/qTz80m1X785c6oDp19QWRZwm0zkn6G3v8Drf3s27OfHGsQn27ixfW9hCI6jz44HeNRltENmp/wpqX0rlkK6Bi8+aRc9wUCxuQlTWPLVtpf+mnCmFw0hd93zGRLTTf/5/ZJjCmIBaV9wQiNnX6Wr61lYYWEp+kj5EAXtnHz0LxlRKRY4H+8994cbKPzpR8R6Y4twyvONDyLv0rAdGJLrk1dAb7+25VJ4w8tHssTr+1gVF4Kd15VTrrLdNqfJUWRefz1nfx7/cGE8fICN1+7czLaEGvWw/F/X5LA0ldHoGYDWn83ppxRyBmj8BvOvXJB58PvvuEy1LWLQFIQht9pL1UvX76cxx57jMcff/yUg8aLgabpSJLElr1tCUEjwIv/3suX75w4bBnFvlCUHz+1jq7e2F65xnYf//XkWh771MyjFpM+k0yqTEmmg5LM2C/1/lCUf63eT3ffwCza5Ip03McJGkNRjYZ2H/Vt/bR1BXh9VQ2XT8rljqsrjnncierqDfLke81J46GIztGawEgd++ld88/4z+HWWnxrX8Uw6x6ig/ouSxJJy+AAUsseynIuZ3e9Qs7g4tk6/OrvG+n3h/lwK+Rnjqaq0MPN3hKOURkpScCURsrN30LvbgBdR3bnEDG5kaLREwr0Up0minNc7Kvvjo+luy2ML0nlp5+bjckgI3PcFtonRNP0xNfgkPwMO2dzD4Oug8+Wh3FcFnI0REQ2EdTPQLtNQRCEC8BpRw1f+9rXMBgMfOELX4iP/fnPf8btPrdqsp0rhvrzN9x/Ett7gvGg8bB+f5j27gD29NPbInAqbEaFb947jVdW7GN3baxbzeUTc46ZhNMfivK7l7aydV8s29vjNHPn1RU8/s/tjCv1MjrfRbcvQntPAKfNiMduGHIf4LFkplqRpMQgaFJ5OjbT0EFDOKqhtx5MGg/UbMQy4zaig8rz6DoYUof4YpU7lrBs5jsPTMdjG+jO09TpTyhxVNvUS21TL1dMziXNcXLbDIKqE1KdmIMtBLYtJ9y0B0vFbKScMYSVY7//Blni8x8bz+sf7mf9rhZGF6Vyw+ziYdlzqOs6k8q9LF25P/55tZpV5k8duuD5cAtpKkjqOZXxLQiCMNJOO3D88MMPz8R5XBR0XWdcaSrPLZOIDKp3t/iykmGtX2i3GFCVxOeUJbBbz0wW7KlItRu575pKIlEdo8pxM2f3HOyOB40AHT0Bdh3opCTHxa4DHZiNCj95ah3BcBRZgjsXVFKS4yTNZcZqOLHZonSXiS8umcQfXtlKT3+IMSWp3L1g6NaBwYjGE2/sZFGRE8sRtxlzK4kq5qSAQ/cU4ph2Q2yGUtcwZBTimnEjN5q8gJ5Qlsc8xBSnqsgYTzFgM0W66Hj+v4j2xXpMB/ZvxTFtIfKkm9C0YwfYLovKkitKuWlOSez5h3F/g9tq5NEHplPbEtu6kp9hJ+UUstYFQRCE4SFaDp5lGS4z3/3ETJatraPPH+bKqXkUZgzvrF+K1cDHr63iT68OJB3cflUFnhPYTzisdB1VPn7QKMsSB5p6ksZrGnsoyXExoTydX/19A8FwLOtV0+HJ13dw54IK/vflrfznnZNxmo//UZeQGFvo5rFPzSQY1nBYDckZzYc0dPhYtbWJNFMq80svQa+O7QNUHKnYpy8moCcHeGHZgjLxRtIqZkMkiG73EsTMUFNa6SlmplVl8NH2gaXzJfPLcFmP3zN8KHpnfTxoPKx37eukjb7ixDq26GBUpGENGg9zmFVG5w8kIImgURAE4dwhAsezTYfMFDMfv7r8rJbjmVmZTmmOi7ZuP6lOM+ku87na+TCJpumU5ycHN5Mr0inMdGAzqwn7JQ8LRzTqW/vZVN3GnLEnVnBd13UsBgXLcWYp+3yxZeRX17XTVjGFy2dMRyFMSUUZAePRE52iukzUnH7c8zAqMvdcU8ncSbm0dvnJTXeQk2Y9paAxZoh3+3z5AAiCIAjnDFFwcYREo9pZreGoyBKZKWbGFLjJcltQhuj2ci4rynRw45zieJeaMSWpWEwqf3ltB5IkkXpEC0dZGuj0UtPQfUZqi5pD7Rga1mM4uIZxqQFSDs3YrtrZxWOvtfPaXjPaMYLGk2UxyJTnupg9JpPCdBuG47xnoahGXZuP3Q099AQSeyxL7hwUR2JWsGPKdYRNKQiCIAjCiRIzjsI5qScQYV9DN75glJJsJxkpZm64pJAZY7LYWN3K3oPdPLdsDwA/e3odX7h1Aj9/Zj3dfSFMBoWbryjl3UNlXSaXZxCNnl6Qbg600P6P76P5Ykvmkmrk53d+g//714N09gaZUObl7gUVIzaJd3jP5aqtTQBYTCrfum8amSmxgDqouvDc9DVC1asJNe3FUj4TKauK0HH2NwqCIAjCYCJwFM453f4I33/8I9p7Yi0MZQm+df90Crw2evtD/O3N3Qn3b+n0Y1RlfvDQTNp7gnT0BPjbm7to7wnysbmllOac3iygJEmE9m+IB40AeiREdPvb/Ogz9+MLRrGb1bM6fR/Vddp6guxp7MVlM+IPReJBI4A/GOHPr23nS7dPRD009RgwpSGPvR7zeInwOdixSBAEQTj3icDxHBaIaBxs7affHyYrzYbXabrgt6UpisS+hu540AixZJdn3trFl++YRIrDhCyR0PPaZlaxmw1YDQrWVCt5aVaKPj4VSQKnRT3t5ApZloh0tySNRzsbsamgSmf3v5Guw4otTfzltR3xsfsXjiYrzUZj20Af6f0NPYQjOqph4FOjafqIlLY5X8myJF4vQRCEQcQex3NUIKLxu5e38sO/rOGXz27ka7/5gN31PQn71i4kxmg/hsaNRFY9xZjoNu6fm5hA0tkbJKrpeOxGPrl4bHyvo1GVefj2SdgHZ03rsYDRYT79oBFi+1HNpVOSxm3j5xMZgaXejv4QT76+I2Hsydd3cOmEnISx6aMzsRiH77+4qgcx9RzA2LoNU6jtgvps9gUjrN3TxvPv1bC7oYdQVASPgiAIIGYcz1n1bf1s2tMW/1nX4X9f3soPHpqB6Sjt785XiqQR2riU3nWvxccmZJRy47RFvPRRrFXetTMLMakymqYzrcxL6Wdn0d0XwuM0U5ybQnt737CeY9RTgueaT9O94u/okRDO6TdC9tgRKRXT4wtx5CRYOKKRm27HZFAIhqNUFnq46fLSYStebdACBD96jv7Ny4DYns+0m79KwFU8PE94FgXCGr/4+0ZqGmJbE15dsY87ri5n/uRcdDH7KAjCRU4EjueowV1DDuvsDRCJ6Bylkcl5Sw2007X+jYQxvbmaaRVR3nOauW5WIdOrMhKWDD02Ix5bLKtZPgsZ4hHJiJQ/A/dtYwGNsOogPEJBRJrLjMWk4g9G4mMpdhMl2Q5+/NlZhCIaKTbjSbUmPEwmisHXjNbbhmxzE7VnEsGQfL+e+njQCLE9n51v/i+uj32bkHRkSfTzS317fzxoPOwfy/YwvTIDxwnUAxUEQbiQid+C56isNFvSXr4ZY7KwmpULrwWapoGenKyR57Xyw4fGYzbI58Q+M13XCR5uIziC5+M0G/jK3VP49XMbae8OkOmx8vlbx2M1KOin8T9aknSUA2toe+O38THXpbehVF5F9IhS6Fp/Z9Lxkc5G5EgADGcgcJSgvTdES6cPp81IRooF9SyVkBoqAz8c1dBEJXJBEAQROJ6rvE4TX7tnKr9/eSttXX4uGZvNrfNKh7U14UjRrB4spVPwV6+NjymONGRXFkblwk5OkCQwRnrA1wkmByGT57jL37quU+C18r1PzCCqx/pJGxUp4ThJAmOoC72rAUk1oruyCQ3qnT0UU7CTtrf/lDDW/f6zZOSUEXAVoQ3qhqO4MpOPzx9N1HD6XZAkCXbV9/Djp9bF3/trZhaw+NLisxI8ZqXZsFsM9A2a9b9sYi5Oq+HC+9ImCIJwkkTgeK7SoTTLwfcenEE4qmE1KRdk0AgQ1g3Y5tyFMbME/+4PMeZWYBl7JQHFMdKnNqwkCUzdNbS/8gs0Xw+SwYT7qk8SzZ2YEKRJEpiC7WhdTUhmG5ojkzBmzKqM1+ugtbU36bHNvkbanvsBWiC299OUW4Hjqk8TVF1HPR892IceObIDj06wbjtGyUDAkR8fDdsz8VzzaTqXPY4eCmBIL8Q59x4CQyxrnyxfSOO3L2xJ+MLw+qoDzBybRa7n2MHvmeA0q3z7gem8tnI/1Qe7mDMhh5ljMi/Y/3+CIAgnQwSO5zD9UH9go3IBLk8fIaimII+5Fvvo+UQllcBFUGbQGOmj45+/jNeH1MNBOl77b7x3P0bAnBG/n6lrH23P/1c8qLONnoNp5hLCR5lBVCWN3tUvxYNGgODBndha9kL2pKOfkNWD4vAQ7R3oaS0ZTLHjD2xCHlcQD+aiqGj500m9swzCQXRLCgHJPOTDnqxAKEJXXzBpvKcvBGchcNR1SLMbuefqciKajuECn/UWBEE4GRdWeq5wXtM0nbCuol0EQSMAgW6ifV2JAH561gAAFkVJREFUY7qG1tse/9FIgK63/5gwE9i/7T2kroNHfVhZCxFu2pc0HulsQDpGzZygYif1hi+iumPL0IrDg/uy2+nduAzJYE7qk63rEDS4CVozCZ2hoBHAYTVQkpM4MypLkHEWgsbBdF1HkRBBoyAIwiAicBTOKTrQ1BVg49529jT04A9fwFGk2YFsOXJPoIRkc8d/kiNBwu31SYfqvu6jPmxEMWOpmJk0bswsTQr+juS35ZF27WdImXUz1lFT6Xz/WbRQAGP+2Ss9pEoSn75pLMWHgkenzciX7pyM51BvcEEQBGHkiKVq4ZwhSbCjtpuf/nVdPEgZV5rGp24cg1m98L7jhFQXnms/R/vLP4/NKEoyKfPuIWL1xu8TNdgwF40nULMp4VjZFVvKjkY1ZEVCG1SgWtPANHoukY56/NXrkBQDzktuJuopPKHz8ttyMOZFie5bj3PqQoxFEwhaM8/qdgmPzchX75xEjz+MxahiNSrHDXoFQRCE4ScCR+Gc4Y9o/OGVrQkzW5ur26hv66ck88JLlNF1nVBaBWl3PYbW14FscRKyehMSY8K6ivOyu9AC/YQaq5GMFtxX3EPQns3u+h5e/8dmrBYD184sICfVAnpsKTpocGOZ92lsl3SCrBA2eYicYNyloRBwFiFPKkbXIaDrI7LHVpWleK1OETQKgiCcG0TgKJwzwhFtyKQIXyAyxL0vDLoOAWMqeFIPDSTfx2/0Yl/4n8j+LjCYCRtT2FHbxU+eWhe/z+ptTXz/kzPJTBnYaxjWVTB7j/q4xyP29gmCIAhHuvDW/4Tzlt2kMmNMVsKYLEtkpdlG6IzOHWFMBC0ZBFUXUV3npff2JtyuaTob9rSelS46giAIwsVLzDgK5wwJWDJvFAZFYsXmRjLdVh64YTRpIikigQQYhuhXblDkEemdLQiCIFw8ROAonDZJkugPRpEksJmU01ridJhV7r2mglvnjcKgyBjEDBqSBP0hDUUGsxoLDhdfVsL2moF6iwZVZnxpmtgLKAiCIAwrETgKp8UfjrJs3UFeXVGD0aBw19XlTC7znl5rOB2sBuX497sI+EJR/rWmljdWHcBuMXDPdVWMKUihKMPBIw9MZ+WWRixmlZljMkl3mcSMoyAIgjCsxB5H4ZRJksS6XW288O+9hCMa/f4wv3tpK/ub+45/sHBcsiyxYksjr75fQzii0dkb5P/9bQP17X5kCfK9Nj5/20QWzy4i3WkWQaMgCIIw7ETgKJwyDZ1la2uTxjdViySNMyEQ1nh7TfLrW32wi8ENYET2syAIgnC2iMBROGWyJJGXnlxfMSvVJvbanQGqIpHhSc4oT3GIJWlBEARhZJx24Pib3/yGhQsXcuONN7Jo0SJee+21M3FewnlA13Sun12E2TiwH9HrtjC6KFUENmeADCy5sgx1UAZ1brqd0lzX0Q8SBEEQhGEk6ac5NdTb24vDEZt1am5u5pprrmH58uW4XCf3x629ve+Ul9y8Xgetrb2ndOyFYCSvX5Kgsz/MwdY+VEUmz2vHZjq7iS0X8vsvSdDaE6S+rR+TQSEv3Y5tcKB+AV/7iRDXf/Fe/1DX7vVeeB2mBOFcc9pZ1YeDRgCfz4ckSWiadroPK5wndB1SrAZSCtwjfSoXJF2HNIeJNIdppE9FEARBEM5MOZ5nnnmGv/zlLzQ1NfHDH/4Qt1sEEYIgnP8kCYyRXgj0gtlJSLWLbRiCIFzUjrtUvXjxYhoaGoa8beXKlSjKwLLZrl27+NKXvsQTTzwhgkdBEM57vn0baX3lV0T7u1HsKaTf8DCWorEjfVqCIAgj5rT3OB7pgQce4NZbb+Xqq68+qePEHsdTJ67//L5+RQFZ14no0knPZp3v1366huv6TeFu5N5GWl/5JXrIHx+XzTY8d/yQoHpuJChdzO+/2OMoCCPjtLOqq6ur4/+uq6tjx44dlJaWnu7DCsIFT5LA4msgvOIv9L/yA5T9KzFG+0f6tC56Bj1I7zt/INS4JyFoBNAC/ej97SN0ZoIgCCPvtPc4/vrXv6a6uhpVVVEUhW9+85uUlJSciXMThAuaKdBK67PfiwcnwYZqXLNvRR5zLcOZX6bIGoZwH5psJCSZh++JzlNyXzPBA1uw5FeArIAWHbhRUZHMzpE7OUEQhBF22oHjL3/5yzNxHoJw0Ym21yXNaPWsfpnU8ksJKsOz5GYOd+Jb8zJd299HdaWTMu9ewqllaLro9BN3aL9A//aVpMy4ga6VLwE6SDKe+Q8SNqeCSJARBOEidUayqgVBOHmSnFzvUlIMJPQTPIMUSaN/9fP4tq8AINLZSNvzPyLtzu8TtGYPy3Oej3RHBsbsUYQa9uCrXo/7sttAVjHkVhG0ZIggWxCEi5poOSgII0RKzUOxpySMuWbfRlgdntlGNdyDb8fKxEFdQ+tsTLqvKdyJsXEjhoNrMPmbhiuWPSeFJDPOqz+D69IlSEYzGjJK4RT8lmw0zm5xe0EQhHONmHEUhBESNHjw3PwNQvs3EulsxFwyBS2t5JSrCxyXbESxpxDt7UgYlkzWhJ/NoXY6nn+MaG9bbEBR8d72bQL2/OE5r3NQ0OBGHn0N1jHXENUgKIo3CoIgAGLGURBGVMDkRa+4CsOsewl5q4hIw9chJqRYcc+7HxiYPjTlVYE7L+F+kYYdA0EjQDRC74cvoMoXV0coTdOJRHXOcMUyQRCE85qYcRSEEabrOtHo8Acnug7hjCq8d3yPaGcDktkGnnxCij1+H1mWkmYkAaJdLUhaGBCtDwVBEC5mYsbxIibLEr5wlG5/RCSJXiQ0XSZgzyWcN42QdzShI7K3NU3HmFeVdJxtwnwionSPIAjCRU/MOF6kNB02Vrfzp1e30ecPM6UynbuursBpFh+Ji10kpQDP9V+g+92n0IJ+HFOvQymaSlQs2QqCIFz0RJRwkWrq9POrZzfGf167owW7xcjHry4TNeouclEMaDmTcC+pBD1KWLUTuri2NwqCIAhHIZaqL1KN7cmt7T7Y3IBfRAgCsf2QQclCULYPaxcbQRAE4fwiAseLlNNmTBrL8doxqBdRwT5BEARBEE6KCBwvUrleOxPK0uI/q4rM/ddXoV5MlZ4FQRAEQTgpYo/jRcpikHnohjE0dPjwByJkplpJtRsR+Q+CIAiCIByNCBwvYiZVpih9oIafCBoFQRAEQTgWsVQtCIIgCIIgnBAROAqCIAiCIAgnRASOgiAIgiAIwgkRgaMgCIIgCIJwQkTgKAiCIAiCIJwQETgKgiAIgiAIJ+ScKccjy6dXePp0jz/fieu/eK//Yr52ENd/MV//xXztgjBSJF0X1fsEQRAEQRCE4xNL1YIgCIIgCMIJEYGjIAiCIAiCcEJE4CgIgiAIgiCcEBE4CoIgCIIgCCdEBI6CIAiCIAjCCRGBoyAIgiAIgnBCROAoCIIgCIIgnBAROAqCIAiCIAgnRASOgiAIgiAIwgm5oALHJ598kgULFrBw4UIWLVo00qczIlavXk1lZSVPPfXUSJ/KWfXoo4+yYMECbrjhBpYsWcKWLVv+//buP6aq+o/j+JPL5YfESgRFFCv8I8AhwbhwKwl/BxhwQefEEnOKZPMuxZwyNsKsaU5LnD9yzokiVDMXqFiAbDfHUvlVmoZaIzKNX4HiD5hX4PL9w33PJH9RxD0C78df99xzzr2vcy7cvfY559yjdqQ+V1NTw5w5cwgPD2fOnDn8/vvvakeyimvXrrF48WLCw8OJjo7GaDRy9epVtWOpYtu2bXh7e/PLL7+oHcWqzGYz6enpvPbaa0RHR5OWlqZ2JCEGjSfmXtW9VVRUREFBAQcPHsTZ2Zmmpia1I1ndrVu32LRpE2FhYWpHsbqwsDBSU1Oxs7PDZDKRnJxMcXGx2rH6VHp6Om+88QYGg4FDhw7x/vvvk5WVpXasPmdjY0NiYiJ6vR6ADRs2sGnTJtatW6dyMuv6+eefOX36NKNHj1Y7itVt3LgRBwcHCgsLsbGxGZTf90KoZcCMOO7Zswej0YizszMAbm5uKieyvo8//phFixbh4uKidhSrmzx5MnZ2dgAEBARQX1+PxWJROVXfaW5upqqqiqioKACioqKoqqoaFCNvQ4cOVUoj3P28a2trVUxkfXfu3GHt2rWsWbNG7ShW19raSl5eHsuWLcPGxgYYnN/3QqhlwBTH6upqzpw5Q3x8PDNnzuTAgQNqR7Kq48ePc/PmTSIiItSOorqcnBwmTZqERjNg/rzvU1dXh7u7O7a2tgDY2toyYsQI6urqVE5mXRaLhS+++IIpU6aoHcWqtmzZQkxMDJ6enmpHsbrLly8zdOhQtm3bxsyZM0lISKCiokLtWEIMGv3mUHVcXNxDRxVOnDhBZ2cndXV1fP7551y7do25c+fi5eVFcHCwlZP2jUdtf0FBAZ988gmZmZlWTmU9j/v8/1+gjh49ypEjR8jJybFmPKGSDz/8ECcnJ+bNm6d2FKv58ccfOXfuHCtXrlQ7iio6Ozu5fPky48aNY/Xq1Zw5c4YlS5Zw7Ngx5YiTEKLv9JvimJub+8j5o0aNIioqCo1Gg6urK6+88go//fTTgCmOj9r+iooK/vrrL2bPng3cvXjAZDLR0tKC0Wi0VsQ+9bjPH+DYsWNs3ryZvXv3DvhDVx4eHjQ0NNDZ2YmtrS2dnZ00Njbi4eGhdjSr2bBhA5cuXWLnzp0DenT578rLy6murmbq1KkA1NfXs2jRItavX09oaKjK6fqeh4cHWq1WOU3jxRdfxMXFhZqaGsaPH69yOiEGvn5THB8nKiqKkpISgoODaWtro7KykunTp6sdyyp0Oh0nT55UplNSUvDz8xtUozAmk4n169eTmZk5KA7fubq64uvrS35+PgaDgfz8fHx9fRk2bJja0azi008/5dy5c+zatQt7e3u141hVUlISSUlJyvSUKVPYuXMnL7zwgoqprGfYsGHo9Xq+//57QkNDqampobm5meeee07taEIMCjZdXV1daof4L9y+fZu0tDSqqqoAMBgM3b5cB5PBWBxfeukl7OzsuhWnvXv3DugLhaqrq0lJSeHGjRs8/fTTbNiwgbFjx6odq8/9+uuvREVF8fzzz+Po6AiAp6cn27dvVzmZOgZbcYS75zmmpqbS0tKCVqtl+fLlTJw4Ue1YQgwKA6Y4CiGEEEKIvjV4TgwSQgghhBC9IsVRCCGEEEL0iBRHIYQQQgjRI1IchRBCCCFEj0hxFEIIIYQQPSLFUQx4r7/+OqWlpWrHeKiUlBQ2b94M3P0x9/DwcJUTdZeYmNijH2AXQggx8ElxFP3aokWL2LJly33PFxcXM2HCBDo6Ojh69Ch6vb7PMjQ2NrJkyRJCQ0Px9vbmypUr//q1dDodhYWF/2G63tu9ezdxcXH/+eueOnWKhIQEgoKCBt29poUQor+S4ij6tbi4OA4fPszff4708OHDREdHo9X2/c2RNBoNr776Klu3bu3z9xpInJycmDVrFqtWrVI7ihBCiB6S4ij6tWnTptHS0kJFRYXy3PXr1zGZTMTGxgJ376xx4sQJACwWC7t27WLatGno9XqWLVtGS0sLAKtXr2bPnj0ANDQ04O3tTU5ODgB//PEHISEhWCyW+zK4ubnx5ptv9vg+uVVVVcTFxREYGMjy5csxm83KvNLSUsLCwpTpKVOmsHv3bqKjowkICCA1NZWmpiYSExMJDAxkwYIFXL9+XVn+9OnTxMfHo9PpiImJ6XaIPiEhgYyMDOLj4wkMDGThwoVcvXoVALPZzMqVK9Hr9eh0OmbNmkVTU5Oy3ldffaXsvx07djB58mRefvllVq1axc2bNwG4cuUK3t7e5ObmMmnSJPR6PZ999tlD94O/vz+xsbGMGTOmR/tNCCGE+qQ4in7N0dGRyMhI8vLylOe+/fZbxo4di4+Pz33L79+/n+LiYrKzsykpKeGZZ55h7dq1AAQHB1NWVgZAWVkZY8aMoby8XJkOCgpCo+ndv8ydO3dYunQpBoOBsrIyIiIiKCoqeuQ6RUVFZGZmUlhYiMlkYvHixaxYsYJTp05hsVjYv38/cLfsvv3227zzzjuUlZWxevVq3n33XaUcAuTn57N+/XpOnjxJe3u7UpRzc3O5desW3333HaWlpXzwwQfK7fzu9fXXX5Obm0tWVhbFxcW0tbUp++//KisrKSgoYN++fWzfvp3q6upe7TMhhBBPDimOot+LjY2lsLBQGbnLy8t76Dl5X375JcnJyYwcORJ7e3uMRiOFhYV0dHQQEhJCZWUlFouF8vJyEhMT+eGHHwAoLy8nJCSk11nPnDlDe3s7b731FnZ2dkRERDx2pHLevHm4ubnh7u6OTqfD39+fcePG4eDgwPTp05X7sx86dIiwsDAmTpyIRqNhwoQJ+Pn5cfz4ceW1Zs6ciZeXF46OjkRERHD+/HkAtFotLS0tXLp0CVtbW/z8/HB2dr4vy5EjR1iwYAFjxozhqaeeYsWKFXzzzTd0dHQoyxiNRhwdHfHx8cHHx4cLFy70er8JIYR4MvT9CWBC9DGdToeLiwvFxcWMHz+es2fPsm3btgcuW1tby9KlS7uNHGo0Gpqbm3n22WcZMmQI58+fp7KykqVLl3Lw4EF+++03ysvLSUhI6HXWxsZG3N3dsbGxUZ4bNWrUI9dxc3NTHjs4OHSbdnR0pK2tTdm2goICTCaTMr+jo6PbhUHDhw9XHg8ZMkRZ12AwUF9fz4oVK7hx4wYxMTEkJydjZ2d3X/7Ro0cr06NHj6ajo4Pm5uYH5r33PYQQQvR/UhzFgGAwGMjLy6OmpobQ0NBu5eVeI0eOZN26dQQFBT1wfnBwMIWFhbS3t+Pu7k5wcDB5eXlcv34dX1/fXuccPnw4DQ0NdHV1KeWxtrb2PznPz8PDA4PBwEcfffSP17Wzs8NoNGI0Grly5QpJSUl4eXkxe/bsbsuNGDGCP//8U5mura1Fq9Xi6upKfX19r7dBCCHEk00OVYsBITY2lpMnT3LgwAHlopgHmTt3LhkZGUr5uXr1KsXFxcr8kJAQsrOz0el0AOj1erKzswkKCsLW1vahr2s2m7lz5w5w9zzGey94uVdAQABarZasrCza29spKiri7Nmz/3h7HyQmJgaTyURJSQmdnZ2YzWZKS0t7VOhOnTrFxYsX6ezsxNnZGa1W+8DzOaOioti3bx+XL1+mtbWVzZs3ExkZ+a+uXrdYLJjNZtrb2+nq6uq2D4UQQjyZZMRRDAienp4EBgZy4cIFpk6d+tDl5s+fT1dXFwsXLqSxsRFXV1dmzJjBtGnTgLsjjq2trQQHBwMQFBTE7du3lSL5MP7+/srjyMhIAC5evHjfcvb29mzdupW0tDQyMjKYOHEi06dP/8fb+yAeHh7s2LGDjRs38t5776HRaPD392fNmjWPXbepqYn09HQaGhpwcnJixowZGAyG+5abNWsWDQ0NzJs3D7PZTGhoKGlpaf8qb3l5OfPnz1em/f39CQkJUS72EUII8eSx6fr7D+AJIYQQQgjxAHKoWgghhBBC9IgURyGEEEII0SNSHIUQQgghRI9IcRRCCCGEED0ixVEIIYQQQvSIFEchhBBCCNEjUhyFEEIIIUSPSHEUQgghhBA9IsVRCCGEEEL0yP8AzI7JutaZ+sQAAAAASUVORK5CYII=\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": "
" - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaQAAAEUCAYAAABkhkJAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de0BUZd4H8C8zXASFuAg4aOmqpYigJN5WDW8ErihJSxZm5QVNK1e3fCF3g7xUsta2al6yNDUr2fLSSt43lTSvm7cgrQi6yAA6QKjgAMPz/uEy68QwMDDMnJnz/fzlzHlmzu858pzfnOc853mchBACRERENqawdQBEREQAExIREUkEExIREUkCExIREUkCExIREUkCExIREUkCE5KFpKSk4M0332xS2ZEjR+LLL780ex9r167FX/7yF7M/J2VnzpxBdHR0k8qePHkSDzzwQCtHRLbCNtQ8jtSGnG0dADXd008/3er7+Mc//oF///vfyM3NxaxZs/Dcc8+16v4iIiKwb98+i3xXSkoKAgMDMW/ePIt8Hzme1m5DGo0Gr7zyCk6dOoXKykrce++9ePHFF9GnT59W26cjtSFeIZGBzp0744UXXkBkZKStQyGyOxUVFQgNDcX27dtx6tQpTJgwATNmzMDNmzdtHZpdkFVCGjlyJN59912MGzcOffv2xYIFC3Dt2jVMnz4d4eHheOqpp/Drr7/qy//73//G2LFjERERgcmTJyM3N1e/LScnBxMmTEB4eDjmzp0LrVZrsK9Dhw4hLi4OERERePTRR3Hp0qVG4zt//jyGDBkCnU6nf+/AgQMYN24cAGDlypV44YUX9NvOnTuHRx99FBERERg/fjxOnjwJADhx4oT+MwAwZcoUPPzww/rXiYmJOHjwoNEYJkyYgMjISLRt29ZkrFqtFmFhYSgpKQEArFmzBr169cKNGzcA3L7SeuWVVwAAVVVVSE9Px/Dhw/H73/8eqampuHXrFoD6XQjZ2dl46KGHEB4ejjlz5mDu3Ln1unE2bNiAwYMHY+jQodi2bRsAICMjA7t27cL69esRHh6u/yW8bt06DBs2DOHh4YiOjsbx48dN1otMYxu6raE2dPfdd2PKlCkICAiAUqnExIkTUV1djby8vHpl2YaMEDIyYsQIkZCQIK5evSoKCwvFoEGDxEMPPSSys7PFrVu3xOTJk8XKlSuFEEL88MMPok+fPuLo0aOiqqpKrFu3TowePVpotVqh1WrF8OHDxXvvvSeqqqrEnj17RK9evcTf//53IYQQ2dnZYtCgQeLcuXOipqZGbN++XYwYMUJotVp9HMeOHTMa46hRo8TRo0f1r5977jnx9ttvCyGEWLFihXj++eeFEEIUFhaKAQMGiMOHDwudTieOHj0qBgwYIDQajaisrBS9e/cWGo1GVFVVicGDB4uhQ4eK69evi8rKShEaGipKSkpMHqvnn39erFixwmSZxMREsXfvXiGEEFOmTBGjRo0Shw8f1m/bv3+/EEKIV155RcycOVOUlpaK69evi5kzZ4rXX39dCCHEiRMnxLBhw4QQQn9cN27cKKqqqsS+fftESEiI/rieOHFCBAcHi3/84x+iqqpKHD58WISFhYmysjIhhBDJycn6skIIkZubKx544AFRWFgohBDi559/Fj/++KPJOpFpbENNb0NCCJGTkyN69+4tysvLjW5nGzIkqyskAHj88cfRvn17BAYGIiIiAmFhYejVqxfc3NwQFRWFnJwcAMDu3bsRGRmJIUOGwMXFBdOmTcOtW7dw9uxZnD9/HtXV1XjyySfh4uKCmJgYhIaG6veRkZGBiRMnok+fPlAqlZgwYQJcXFxw7ty5RuMbO3YsMjMzAQA3btxAVlYWxo4dW6/cp59+igceeACRkZFQKBQYMmQIevfujSNHjqBNmzYIDQ3FmTNnkJ2djZ49e+L+++/HV199hXPnzqFz587w8fFp8bHs378/Tp8+jZqaGly+fBmTJ0/G6dOnodVqcfHiRUREREAIgX/+859YsGABvL290a5dO8ycOROfffZZve87f/48ampq8MQTT8DFxQUPPvigwXEFAGdnZzzzzDNwcXFBZGQkPDw8jP76BAClUomqqirk5uaiuroanTp1wj333NPiessd21DT2tCNGzfwf//3f3j22Wfh6elptAzbkCHZDWpo3769/t9ubm4Gr9u0aYOKigoAQHFxMYKCgvTbFAoFVCoVioqKoFQqERgYCCcnJ/32O8sWFBRg586d2LJli/696upqFBcXNxrfuHHj8Oijj2LhwoU4cOAAevXqhY4dO9YrV1BQgL179+LQoUP692pqajBw4EAAt//QT506hcDAQPTv3x9eXl44ffo0XF1dMWDAgEbjaIoB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\n", - "text/plain": "
" - }, - "metadata": {} - } - ], - "_view_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_view_count": null, - "_view_module_version": "1.0.0", - "layout": "IPY_MODEL_64aa754a29844764b480dc5c91b700be", - "_model_module": "@jupyter-widgets/output" - } - }, - "75aa1b12f01448198ad356465332fffd": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "6049c8f1a0c54f11955956952181b242": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "ed894f48599f4920bf247584398d2e0e": { - "model_module": "@jupyter-widgets/controls", - "model_name": "SliderStyleModel", - "model_module_version": "1.5.0", - "state": { - "_view_name": "StyleView", - "handle_color": null, - "_model_name": "SliderStyleModel", - "description_width": "initial", - "_view_module": "@jupyter-widgets/base", - "_model_module_version": "1.5.0", - "_view_count": null, - "_view_module_version": "1.2.0", - "_model_module": "@jupyter-widgets/controls" - } - }, - "ca3f1c4c5a804634a7f13b0b9ab0ebdd": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - }, - "64aa754a29844764b480dc5c91b700be": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_view_name": "LayoutView", - "grid_template_rows": null, - "right": null, - "justify_content": null, - "_view_module": "@jupyter-widgets/base", - "overflow": null, - "_model_module_version": "1.2.0", - "_view_count": null, - "flex_flow": null, - "width": null, - "min_width": null, - "border": null, - "align_items": null, - "bottom": null, - "_model_module": "@jupyter-widgets/base", - "top": null, - "grid_column": null, - "overflow_y": null, - "overflow_x": null, - "grid_auto_flow": null, - "grid_area": null, - "grid_template_columns": null, - "flex": null, - "_model_name": "LayoutModel", - "justify_items": null, - "grid_row": null, - "max_height": null, - "align_content": null, - "visibility": null, - "align_self": null, - "height": null, - "min_height": null, - "padding": null, - "grid_auto_rows": null, - "grid_gap": null, - "max_width": null, - "order": null, - "_view_module_version": "1.2.0", - "grid_template_areas": null, - "object_position": null, - "object_fit": null, - "grid_auto_columns": null, - "margin": null, - "display": null, - "left": null - } - } - } - } - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "h9McsiUZLsp0" - }, - "source": [ - "# An interactive introduction to Multiview Learning with Canonical Correlation Analysis and Partial Least Squares using cca-zoo\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UmQifwjwI1mh" - }, - "source": [ - "## Table of Contents\n", - "1. [Objective of the Tutorial](#obj)\n", - "2. [Set-up](#set-up)\n", - "3. [Introduction to multiview machine learning](#mv)\n", - " * [Machine Learning Framework](#mlframework)\n", - " * [Latent Variable Models](#lv)\n", - "4. [Simulating Data](#datagen)\n", - "5. [Overfitting and Sample Size](#samplesize)\n", - "6. [Ridge Regularisation: From CCA to PLS](#ridge)\n", - "7. [Lasso Regularisation](#lasso)\n", - " * [Sparse Partial Least Squares](#spls)\n", - " * [Sparse Canonical Correlation Analysis](#scca)\n", - "8. [Application: a neuroimaging case study](#haxby)\n", - " * [Regularised CCA](#rccahaxby)\n", - " * [Sparse Partial Least Squares](#splshaxby)\n", - " * [Sparse Canonical Correlation Analysis](#sccahaxby)\n", - "9. [Conclusion](#conclusion)\n", - "10. [References](#references)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WoKA75AsKSTp" - }, - "source": [ - "## Objective \n", - "With the large multiview and multimodal datasets available in medical imaging and healthcare informatics, there is a strong need for multivariate statistical and machine learning methods which can capture relationships across different views of data.\n", - "\n", - "Canonical correlation analysis and partial least squares are arguably the two most commonly used methods in modelling two-view data with latent variable models. Extensions to these methods have included Generalized CCA (and Multiset CCA) which can capture relationships between more than 2 views and various forms of regularised CCA and PLS.\n", - "\n", - "This tutorial has three main aims \n", - "\n", - "* to give an interactive and visual introduction to CCA and PLS and to demonstrate the effect and benefits of using regularised versions of these classical methods\n", - "* to demonstrate how we might use these methods in a neuroimaging example\n", - "* to introduce cca-zoo, an open source python package containing implementations of this family of algorithms. The source code is available at https://github.com/jameschapman19/cca_zoo and documentation at https://cca-zoo.readthedocs.io/en/latest/index.html" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Jp_CSLIvJKBJ" - }, - "source": [ - "## Set-up \n", - "This tutorial depends on the package cca-zoo and nilearn for a simulated neuroimaging example.\n", - "\n", - "In order to make use of the interactive elements, the notebook should be run in a google colab instance." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mvcwPQARKlkY" - }, - "source": [ - "### Installation and Imports" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "0OhyKPp0Lwit", - "outputId": "b6e6666b-8247-4a90-dae5-edc43f539f1f" - }, - "source": [ - "# @markdown Execute this cell to install cca-zoo and nilearn\n", - "!pip install cca-zoo --upgrade\n", - "!pip install nilearn" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Collecting cca-zoo\n", - " Downloading cca_zoo-1.9.0-py3-none-any.whl (68 kB)\n", - "\u001b[?25l\r\u001b[K |████▊ | 10 kB 22.2 MB/s eta 0:00:01\r\u001b[K |█████████▌ | 20 kB 25.8 MB/s eta 0:00:01\r\u001b[K |██████████████▎ | 30 kB 13.4 MB/s eta 0:00:01\r\u001b[K |███████████████████ | 40 kB 9.9 MB/s eta 0:00:01\r\u001b[K |███████████████████████▊ | 51 kB 7.2 MB/s eta 0:00:01\r\u001b[K |████████████████████████████▌ | 61 kB 7.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████████████| 68 kB 2.7 MB/s \n", - "\u001b[?25hRequirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.0.1)\n", - "Collecting scikit-learn>=0.23\n", - " Downloading scikit_learn-1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (23.1 MB)\n", - "\u001b[K |████████████████████████████████| 23.1 MB 1.6 MB/s \n", - "\u001b[?25hCollecting mvlearn\n", - " Downloading mvlearn-0.4.1-py3-none-any.whl (2.1 MB)\n", - "\u001b[K |████████████████████████████████| 2.1 MB 37.2 MB/s \n", - "\u001b[?25hCollecting tensorly\n", - " Downloading tensorly-0.6.0-py3-none-any.whl (160 kB)\n", - "\u001b[K |████████████████████████████████| 160 kB 86.6 MB/s \n", - "\u001b[?25hCollecting scipy>=1.7\n", - " Downloading scipy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (28.5 MB)\n", - "\u001b[K |████████████████████████████████| 28.5 MB 50 kB/s \n", - "\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.19.5)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.1.5)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (3.2.2)\n", - "Requirement already satisfied: seaborn in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (0.11.2)\n", - "Collecting threadpoolctl>=2.0.0\n", - " Downloading threadpoolctl-3.0.0-py3-none-any.whl (14 kB)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (0.10.0)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (2.4.7)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (2.8.2)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (1.3.2)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from cycler>=0.10->matplotlib->cca-zoo) (1.15.0)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->cca-zoo) (2018.9)\n", - "Collecting nose\n", - " Downloading nose-1.3.7-py3-none-any.whl (154 kB)\n", - "\u001b[K |████████████████████████████████| 154 kB 87.2 MB/s \n", - "\u001b[?25hInstalling collected packages: threadpoolctl, scipy, scikit-learn, nose, tensorly, mvlearn, cca-zoo\n", - " Attempting uninstall: scipy\n", - " Found existing installation: scipy 1.4.1\n", - " Uninstalling scipy-1.4.1:\n", - " Successfully uninstalled scipy-1.4.1\n", - " Attempting uninstall: scikit-learn\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "albumentations 0.1.12 requires imgaug<0.2.7,>=0.2.5, but you have imgaug 0.2.9 which is incompatible.\u001b[0m\n", - "Successfully installed cca-zoo-1.9.0 mvlearn-0.4.1 nose-1.3.7 scikit-learn-1.0 scipy-1.7.1 tensorly-0.6.0 threadpoolctl-3.0.0\n", - "Collecting nilearn\n", - " Downloading nilearn-0.8.1-py3-none-any.whl (10.0 MB)\n", - "\u001b[K |████████████████████████████████| 10.0 MB 5.8 MB/s \n", - "\u001b[?25hRequirement already satisfied: requests>=2 in /usr/local/lib/python3.7/dist-packages (from nilearn) (2.23.0)\n", - "Requirement already satisfied: numpy>=1.16 in /usr/local/lib/python3.7/dist-packages (from nilearn) (1.19.5)\n", - "Requirement already satisfied: joblib>=0.12 in /usr/local/lib/python3.7/dist-packages (from nilearn) (1.0.1)\n", - "Requirement already satisfied: pandas>=0.24.0 in /usr/local/lib/python3.7/dist-packages (from nilearn) (1.1.5)\n", - "Requirement already satisfied: scipy>=1.2 in /usr/local/lib/python3.7/dist-packages (from nilearn) (1.7.1)\n", - "Requirement already satisfied: nibabel>=2.5 in /usr/local/lib/python3.7/dist-packages (from nilearn) (3.0.2)\n", - "Requirement already satisfied: scikit-learn>=0.21 in /usr/local/lib/python3.7/dist-packages (from nilearn) (1.0)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.24.0->nilearn) (2018.9)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas>=0.24.0->nilearn) (2.8.2)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas>=0.24.0->nilearn) (1.15.0)\n", - "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests>=2->nilearn) (1.24.3)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2->nilearn) (2021.5.30)\n", - "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2->nilearn) (2.10)\n", - "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2->nilearn) (3.0.4)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.21->nilearn) (3.0.0)\n", - "Installing collected packages: nilearn\n", - "Successfully installed nilearn-0.8.1\n" - ] - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "h9PE1A0fS266", - "cellView": "form", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "076de56d-adde-44ea-f240-b116cdef4470" - }, - "source": [ - "# @markdown Execute this cell to import modules\n", - "import ipywidgets as widgets\n", - "import seaborn as sns\n", - "from cca_zoo.models import rCCA, PMD, SCCA\n", - "from cca_zoo.data import generate_covariance_data\n", - "import numpy as np\n", - "import pandas as pd\n", - "from sklearn.model_selection import train_test_split\n", - "import matplotlib.pyplot as plt\n", - "from nilearn import datasets\n", - "from nilearn.image import index_img\n", - "from nilearn.input_data import NiftiMasker\n", - "from nilearn.plotting import view_img\n", - "from sklearn.preprocessing import OneHotEncoder\n", - "from nilearn.plotting import plot_stat_map, show\n", - "from sklearn.model_selection import train_test_split\n", - "np.random.seed(42)\n", - "sns.set(font_scale=1)" - ], - "execution_count": 2, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.7/dist-packages/nilearn/datasets/__init__.py:96: FutureWarning: Fetchers from the nilearn.datasets module will be updated in version 0.9 to return python strings instead of bytes and Pandas dataframes instead of Numpy arrays.\n", - " \"Numpy arrays.\", FutureWarning)\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TMApL_uRnguy" - }, - "source": [ - "### Plotting Helpers\n", - "We will use a number of standard figures to visualise the results of models used throughout this notebook" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "MieUEzyKS0eA", - "cellView": "form" - }, - "source": [ - "# @markdown Execute this cell to access plotting functions\n", - "def plot_latent_train_test(train_scores, test_scores, title='Projections of data in latent space'):\n", - " train_data = pd.DataFrame(\n", - " {'phase': np.asarray(['train'] * train_scores[0].shape[0]).astype(str)})\n", - " x_vars=[f'View 1 dimension {f}' for f in range(1,train_scores[0].shape[1]+1)]\n", - " y_vars=[f'View 2 dimension {f}' for f in range(1,train_scores[1].shape[1]+1)]\n", - " train_data[x_vars] = train_scores[0]\n", - " train_data[y_vars] = train_scores[1]\n", - " test_data = pd.DataFrame(\n", - " {'phase': np.asarray(['test'] * test_scores[0].shape[0]).astype(str)})\n", - " test_data[x_vars] = test_scores[0]\n", - " test_data[y_vars] = test_scores[1]\n", - " data = pd.concat([train_data, test_data], axis=0)\n", - " cca_pp = sns.pairplot(data, hue='phase',x_vars=x_vars,y_vars=y_vars, corner=True)\n", - " cca_pp.fig.set_size_inches(10,5)\n", - " if title:\n", - " cca_pp.fig.suptitle(title)\n", - " return cca_pp\n", - "\n", - "def plot_train_test_corrs(train_scores, test_scores, title='Train vs Test correlations'):\n", - " latent_dims=train_scores[0].shape[1]\n", - " train_corrs=np.diag(np.corrcoef(train_scores[0],train_scores[1],rowvar=False)[:latent_dims,latent_dims:])\n", - " test_corrs=np.diag(np.corrcoef(test_scores[0],test_scores[1],rowvar=False)[:latent_dims,latent_dims:])\n", - " train_corr_data=pd.DataFrame({'correlation':train_corrs,'dimension':np.arange(latent_dims)+1,'phase': np.asarray(['train'] * latent_dims).astype(str)})\n", - " test_corr_data=pd.DataFrame({'correlation':test_corrs,'dimension':np.arange(latent_dims)+1,'phase': np.asarray(['test'] * latent_dims).astype(str)})\n", - " corr_data = pd.concat([train_corr_data, test_corr_data], axis=0)\n", - " # setting the dimensions of the plot\n", - " fig2, ax = plt.subplots()\n", - " cca_bp=sns.barplot(x=\"dimension\", y=\"correlation\", hue=\"phase\", data=corr_data,ax=ax)\n", - " cca_bp.set_ylim(bottom=-1, top=1)\n", - " if title:\n", - " cca_bp.set_title(title)\n", - " return cca_bp\n", - "\n", - "def plot_true_weights_coloured(ax, weights, true_weights=None, title=''):\n", - " if true_weights is None:\n", - " true_weights=np.ones(len(weights))\n", - " ind = np.arange(len(true_weights))\n", - " mask = np.squeeze(true_weights == 0)\n", - " ax.scatter(ind[~mask], weights[~mask], c='b',label='Non-Zero')\n", - " ax.scatter(ind[mask], weights[mask], c='r',label='Zero')\n", - " ax.set_title(title)\n", - "\n", - "def plot_model_weights(wx,wy,tx=None,ty=None, title='Model weights vs. True weights'):\n", - " if tx is None and ty is None:\n", - " fig,axs=plt.subplots(1,2)\n", - " plot_true_weights_coloured(axs[0],wx,title='model view 1 weights')\n", - " plot_true_weights_coloured(axs[1],wy,title='model view 2 weights')\n", - " axs[1].legend()\n", - " else:\n", - " fig,axs=plt.subplots(2,2,sharex=True,sharey=True)\n", - " plot_true_weights_coloured(axs[0,0],tx,tx,title='true view 1 weights')\n", - " plot_true_weights_coloured(axs[0,1],ty,ty,title='true view 2 weights')\n", - " plot_true_weights_coloured(axs[1,0],wx,tx,title='model view 1 weights')\n", - " plot_true_weights_coloured(axs[1,1],wy,ty,title='model view 2 weights')\n", - " axs[0,1].legend()\n", - " plt.tight_layout()\n", - " return fig" - ], - "execution_count": 3, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cqonSYEbE4c6" - }, - "source": [ - "## Introduction to Multivariate Machine Learning " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "hhiQ_Yg8qc1b" - }, - "source": [ - "### Latent Variable Models \n", - "Latent variable models assume that the two views of data are derived from some shared latent (unobserved) variables. For neuroimaging and behaviour studies we might expect an underlying health condition to influence both an MRI scan of a patient and data relating to their behaviour.\n", - "\n", - "PLS and CCA models work by projecting the observed data from each view into latent variables for each view that are highly correlated. These can be interpeted as estimates of the true latent variable. Often in neuroimaging studies the biggest components can be shown to be related to variables like Age, Scanner Type and Gender.\n", - "\n", - "![Latent Variable Model of Brain-Behaviour data](https://raw.githubusercontent.com/jameschapman19/education-challenge/main/image.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "SmxsMVGQ6VV2" - }, - "source": [ - "### Optimisation Problems\n", - "Partial Least Squares and Canonical Correlation Analysis share the same objective function (maximising covariance between the projections of each view). The difference is that PLS constrains the variance of the weights (their 2 norm) whereas CCA constrains the variance of the projections." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-NDpDlMDdCqj" - }, - "source": [ - "#### Partial Least Squares\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w_1^TX_1^TX_2w_2 \\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$\\|w_1\\|_2=1$,\n", - "$\\|w_2\\|_2=1$" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BdAh2shKdhKr" - }, - "source": [ - "#### Canonical Correlation Analysis\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w_1^TX_1^TX_2w_2 \\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$\\|X_1w_1\\|_2=1$\n", - "\n", - "$\\|X_2w_2\\|_2=1$\n", - "\n", - "\n", - "The result of these optimisations is that PLS is much more biased towards the largest principal components in the data whereas CCA is sensitive to high correlations, even if they only explain small ammounts of the data.\n", - "\n", - "Also notice that in the special case where the covariance matrices for each view ($X_1^TX_1$ and $X_2^TX_2$) are identity matrices $I$, the CCA and PLS problems are identical (since $\\|X_iw_i\\|_2=w_i^TX_i^TX_iw_i=w_i^Tw_i$)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2YYL_xght8Dk" - }, - "source": [ - "#### Relationship to PCA\n", - "It is interesting to compare these objectives to the more familiar principal components analysis. In PCA, we find weights that project the data in directions that maximise variance. If our two views are the same then PLS is exactly the same model as PCA!\n", - "\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w^TX^TXw \\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$\\|w\\|_2=1$," - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_irxVD8gTY68" - }, - "source": [ - "### Machine Learning Framework \n", - "\n", - "In a machine learning framework, we are primarily interested in the generalization of a model to data not used in the training process. In this tutorial we will generate simulated data which we will split into train and test data to demonstrate how different models generalize.\n", - "\n", - "![Framework](https://raw.githubusercontent.com/jameschapman19/education-challenge/main/image2.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "on2NP1JlnmCf" - }, - "source": [ - "### Simulated Data \n", - "We will use a joint multivariate normal distribution to generate two views of data: view 1 and view 2. This method, described in detail by Helmer et al [1], allows us to control:\n", - "\n", - "* Number of training samples\n", - "* Number of test samples\n", - "* Number of features in view 1\n", - "* Number of features in view 2\n", - "* Level of sparsity in view 1 (fraction of variables from view 1 involved in correlation)\n", - "* Level of sparsity in view 2 (fraction of variables from view 2 involved in correlation)\n", - "\n", - "As well as the strength of the population correlation and the covariance structure within each view. We assume that the underlying correlation is perfect (1) and that the within-view variance is identity.\n", - "\n", - "This data generation process is also equivalent to a latent variable model where view 1 and view 2 are conditionally independent given some latent variable Z." - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "CQ_1X7_inoCH", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 524 - }, - "cellView": "form", - "outputId": "2b82696b-dad7-45c2-b5f2-6c66adc35968" - }, - "source": [ - "# @markdown Execute this cell to choose data parameters! You can see the projections of the generated data using the true weights. These should have almost perfect correlations in both the training and test data.\n", - "style = {'description_width': 'initial'}\n", - "\n", - "N_train= widgets.IntSlider(value=100,min=10,max=500,description='Train Samples',style=style,continuous_update=False)\n", - "N_test= widgets.IntSlider(value=100,min=10,max=500,description='Test Samples',style=style,continuous_update=False)\n", - "X_features=widgets.IntSlider(value=60,min=10,max=500,description='View 1 features',style=style,continuous_update=False)\n", - "Y_features=widgets.IntSlider(value=60,min=10,max=500,description='View 2 features',style=style,continuous_update=False)\n", - "view_1_sparsity=widgets.FloatSlider(value=0.5,min=0,max=1,description='View 1 Sparsity',style=style,continuous_update=False)\n", - "view_2_sparsity=widgets.FloatSlider(value=0.5,min=0,max=1,description='View 2 Sparsity',style=style,continuous_update=False)\n", - "\n", - "def generate_data(N_train,N_test,X_features,Y_features, view_1_sparsity, view_2_sparsity):\n", - " (X,Y),(tx,ty)=generate_covariance_data(N_train+N_test,view_features=[X_features,Y_features],latent_dims=1,correlation=1, view_sparsity=[view_1_sparsity, view_2_sparsity])\n", - " X_tr,X_te,Y_tr,Y_te=train_test_split(X,Y,train_size=N_train)\n", - " plot_latent_train_test(np.stack((X_tr@tx,Y_tr@ty)), np.stack((X_te@tx,Y_te@ty)), title='Projections of data in latent space using true model weights')\n", - " return (X_tr,X_te,Y_tr,Y_te,tx,ty)\n", - "\n", - "out=widgets.interactive(generate_data, N_train=N_train,N_test=N_test,X_features=X_features,Y_features=Y_features,view_1_sparsity=view_1_sparsity,view_2_sparsity=view_2_sparsity)\n", - "display(out)" - ], - "execution_count": 4, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "484098178a6341af93cb9d9f95172a31", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(IntSlider(value=100, continuous_update=False, description='Train Samples', max=500, min=…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7bQazPYUFtVD" - }, - "source": [ - "## Overfitting and Sample Size \n", - "When the sample size is smaller than the number of features, CCA will overfit the data (you can try this by changing the data parameters). Visually, CCA overfitting looks like all the training samples are projected with perfect correlation but the test samples are almost completely uncorrelated. The intuition behind this is that there will be at least one spurious relationship when the data are not full rank.\n", - "\n", - "PLS on the other hand is always well-posed even when the number of samples is less than the number of features. However since PLS maximises covariance rather than correlation, the correlation of the latent variables in the training data is typically much lower. " - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 383, - "referenced_widgets": [ - "d5f617ab68ec42b0a81333040a03ce70", - "082d16684fce464e8ab300fd88db366e", - "42a064361141414d9735dd76a475268a", - "00c063db01344e58b6f4155605f5036e", - "eb5dd98764d741b8ac1d86b5ac566ad2", - "76bd49f092e24228924699e3fd9b735c", - "f2c9bdfd43f4436dab5dfcf0cf8e8b80" - ] - }, - "id": "p8Yy3n_mPsJI", - "outputId": "2d1a9ff0-f548-4417-c52a-3b87a4720cf7" - }, - "source": [ - "# @markdown Execute this cell to toggle between CCA and PLS models (you may need to toggle when you change the data!)\n", - "X_tr,X_te,Y_tr,Y_te, tx,ty=out.result\n", - "tog=widgets.ToggleButtons(\n", - " options=['CCA', 'PLS'],\n", - " description='Model:',\n", - " disabled=False,\n", - " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n", - ")\n", - "def interactive_cca(tog):\n", - " if tog=='CCA':\n", - " rcca=rCCA(latent_dims=1,c=1e-9).fit([X_tr,Y_tr])\n", - " elif tog=='PLS':\n", - " rcca=rCCA(latent_dims=1,c=1).fit([X_tr,Y_tr])\n", - " test_scores=rcca.transform([X_te,Y_te])\n", - " plot_latent_train_test(rcca.scores,test_scores)\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca,tog=tog)\n", - "display(plot_widget)" - ], - "execution_count": 5, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d5f617ab68ec42b0a81333040a03ce70", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(ToggleButtons(description='Model:', options=('CCA', 'PLS'), value='CCA'), Output()), _do…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jDWExSvJKvfv" - }, - "source": [ - "## Ridge Regularised CCA: from CCA to PLS \n", - "\n", - "Given that PLS has an effect that is similar to ridge regularisation, a natural extension is to mix CCA and PLS to control this regularisation effect. Vinod proposed the Canonical Ridge model [2] which combines the constraints from the two models with mixing parameters $c_1$ and $c_2$ which make the model more or less CCA-like or PLS-like.\n", - "\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w_1^TX_1^TX_2w_2 \\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$(1-c_1)\\|X_1w_1\\|_2+c_1\\|w_1\\|_2=1$\n", - "\n", - "$(1-c_2)\\|X_2w_2\\|_2+c_2\\|w_2\\|_2=1$\n", - "\n", - "In the next interactive widget, you can vary $c_1$ and $c_2$ yourself. The plots are provided to help visualise:\n", - "\n", - "* The projections into latent space\n", - "* The train and test correlations\n", - "* The model weights vs. the true weights" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 642, - "referenced_widgets": [ - "4186484e492348708679feb6fc1a5937", - "e9033710a974478088014f255d040332", - "a10e2175e99542f8984a5e1ac038d6c4", - "07f1da8af80c4a33873945c3d206835d", - "10adc5e3157243a5ba6c391d59254072", - "e17dc362cb1c46379c2ed85b398d9250", - "098c336771204a41aa40df3c5a820c70", - "690cbdac87c94770afaa6fae82747ec4", - "1b30b8c885004e3e8e4aaeb9c703de24", - "adc24ca24d0748edb2e13da7e26f635d" - ] - }, - "id": "AmfuhADyLyrz", - "outputId": "f5c0bf75-22ce-41ad-e646-8abe5e105213" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation (there's a bit of a lag as the model needs to fit in the background!)\n", - "X_tr,X_te,Y_tr,Y_te, tx,ty=out.result\n", - "style = {'description_width': 'initial'}\n", - "cx=widgets.FloatLogSlider(base=10,value=-1, min=-10, max=0,description='cx',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "cy=widgets.FloatLogSlider(base=10,value=-1, min=-10, max=0,description='cy',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "def interactive_cca(cx,cy):\n", - " rcca=rCCA(latent_dims=1,c=[cx,cy]).fit([X_tr,Y_tr])\n", - " test_scores=rcca.transform(X_te,Y_te)\n", - " plot_latent_train_test(rcca.scores,test_scores)\n", - " plot_train_test_corrs(rcca.scores,test_scores)\n", - " plot_model_weights(rcca.weights[0],rcca.weights[1],tx,ty, title='Model weights vs. True weights')\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca, cx=cx,cy=cy)\n", - "display(plot_widget)" - ], - "execution_count": 6, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4186484e492348708679feb6fc1a5937", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatLogSlider(value=1e-10, continuous_update=False, description='cx', max=0.0, min=-10.…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2CYEeK4Gp51n" - }, - "source": [ - "As we increase $c_1$ and $c_2$, the correlation of the training data decreases (and the covariance increases). \n", - "\n", - "There is often a 'sweet spot' somewhere between CCA and PLS where the test correlation is maximised. You can see the train and test correlations in the bar plot.\n", - "\n", - "We now also display the learnt model weights alongside the true weights." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DSSQVqm80IX3" - }, - "source": [ - "## Lasso Regularisation \n", - "Just as the canonical ridge is analagous to ridge regularised regression, we can also use lasso regularisation in PLS and CCA models. Lasso regularisation is helpful in biomedical applications as the effects we are looking for are often parsimonious (only a small number of the features contain a signal) and the natural feature selection mechanism helps interpretability.\n", - "\n", - "In this section, we introduce Sparse Partial Least Squares and Sparse Canonical Correlation Analysis." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "r_r4wkcoLClz" - }, - "source": [ - "### Sparse Partial Least Squares \n", - "By adding an additional constraint on the weights, Witten demonstrated a sparse partial least squares model [3] able to recover signals that were only present in a fraction of the variables. Since this method adapts the PLS optimization problem, it also tends to show the same ridge regularisation properties.\n", - "\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w_1^TX_1^TX_2w_2 \\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$\\|w_1\\|_2=1$,\n", - "$\\|w_1\\|_2=1$\n", - "\n", - "$\\|w_1\\|_1\\leq c_1$,\n", - "$\\|w_2\\|_1\\leq c_2$\n", - "\n", - "reducing the value of $c_1$ and $c_2$ leads to a sparser solution with more zero weights \n", - "\n", - "Have a look for yourself!" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "vOowgyeJ2xV_", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 956, - "referenced_widgets": [ - "fd8e3e038fa446c48f29f3c719a88ee8", - "f5588cc41bfe4dfc99d34f0b8c7092ed", - "9032567411404f3d9e85862bff74b0be", - "08ff7d25488b4b53ad841974fa97e8bd", - "53f71b9ae00a4b4cb836d40358392790", - "1947b94e59c0410b875b21164719c876", - "b2afc9cdf727400487ce19f9f0ce7d53", - "16b08cb41fc94870b7578021c7927d1a", - "654c62b8b9c3489c9f03fc76e81a2ed9", - "83245b18235e4bad8141bc399ea5cd94" - ] - }, - "outputId": "92f7440c-26d2-43cb-dbff-482d6eadbe6a" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation\n", - "X_tr,X_te,Y_tr,Y_te, tx,ty=out.result\n", - "style = {'description_width': 'initial'}\n", - "c1=widgets.FloatSlider(value=3, min=1, max=np.sqrt(X_tr.shape[1]),description='c1',readout=True,readout_format='.5f',style=style,continuous_update=False)\n", - "c2=widgets.FloatSlider(value=3, min=1, max=np.sqrt(Y_tr.shape[1]),description='c2',readout=True,readout_format='.5f',style=style,continuous_update=False)\n", - "def interactive_cca(c1,c2):\n", - " spls=PMD(latent_dims=1,c=[c1,c2]).fit([X_tr,Y_tr])\n", - " test_scores=spls.transform([X_te,Y_te])\n", - " plot_latent_train_test(spls.scores,test_scores)\n", - " plot_train_test_corrs(spls.scores,test_scores)\n", - " plot_model_weights(spls.weights[0],spls.weights[1],tx,ty, title='Model weights vs. True weights')\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca, c1=c1,c2=c2)\n", - "display(plot_widget)" - ], - "execution_count": 7, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fd8e3e038fa446c48f29f3c719a88ee8", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=3.0, continuous_update=False, description='c1', max=7.745966692414834,…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "53q1yj1CLGG6" - }, - "source": [ - "### Sparse Canonical Correlation Analysis \n", - "There have been a number of attempts to construct sparse CCA models. One of the most succesful is Mai's variant [4] which uses a lasso penalty with a form much like lasso regression [7].\n", - "\n", - "\n", - "$w_{opt}=\\underset{w}{\\mathrm{argmax}}\\{ w_1^TX_1^TX_2w_2 - c_1\\|w_1\\|_1- c_2\\|w_2\\|_1\\}$\n", - "\n", - "$\\text{subject to:}$\n", - "\n", - "$w_1^TX_1^TX_1w_1=1$\n", - "\n", - "$w_2^TX_2^TX_2w_2=1$\n", - "\n", - "This is a slightly different form to the sparse PLS model. Since the 1-norm of the weights penalizes the objective rather than being a constraint, a higher value of $c_1$ and $c_2$ leads to a more sparse (more weights equal to zero) solution.\n", - "\n", - "Have a go for yourself!" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 956, - "referenced_widgets": [ - "d841bfa912f54aec8f0b185e97f33c06", - "da249cda804d4ca9b5ff93c27bfb3a65", - "71bdb348f5804e45a2adeb256ee72c72", - "116218d7741949e08f547ac9844aa7e1", - "9a3e2484604b49fdbae2dad6d60f43b2", - "e93f9ba98fe34786a3064647deae6cfd", - "f2174b7c3df64f278e748afaf20ff90d", - "94b31bb0dadf44eaa94e43614a626040", - "6c7b054a3a7b4aa6bae9122dfcb83bde", - "f2be86b07cc34fec98d8a0ac03f6e410" - ] - }, - "id": "UYKWAgTTLI5C", - "outputId": "88b69d24-5768-4880-9371-6619f5b21320" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation\n", - "X_tr,X_te,Y_tr,Y_te, tx,ty=out.result\n", - "style = {'description_width': 'initial'}\n", - "c1=widgets.FloatLogSlider(value=0.005,base=10,min=-10, max=0,description='c1',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "c2=widgets.FloatLogSlider(value=0.005,base=10,min=-10, max=0,description='c2',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "def interactive_cca(c1,c2):\n", - " scca=SCCA(latent_dims=1,c=[c1,c2]).fit([X_tr,Y_tr])\n", - " test_scores=scca.transform([X_te,Y_te])\n", - " plot_latent_train_test(scca.scores,test_scores)\n", - " plot_train_test_corrs(scca.scores,test_scores)\n", - " plot_model_weights(scca.weights[0],scca.weights[1],tx,ty, title='Model weights vs. True weights')\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca, c1=c1,c2=c2)\n", - "display(plot_widget)" - ], - "execution_count": 8, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d841bfa912f54aec8f0b185e97f33c06", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatLogSlider(value=0.005, continuous_update=False, description='c1', max=0.0, min=-10.…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cnhys1o9Xdh7" - }, - "source": [ - "## Apply what we have learnt: a neuroimaging example \n", - "\n", - "This part leans heavily on the nilearn [5,6] tutorial in:\n", - "https://nilearn.github.io/auto_examples/02_decoding/plot_haxby_different_estimators.html#sphx-glr-auto-examples-02-decoding-plot-haxby-different-estimators-py.\n", - "\n", - "The Haxby dataset a well known neuroscience dataset which contains functional magnetic resonance images (fMRI) from various subjects while they observe different tasks (e.g. house, cat). \n", - "\n", - "We will apply our multivariate models to this data.\n", - "\n", - "We take masked versions of the fMRI data as view 1. As the other view we will take one hot encoded matrix of the 12 task labels. We will also add 12 random columns to view 2 which we hope our model will learn to ignore since there should be no correlated signal with the fMRI data.\n", - "\n", - "![Hax](https://raw.githubusercontent.com/jameschapman19/education-challenge/main/image3.png)" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "1YS65dje8BHO", - "cellView": "form", - "outputId": "a4060ed6-6733-4d67-ee19-44c8ad33845d" - }, - "source": [ - "# @markdown Execute this cell to fetch and preprocess the Haxby Dataset!\n", - "haxby_dataset = datasets.fetch_haxby()\n", - "mask_filename = haxby_dataset.mask_vt[0]\n", - "fmri_filename = haxby_dataset.func[0]\n", - "behavioural = pd.read_csv(haxby_dataset.session_target[0], delimiter=' ')\n", - "condition_mask=np.ones(len(behavioural),dtype=bool)\n", - "fmri_niimgs = index_img(fmri_filename,condition_mask)\n", - "session_label = behavioural['chunks']\n", - "masker = NiftiMasker(mask_img=mask_filename, sessions=session_label,\n", - " smoothing_fwhm=4, standardize=True,\n", - " memory=\"nilearn_cache\", memory_level=1)\n", - "#Transform data so it is usable\n", - "fmri_masked = masker.fit_transform(fmri_niimgs)\n", - "session_label = OneHotEncoder(handle_unknown='ignore').fit_transform(session_label[:,None])\n", - "session_label = np.hstack((session_label.toarray(),np.zeros((session_label.shape[0],12))))\n", - "session_label = session_label + 0.00001*np.random.normal(size=(session_label.shape))\n", - "HX_tr,HX_te,HY_tr,HY_te=train_test_split(fmri_masked,session_label)" - ], - "execution_count": 9, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\n", - "Dataset created in /root/nilearn_data/haxby2001\n", - "\n", - "Downloading data from https://www.nitrc.org/frs/download.php/7868/mask.nii.gz ...\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - " ...done. (0 seconds, 0 min)\n", - " ...done. (0 seconds, 0 min)\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Downloading data from http://data.pymvpa.org/datasets/haxby2001/MD5SUMS ...\n", - "Downloading data from http://data.pymvpa.org/datasets/haxby2001/subj2-2010.01.14.tar.gz ...\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "Downloaded 290586624 of 291168628 bytes (99.8%, 0.0s remaining) ...done. (9 seconds, 0 min)\n", - "Extracting data from /root/nilearn_data/haxby2001/f33ff337e914bf7fded743c7107979f9/subj2-2010.01.14.tar.gz..... done.\n", - "/usr/local/lib/python3.7/dist-packages/nilearn/_utils/helpers.py:145: FutureWarning: The parameter \"sessions\" will be removed in 0.9.0 release of Nilearn. Please use the parameter \"runs\" instead.\n", - " return func(*args, **kwargs)\n", - "/usr/local/lib/python3.7/dist-packages/nilearn/input_data/nifti_masker.py:529: UserWarning: Persisting input arguments took 1.69s to run.\n", - "If this happens often in your code, it can cause performance problems \n", - "(results will be correct in all cases). \n", - "The reason for this is probably some large input arguments for a wrapped\n", - " function (e.g. large strings).\n", - "THIS IS A JOBLIB ISSUE. If you can, kindly provide the joblib's team with an\n", - " example so that they can fix the problem.\n", - " dtype=self.dtype\n", - "/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:14: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead.\n", - " \n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "xOG2MTBtMD0M" - }, - "source": [ - "### Regularised CCA " - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 414, - "referenced_widgets": [ - "e7c5993c7704484db6390038d50e8e52", - "156c40392f954961b7c6cebee3912d7d", - "13fc9e6c26604f58a3a821c119bd268a", - "73ae601863af45f6b0d97be47ab8149b", - "04140cf3fa164a64930ec261b9f556f6", - "1172e605bb634b31ad0c9a437db7c047", - "33780801a70f4d168ec7fd904ee99685", - "e7a68ca2f9794421b2f047ddadfc6741", - "6aa67cbfb05b4ff9ba03eb365e94c458", - "d83a784c9575476e86292a2f795a439c" - ] - }, - "id": "kAXZD20_Mhl8", - "outputId": "9a420f5a-9182-4c2d-832c-1be65f174bd2" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation (there's a bit of a lag as the model needs to fit in the background!)\n", - "style = {'description_width': 'initial'}\n", - "cx=widgets.FloatLogSlider(base=10,value=-1, min=-10, max=0,description='cx',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "cy=widgets.FloatLogSlider(base=10,value=-1, min=-10, max=0,description='cy',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "def interactive_cca(cx,cy):\n", - " rcca=rCCA(latent_dims=1,c=[cx,cy]).fit([HX_tr,HY_tr])\n", - " test_scores=rcca.transform([HX_te,HY_te])\n", - " plot_latent_train_test(rcca.scores,test_scores)\n", - " plot_train_test_corrs(rcca.scores,test_scores)\n", - " plot_model_weights(rcca.weights[0],rcca.weights[1], title='Model weights')\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca, cx=cx,cy=cy)\n", - "display(plot_widget)" - ], - "execution_count": 10, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e7c5993c7704484db6390038d50e8e52", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatLogSlider(value=1e-10, continuous_update=False, description='cx', max=0.0, min=-10.…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O3tNrjn4MDp5" - }, - "source": [ - "### Sparse PLS " - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 956, - "referenced_widgets": [ - "2c3a73827f794598bb0bb9a749c4b70f", - "f4a50fa01e66441090ebd78f4be413d4", - "6590606b34d643fb8c6c7e06599d5bca", - "886e8a7d34ca460db623ee35abce05a6", - "6097fdf1adb94dc9bf89bd5e0a101fc8", - "75aa1b12f01448198ad356465332fffd", - "6049c8f1a0c54f11955956952181b242", - "ed894f48599f4920bf247584398d2e0e", - "ca3f1c4c5a804634a7f13b0b9ab0ebdd", - "64aa754a29844764b480dc5c91b700be" - ] - }, - "id": "OmbViQYYMjjL", - "outputId": "8f6727d4-9fad-40b4-bf73-920ed56a20fd" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation\n", - "style = {'description_width': 'initial'}\n", - "c1=widgets.FloatSlider(value=3, min=1, max=np.sqrt(HX_tr.shape[1]),description='c1',readout=True,readout_format='.5f',style=style,continuous_update=False)\n", - "c2=widgets.FloatSlider(value=3, min=1, max=np.sqrt(HY_tr.shape[1]),description='c2',readout=True,readout_format='.5f',style=style,continuous_update=False)\n", - "def interactive_cca(c1,c2):\n", - " spls=PMD(latent_dims=1,c=[c1,c2]).fit([HX_tr,HY_tr])\n", - " test_scores=spls.transform([HX_te,HY_te])\n", - " plot_latent_train_test(spls.scores,test_scores)\n", - " plot_train_test_corrs(spls.scores,test_scores)\n", - " plot_model_weights(spls.weights[0],spls.weights[1], title='Model weights')\n", - "\n", - "plot_widget=widgets.interactive(interactive_cca, c1=c1,c2=c2)\n", - "display(plot_widget)" - ], - "execution_count": 11, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2c3a73827f794598bb0bb9a749c4b70f", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=3.0, continuous_update=False, description='c1', max=21.540659228538015…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BiFXzjF2MDMh" - }, - "source": [ - "### Sparse CCA " - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 956 - }, - "id": "xzUD-ylNgq2L", - "cellView": "form", - "outputId": "8a9780ef-5566-4bc1-eade-6ba08a0acc66" - }, - "source": [ - "# @markdown Execute this cell to change model regularisation\n", - "style = {'description_width': 'initial'}\n", - "c1=widgets.FloatLogSlider(value=0.005,base=10,min=-10, max=0,description='c1',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "c2=widgets.FloatLogSlider(value=0.005,base=10,min=-10, max=0,description='c2',readout=True,readout_format='.9f',style=style,continuous_update=False)\n", - "def interactive_cca(c1,c2):\n", - " scca=SCCA(latent_dims=1,c=0.01,initialization='random').fit([HX_tr,HY_tr])\n", - " test_scores=scca.transform([HX_te,HY_te])\n", - " plot_latent_train_test(scca.scores,test_scores)\n", - " plot_train_test_corrs(scca.scores,test_scores)\n", - " plot_model_weights(scca.weights[0],scca.weights[1], title='Model weights')\n", - " \n", - "plot_widget=widgets.interactive(interactive_cca, c1=c1,c2=c2)\n", - "display(plot_widget)" - ], - "execution_count": 12, - "outputs": [ - { - "output_type": "display_data", - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5df438a7257a4684b33107ad602d5bb4", - "version_minor": 0, - "version_major": 2 - }, - "text/plain": [ - "interactive(children=(FloatLogSlider(value=0.005, continuous_update=False, description='c1', max=0.0, min=-10.…" - ] - }, - "metadata": {} - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JWvWx2SdwDlB" - }, - "source": [ - "## Conclusion \n", - "We have introduced Canonical Correlation Analysis and Partial Least Squares in an interactive way.\n", - "\n", - "We showed how regularisation can be used to improve the generalizability and interpretability of our models.\n", - "\n", - "We applied these models to a well-known neuroimaging dataset.\n", - "\n", - "We introduced cca-zoo, an open-source python package that implements variants of CCA and PLS models with different regularisation effects. The goal of the package is to give researchers access to flexible regularisation methods. \n", - "\n", - "Further documentation is available at https://cca-zoo.readthedocs.io/en/latest/index.html \n", - "\n", - "the source code is available at https://github.com/jameschapman19/cca_zoo\n", - "\n", - "\n", - "### Acknowledgements\n", - "Thanks to the authors of the winners of last year's MEC 'Introduction to Medical Image Registration with DeepReg, Between Old and New'[8] for the basic structure of a tutorial notebook. Also thanks to the Neuromatch Academy summer school [9] who's extensive use of notebooks with widgets taught me everything I know!\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "holjn-oSNdy6" - }, - "source": [ - "## References " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IvJXI8Vg-OMM" - }, - "source": [ - "[1] Helmer, Markus, et al. \"On stability of Canonical Correlation Analysis and Partial Least Squares with application to brain-behavior associations.\" BioRxiv (2021): 2020-08.\n", - "\n", - "[2] Vinod, Hrishikesh D. \"Canonical ridge and econometrics of joint production.\" Journal of econometrics 4.2 (1976): 147-166.\n", - "\n", - "[3] Witten, Daniela M., Robert Tibshirani, and Trevor Hastie. \"A penalized matrix decomposition, with applications to sparse principal components and canonical correlation analysis.\" Biostatistics 10.3 (2009): 515-534.\n", - "\n", - "[4] Mai, Qing, and Xin Zhang. \"An iterative penalized least squares approach to sparse canonical correlation analysis.\" Biometrics 75.3 (2019): 734-744.\n", - "\n", - "[5] Abraham, Alexandre, et al. \"Machine learning for neuroimaging with scikit-learn.\" Frontiers in neuroinformatics 8 (2014): 14.\n", - "\n", - "[6] Pedregosa, Fabian, et al. \"Scikit-learn: Machine learning in Python.\" the Journal of machine Learning research 12 (2011): 2825-2830.\n", - "\n", - "[7] Tibshirani, Robert. \"Regression shrinkage and selection via the lasso.\" Journal of the Royal Statistical Society: Series B (Methodological) 58.1 (1996): 267-288.\n", - "\n", - "[8] Fu, Yunguan, et al. \"DeepReg: a deep learning toolkit for medical image registration.\" arXiv preprint arXiv:2011.02580 (2020).\n", - "\n", - "[9] van Viegen, Tara, et al. \"Neuromatch Academy: teaching computational neuroscience with global accessibility.\" arXiv preprint arXiv:2012.08973 (2020)." - ] - } - ] -} \ No newline at end of file diff --git a/tutorial_notebooks/cca_zoo_mnist.ipynb b/tutorial_notebooks/cca_zoo_mnist.ipynb deleted file mode 100644 index 09f8fda5..00000000 --- a/tutorial_notebooks/cca_zoo_mnist.ipynb +++ /dev/null @@ -1,2356 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" - }, - "colab": { - "name": "cca_zoo_tutorial.ipynb", - "provenance": [], - "include_colab_link": true - } - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "Gn1KbNv1vLTj" - }, - "source": [ - "# A tutorial comparing the train and test correlations of different models on MNIST data" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "gYoDpAd1vLTk", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "3e927908-a3b5-4e5a-bce3-d807688c7a9a" - }, - "source": [ - "!pip install --upgrade cca-zoo[deep,probabilistic]" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Requirement already satisfied: cca-zoo[deep,probabilistic] in /usr/local/lib/python3.7/dist-packages (1.8.0)\n", - "Requirement already satisfied: mvlearn in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.4.1)\n", - "Requirement already satisfied: scipy>=1.7 in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.7.1)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (3.2.2)\n", - "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.0)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.19.5)\n", - "Requirement already satisfied: seaborn in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.11.2)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.1.5)\n", - "Requirement already satisfied: tensorly in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.6.0)\n", - "Requirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.0.1)\n", - "Requirement already satisfied: jax~=0.2.20 in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.2.20)\n", - "Requirement already satisfied: arviz in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.11.2)\n", - "Requirement already satisfied: numpyro in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.8.0)\n", - "Requirement already satisfied: torch>=1.9.0 in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (1.9.0+cu102)\n", - "Requirement already satisfied: torchvision in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (0.10.0+cu102)\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.7/dist-packages (from cca-zoo[deep,probabilistic]) (7.1.2)\n", - "Requirement already satisfied: absl-py in /usr/local/lib/python3.7/dist-packages (from jax~=0.2.20->cca-zoo[deep,probabilistic]) (0.12.0)\n", - "Requirement already satisfied: opt-einsum in /usr/local/lib/python3.7/dist-packages (from jax~=0.2.20->cca-zoo[deep,probabilistic]) (3.3.0)\n", - "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from torch>=1.9.0->cca-zoo[deep,probabilistic]) (3.7.4.3)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from absl-py->jax~=0.2.20->cca-zoo[deep,probabilistic]) (1.15.0)\n", - "Requirement already satisfied: xarray>=0.16.1 in /usr/local/lib/python3.7/dist-packages (from arviz->cca-zoo[deep,probabilistic]) (0.18.2)\n", - "Requirement already satisfied: netcdf4 in /usr/local/lib/python3.7/dist-packages (from arviz->cca-zoo[deep,probabilistic]) (1.5.7)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.7/dist-packages (from arviz->cca-zoo[deep,probabilistic]) (21.0)\n", - "Requirement already satisfied: setuptools>=38.4 in /usr/local/lib/python3.7/dist-packages (from arviz->cca-zoo[deep,probabilistic]) (57.4.0)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo[deep,probabilistic]) (1.3.2)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo[deep,probabilistic]) (2.4.7)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo[deep,probabilistic]) (0.10.0)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo[deep,probabilistic]) (2.8.2)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->cca-zoo[deep,probabilistic]) (2018.9)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->cca-zoo[deep,probabilistic]) (2.2.0)\n", - "Requirement already satisfied: cftime in /usr/local/lib/python3.7/dist-packages (from netcdf4->arviz->cca-zoo[deep,probabilistic]) (1.5.0)\n", - "Requirement already satisfied: jaxlib>=0.1.65 in /usr/local/lib/python3.7/dist-packages (from numpyro->cca-zoo[deep,probabilistic]) (0.1.71+cuda111)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from numpyro->cca-zoo[deep,probabilistic]) (4.62.2)\n", - "Requirement already satisfied: flatbuffers<3.0,>=1.12 in /usr/local/lib/python3.7/dist-packages (from jaxlib>=0.1.65->numpyro->cca-zoo[deep,probabilistic]) (1.12)\n", - "Requirement already satisfied: nose in /usr/local/lib/python3.7/dist-packages (from tensorly->cca-zoo[deep,probabilistic]) (1.3.7)\n" - ] - } - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "O5VEWk1BvLTl", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "5870455a-2f40-40ec-8878-6ecc9d9e2a89" - }, - "source": [ - "# Imports\n", - "import numpy as np\n", - "from cca_zoo.data import Noisy_MNIST_Dataset\n", - "import itertools\n", - "import matplotlib.pyplot as plt\n", - "from torch.utils.data import Subset\n", - "from torch import optim\n", - "from cca_zoo.deepmodels import objectives, architectures, DeepWrapper, DCCA,DCCA_NOI,DVCCA,DCCAE,DTCCA\n", - "from sklearn.utils.fixes import loguniform\n", - "# Load MNIST Data\n", - "N = 500\n", - "dataset = Noisy_MNIST_Dataset(mnist_type='FashionMNIST', train=True)\n", - "ids = np.arange(min(2 * N, len(dataset)))\n", - "np.random.shuffle(ids)\n", - "train_ids, val_ids = np.array_split(ids, 2)\n", - "val_dataset = Subset(dataset, val_ids)\n", - "train_dataset = Subset(dataset, train_ids)\n", - "test_dataset = Noisy_MNIST_Dataset(mnist_type='FashionMNIST', train=False)\n", - "test_ids = np.arange(min(N, len(test_dataset)))\n", - "np.random.shuffle(test_ids)\n", - "test_dataset = Subset(test_dataset, test_ids)\n", - "(train_view_1, train_view_2),_ = train_dataset.dataset.to_numpy(\n", - " train_dataset.indices)\n", - "(val_view_1, val_view_2),_ = val_dataset.dataset.to_numpy(val_dataset.indices)\n", - "(test_view_1, test_view_2),_ = test_dataset.dataset.to_numpy(\n", - " test_dataset.indices)\n", - "\n", - "# Settings\n", - "\n", - "# The number of latent dimensions across models\n", - "latent_dims = 2\n", - "# The number of cv used for cross-validation/hyperparameter tuning\n", - "cv = 3\n", - "# For running hyperparameter tuning in parallel (0 if not)\n", - "jobs = 4\n", - "# Number of iterations for iterative algorithms\n", - "max_iter = 2\n", - "# number of epochs for deep models\n", - "epochs = 50" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.7/dist-packages/torchvision/datasets/mnist.py:498: UserWarning: The given NumPy array is not writeable, and PyTorch does not support non-writeable tensors. This means you can write to the underlying (supposedly non-writeable) NumPy array using the tensor. You may want to copy the array to protect its data or make it writeable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at /pytorch/torch/csrc/utils/tensor_numpy.cpp:180.)\n", - " return torch.from_numpy(parsed.astype(m[2], copy=False)).view(*s)\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "C97m5-5tvLTn" - }, - "source": [ - "# Canonical Correlation Analysis" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "gKYi0wtkvLTn" - }, - "source": [ - "from cca_zoo.models import CCA, CCA_ALS\n", - "\"\"\"\n", - "### Linear CCA by eigendecomposition\n", - "\"\"\"\n", - "linear_cca = CCA(latent_dims=latent_dims)\n", - "\n", - "linear_cca.fit((train_view_1, train_view_2))\n", - "\n", - "linear_cca_results = np.stack(\n", - " (linear_cca.score((train_view_1, train_view_2)), linear_cca.score((test_view_1, test_view_2))))\n", - "\n", - "\"\"\"\n", - "### Linear CCA by alternating least squares (can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "linear_cca_als = CCA_ALS(latent_dims=latent_dims)\n", - "\n", - "linear_cca_als.fit((train_view_1, train_view_2))\n", - "\n", - "linear_cca_als_results = np.stack(\n", - " (linear_cca_als.score((train_view_1, train_view_2)), linear_cca_als.score((test_view_1, test_view_2))))" - ], - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "OeqtGYW6vLTo" - }, - "source": [ - "# Partial Least Squares\n" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "-Z4jHKvovLTp" - }, - "source": [ - "from cca_zoo.models import PLS, PLS_ALS\n", - "\"\"\"\n", - "### PLS (2 views)\n", - "\"\"\"\n", - "pls = PLS(latent_dims=latent_dims)\n", - "\n", - "pls.fit((train_view_1, train_view_2))\n", - "\n", - "pls_results = np.stack(\n", - " (pls.score((train_view_1, train_view_2)), pls.score((test_view_1, test_view_2))))\n", - "\n", - "pls_als = PLS_ALS(latent_dims=latent_dims)\n", - "\n", - "pls_als.fit((train_view_1, train_view_2))\n", - "\n", - "pls_als_results = np.stack(\n", - " (pls_als.score((train_view_1, train_view_2)), pls_als.score((test_view_1, test_view_2))))" - ], - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "thDJioUZvLTp" - }, - "source": [ - "# Extension to multiple views\n", - "\n" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "CYTlHO8qvLTq", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "7f043566-28b6-4a0c-91ea-bd4efb9083f1" - }, - "source": [ - "from cca_zoo.models import GCCA, MCCA, PLS_ALS\n", - "\"\"\"\n", - "### (Regularized) Generalized CCA(can pass more than 2 views)\n", - "\"\"\"\n", - "train_view_3=train_view_1+np.random.rand(*train_view_1.shape)\n", - "test_view_3=test_view_1+np.random.rand(*test_view_1.shape)\n", - "\n", - "# small ammount of regularisation added since data is not full rank\n", - "c=[0.5,0.5,0.5]\n", - "\n", - "gcca = GCCA(latent_dims=latent_dims,c=c)\n", - "\n", - "gcca.fit((train_view_1, train_view_2,train_view_3))\n", - "\n", - "gcca_results = np.stack((gcca.score((train_view_1, train_view_2, train_view_3)), gcca.score((test_view_1, test_view_2, test_view_3))))\n", - "\n", - "\"\"\"\n", - "### (Regularized) Multiset CCA(can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "mcca = MCCA(latent_dims=latent_dims, c=c)\n", - "\n", - "mcca.fit((train_view_1, train_view_2,train_view_1))\n", - "\n", - "mcca_results = np.stack((mcca.score((train_view_1, train_view_2, train_view_3)), mcca.score((test_view_1, test_view_2, test_view_3))))\n", - "\n", - "\"\"\"\n", - "### Multiset CCA by alternating least squares\n", - "\"\"\"\n", - "mcca_als = CCA_ALS(latent_dims=latent_dims, max_iter=max_iter)\n", - "\n", - "mcca_als.fit((train_view_1, train_view_2,train_view_3))\n", - "\n", - "mcca_als_results = np.stack(\n", - " (mcca_als.score((train_view_1, train_view_2, train_view_3)), mcca_als.score((test_view_1, test_view_2, test_view_3))))\n", - "\n", - "\"\"\"\n", - "### Multiset PLS by alternating least squares\n", - "\"\"\"\n", - "mcca_pls = PLS_ALS(latent_dims=latent_dims)\n", - "\n", - "mcca_pls.fit((train_view_1, train_view_2,train_view_1))\n", - "\n", - "mcca_pls_results = np.stack(\n", - " (mcca_als.score((train_view_1, train_view_2, train_view_3)), mcca_pls.score((test_view_1, test_view_2, test_view_3))))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.7/dist-packages/cca_zoo/models/innerloop.py:82: UserWarning: For more than 2 views require generalized=True\n", - " warnings.warn(\"For more than 2 views require generalized=True\")\n", - "/usr/local/lib/python3.7/dist-packages/cca_zoo/models/innerloop.py:82: UserWarning: For more than 2 views require generalized=True\n", - " warnings.warn(\"For more than 2 views require generalized=True\")\n", - "/usr/local/lib/python3.7/dist-packages/cca_zoo/models/innerloop.py:82: UserWarning: For more than 2 views require generalized=True\n", - " warnings.warn(\"For more than 2 views require generalized=True\")\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "7gMOkBrsvLTr" - }, - "source": [ - "# Tensor CCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "7ee38U6BvLTr", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "1543df60-fedf-447d-bada-4e97b15fb816" - }, - "source": [ - "from cca_zoo.models import TCCA\n", - "\"\"\"\n", - "### (Regularized) Tensor CCA(can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "tcca = TCCA(latent_dims=latent_dims, c=c)\n", - "\n", - "#memory requirement for tensor is massive so take first 100 features\n", - "tcca.fit((train_view_1[:,:100], train_view_2[:,:100],train_view_3[:,:100]))\n", - "\n", - "tcca_results = np.stack((tcca.score((train_view_1[:,:100], train_view_2[:,:100], train_view_3[:,:100])), tcca.score((test_view_1[:,:100], test_view_2[:,:100], test_view_3[:,:100]))))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "reconstruction error=0.9661771276746018\n", - "iteration 1, reconstruction error: 0.9518385892645673, decrease = 0.014338538410034518, unnormalized = 24.974625897501497\n", - "iteration 2, reconstruction error: 0.9507160541458431, decrease = 0.0011225351187241772, unnormalized = 24.945172484955357\n", - "iteration 3, reconstruction error: 0.9507116250799326, decrease = 4.429065910582786e-06, unnormalized = 24.945056273797878\n", - "iteration 4, reconstruction error: 0.950711606421758, decrease = 1.8658174560926e-08, unnormalized = 24.945055784239106\n", - "iteration 5, reconstruction error: 0.950711606295279, decrease = 1.26479049455952e-10, unnormalized = 24.945055780920512\n", - "PARAFAC converged after 5 iterations\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "lsuPKE35vLTs" - }, - "source": [ - "# Weighted GCCA/Missing Observation GCCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "QgHS4svTvLTt" - }, - "source": [ - "#observation_matrix\n", - "K = np.ones((3, N))\n", - "K[0, 200:] = 0\n", - "K[1, :100] = 0\n", - "\n", - "#view weights\n", - "view_weights=[1,2,1.2]\n", - "\n", - "c=[0.5,0.5,0.5]\n", - "\n", - "gcca = GCCA(latent_dims=latent_dims,c=c,view_weights=view_weights)\n", - "\n", - "gcca.fit((train_view_1, train_view_2,train_view_1),K=K)\n", - "\n", - "gcca_results = np.stack((gcca.score((train_view_1, train_view_2)), gcca.score((test_view_1, test_view_2))))" - ], - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "4O9JHGcBvLTt" - }, - "source": [ - "# Regularised CCA solutions based on alternating minimisation/alternating least squares\n", - "\n", - "We implement Witten's penalized matrix decomposition form of sparse CCA using 'pmd'\n", - "\n", - "We implement Waaijenborg's penalized CCA using elastic net using 'elastic'\n", - "\n", - "We implement Mai's sparse CCA using 'scca'\n", - "\n", - "Furthermore, any of these methods can be extended to multiple views. Witten describes this method explicitly." - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "-PdP_V7WvLTt", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "0ef4ca0c-36d5-43c3-e38b-914b52687254" - }, - "source": [ - "from cca_zoo.model_selection import GridSearchCV, RandomizedSearchCV\n", - "from cca_zoo.models import rCCA, PMD,SCCA,ElasticCCA\n", - "\n", - "def scorer(estimator,X):\n", - " dim_corrs=estimator.score(X)\n", - " return dim_corrs.mean()\n", - "\n", - "\"\"\"\n", - "### Ridge CCA (can pass more than 2 views)\n", - "\"\"\"\n", - "c1 = [0.1, 0.3, 0.7, 0.9]\n", - "c2 = [0.1, 0.3, 0.7, 0.9]\n", - "param_grid = {'c': [c1,c2]}\n", - "\n", - "ridge = GridSearchCV(rCCA(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "ridge_results = np.stack((ridge.score((train_view_1,train_view_2)), ridge.score((test_view_1, test_view_2))))\n", - "\n", - "\"\"\"\n", - "### Sparse CCA (Penalized Matrix Decomposition) (can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "# PMD\n", - "c1 = [1, 3, 7, 9]\n", - "c2 = [1, 3, 7, 9]\n", - "param_grid = {'c': [c1,c2]}\n", - "\n", - "pmd = GridSearchCV(PMD(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "pmd_results = np.stack((pmd.score((train_view_1,train_view_2)), pmd.score((test_view_1, test_view_2))))\n", - "\n", - "\"\"\"\n", - "### Sparse CCA (can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "# Sparse CCA\n", - "c1 = [0.00001, 0.0001]\n", - "c2 = [0.00001, 0.0001]\n", - "param_grid = {'c': [c1,c2]}\n", - "\n", - "scca = GridSearchCV(SCCA(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "scca_results = np.stack(\n", - " (scca.score((train_view_1,train_view_2)), scca.score((test_view_1, test_view_2))))\n", - "\n", - "\n", - "\"\"\"\n", - "### Elastic CCA (can pass more than 2 views)\n", - "\"\"\"\n", - "\n", - "# Elastic CCA\n", - "c1 = loguniform(1e-4, 1e0)\n", - "c2 = loguniform(1e-4, 1e0)\n", - "l1_1 = loguniform(1e-4, 1e0)\n", - "l1_2 = loguniform(1e-4, 1e0)\n", - "param_grid = {'c': [c1,c2], 'l1_ratio': [l1_1,l1_2]}\n", - "\n", - "elastic = RandomizedSearchCV(ElasticCCA(latent_dims=latent_dims),param_distributions=param_grid,\n", - " cv=cv,\n", - " verbose=True,n_iter=5,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "elastic_results = np.stack(\n", - " (elastic.score((train_view_1,train_view_2)), elastic.score((test_view_1, test_view_2))))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Fitting 3 folds for each of 16 candidates, totalling 48 fits\n", - "Fitting 3 folds for each of 16 candidates, totalling 48 fits\n", - "Fitting 3 folds for each of 4 candidates, totalling 12 fits\n", - "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "RLvrF3bGvLTu" - }, - "source": [ - "# Kernel CCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "cdu62DxIvLTv", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "1d94ef2a-0242-421e-bdd2-69a80414d682" - }, - "source": [ - "from cca_zoo.models import KCCA\n", - "\"\"\"\n", - "### Kernel CCA\n", - "\n", - "Similarly, we can use kernel CCA methods with [method='kernel']\n", - "\n", - "We can use different kernels and their associated parameters in a similar manner to before\n", - "- regularized linear kernel CCA: parameters : 'kernel'='linear', 0<'c'<1\n", - "- polynomial kernel CCA: parameters : 'kernel'='poly', 'degree', 0<'c'<1\n", - "- gaussian rbf kernel CCA: parameters : 'kernel'='gaussian', 'sigma', 0<'c'<1\n", - "\"\"\"\n", - "# %%\n", - "# r-kernel cca\n", - "c1 = [0.9, 0.99]\n", - "c2 = [0.9, 0.99]\n", - "\n", - "param_grid = {'kernel': ['linear'], 'c': [c1,c2]}\n", - "\n", - "kernel_reg = GridSearchCV(KCCA(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "kernel_reg_results = np.stack((\n", - " kernel_reg.score((train_view_1,train_view_2)),\n", - " kernel_reg.score((test_view_1, test_view_2))))\n", - "\n", - "# kernel cca (poly)\n", - "degree1 = [2, 3]\n", - "degree2 = [2, 3]\n", - "\n", - "param_grid = {'kernel': ['poly'], 'degree': [degree1,degree2],\n", - " 'c': [c1,c2]}\n", - "\n", - "kernel_poly = GridSearchCV(KCCA(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "kernel_poly_results = np.stack((\n", - " kernel_poly.score((train_view_1,train_view_2)),\n", - " kernel_poly.score((test_view_1, test_view_2))))\n", - "\n", - "# kernel cca (gaussian)\n", - "gamma1 = [1e+1, 1e+2, 1e+3]\n", - "gamma2 = [1e+1, 1e+2, 1e+3]\n", - "\n", - "param_grid = {'kernel': ['rbf'], 'gamma': [gamma1,gamma2],\n", - " 'c': [c1,c2]}\n", - "\n", - "kernel_gaussian = GridSearchCV(KCCA(latent_dims=latent_dims),param_grid=param_grid,\n", - " cv=cv,\n", - " verbose=True,scoring=scorer).fit([train_view_1,train_view_2]).best_estimator_\n", - "\n", - "kernel_gaussian_results = np.stack((\n", - " kernel_gaussian.score((train_view_1,train_view_2)),\n", - " kernel_gaussian.score((test_view_1, test_view_2))))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Fitting 3 folds for each of 4 candidates, totalling 12 fits\n", - "Fitting 3 folds for each of 16 candidates, totalling 48 fits\n", - "Fitting 3 folds for each of 36 candidates, totalling 108 fits\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n", - "/usr/local/lib/python3.7/dist-packages/sklearn/model_selection/_search.py:972: UserWarning: One or more of the test scores are non-finite: [nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan\n", - " nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan]\n", - " category=UserWarning,\n", - "/usr/local/lib/python3.7/dist-packages/numpy/lib/function_base.py:2559: RuntimeWarning: invalid value encountered in true_divide\n", - " c /= stddev[:, None]\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "iJfzMn4KvLTv" - }, - "source": [ - "# Deep CCA\n", - "\n", - "DCCA can be optimized using Andrew's original tracenorm objective or Wang's DCCA by nonlinear orthogonal iterations using the argument als=True." - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "KvW0LO4KvLTw", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "3e57f638-f6c1-483b-a088-aaf23fd1b333" - }, - "source": [ - "\"\"\"\n", - "### Deep Learning\n", - "\n", - "We also have deep CCA methods (and autoencoder variants)\n", - "- Deep CCA (DCCA)\n", - "- Deep Canonically Correlated Autoencoders (DCCAE)\n", - "\n", - "\"\"\"\n", - "\n", - "# %%\n", - "# DCCA\n", - "print('DCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dcca_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2])\n", - "\n", - "dcca_model = DeepWrapper(dcca_model)\n", - "\n", - "dcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dcca_results = np.stack((dcca_model.score(train_dataset), dcca_model.score(test_dataset)))\n", - "\n", - "# DCCA_NOI\n", - "print('DCCA by non-linear orthogonal iterations')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dcca_noi_model = DCCA_NOI(latent_dims=latent_dims, encoders=[encoder_1, encoder_2],N=len(train_dataset))\n", - "\n", - "dcca_noi_model = DeepWrapper(dcca_noi_model)\n", - "\n", - "dcca_noi_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dcca_noi_results = np.stack(\n", - " (dcca_noi_model.score(train_dataset), dcca_noi_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DCCA\n", - "total parameters: 201476\n", - "====> Epoch: 1 Average train loss: -0.2531\n", - "====> Epoch: 1 Average val loss: -0.1096\n", - "Min loss -0.11\n", - "====> Epoch: 2 Average train loss: -0.1009\n", - "====> Epoch: 2 Average val loss: -0.1308\n", - "Min loss -0.13\n", - "====> Epoch: 3 Average train loss: -0.2040\n", - "====> Epoch: 3 Average val loss: -0.1842\n", - "Min loss -0.18\n", - "====> Epoch: 4 Average train loss: -0.1796\n", - "====> Epoch: 4 Average val loss: -0.1576\n", - "====> Epoch: 5 Average train loss: -0.2058\n", - "====> Epoch: 5 Average val loss: -0.3233\n", - "Min loss -0.32\n", - "====> Epoch: 6 Average train loss: -0.3632\n", - "====> Epoch: 6 Average val loss: -0.4711\n", - "Min loss -0.47\n", - "====> Epoch: 7 Average train loss: -0.4117\n", - "====> Epoch: 7 Average val loss: -0.4762\n", - "Min loss -0.48\n", - "====> Epoch: 8 Average train loss: -0.4564\n", - "====> Epoch: 8 Average val loss: -0.4032\n", - "====> Epoch: 9 Average train loss: -0.5325\n", - "====> Epoch: 9 Average val loss: -0.4755\n", - "====> Epoch: 10 Average train loss: -0.5422\n", - "====> Epoch: 10 Average val loss: -0.5092\n", - "Min loss -0.51\n", - "====> Epoch: 11 Average train loss: -0.7966\n", - "====> Epoch: 11 Average val loss: -0.7684\n", - "Min loss -0.77\n", - "====> Epoch: 12 Average train loss: -0.8014\n", - "====> Epoch: 12 Average val loss: -0.7995\n", - "Min loss -0.80\n", - "====> Epoch: 13 Average train loss: -0.8132\n", - "====> Epoch: 13 Average val loss: -0.7553\n", - "====> Epoch: 14 Average train loss: -0.8314\n", - "====> Epoch: 14 Average val loss: -0.7947\n", - "====> Epoch: 15 Average train loss: -0.8431\n", - "====> Epoch: 15 Average val loss: -0.7909\n", - "====> Epoch: 16 Average train loss: -0.7976\n", - "====> Epoch: 16 Average val loss: -0.8460\n", - "Min loss -0.85\n", - "====> Epoch: 17 Average train loss: -0.8252\n", - "====> Epoch: 17 Average val loss: -0.8178\n", - "====> Epoch: 18 Average train loss: -0.5628\n", - "====> Epoch: 18 Average val loss: -0.5937\n", - "====> Epoch: 19 Average train loss: -0.6322\n", - "====> Epoch: 19 Average val loss: -0.6510\n", - "====> Epoch: 20 Average train loss: -0.7647\n", - "====> Epoch: 20 Average val loss: -0.7421\n", - "====> Epoch: 21 Average train loss: -0.7851\n", - "====> Epoch: 21 Average val loss: -0.7247\n", - "====> Epoch: 22 Average train loss: -0.7602\n", - "====> Epoch: 22 Average val loss: -0.8007\n", - "====> Epoch: 23 Average train loss: -0.8833\n", - "====> Epoch: 23 Average val loss: -0.7915\n", - "====> Epoch: 24 Average train loss: -0.9343\n", - "====> Epoch: 24 Average val loss: -1.0192\n", - "Min loss -1.02\n", - "====> Epoch: 25 Average train loss: -1.0146\n", - "====> Epoch: 25 Average val loss: -0.9402\n", - "====> Epoch: 26 Average train loss: -1.1286\n", - "====> Epoch: 26 Average val loss: -1.1075\n", - "Min loss -1.11\n", - "====> Epoch: 27 Average train loss: -1.3057\n", - "====> Epoch: 27 Average val loss: -1.1418\n", - "Min loss -1.14\n", - "====> Epoch: 28 Average train loss: -1.2272\n", - "====> Epoch: 28 Average val loss: -1.2574\n", - "Min loss -1.26\n", - "====> Epoch: 29 Average train loss: -1.2456\n", - "====> Epoch: 29 Average val loss: -1.2860\n", - "Min loss -1.29\n", - "====> Epoch: 30 Average train loss: -1.1873\n", - "====> Epoch: 30 Average val loss: -1.2548\n", - "====> Epoch: 31 Average train loss: -0.4281\n", - "====> Epoch: 31 Average val loss: -0.4875\n", - "====> Epoch: 32 Average train loss: -0.4639\n", - "====> Epoch: 32 Average val loss: -0.4214\n", - "====> Epoch: 33 Average train loss: -0.5640\n", - "====> Epoch: 33 Average val loss: -0.5791\n", - "====> Epoch: 34 Average train loss: -0.5631\n", - "====> Epoch: 34 Average val loss: -0.5806\n", - "====> Epoch: 35 Average train loss: -0.6183\n", - "====> Epoch: 35 Average val loss: -0.7391\n", - "====> Epoch: 36 Average train loss: -0.7337\n", - "====> Epoch: 36 Average val loss: -0.7412\n", - "====> Epoch: 37 Average train loss: -0.8041\n", - "====> Epoch: 37 Average val loss: -0.7272\n", - "====> Epoch: 38 Average train loss: -0.8215\n", - "====> Epoch: 38 Average val loss: -0.7341\n", - "====> Epoch: 39 Average train loss: -0.9231\n", - "====> Epoch: 39 Average val loss: -0.8679\n", - "====> Epoch: 40 Average train loss: -0.9075\n", - "====> Epoch: 40 Average val loss: -0.9036\n", - "====> Epoch: 41 Average train loss: -0.9635\n", - "====> Epoch: 41 Average val loss: -1.0391\n", - "====> Epoch: 42 Average train loss: -0.5608\n", - "====> Epoch: 42 Average val loss: -0.5265\n", - "====> Epoch: 43 Average train loss: -0.5490\n", - "====> Epoch: 43 Average val loss: -0.4652\n", - "====> Epoch: 44 Average train loss: -0.5735\n", - "====> Epoch: 44 Average val loss: -0.5087\n", - "====> Epoch: 45 Average train loss: -0.4709\n", - "====> Epoch: 45 Average val loss: -0.5251\n", - "====> Epoch: 46 Average train loss: -0.5153\n", - "====> Epoch: 46 Average val loss: -0.5300\n", - "====> Epoch: 47 Average train loss: -0.6953\n", - "====> Epoch: 47 Average val loss: -0.6359\n", - "====> Epoch: 48 Average train loss: -0.7484\n", - "====> Epoch: 48 Average val loss: -0.7373\n", - "====> Epoch: 49 Average train loss: -0.7750\n", - "====> Epoch: 49 Average val loss: -0.6816\n", - "====> Epoch: 50 Average train loss: -0.4636\n", - "====> Epoch: 50 Average val loss: -0.3775\n", - "DCCA by non-linear orthogonal iterations\n", - "total parameters: 201484\n", - "====> Epoch: 1 Average train loss: 0.0077\n", - "====> Epoch: 1 Average val loss: 0.0074\n", - "Min loss 0.01\n", - "====> Epoch: 2 Average train loss: 0.0071\n", - "====> Epoch: 2 Average val loss: 0.0069\n", - "Min loss 0.01\n", - "====> Epoch: 3 Average train loss: 0.0072\n", - "====> Epoch: 3 Average val loss: 0.0069\n", - "====> Epoch: 4 Average train loss: 0.0067\n", - "====> Epoch: 4 Average val loss: 0.0070\n", - "====> Epoch: 5 Average train loss: 0.0064\n", - "====> Epoch: 5 Average val loss: 0.0069\n", - "====> Epoch: 6 Average train loss: 0.0066\n", - "====> Epoch: 6 Average val loss: 0.0070\n", - "====> Epoch: 7 Average train loss: 0.0067\n", - "====> Epoch: 7 Average val loss: 0.0068\n", - "Min loss 0.01\n", - "====> Epoch: 8 Average train loss: 0.0066\n", - "====> Epoch: 8 Average val loss: 0.0063\n", - "Min loss 0.01\n", - "====> Epoch: 9 Average train loss: 0.0068\n", - "====> Epoch: 9 Average val loss: 0.0064\n", - "====> Epoch: 10 Average train loss: 0.0066\n", - "====> Epoch: 10 Average val loss: 0.0065\n", - "====> Epoch: 11 Average train loss: 0.0067\n", - "====> Epoch: 11 Average val loss: 0.0063\n", - "Min loss 0.01\n", - "====> Epoch: 12 Average train loss: 0.0066\n", - "====> Epoch: 12 Average val loss: 0.0068\n", - "====> Epoch: 13 Average train loss: 0.0065\n", - "====> Epoch: 13 Average val loss: 0.0066\n", - "====> Epoch: 14 Average train loss: 0.0065\n", - "====> Epoch: 14 Average val loss: 0.0066\n", - "====> Epoch: 15 Average train loss: 0.0063\n", - "====> Epoch: 15 Average val loss: 0.0064\n", - "====> Epoch: 16 Average train loss: 0.0060\n", - "====> Epoch: 16 Average val loss: 0.0057\n", - "Min loss 0.01\n", - "====> Epoch: 17 Average train loss: 0.0057\n", - "====> Epoch: 17 Average val loss: 0.0054\n", - "Min loss 0.01\n", - "====> Epoch: 18 Average train loss: 0.0045\n", - "====> Epoch: 18 Average val loss: 0.0045\n", - "Min loss 0.00\n", - "====> Epoch: 19 Average train loss: 0.0044\n", - "====> Epoch: 19 Average val loss: 0.0043\n", - "Min loss 0.00\n", - "====> Epoch: 20 Average train loss: 0.0042\n", - "====> Epoch: 20 Average val loss: 0.0045\n", - "====> Epoch: 21 Average train loss: 0.0042\n", - "====> Epoch: 21 Average val loss: 0.0042\n", - "Min loss 0.00\n", - "====> Epoch: 22 Average train loss: 0.0042\n", - "====> Epoch: 22 Average val loss: 0.0042\n", - "Min loss 0.00\n", - "====> Epoch: 23 Average train loss: 0.0042\n", - "====> Epoch: 23 Average val loss: 0.0041\n", - "Min loss 0.00\n", - "====> Epoch: 24 Average train loss: 0.0042\n", - "====> Epoch: 24 Average val loss: 0.0040\n", - "Min loss 0.00\n", - "====> Epoch: 25 Average train loss: 0.0042\n", - "====> Epoch: 25 Average val loss: 0.0043\n", - "====> Epoch: 26 Average train loss: 0.0041\n", - "====> Epoch: 26 Average val loss: 0.0043\n", - "====> Epoch: 27 Average train loss: 0.0042\n", - "====> Epoch: 27 Average val loss: 0.0044\n", - "====> Epoch: 28 Average train loss: 0.0041\n", - "====> Epoch: 28 Average val loss: 0.0042\n", - "====> Epoch: 29 Average train loss: 0.0041\n", - "====> Epoch: 29 Average val loss: 0.0043\n", - "====> Epoch: 30 Average train loss: 0.0039\n", - "====> Epoch: 30 Average val loss: 0.0039\n", - "Min loss 0.00\n", - "====> Epoch: 31 Average train loss: 0.0038\n", - "====> Epoch: 31 Average val loss: 0.0037\n", - "Min loss 0.00\n", - "====> Epoch: 32 Average train loss: 0.0079\n", - "====> Epoch: 32 Average val loss: 0.0079\n", - "====> Epoch: 33 Average train loss: 0.0081\n", - "====> Epoch: 33 Average val loss: 0.0077\n", - "====> Epoch: 34 Average train loss: 0.0072\n", - "====> Epoch: 34 Average val loss: 0.0075\n", - "====> Epoch: 35 Average train loss: 0.0065\n", - "====> Epoch: 35 Average val loss: 0.0062\n", - "====> Epoch: 36 Average train loss: 0.0060\n", - "====> Epoch: 36 Average val loss: 0.0056\n", - "====> Epoch: 37 Average train loss: 0.0057\n", - "====> Epoch: 37 Average val loss: 0.0057\n", - "====> Epoch: 38 Average train loss: 0.0045\n", - "====> Epoch: 38 Average val loss: 0.0045\n", - "====> Epoch: 39 Average train loss: 0.0043\n", - "====> Epoch: 39 Average val loss: 0.0044\n", - "====> Epoch: 40 Average train loss: 0.0042\n", - "====> Epoch: 40 Average val loss: 0.0042\n", - "====> Epoch: 41 Average train loss: 0.0040\n", - "====> Epoch: 41 Average val loss: 0.0039\n", - "====> Epoch: 42 Average train loss: 0.0040\n", - "====> Epoch: 42 Average val loss: 0.0040\n", - "====> Epoch: 43 Average train loss: 0.0042\n", - "====> Epoch: 43 Average val loss: 0.0041\n", - "====> Epoch: 44 Average train loss: 0.0040\n", - "====> Epoch: 44 Average val loss: 0.0038\n", - "====> Epoch: 45 Average train loss: 0.0040\n", - "====> Epoch: 45 Average val loss: 0.0037\n", - "Min loss 0.00\n", - "====> Epoch: 46 Average train loss: 0.0041\n", - "====> Epoch: 46 Average val loss: 0.0038\n", - "====> Epoch: 47 Average train loss: 0.0039\n", - "====> Epoch: 47 Average val loss: 0.0040\n", - "====> Epoch: 48 Average train loss: 0.0035\n", - "====> Epoch: 48 Average val loss: 0.0034\n", - "Min loss 0.00\n", - "====> Epoch: 49 Average train loss: 0.0033\n", - "====> Epoch: 49 Average val loss: 0.0035\n", - "====> Epoch: 50 Average train loss: 0.0034\n", - "====> Epoch: 50 Average val loss: 0.0033\n", - "Min loss 0.00\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "LvhEwkx2vLTx" - }, - "source": [ - "# DCCA with custom optimizers and schedulers" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "pyNA7lBEvLTx", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "ff1b9c97-a2d6-4f18-9204-343ff3fd0dc8" - }, - "source": [ - "# DCCA\n", - "print('DCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dcca_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2])\n", - "optimizer = optim.Adam(dcca_model.parameters(), lr=1e-4)\n", - "scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, 1)\n", - "dcca_model = DeepWrapper(dcca_model,optimizer=optimizer,scheduler=scheduler)\n", - "\n", - "dcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dcca_results = np.stack((dcca_model.score(train_dataset), dcca_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DCCA\n", - "total parameters: 201476\n", - "====> Epoch: 1 Average train loss: -0.2345\n", - "====> Epoch: 1 Average val loss: -0.1404\n", - "Min loss -0.14\n", - "====> Epoch: 2 Average train loss: -0.1096\n", - "====> Epoch: 2 Average val loss: -0.1372\n", - "====> Epoch: 3 Average train loss: -0.0880\n", - "====> Epoch: 3 Average val loss: -0.0736\n", - "====> Epoch: 4 Average train loss: -0.0473\n", - "====> Epoch: 4 Average val loss: -0.1699\n", - "Min loss -0.17\n", - "====> Epoch: 5 Average train loss: -0.1097\n", - "====> Epoch: 5 Average val loss: -0.1187\n", - "====> Epoch: 6 Average train loss: -0.1023\n", - "====> Epoch: 6 Average val loss: -0.0718\n", - "====> Epoch: 7 Average train loss: -0.1285\n", - "====> Epoch: 7 Average val loss: -0.1539\n", - "====> Epoch: 8 Average train loss: -0.0861\n", - "====> Epoch: 8 Average val loss: -0.0859\n", - "====> Epoch: 9 Average train loss: -0.0531\n", - "====> Epoch: 9 Average val loss: -0.0679\n", - "====> Epoch: 10 Average train loss: -0.0867\n", - "====> Epoch: 10 Average val loss: -0.0726\n", - "====> Epoch: 11 Average train loss: -0.0745\n", - "====> Epoch: 11 Average val loss: -0.0786\n", - "====> Epoch: 12 Average train loss: -0.0469\n", - "====> Epoch: 12 Average val loss: -0.1140\n", - "====> Epoch: 13 Average train loss: -0.0485\n", - "====> Epoch: 13 Average val loss: -0.1343\n", - "====> Epoch: 14 Average train loss: -0.1541\n", - "====> Epoch: 14 Average val loss: -0.0680\n", - "====> Epoch: 15 Average train loss: -0.1251\n", - "====> Epoch: 15 Average val loss: -0.1210\n", - "====> Epoch: 16 Average train loss: -0.0867\n", - "====> Epoch: 16 Average val loss: -0.0625\n", - "====> Epoch: 17 Average train loss: -0.0633\n", - "====> Epoch: 17 Average val loss: -0.0377\n", - "====> Epoch: 18 Average train loss: -0.0532\n", - "====> Epoch: 18 Average val loss: -0.0286\n", - "====> Epoch: 19 Average train loss: -0.1026\n", - "====> Epoch: 19 Average val loss: -0.1444\n", - "====> Epoch: 20 Average train loss: -0.1870\n", - "====> Epoch: 20 Average val loss: -0.1416\n", - "====> Epoch: 21 Average train loss: -0.1480\n", - "====> Epoch: 21 Average val loss: -0.0909\n", - "====> Epoch: 22 Average train loss: -0.1719\n", - "====> Epoch: 22 Average val loss: -0.1753\n", - "Min loss -0.18\n", - "====> Epoch: 23 Average train loss: -0.0477\n", - "====> Epoch: 23 Average val loss: -0.1516\n", - "====> Epoch: 24 Average train loss: -0.1052\n", - "====> Epoch: 24 Average val loss: -0.0912\n", - "====> Epoch: 25 Average train loss: -0.0992\n", - "====> Epoch: 25 Average val loss: -0.0723\n", - "====> Epoch: 26 Average train loss: -0.1183\n", - "====> Epoch: 26 Average val loss: -0.0333\n", - "====> Epoch: 27 Average train loss: -0.0867\n", - "====> Epoch: 27 Average val loss: -0.1468\n", - "====> Epoch: 28 Average train loss: -0.0383\n", - "====> Epoch: 28 Average val loss: -0.0647\n", - "====> Epoch: 29 Average train loss: -0.1259\n", - "====> Epoch: 29 Average val loss: -0.0760\n", - "====> Epoch: 30 Average train loss: -0.1398\n", - "====> Epoch: 30 Average val loss: -0.0601\n", - "====> Epoch: 31 Average train loss: -0.1736\n", - "====> Epoch: 31 Average val loss: -0.1128\n", - "====> Epoch: 32 Average train loss: -0.1219\n", - "====> Epoch: 32 Average val loss: -0.0435\n", - "====> Epoch: 33 Average train loss: -0.0648\n", - "====> Epoch: 33 Average val loss: -0.1038\n", - "====> Epoch: 34 Average train loss: -0.2130\n", - "====> Epoch: 34 Average val loss: -0.0537\n", - "====> Epoch: 35 Average train loss: -0.0897\n", - "====> Epoch: 35 Average val loss: -0.1286\n", - "====> Epoch: 36 Average train loss: -0.0613\n", - "====> Epoch: 36 Average val loss: -0.1210\n", - "====> Epoch: 37 Average train loss: -0.2377\n", - "====> Epoch: 37 Average val loss: -0.0619\n", - "====> Epoch: 38 Average train loss: -0.0458\n", - "====> Epoch: 38 Average val loss: -0.1473\n", - "====> Epoch: 39 Average train loss: -0.0578\n", - "====> Epoch: 39 Average val loss: -0.1190\n", - "====> Epoch: 40 Average train loss: -0.1635\n", - "====> Epoch: 40 Average val loss: -0.1229\n", - "====> Epoch: 41 Average train loss: -0.1248\n", - "====> Epoch: 41 Average val loss: -0.0981\n", - "====> Epoch: 42 Average train loss: -0.0984\n", - "====> Epoch: 42 Average val loss: -0.0647\n", - "====> Epoch: 43 Average train loss: -0.1104\n", - "====> Epoch: 43 Average val loss: -0.1612\n", - "====> Epoch: 44 Average train loss: -0.0655\n", - "====> Epoch: 44 Average val loss: -0.1284\n", - "====> Epoch: 45 Average train loss: -0.0875\n", - "====> Epoch: 45 Average val loss: -0.0430\n", - "====> Epoch: 46 Average train loss: -0.1070\n", - "====> Epoch: 46 Average val loss: -0.1176\n", - "====> Epoch: 47 Average train loss: -0.0518\n", - "====> Epoch: 47 Average val loss: -0.0676\n", - "====> Epoch: 48 Average train loss: -0.2479\n", - "====> Epoch: 48 Average val loss: -0.1518\n", - "====> Epoch: 49 Average train loss: -0.0629\n", - "====> Epoch: 49 Average val loss: -0.1117\n", - "====> Epoch: 50 Average train loss: -0.1809\n", - "====> Epoch: 50 Average val loss: -0.1175\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "5eV-WnWRvLTy" - }, - "source": [ - "# DGCCA and DMCCA for more than 2 views\n", - "\n", - "The only change we need to make is to the objective argument to perform DGCCA and DMCCA." - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "_XKkNitdvLTy", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "3e35ad57-ea2b-4631-e0d2-8f14282affb5" - }, - "source": [ - "# DGCCA\n", - "print('DGCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dgcca_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.GCCA)\n", - "\n", - "dgcca_model = DeepWrapper(dgcca_model)\n", - "\n", - "dgcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dgcca_results = np.stack(\n", - " (dgcca_model.score(train_dataset), dgcca_model.score(test_dataset)))\n", - "\n", - "# DMCCA\n", - "print('DMCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dmcca_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2], objective=objectives.MCCA)\n", - "\n", - "dmcca_model = DeepWrapper(dmcca_model)\n", - "\n", - "dmcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dmcca_results = np.stack(\n", - " (dmcca_model.score(train_dataset), dmcca_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DGCCA\n", - "total parameters: 201476\n", - "====> Epoch: 1 Average train loss: -0.3717\n", - "====> Epoch: 1 Average val loss: -0.2874\n", - "Min loss -0.29\n", - "====> Epoch: 2 Average train loss: -0.4010\n", - "====> Epoch: 2 Average val loss: -0.3402\n", - "Min loss -0.34\n", - "====> Epoch: 3 Average train loss: -0.5026\n", - "====> Epoch: 3 Average val loss: -0.4891\n", - "Min loss -0.49\n", - "====> Epoch: 4 Average train loss: -0.4654\n", - "====> Epoch: 4 Average val loss: -0.4745\n", - "====> Epoch: 5 Average train loss: -0.6690\n", - "====> Epoch: 5 Average val loss: -0.5786\n", - "Min loss -0.58\n", - "====> Epoch: 6 Average train loss: -0.5903\n", - "====> Epoch: 6 Average val loss: -0.5848\n", - "Min loss -0.58\n", - "====> Epoch: 7 Average train loss: -0.6430\n", - "====> Epoch: 7 Average val loss: -0.6894\n", - "Min loss -0.69\n", - "====> Epoch: 8 Average train loss: -0.7384\n", - "====> Epoch: 8 Average val loss: -0.6960\n", - "Min loss -0.70\n", - "====> Epoch: 9 Average train loss: -0.7716\n", - "====> Epoch: 9 Average val loss: -0.7567\n", - "Min loss -0.76\n", - "====> Epoch: 10 Average train loss: -0.8274\n", - "====> Epoch: 10 Average val loss: -0.7267\n", - "====> Epoch: 11 Average train loss: -0.8185\n", - "====> Epoch: 11 Average val loss: -0.7173\n", - "====> Epoch: 12 Average train loss: -0.8574\n", - "====> Epoch: 12 Average val loss: -0.7553\n", - "====> Epoch: 13 Average train loss: -0.8279\n", - "====> Epoch: 13 Average val loss: -0.7826\n", - "Min loss -0.78\n", - "====> Epoch: 14 Average train loss: -0.8461\n", - "====> Epoch: 14 Average val loss: -0.7677\n", - "====> Epoch: 15 Average train loss: -0.9792\n", - "====> Epoch: 15 Average val loss: -0.8070\n", - "Min loss -0.81\n", - "====> Epoch: 16 Average train loss: -0.8722\n", - "====> Epoch: 16 Average val loss: -0.8461\n", - "Min loss -0.85\n", - "====> Epoch: 17 Average train loss: -0.8155\n", - "====> Epoch: 17 Average val loss: -0.8062\n", - "====> Epoch: 18 Average train loss: -0.8851\n", - "====> Epoch: 18 Average val loss: -0.8348\n", - "====> Epoch: 19 Average train loss: -1.0884\n", - "====> Epoch: 19 Average val loss: -0.8903\n", - "Min loss -0.89\n", - "====> Epoch: 20 Average train loss: -0.9305\n", - "====> Epoch: 20 Average val loss: -0.9799\n", - "Min loss -0.98\n", - "====> Epoch: 21 Average train loss: -0.8848\n", - "====> Epoch: 21 Average val loss: -0.9489\n", - "====> Epoch: 22 Average train loss: -0.9700\n", - "====> Epoch: 22 Average val loss: -0.9913\n", - "Min loss -0.99\n", - "====> Epoch: 23 Average train loss: -1.0768\n", - "====> Epoch: 23 Average val loss: -0.9465\n", - "====> Epoch: 24 Average train loss: -0.9855\n", - "====> Epoch: 24 Average val loss: -0.9430\n", - "====> Epoch: 25 Average train loss: -0.9074\n", - "====> Epoch: 25 Average val loss: -0.9642\n", - "====> Epoch: 26 Average train loss: -1.0501\n", - "====> Epoch: 26 Average val loss: -1.0461\n", - "Min loss -1.05\n", - "====> Epoch: 27 Average train loss: -1.0541\n", - "====> Epoch: 27 Average val loss: -1.0062\n", - "====> Epoch: 28 Average train loss: -1.0942\n", - "====> Epoch: 28 Average val loss: -1.0910\n", - "Min loss -1.09\n", - "====> Epoch: 29 Average train loss: -1.0717\n", - "====> Epoch: 29 Average val loss: -1.0549\n", - "====> Epoch: 30 Average train loss: -1.0560\n", - "====> Epoch: 30 Average val loss: -1.0381\n", - "====> Epoch: 31 Average train loss: -1.0791\n", - "====> Epoch: 31 Average val loss: -1.0678\n", - "====> Epoch: 32 Average train loss: -0.7371\n", - "====> Epoch: 32 Average val loss: -0.6544\n", - "====> Epoch: 33 Average train loss: -0.7659\n", - "====> Epoch: 33 Average val loss: -0.6864\n", - "====> Epoch: 34 Average train loss: -0.7129\n", - "====> Epoch: 34 Average val loss: -0.6451\n", - "====> Epoch: 35 Average train loss: -0.7985\n", - "====> Epoch: 35 Average val loss: -0.8138\n", - "====> Epoch: 36 Average train loss: -0.7538\n", - "====> Epoch: 36 Average val loss: -0.6864\n", - "====> Epoch: 37 Average train loss: -0.7906\n", - "====> Epoch: 37 Average val loss: -0.7654\n", - "====> Epoch: 38 Average train loss: -0.8417\n", - "====> Epoch: 38 Average val loss: -0.8218\n", - "====> Epoch: 39 Average train loss: -0.8689\n", - "====> Epoch: 39 Average val loss: -0.8711\n", - "====> Epoch: 40 Average train loss: -0.8193\n", - "====> Epoch: 40 Average val loss: -0.7741\n", - "====> Epoch: 41 Average train loss: -0.9110\n", - "====> Epoch: 41 Average val loss: -0.8667\n", - "====> Epoch: 42 Average train loss: -0.9737\n", - "====> Epoch: 42 Average val loss: -0.9454\n", - "====> Epoch: 43 Average train loss: -0.9981\n", - "====> Epoch: 43 Average val loss: -0.9658\n", - "====> Epoch: 44 Average train loss: -1.1852\n", - "====> Epoch: 44 Average val loss: -1.1355\n", - "Min loss -1.14\n", - "====> Epoch: 45 Average train loss: -0.9776\n", - "====> Epoch: 45 Average val loss: -0.7820\n", - "====> Epoch: 46 Average train loss: -0.7977\n", - "====> Epoch: 46 Average val loss: -0.7567\n", - "====> Epoch: 47 Average train loss: -1.1455\n", - "====> Epoch: 47 Average val loss: -0.9938\n", - "====> Epoch: 48 Average train loss: -1.0858\n", - "====> Epoch: 48 Average val loss: -1.0856\n", - "====> Epoch: 49 Average train loss: -1.0088\n", - "====> Epoch: 49 Average val loss: -1.0482\n", - "====> Epoch: 50 Average train loss: -1.0798\n", - "====> Epoch: 50 Average val loss: -1.1690\n", - "Min loss -1.17\n", - "DMCCA\n", - "total parameters: 201476\n", - "====> Epoch: 1 Average train loss: -0.7272\n", - "====> Epoch: 1 Average val loss: -0.5648\n", - "Min loss -0.56\n", - "====> Epoch: 2 Average train loss: -0.5396\n", - "====> Epoch: 2 Average val loss: -0.5257\n", - "====> Epoch: 3 Average train loss: -0.5592\n", - "====> Epoch: 3 Average val loss: -0.5761\n", - "Min loss -0.58\n", - "====> Epoch: 4 Average train loss: -0.8326\n", - "====> Epoch: 4 Average val loss: -0.6956\n", - "Min loss -0.70\n", - "====> Epoch: 5 Average train loss: -0.7508\n", - "====> Epoch: 5 Average val loss: -0.7319\n", - "Min loss -0.73\n", - "====> Epoch: 6 Average train loss: -0.7918\n", - "====> Epoch: 6 Average val loss: -0.7501\n", - "Min loss -0.75\n", - "====> Epoch: 7 Average train loss: -0.7197\n", - "====> Epoch: 7 Average val loss: -0.7025\n", - "====> Epoch: 8 Average train loss: -0.8067\n", - "====> Epoch: 8 Average val loss: -0.7396\n", - "====> Epoch: 9 Average train loss: -0.7109\n", - "====> Epoch: 9 Average val loss: -0.7343\n", - "====> Epoch: 10 Average train loss: -0.7181\n", - "====> Epoch: 10 Average val loss: -0.6935\n", - "====> Epoch: 11 Average train loss: -0.7715\n", - "====> Epoch: 11 Average val loss: -0.7396\n", - "====> Epoch: 12 Average train loss: -0.8081\n", - "====> Epoch: 12 Average val loss: -0.7593\n", - "Min loss -0.76\n", - "====> Epoch: 13 Average train loss: -0.8056\n", - "====> Epoch: 13 Average val loss: -0.7780\n", - "Min loss -0.78\n", - "====> Epoch: 14 Average train loss: -0.7139\n", - "====> Epoch: 14 Average val loss: -0.7234\n", - "====> Epoch: 15 Average train loss: -0.8010\n", - "====> Epoch: 15 Average val loss: -0.7624\n", - "====> Epoch: 16 Average train loss: -0.8537\n", - "====> Epoch: 16 Average val loss: -0.8110\n", - "Min loss -0.81\n", - "====> Epoch: 17 Average train loss: -0.8014\n", - "====> Epoch: 17 Average val loss: -0.7729\n", - "====> Epoch: 18 Average train loss: -0.9397\n", - "====> Epoch: 18 Average val loss: -0.7617\n", - "====> Epoch: 19 Average train loss: -0.9068\n", - "====> Epoch: 19 Average val loss: -0.8688\n", - "Min loss -0.87\n", - "====> Epoch: 20 Average train loss: -0.9523\n", - "====> Epoch: 20 Average val loss: -0.9657\n", - "Min loss -0.97\n", - "====> Epoch: 21 Average train loss: -0.9830\n", - "====> Epoch: 21 Average val loss: -0.9828\n", - "Min loss -0.98\n", - "====> Epoch: 22 Average train loss: -0.8908\n", - "====> Epoch: 22 Average val loss: -1.0201\n", - "Min loss -1.02\n", - "====> Epoch: 23 Average train loss: -1.0304\n", - "====> Epoch: 23 Average val loss: -1.0090\n", - "====> Epoch: 24 Average train loss: -1.0930\n", - "====> Epoch: 24 Average val loss: -1.0294\n", - "Min loss -1.03\n", - "====> Epoch: 25 Average train loss: -1.1140\n", - "====> Epoch: 25 Average val loss: -1.0150\n", - "====> Epoch: 26 Average train loss: -0.8270\n", - "====> Epoch: 26 Average val loss: -0.8099\n", - "====> Epoch: 27 Average train loss: -0.8180\n", - "====> Epoch: 27 Average val loss: -0.7855\n", - "====> Epoch: 28 Average train loss: -0.8484\n", - "====> Epoch: 28 Average val loss: -0.8272\n", - "====> Epoch: 29 Average train loss: -0.9530\n", - "====> Epoch: 29 Average val loss: -0.9293\n", - "====> Epoch: 30 Average train loss: -1.0448\n", - "====> Epoch: 30 Average val loss: -1.0305\n", - "Min loss -1.03\n", - "====> Epoch: 31 Average train loss: -1.1297\n", - "====> Epoch: 31 Average val loss: -1.1626\n", - "Min loss -1.16\n", - "====> Epoch: 32 Average train loss: -1.2066\n", - "====> Epoch: 32 Average val loss: -1.2592\n", - "Min loss -1.26\n", - "====> Epoch: 33 Average train loss: -1.2493\n", - "====> Epoch: 33 Average val loss: -1.2980\n", - "Min loss -1.30\n", - "====> Epoch: 34 Average train loss: -1.1872\n", - "====> Epoch: 34 Average val loss: -1.2153\n", - "====> Epoch: 35 Average train loss: -1.2708\n", - "====> Epoch: 35 Average val loss: -1.2851\n", - "====> Epoch: 36 Average train loss: -1.2831\n", - "====> Epoch: 36 Average val loss: -1.3056\n", - "Min loss -1.31\n", - "====> Epoch: 37 Average train loss: -1.2999\n", - "====> Epoch: 37 Average val loss: -1.3194\n", - "Min loss -1.32\n", - "====> Epoch: 38 Average train loss: -1.3256\n", - "====> Epoch: 38 Average val loss: -1.3528\n", - "Min loss -1.35\n", - "====> Epoch: 39 Average train loss: -1.3218\n", - "====> Epoch: 39 Average val loss: -1.2652\n", - "====> Epoch: 40 Average train loss: -1.3106\n", - "====> Epoch: 40 Average val loss: -1.2634\n", - "====> Epoch: 41 Average train loss: -0.8961\n", - "====> Epoch: 41 Average val loss: -0.7964\n", - "====> Epoch: 42 Average train loss: -1.3604\n", - "====> Epoch: 42 Average val loss: -1.3359\n", - "====> Epoch: 43 Average train loss: -1.2987\n", - "====> Epoch: 43 Average val loss: -1.2966\n", - "====> Epoch: 44 Average train loss: -1.4320\n", - "====> Epoch: 44 Average val loss: -1.4049\n", - "Min loss -1.40\n", - "====> Epoch: 45 Average train loss: -1.4297\n", - "====> Epoch: 45 Average val loss: -1.3719\n", - "====> Epoch: 46 Average train loss: -1.3900\n", - "====> Epoch: 46 Average val loss: -1.4160\n", - "Min loss -1.42\n", - "====> Epoch: 47 Average train loss: -1.4094\n", - "====> Epoch: 47 Average val loss: -1.4063\n", - "====> Epoch: 48 Average train loss: -1.4272\n", - "====> Epoch: 48 Average val loss: -1.4198\n", - "Min loss -1.42\n", - "====> Epoch: 49 Average train loss: -1.3373\n", - "====> Epoch: 49 Average val loss: -1.3366\n", - "====> Epoch: 50 Average train loss: -1.3213\n", - "====> Epoch: 50 Average val loss: -1.3334\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "i6Czf36DvLTy" - }, - "source": [ - "# Deep Canonically Correlated Autoencoders\n", - "We need to add decoders in order to model deep canonically correlated autoencoders and we also use the DCCAE class which inherits from DCCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "jphk92IhvLTz", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "4982a7e6-e80a-4bef-cd4d-2557d39a8a45" - }, - "source": [ - "# DCCAE\n", - "print('DCCAE')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "decoder_1 = architectures.Decoder(latent_dims=latent_dims, feature_size=784)\n", - "decoder_2 = architectures.Decoder(latent_dims=latent_dims, feature_size=784)\n", - "dccae_model = DCCAE(latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2])\n", - "\n", - "dccae_model = DeepWrapper(dccae_model)\n", - "\n", - "#can also pass a tuple of numpy arrays\n", - "dccae_model.fit((train_view_1, train_view_2), epochs=epochs)\n", - "\n", - "dccae_results = np.stack(\n", - " (dccae_model.score(train_dataset), dccae_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DCCAE\n", - "total parameters: 404516\n", - "====> Epoch: 1 Average train loss: -0.0019\n", - "====> Epoch: 2 Average train loss: -0.0677\n", - "====> Epoch: 3 Average train loss: -0.1219\n", - "====> Epoch: 4 Average train loss: -0.1684\n", - "====> Epoch: 5 Average train loss: -0.2075\n", - "====> Epoch: 6 Average train loss: -0.2417\n", - "====> Epoch: 7 Average train loss: -0.2722\n", - "====> Epoch: 8 Average train loss: -0.3001\n", - "====> Epoch: 9 Average train loss: -0.3262\n", - "====> Epoch: 10 Average train loss: -0.3502\n", - "====> Epoch: 11 Average train loss: -0.3730\n", - "====> Epoch: 12 Average train loss: -0.3943\n", - "====> Epoch: 13 Average train loss: -0.4143\n", - "====> Epoch: 14 Average train loss: -0.4332\n", - "====> Epoch: 15 Average train loss: -0.4514\n", - "====> Epoch: 16 Average train loss: -0.4691\n", - "====> Epoch: 17 Average train loss: -0.4865\n", - "====> Epoch: 18 Average train loss: -0.5030\n", - "====> Epoch: 19 Average train loss: -0.5188\n", - "====> Epoch: 20 Average train loss: -0.5341\n", - "====> Epoch: 21 Average train loss: -0.5490\n", - "====> Epoch: 22 Average train loss: -0.5638\n", - "====> Epoch: 23 Average train loss: -0.5781\n", - "====> Epoch: 24 Average train loss: -0.5919\n", - "====> Epoch: 25 Average train loss: -0.6053\n", - "====> Epoch: 26 Average train loss: -0.6185\n", - "====> Epoch: 27 Average train loss: -0.6317\n", - "====> Epoch: 28 Average train loss: -0.6447\n", - "====> Epoch: 29 Average train loss: -0.6575\n", - "====> Epoch: 30 Average train loss: -0.6700\n", - "====> Epoch: 31 Average train loss: -0.6821\n", - "====> Epoch: 32 Average train loss: -0.6939\n", - "====> Epoch: 33 Average train loss: -0.7054\n", - "====> Epoch: 34 Average train loss: -0.7167\n", - "====> Epoch: 35 Average train loss: -0.7276\n", - "====> Epoch: 36 Average train loss: -0.7384\n", - "====> Epoch: 37 Average train loss: -0.7490\n", - "====> Epoch: 38 Average train loss: -0.7595\n", - "====> Epoch: 39 Average train loss: -0.7698\n", - "====> Epoch: 40 Average train loss: -0.7800\n", - "====> Epoch: 41 Average train loss: -0.7901\n", - "====> Epoch: 42 Average train loss: -0.7999\n", - "====> Epoch: 43 Average train loss: -0.8096\n", - "====> Epoch: 44 Average train loss: -0.8191\n", - "====> Epoch: 45 Average train loss: -0.8285\n", - "====> Epoch: 46 Average train loss: -0.8378\n", - "====> Epoch: 47 Average train loss: -0.8471\n", - "====> Epoch: 48 Average train loss: -0.8563\n", - "====> Epoch: 49 Average train loss: -0.8654\n", - "====> Epoch: 50 Average train loss: -0.8743\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "WEK3sUSuvLTz" - }, - "source": [ - "# Deep Variational CCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "9lqcopiSvLTz", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "484a9a77-0b35-41b7-ddef-6aacbb8be6c0" - }, - "source": [ - "\"\"\"\n", - "### Deep Variational Learning\n", - "Finally we have Deep Variational CCA methods.\n", - "- Deep Variational CCA (DVCCA)\n", - "- Deep Variational CCA - private (DVVCA_p)\n", - "\n", - "These are both implemented by the DVCCA class with private=True/False and both_encoders=True/False. If both_encoders,\n", - "the encoder to the shared information Q(z_shared|x) is modelled for both x_1 and x_2 whereas if both_encoders is false\n", - "it is modelled for x_1 as in the paper\n", - "\"\"\"\n", - "\n", - "# %%\n", - "# DVCCA (technically bi-DVCCA)\n", - "print('DVCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "decoder_1 = architectures.Decoder(latent_dims=latent_dims, feature_size=784, norm_output=True)\n", - "decoder_2 = architectures.Decoder(latent_dims=latent_dims, feature_size=784, norm_output=True)\n", - "dvcca_model = DVCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2])\n", - "\n", - "dvcca_model = DeepWrapper(dvcca_model)\n", - "\n", - "dvcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dvcca_model_results = np.stack(\n", - " (dvcca_model.score(train_dataset), dvcca_model.score(test_dataset)))\n", - "\n", - "# DVCCA_private (technically bi-DVCCA_private)\n", - "print('DVCCA_private')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "private_encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "private_encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784, variational=True)\n", - "decoder_1 = architectures.Decoder(latent_dims=latent_dims * 2, feature_size=784, norm_output=True)\n", - "decoder_2 = architectures.Decoder(latent_dims=latent_dims * 2, feature_size=784, norm_output=True)\n", - "dvccap_model = DVCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2], decoders=[decoder_1, decoder_2],\n", - " private_encoders=[private_encoder_1, private_encoder_2])\n", - "\n", - "dvccap_model = DeepWrapper(dvccap_model)\n", - "\n", - "dvccap_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dvccap_model_results = np.stack(\n", - " (dvccap_model.score(train_dataset), dvccap_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DVCCA\n", - "total parameters: 405032\n", - "====> Epoch: 1 Average train loss: 1109.5615\n", - "====> Epoch: 1 Average val loss: 1106.4091\n", - "Min loss 1106.41\n", - "====> Epoch: 2 Average train loss: 1106.5356\n", - "====> Epoch: 2 Average val loss: 1103.5577\n", - "Min loss 1103.56\n", - "====> Epoch: 3 Average train loss: 1103.5905\n", - "====> Epoch: 3 Average val loss: 1100.6377\n", - "Min loss 1100.64\n", - "====> Epoch: 4 Average train loss: 1100.4827\n", - "====> Epoch: 4 Average val loss: 1097.5350\n", - "Min loss 1097.54\n", - "====> Epoch: 5 Average train loss: 1097.5166\n", - "====> Epoch: 5 Average val loss: 1094.5754\n", - "Min loss 1094.58\n", - "====> Epoch: 6 Average train loss: 1094.8094\n", - "====> Epoch: 6 Average val loss: 1091.9133\n", - "Min loss 1091.91\n", - "====> Epoch: 7 Average train loss: 1092.1809\n", - "====> Epoch: 7 Average val loss: 1089.2421\n", - "Min loss 1089.24\n", - "====> Epoch: 8 Average train loss: 1089.0867\n", - "====> Epoch: 8 Average val loss: 1086.4995\n", - "Min loss 1086.50\n", - "====> Epoch: 9 Average train loss: 1086.4094\n", - "====> Epoch: 9 Average val loss: 1083.7737\n", - "Min loss 1083.77\n", - "====> Epoch: 10 Average train loss: 1083.9321\n", - "====> Epoch: 10 Average val loss: 1081.5156\n", - "Min loss 1081.52\n", - "====> Epoch: 11 Average train loss: 1081.5007\n", - "====> Epoch: 11 Average val loss: 1078.8384\n", - "Min loss 1078.84\n", - "====> Epoch: 12 Average train loss: 1078.7391\n", - "====> Epoch: 12 Average val loss: 1076.0708\n", - "Min loss 1076.07\n", - "====> Epoch: 13 Average train loss: 1076.5303\n", - "====> Epoch: 13 Average val loss: 1073.5686\n", - "Min loss 1073.57\n", - "====> Epoch: 14 Average train loss: 1073.3767\n", - "====> Epoch: 14 Average val loss: 1071.1628\n", - "Min loss 1071.16\n", - "====> Epoch: 15 Average train loss: 1071.1952\n", - "====> Epoch: 15 Average val loss: 1068.8904\n", - "Min loss 1068.89\n", - "====> Epoch: 16 Average train loss: 1069.1177\n", - "====> Epoch: 16 Average val loss: 1066.5808\n", - "Min loss 1066.58\n", - "====> Epoch: 17 Average train loss: 1066.5830\n", - "====> Epoch: 17 Average val loss: 1064.4114\n", - "Min loss 1064.41\n", - "====> Epoch: 18 Average train loss: 1063.8755\n", - "====> Epoch: 18 Average val loss: 1061.8417\n", - "Min loss 1061.84\n", - "====> Epoch: 19 Average train loss: 1062.0535\n", - "====> Epoch: 19 Average val loss: 1059.6287\n", - "Min loss 1059.63\n", - "====> Epoch: 20 Average train loss: 1059.5776\n", - "====> Epoch: 20 Average val loss: 1057.6716\n", - "Min loss 1057.67\n", - "====> Epoch: 21 Average train loss: 1057.6267\n", - "====> Epoch: 21 Average val loss: 1055.5505\n", - "Min loss 1055.55\n", - "====> Epoch: 22 Average train loss: 1055.3225\n", - "====> Epoch: 22 Average val loss: 1053.0654\n", - "Min loss 1053.07\n", - "====> Epoch: 23 Average train loss: 1053.1990\n", - "====> Epoch: 23 Average val loss: 1051.1343\n", - "Min loss 1051.13\n", - "====> Epoch: 24 Average train loss: 1051.1526\n", - "====> Epoch: 24 Average val loss: 1049.0571\n", - "Min loss 1049.06\n", - "====> Epoch: 25 Average train loss: 1048.9647\n", - "====> Epoch: 25 Average val loss: 1046.9143\n", - "Min loss 1046.91\n", - "====> Epoch: 26 Average train loss: 1047.0123\n", - "====> Epoch: 26 Average val loss: 1044.8156\n", - "Min loss 1044.82\n", - "====> Epoch: 27 Average train loss: 1044.7405\n", - "====> Epoch: 27 Average val loss: 1043.0496\n", - "Min loss 1043.05\n", - "====> Epoch: 28 Average train loss: 1043.0582\n", - "====> Epoch: 28 Average val loss: 1041.3112\n", - "Min loss 1041.31\n", - "====> Epoch: 29 Average train loss: 1041.1470\n", - "====> Epoch: 29 Average val loss: 1039.2390\n", - "Min loss 1039.24\n", - "====> Epoch: 30 Average train loss: 1039.4268\n", - "====> Epoch: 30 Average val loss: 1037.7975\n", - "Min loss 1037.80\n", - "====> Epoch: 31 Average train loss: 1037.5386\n", - "====> Epoch: 31 Average val loss: 1035.5798\n", - "Min loss 1035.58\n", - "====> Epoch: 32 Average train loss: 1035.8229\n", - "====> Epoch: 32 Average val loss: 1033.9326\n", - "Min loss 1033.93\n", - "====> Epoch: 33 Average train loss: 1033.8650\n", - "====> Epoch: 33 Average val loss: 1031.8835\n", - "Min loss 1031.88\n", - "====> Epoch: 34 Average train loss: 1032.1606\n", - "====> Epoch: 34 Average val loss: 1030.4136\n", - "Min loss 1030.41\n", - "====> Epoch: 35 Average train loss: 1030.4910\n", - "====> Epoch: 35 Average val loss: 1028.5079\n", - "Min loss 1028.51\n", - "====> Epoch: 36 Average train loss: 1028.8259\n", - "====> Epoch: 36 Average val loss: 1026.8826\n", - "Min loss 1026.88\n", - "====> Epoch: 37 Average train loss: 1027.3547\n", - "====> Epoch: 37 Average val loss: 1025.3986\n", - "Min loss 1025.40\n", - "====> Epoch: 38 Average train loss: 1025.0544\n", - "====> Epoch: 38 Average val loss: 1023.9811\n", - "Min loss 1023.98\n", - "====> Epoch: 39 Average train loss: 1024.1343\n", - "====> Epoch: 39 Average val loss: 1021.9122\n", - "Min loss 1021.91\n", - "====> Epoch: 40 Average train loss: 1022.0138\n", - "====> Epoch: 40 Average val loss: 1019.9561\n", - "Min loss 1019.96\n", - "====> Epoch: 41 Average train loss: 1020.5950\n", - "====> Epoch: 41 Average val loss: 1019.5484\n", - "Min loss 1019.55\n", - "====> Epoch: 42 Average train loss: 1018.7665\n", - "====> Epoch: 42 Average val loss: 1017.3838\n", - "Min loss 1017.38\n", - "====> Epoch: 43 Average train loss: 1017.5677\n", - "====> Epoch: 43 Average val loss: 1016.0297\n", - "Min loss 1016.03\n", - "====> Epoch: 44 Average train loss: 1016.1881\n", - "====> Epoch: 44 Average val loss: 1014.4371\n", - "Min loss 1014.44\n", - "====> Epoch: 45 Average train loss: 1014.6393\n", - "====> Epoch: 45 Average val loss: 1013.1492\n", - "Min loss 1013.15\n", - "====> Epoch: 46 Average train loss: 1013.3342\n", - "====> Epoch: 46 Average val loss: 1012.0658\n", - "Min loss 1012.07\n", - "====> Epoch: 47 Average train loss: 1011.7484\n", - "====> Epoch: 47 Average val loss: 1010.7506\n", - "Min loss 1010.75\n", - "====> Epoch: 48 Average train loss: 1010.6251\n", - "====> Epoch: 48 Average val loss: 1008.8579\n", - "Min loss 1008.86\n", - "====> Epoch: 49 Average train loss: 1009.0502\n", - "====> Epoch: 49 Average val loss: 1007.8226\n", - "Min loss 1007.82\n", - "====> Epoch: 50 Average train loss: 1007.1901\n", - "====> Epoch: 50 Average val loss: 1005.9260\n", - "Min loss 1005.93\n", - "DVCCA_private\n", - "total parameters: 607536\n", - "====> Epoch: 1 Average train loss: 1111.1758\n", - "====> Epoch: 1 Average val loss: 1108.3516\n", - "Min loss 1108.35\n", - "====> Epoch: 2 Average train loss: 1108.4519\n", - "====> Epoch: 2 Average val loss: 1105.1642\n", - "Min loss 1105.16\n", - "====> Epoch: 3 Average train loss: 1105.5024\n", - "====> Epoch: 3 Average val loss: 1102.2720\n", - "Min loss 1102.27\n", - "====> Epoch: 4 Average train loss: 1102.6145\n", - "====> Epoch: 4 Average val loss: 1099.6088\n", - "Min loss 1099.61\n", - "====> Epoch: 5 Average train loss: 1099.9021\n", - "====> Epoch: 5 Average val loss: 1097.1158\n", - "Min loss 1097.12\n", - "====> Epoch: 6 Average train loss: 1097.0347\n", - "====> Epoch: 6 Average val loss: 1094.3152\n", - "Min loss 1094.32\n", - "====> Epoch: 7 Average train loss: 1094.4141\n", - "====> Epoch: 7 Average val loss: 1091.6289\n", - "Min loss 1091.63\n", - "====> Epoch: 8 Average train loss: 1091.6658\n", - "====> Epoch: 8 Average val loss: 1088.9984\n", - "Min loss 1089.00\n", - "====> Epoch: 9 Average train loss: 1089.0031\n", - "====> Epoch: 9 Average val loss: 1086.1648\n", - "Min loss 1086.16\n", - "====> Epoch: 10 Average train loss: 1086.3365\n", - "====> Epoch: 10 Average val loss: 1083.7288\n", - "Min loss 1083.73\n", - "====> Epoch: 11 Average train loss: 1083.8301\n", - "====> Epoch: 11 Average val loss: 1081.3557\n", - "Min loss 1081.36\n", - "====> Epoch: 12 Average train loss: 1081.5806\n", - "====> Epoch: 12 Average val loss: 1078.7136\n", - "Min loss 1078.71\n", - "====> Epoch: 13 Average train loss: 1078.9102\n", - "====> Epoch: 13 Average val loss: 1076.4788\n", - "Min loss 1076.48\n", - "====> Epoch: 14 Average train loss: 1076.6189\n", - "====> Epoch: 14 Average val loss: 1074.0825\n", - "Min loss 1074.08\n", - "====> Epoch: 15 Average train loss: 1074.3435\n", - "====> Epoch: 15 Average val loss: 1071.4995\n", - "Min loss 1071.50\n", - "====> Epoch: 16 Average train loss: 1072.0078\n", - "====> Epoch: 16 Average val loss: 1069.3938\n", - "Min loss 1069.39\n", - "====> Epoch: 17 Average train loss: 1069.2450\n", - "====> Epoch: 17 Average val loss: 1067.1929\n", - "Min loss 1067.19\n", - "====> Epoch: 18 Average train loss: 1067.4008\n", - "====> Epoch: 18 Average val loss: 1064.8755\n", - "Min loss 1064.88\n", - "====> Epoch: 19 Average train loss: 1065.0360\n", - "====> Epoch: 19 Average val loss: 1062.5830\n", - "Min loss 1062.58\n", - "====> Epoch: 20 Average train loss: 1062.9507\n", - "====> Epoch: 20 Average val loss: 1060.5204\n", - "Min loss 1060.52\n", - "====> Epoch: 21 Average train loss: 1060.8132\n", - "====> Epoch: 21 Average val loss: 1058.4624\n", - "Min loss 1058.46\n", - "====> Epoch: 22 Average train loss: 1058.6918\n", - "====> Epoch: 22 Average val loss: 1056.5654\n", - "Min loss 1056.57\n", - "====> Epoch: 23 Average train loss: 1056.6611\n", - "====> Epoch: 23 Average val loss: 1054.4075\n", - "Min loss 1054.41\n", - "====> Epoch: 24 Average train loss: 1054.8269\n", - "====> Epoch: 24 Average val loss: 1052.1930\n", - "Min loss 1052.19\n", - "====> Epoch: 25 Average train loss: 1052.5613\n", - "====> Epoch: 25 Average val loss: 1050.3650\n", - "Min loss 1050.36\n", - "====> Epoch: 26 Average train loss: 1050.4044\n", - "====> Epoch: 26 Average val loss: 1048.1295\n", - "Min loss 1048.13\n", - "====> Epoch: 27 Average train loss: 1048.4557\n", - "====> Epoch: 27 Average val loss: 1046.2268\n", - "Min loss 1046.23\n", - "====> Epoch: 28 Average train loss: 1046.7646\n", - "====> Epoch: 28 Average val loss: 1044.4366\n", - "Min loss 1044.44\n", - "====> Epoch: 29 Average train loss: 1044.8062\n", - "====> Epoch: 29 Average val loss: 1042.5750\n", - "Min loss 1042.57\n", - "====> Epoch: 30 Average train loss: 1042.6490\n", - "====> Epoch: 30 Average val loss: 1040.4587\n", - "Min loss 1040.46\n", - "====> Epoch: 31 Average train loss: 1040.9629\n", - "====> Epoch: 31 Average val loss: 1038.8336\n", - "Min loss 1038.83\n", - "====> Epoch: 32 Average train loss: 1038.8879\n", - "====> Epoch: 32 Average val loss: 1036.8384\n", - "Min loss 1036.84\n", - "====> Epoch: 33 Average train loss: 1037.3240\n", - "====> Epoch: 33 Average val loss: 1035.5549\n", - "Min loss 1035.55\n", - "====> Epoch: 34 Average train loss: 1035.4932\n", - "====> Epoch: 34 Average val loss: 1033.6082\n", - "Min loss 1033.61\n", - "====> Epoch: 35 Average train loss: 1033.9220\n", - "====> Epoch: 35 Average val loss: 1031.9670\n", - "Min loss 1031.97\n", - "====> Epoch: 36 Average train loss: 1032.3682\n", - "====> Epoch: 36 Average val loss: 1030.0439\n", - "Min loss 1030.04\n", - "====> Epoch: 37 Average train loss: 1030.5251\n", - "====> Epoch: 37 Average val loss: 1028.3313\n", - "Min loss 1028.33\n", - "====> Epoch: 38 Average train loss: 1028.4504\n", - "====> Epoch: 38 Average val loss: 1026.9329\n", - "Min loss 1026.93\n", - "====> Epoch: 39 Average train loss: 1027.0562\n", - "====> Epoch: 39 Average val loss: 1025.2065\n", - "Min loss 1025.21\n", - "====> Epoch: 40 Average train loss: 1025.8102\n", - "====> Epoch: 40 Average val loss: 1023.5693\n", - "Min loss 1023.57\n", - "====> Epoch: 41 Average train loss: 1023.5449\n", - "====> Epoch: 41 Average val loss: 1022.0502\n", - "Min loss 1022.05\n", - "====> Epoch: 42 Average train loss: 1022.1390\n", - "====> Epoch: 42 Average val loss: 1020.6116\n", - "Min loss 1020.61\n", - "====> Epoch: 43 Average train loss: 1020.8058\n", - "====> Epoch: 43 Average val loss: 1019.0730\n", - "Min loss 1019.07\n", - "====> Epoch: 44 Average train loss: 1019.7094\n", - "====> Epoch: 44 Average val loss: 1018.3165\n", - "Min loss 1018.32\n", - "====> Epoch: 45 Average train loss: 1017.3981\n", - "====> Epoch: 45 Average val loss: 1016.1403\n", - "Min loss 1016.14\n", - "====> Epoch: 46 Average train loss: 1016.0779\n", - "====> Epoch: 46 Average val loss: 1014.8041\n", - "Min loss 1014.80\n", - "====> Epoch: 47 Average train loss: 1014.8489\n", - "====> Epoch: 47 Average val loss: 1013.4117\n", - "Min loss 1013.41\n", - "====> Epoch: 48 Average train loss: 1013.5171\n", - "====> Epoch: 48 Average val loss: 1011.6441\n", - "Min loss 1011.64\n", - "====> Epoch: 49 Average train loss: 1012.0751\n", - "====> Epoch: 49 Average val loss: 1010.4889\n", - "Min loss 1010.49\n", - "====> Epoch: 50 Average train loss: 1011.1703\n", - "====> Epoch: 50 Average val loss: 1009.1624\n", - "Min loss 1009.16\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "-li2xlrmvLT0" - }, - "source": [ - "# Convolutional Deep CCA (and using other architectures)\n", - "We provide a standard CNN encoder and decoder but users can build their own encoders and decoders by inheriting BaseEncoder and BaseDecoder for seamless integration with the pipeline" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "AzfBwb3NvLT0", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "93bed1b4-1d78-40b8-c505-6caad6375276" - }, - "source": [ - "print('Convolutional DCCA')\n", - "encoder_1 = architectures.CNNEncoder(latent_dims=latent_dims, channels=[3, 3])\n", - "encoder_2 = architectures.CNNEncoder(latent_dims=latent_dims, channels=[3, 3])\n", - "dcca_conv_model = DCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2])\n", - "\n", - "dcca_conv_model = DeepWrapper(dcca_conv_model)\n", - "\n", - "# to change the models used change the cfg.encoder_models. We implement a CNN_Encoder and CNN_decoder as well\n", - "# as some based on brainnet architecture in cca_zoo.architectures. Equally you could pass your own encoder/decoder models\n", - "\n", - "dcca_conv_model.fit((train_view_1.reshape((-1, 1, 28, 28)), train_view_2.reshape((-1, 1, 28, 28))), epochs=10)\n", - "\n", - "dcca_conv_results = np.stack((\n", - " dcca_conv_model.score((test_view_1.reshape((-1, 1, 28, 28)),test_view_2.reshape((-1, 1, 28, 28)))), \n", - " dcca_conv_model.score((test_view_1.reshape((-1, 1, 28, 28)),test_view_2.reshape((-1, 1, 28, 28))))))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Convolutional DCCA\n", - "total parameters: 9568\n", - "====> Epoch: 1 Average train loss: -0.6154\n", - "====> Epoch: 2 Average train loss: -0.7844\n", - "====> Epoch: 3 Average train loss: -0.9141\n", - "====> Epoch: 4 Average train loss: -1.0193\n", - "====> Epoch: 5 Average train loss: -1.1371\n", - "====> Epoch: 6 Average train loss: -1.2470\n", - "====> Epoch: 7 Average train loss: -1.3430\n", - "====> Epoch: 8 Average train loss: -1.4286\n", - "====> Epoch: 9 Average train loss: -1.4995\n", - "====> Epoch: 10 Average train loss: -1.5591\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "id": "78IxzigYvLT0" - }, - "source": [ - "# DTCCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "id": "MHvAzaiGvLT1", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "2fd16f5f-0579-44c6-acc0-1f66a450ba40" - }, - "source": [ - "# %%\n", - "# DTCCA\n", - "print('DTCCA')\n", - "encoder_1 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "encoder_2 = architectures.Encoder(latent_dims=latent_dims, feature_size=784)\n", - "dtcca_model = DTCCA(latent_dims=latent_dims, encoders=[encoder_1, encoder_2])\n", - "\n", - "dtcca_model = DeepWrapper(dtcca_model)\n", - "\n", - "dtcca_model.fit(train_dataset, val_dataset=val_dataset, epochs=epochs)\n", - "\n", - "dtcca_results = np.stack((dtcca_model.score(train_dataset), dtcca_model.score(test_dataset)))" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "DTCCA\n", - "total parameters: 201476\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/lib/python3.7/dist-packages/tensorly/backend/core.py:885: UserWarning: In partial_svd: converting to NumPy. Check SVD_FUNS for available alternatives if you want to avoid this.\n", - " warnings.warn('In partial_svd: converting to NumPy.'\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "====> Epoch: 1 Average train loss: 0.0000\n", - "====> Epoch: 1 Average val loss: 0.0000\n", - "Min loss 0.00\n", - "====> Epoch: 2 Average train loss: 0.0000\n", - "====> Epoch: 2 Average val loss: 0.0000\n", - "Min loss 0.00\n", - "====> Epoch: 3 Average train loss: 0.0000\n", - "====> Epoch: 3 Average val loss: 0.0000\n", - "====> Epoch: 4 Average train loss: 0.0000\n", - "====> Epoch: 4 Average val loss: 0.0000\n", - "====> Epoch: 5 Average train loss: 0.0000\n", - "====> Epoch: 5 Average val loss: 0.0000\n", - "====> Epoch: 6 Average train loss: 0.0000\n", - "====> Epoch: 6 Average val loss: 0.0000\n", - "====> Epoch: 7 Average train loss: 0.0000\n", - "====> Epoch: 7 Average val loss: 0.0000\n", - "====> Epoch: 8 Average train loss: 0.0000\n", - "====> Epoch: 8 Average val loss: 0.0000\n", - "====> Epoch: 9 Average train loss: 0.0000\n", - "====> Epoch: 9 Average val loss: 0.0000\n", - "====> Epoch: 10 Average train loss: 0.0000\n", - "====> Epoch: 10 Average val loss: 0.0000\n", - "====> Epoch: 11 Average train loss: 0.0000\n", - "====> Epoch: 11 Average val loss: 0.0000\n", - "====> Epoch: 12 Average train loss: 0.0000\n", - "====> Epoch: 12 Average val loss: 0.0000\n", - "====> Epoch: 13 Average train loss: 0.0000\n", - "====> Epoch: 13 Average val loss: 0.0000\n", - "====> Epoch: 14 Average train loss: 0.0000\n", - "====> Epoch: 14 Average val loss: 0.0000\n", - "====> Epoch: 15 Average train loss: 0.0000\n", - "====> Epoch: 15 Average val loss: 0.0000\n", - "====> Epoch: 16 Average train loss: 0.0000\n", - "====> Epoch: 16 Average val loss: 0.0000\n", - "====> Epoch: 17 Average train loss: 0.0000\n", - "====> Epoch: 17 Average val loss: 0.0000\n", - "====> Epoch: 18 Average train loss: 0.0000\n", - "====> Epoch: 18 Average val loss: 0.0000\n", - "====> Epoch: 19 Average train loss: 0.0000\n", - "====> Epoch: 19 Average val loss: 0.0000\n", - "====> Epoch: 20 Average train loss: 0.0000\n", - "====> Epoch: 20 Average val loss: 0.0000\n", - "====> Epoch: 21 Average train loss: 0.0000\n", - "====> Epoch: 21 Average val loss: 0.0000\n", - "====> Epoch: 22 Average train loss: 0.0000\n", - "====> Epoch: 22 Average val loss: 0.0000\n", - "====> Epoch: 23 Average train loss: 0.0000\n", - "====> Epoch: 23 Average val loss: 0.0000\n", - "====> Epoch: 24 Average train loss: 0.0000\n", - "====> Epoch: 24 Average val loss: 0.0000\n", - "====> Epoch: 25 Average train loss: 0.0000\n", - "====> Epoch: 25 Average val loss: 0.0000\n", - "====> Epoch: 26 Average train loss: 0.0000\n", - "====> Epoch: 26 Average val loss: 0.0000\n", - "====> Epoch: 27 Average train loss: 0.0000\n", - "====> Epoch: 27 Average val loss: 0.0000\n", - "====> Epoch: 28 Average train loss: 0.0000\n", - "====> Epoch: 28 Average val loss: 0.0000\n", - "====> Epoch: 29 Average train loss: 0.0000\n", - "====> Epoch: 29 Average val loss: 0.0000\n", - "====> Epoch: 30 Average train loss: 0.0000\n", - "====> Epoch: 30 Average val loss: 0.0000\n", - "====> Epoch: 31 Average train loss: 0.0000\n", - "====> Epoch: 31 Average val loss: 0.0000\n", - "====> Epoch: 32 Average train loss: 0.0000\n", - "====> Epoch: 32 Average val loss: 0.0000\n", - "====> Epoch: 33 Average train loss: 0.0000\n", - "====> Epoch: 33 Average val loss: 0.0000\n", - "====> Epoch: 34 Average train loss: 0.0000\n", - "====> Epoch: 34 Average val loss: 0.0000\n", - "====> Epoch: 35 Average train loss: 0.0000\n", - "====> Epoch: 35 Average val loss: 0.0000\n", - "====> Epoch: 36 Average train loss: 0.0000\n", - "====> Epoch: 36 Average val loss: 0.0000\n", - "====> Epoch: 37 Average train loss: 0.0000\n", - "====> Epoch: 37 Average val loss: 0.0000\n", - "====> Epoch: 38 Average train loss: 0.0000\n", - "====> Epoch: 38 Average val loss: 0.0000\n", - "====> Epoch: 39 Average train loss: 0.0000\n", - "====> Epoch: 39 Average val loss: 0.0000\n", - "====> Epoch: 40 Average train loss: 0.0000\n", - "====> Epoch: 40 Average val loss: 0.0000\n", - "====> Epoch: 41 Average train loss: 0.0000\n", - "====> Epoch: 41 Average val loss: 0.0000\n", - "====> Epoch: 42 Average train loss: 0.0000\n", - "====> Epoch: 42 Average val loss: 0.0000\n", - "====> Epoch: 43 Average train loss: 0.0000\n", - "====> Epoch: 43 Average val loss: 0.0000\n", - "====> Epoch: 44 Average train loss: 0.0000\n", - "====> Epoch: 44 Average val loss: 0.0000\n", - "====> Epoch: 45 Average train loss: 0.0000\n", - "====> Epoch: 45 Average val loss: 0.0000\n", - "====> Epoch: 46 Average train loss: 0.0000\n", - "====> Epoch: 46 Average val loss: 0.0000\n", - "====> Epoch: 47 Average train loss: 0.0000\n", - "====> Epoch: 47 Average val loss: 0.0000\n", - "====> Epoch: 48 Average train loss: 0.0000\n", - "====> Epoch: 48 Average val loss: 0.0000\n", - "====> Epoch: 49 Average train loss: 0.0000\n", - "====> Epoch: 49 Average val loss: 0.0000\n", - "====> Epoch: 50 Average train loss: 0.0000\n", - "====> Epoch: 50 Average val loss: 0.0000\n", - "reconstruction error=1.7372686071707115e-08\n", - "iteration 1, reconstruction error: 1.7372686071707115e-08, decrease = 0.0, unnormalized = 2.634178031930877e-09\n", - "PARAFAC converged after 1 iterations\n" - ] - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "4PLCBruxlAAu" - }, - "source": [ - "" - ], - "execution_count": null, - "outputs": [] - } - ] -} \ No newline at end of file diff --git a/tutorial_notebooks/cca_zoo_weights_and_sparsity.ipynb b/tutorial_notebooks/cca_zoo_weights_and_sparsity.ipynb deleted file mode 100644 index 5dd11ec7..00000000 --- a/tutorial_notebooks/cca_zoo_weights_and_sparsity.ipynb +++ /dev/null @@ -1,1096 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "cca_zoo_sparsity.ipynb", - "provenance": [], - "toc_visible": true, - "include_colab_link": true - }, - "kernelspec": { - "name": "python3", - "language": "python", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rpca4lWRvlwC" - }, - "source": [ - "# A tutorial on using cca-zoo to generate multiview models with sparsity on weights" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "riuTJcsuvRcS", - "outputId": "f7b0b940-9513-4b10-e842-1ba20178c223" - }, - "source": [ - "!pip install cca-zoo --upgrade" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Collecting cca-zoo\n", - " Downloading cca_zoo-1.9.0-py3-none-any.whl (68 kB)\n", - "\u001b[?25l\r\u001b[K |████▊ | 10 kB 23.0 MB/s eta 0:00:01\r\u001b[K |█████████▌ | 20 kB 10.1 MB/s eta 0:00:01\r\u001b[K |██████████████▎ | 30 kB 8.3 MB/s eta 0:00:01\r\u001b[K |███████████████████ | 40 kB 7.7 MB/s eta 0:00:01\r\u001b[K |███████████████████████▊ | 51 kB 4.2 MB/s eta 0:00:01\r\u001b[K |████████████████████████████▌ | 61 kB 4.4 MB/s eta 0:00:01\r\u001b[K |████████████████████████████████| 68 kB 2.8 MB/s \n", - "\u001b[?25hRequirement already satisfied: seaborn in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (0.11.2)\n", - "Collecting scipy>=1.7\n", - " Downloading scipy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (28.5 MB)\n", - "\u001b[K |████████████████████████████████| 28.5 MB 49 kB/s \n", - "\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.19.5)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.1.5)\n", - "Collecting mvlearn\n", - " Downloading mvlearn-0.4.1-py3-none-any.whl (2.1 MB)\n", - "\u001b[K |████████████████████████████████| 2.1 MB 45.2 MB/s \n", - "\u001b[?25hCollecting tensorly\n", - " Downloading tensorly-0.6.0-py3-none-any.whl (160 kB)\n", - "\u001b[K |████████████████████████████████| 160 kB 53.3 MB/s \n", - "\u001b[?25hRequirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (1.0.1)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from cca-zoo) (3.2.2)\n", - "Collecting scikit-learn>=0.23\n", - " Downloading scikit_learn-1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (23.1 MB)\n", - "\u001b[K |████████████████████████████████| 23.1 MB 79.1 MB/s \n", - "\u001b[?25hCollecting threadpoolctl>=2.0.0\n", - " Downloading threadpoolctl-3.0.0-py3-none-any.whl (14 kB)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (2.4.7)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (0.10.0)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (2.8.2)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->cca-zoo) (1.3.2)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from cycler>=0.10->matplotlib->cca-zoo) (1.15.0)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->cca-zoo) (2018.9)\n", - "Collecting nose\n", - " Downloading nose-1.3.7-py3-none-any.whl (154 kB)\n", - "\u001b[K |████████████████████████████████| 154 kB 47.1 MB/s \n", - "\u001b[?25hInstalling collected packages: threadpoolctl, scipy, scikit-learn, nose, tensorly, mvlearn, cca-zoo\n", - " Attempting uninstall: scipy\n", - " Found existing installation: scipy 1.4.1\n", - " Uninstalling scipy-1.4.1:\n", - " Successfully uninstalled scipy-1.4.1\n", - " Attempting uninstall: scikit-learn\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "albumentations 0.1.12 requires imgaug<0.2.7,>=0.2.5, but you have imgaug 0.2.9 which is incompatible.\u001b[0m\n", - "Successfully installed cca-zoo-1.9.0 mvlearn-0.4.1 nose-1.3.7 scikit-learn-1.0 scipy-1.7.1 tensorly-0.6.0 threadpoolctl-3.0.0\n" - ] - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "LVmJ5X8RvV3_" - }, - "source": [ - "from cca_zoo.models import PMD, SCCA, ElasticCCA, CCA, PLS, SCCA_ADMM, SpanCCA\n", - "from cca_zoo.model_selection import GridSearchCV\n", - "from cca_zoo.data import generate_covariance_data\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import itertools\n", - "import pandas as pd" - ], - "execution_count": 20, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IkMwUGzkwbQY" - }, - "source": [ - "## Generate some data\n", - "set the true correlation and the sparsity of the true weights" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "zdYep44wvtKo" - }, - "source": [ - "np.random.seed(42)\n", - "n=200\n", - "p=100\n", - "q=100\n", - "view_1_sparsity=0.1\n", - "view_2_sparsity=0.1\n", - "true_latent_dims=1\n", - "\n", - "(X,Y),(tx, ty)=generate_covariance_data(n,view_features=[p,q],latent_dims=true_latent_dims,\n", - " view_sparsity=[view_1_sparsity,view_2_sparsity],correlation=[0.9])\n", - "#normalize weights for comparability\n", - "tx/=np.sqrt(n)\n", - "ty/=np.sqrt(n)" - ], - "execution_count": 3, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "ijitQkskw_jw" - }, - "source": [ - "def plot_true_weights_coloured(ax, weights, true_weights, title=''):\n", - " ind = np.arange(len(true_weights))\n", - " mask = np.squeeze(true_weights == 0)\n", - " ax.scatter(ind[~mask], weights[~mask], c='b')\n", - " ax.scatter(ind[mask], weights[mask], c='r')\n", - " ax.set_title(title)\n", - "\n", - "def plot_model_weights(wx,wy,tx,ty):\n", - " fig,axs=plt.subplots(2,2,sharex=True,sharey=True)\n", - " plot_true_weights_coloured(axs[0,0],tx,tx,title='true x weights')\n", - " plot_true_weights_coloured(axs[0,1],ty,ty,title='true y weights')\n", - " plot_true_weights_coloured(axs[1,0],wx,tx,title='model x weights')\n", - " plot_true_weights_coloured(axs[1,1],wy,ty,title='model y weights')\n", - " plt.tight_layout()\n", - " plt.show()" - ], - "execution_count": 4, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "x_-JR1lywpNO" - }, - "source": [ - "## First try with CCA" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 297 - }, - "id": "as1irviNwnCW", - "outputId": "9c3a3a77-77f0-41a1-f8ef-08a20c176413" - }, - "source": [ - "#fit a cca model\n", - "cca=CCA().fit([X,Y])\n", - "\n", - "plot_model_weights(cca.weights[0],cca.weights[1],tx,ty)" - ], - "execution_count": 5, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AMLK2z5C1bFf" - }, - "source": [ - "## PLS" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 297 - }, - "id": "VqnBFLwFw1Fi", - "outputId": "55d3dae2-9e3d-4bc4-a3e9-6c2ef72281d3" - }, - "source": [ - "#fit a pls model\n", - "pls=PLS().fit([X,Y])\n", - "\n", - "plot_model_weights(pls.weights[0],pls.weights[1],tx,ty)" - ], - "execution_count": 6, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "maZg5LdP1l3H" - }, - "source": [ - "## Penalized Matrix Decomposition (Sparse CCA by Witten)\n", - "Initially set c=2 for both views arbitrarily" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 297 - }, - "id": "2petCaj61ffh", - "outputId": "445a4f81-9a6f-42e1-8840-b56750f75ff1" - }, - "source": [ - "#fit a pmd model\n", - "pmd=PMD(c=[2,2]).fit([X,Y])\n", - "\n", - "plot_model_weights(pmd.weights[0],pmd.weights[1],tx,ty)" - ], - "execution_count": 7, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gTZEL2SBTijd" - }, - "source": [ - "## Tracking the objective\n", - "For these iterative algorithms, you can access the convergence over iterations" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 312 - }, - "id": "eMutm5DjTh_V", - "outputId": "351effcb-3498-40f2-c09f-cac8167b3c9f" - }, - "source": [ - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(pmd.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 13, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 13 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OusaWIn82Wb7" - }, - "source": [ - "### We can also tune the hyperparameter using GridSearchCV" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "O7VXP9h21vyB", - "outputId": "8343f511-301d-40fd-910f-4071e761893e" - }, - "source": [ - "#Set up a grid. We can't use c<1 or c>sqrt(#features)\n", - "c1 = [1, 3, 7, 9]\n", - "c2 = [1, 3, 7, 9]\n", - "param_grid = {'c': [c1,c2]}\n", - "\n", - "#GridSearchCV can use multiple cores (jobs) and takes folds (number of cv folds) as a parameter. It can also produce a plot.\n", - "pmd = GridSearchCV(PMD(),param_grid=param_grid,\n", - " cv=3,\n", - " verbose=True).fit([X,Y])" - ], - "execution_count": 17, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Fitting 3 folds for each of 16 candidates, totalling 48 fits\n" - ] - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DscFV-7P3dU2" - }, - "source": [ - "Also the model object now has a pandas dataframe containing the results from each fold" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 545 - }, - "id": "2GNSiTjC21fB", - "outputId": "cb6ad3e8-6e82-4c84-f231-35babd967776" - }, - "source": [ - "pd.DataFrame(pmd.cv_results_)" - ], - "execution_count": 21, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
mean_fit_timestd_fit_timemean_score_timestd_score_timeparam_cparamssplit0_test_scoresplit1_test_scoresplit2_test_scoremean_test_scorestd_test_scorerank_test_score
00.0342420.0067900.0016470.000124[1, 1]{'c': [1, 1]}0.052058-0.033554-0.071958-0.0178180.05183813
10.0360550.0054290.0015950.000006[1, 3]{'c': [1, 3]}0.1537890.3604820.0363180.1835300.1340002
20.0331630.0014880.0016050.000035[1, 7]{'c': [1, 7]}0.1608030.331418-0.0130430.1597260.1406284
30.0273500.0030100.0015670.000019[1, 9]{'c': [1, 9]}0.1640700.330148-0.0012630.1643180.1352983
40.0446100.0157160.0015720.000018[3, 1]{'c': [3, 1]}0.073034-0.208602-0.024809-0.0534590.11674915
50.1711060.0709840.0015840.000017[3, 3]{'c': [3, 3]}0.0461860.6000120.4144160.3535380.2301601
60.2430980.0428680.0016190.000019[3, 7]{'c': [3, 7]}0.014920-0.0918100.1638790.0289960.10485810
70.2246370.0199150.0016430.000097[3, 9]{'c': [3, 9]}-0.0605650.2759570.1132120.1095350.1374095
80.0404130.0089710.0017050.000155[7, 1]{'c': [7, 1]}0.024604-0.163847-0.104591-0.0812780.07868116
90.1898190.0656530.0015820.000013[7, 3]{'c': [7, 3]}0.0497190.135290-0.1421860.0142750.11601911
100.1835630.0326230.0017250.000167[7, 7]{'c': [7, 7]}0.0738140.0444160.0444390.0542230.0138537
110.1026140.0168890.0016250.000106[7, 9]{'c': [7, 9]}0.0681250.0665070.0448140.0598150.0106286
120.0323350.0098010.0015770.000022[9, 1]{'c': [9, 1]}0.052391-0.073953-0.130107-0.0505560.07631914
130.1469380.0570940.0017080.000134[9, 3]{'c': [9, 3]}0.0433830.149690-0.219036-0.0086540.15496412
140.1512950.0740130.0017060.000203[9, 7]{'c': [9, 7]}0.0754320.0194890.0524290.0491170.0229588
150.0374960.0067300.0015590.000010[9, 9]{'c': [9, 9]}0.0557170.0376960.0414030.0449390.0077719
\n", - "
" - ], - "text/plain": [ - " mean_fit_time std_fit_time ... std_test_score rank_test_score\n", - "0 0.034242 0.006790 ... 0.051838 13\n", - "1 0.036055 0.005429 ... 0.134000 2\n", - "2 0.033163 0.001488 ... 0.140628 4\n", - "3 0.027350 0.003010 ... 0.135298 3\n", - "4 0.044610 0.015716 ... 0.116749 15\n", - "5 0.171106 0.070984 ... 0.230160 1\n", - "6 0.243098 0.042868 ... 0.104858 10\n", - "7 0.224637 0.019915 ... 0.137409 5\n", - "8 0.040413 0.008971 ... 0.078681 16\n", - "9 0.189819 0.065653 ... 0.116019 11\n", - "10 0.183563 0.032623 ... 0.013853 7\n", - "11 0.102614 0.016889 ... 0.010628 6\n", - "12 0.032335 0.009801 ... 0.076319 14\n", - "13 0.146938 0.057094 ... 0.154964 12\n", - "14 0.151295 0.074013 ... 0.022958 8\n", - "15 0.037496 0.006730 ... 0.007771 9\n", - "\n", - "[16 rows x 12 columns]" - ] - }, - "metadata": {}, - "execution_count": 21 - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O7rWUBmb4apX" - }, - "source": [ - "## Sparse CCA by iterative lasso (Mai)" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 592 - }, - "id": "FimdDUDe3kML", - "outputId": "8af2f49f-2571-4f6f-e8ba-3b5becabadaf" - }, - "source": [ - "#fit a scca model\n", - "scca=SCCA(c=[1e-3,1e-3]).fit([X,Y])\n", - "\n", - "plot_model_weights(scca.weights[0],scca.weights[1],tx,ty)\n", - "\n", - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(scca.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 23, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 23 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OjrY4zGuQQix" - }, - "source": [ - "### Positivity Constraints\n", - "In this case it isn't helpful (the data were generated with positive and negative weights) but is a cool functionality!" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 592 - }, - "id": "gdBFdkvfQUb6", - "outputId": "92fff950-5567-4fbb-a83c-224ca4d6052f" - }, - "source": [ - "#fit a scca model with positivity constraints\n", - "scca_pos=SCCA(c=[1e-3,1e-3],positive=[True,True]).fit([X,Y])\n", - "\n", - "plot_model_weights(scca_pos.weights[0],scca_pos.weights[1],tx,ty)\n", - "\n", - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(scca_pos.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 24, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 24 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KPC7uhls4ycW" - }, - "source": [ - "## Sparse CCA by iterative elastic net (adapted from Waaijenborg)" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 592 - }, - "id": "wuZjjyN24j0P", - "outputId": "b8fba7ce-d303-402f-b78c-a316ec3f42f6" - }, - "source": [ - "#fit an elastic model\n", - "#for some reason this model needs REALLY big l2 regularisation. This is actually\n", - "#the same level of l1 regularisation as SCCA\n", - "elasticcca=ElasticCCA(c=[10000,10000],l1_ratio=[0.000001,0.000001]).fit([X,Y])\n", - "\n", - "plot_model_weights(elasticcca.weights[0],elasticcca.weights[1],tx,ty)\n", - "\n", - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(elasticcca.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 25, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 25 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "E8TEaBhe7CYw" - }, - "source": [ - "## Sparse CCA by ADMM" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 592 - }, - "id": "7RkSPcWR7FY8", - "outputId": "d9590105-6016-4fa3-bae0-655b3e3336a3" - }, - "source": [ - "#fit a scca_admm model\n", - "scca_admm=SCCA_ADMM(c=[1e-3,1e-3]).fit([X,Y])\n", - "\n", - "plot_model_weights(scca_admm.weights[0],scca_admm.weights[1],tx,ty)\n", - "\n", - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(scca_admm.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 27, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 27 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LcbbCAa_5q5C" - }, - "source": [ - "## Sparse CCA by random projection (Span CCA)\n", - "This time the regularisation parameter c is the l0 norm of the weights i.e. the maximum number of non-zero weights. Let's cheat and give it the correct numbers. We can also change the rank of the estimation as described in the paper" - ] - }, - { - "cell_type": "code", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 592 - }, - "id": "F_bT21qk5jA5", - "outputId": "bd683c19-1b7d-4c08-ee44-e398d3323cbb" - }, - "source": [ - "#fit a spancca model\n", - "spancca=SpanCCA(c=[10,10],max_iter=2000,rank=20).fit([X,Y])\n", - "\n", - "plot_model_weights(spancca.weights[0],spancca.weights[1],tx,ty)\n", - "\n", - "#Convergence\n", - "plt.figure()\n", - "plt.title('Objective Convergence')\n", - "plt.plot(np.array(spancca.track[0]['objective']).T)\n", - "plt.ylabel('Objective')\n", - "plt.xlabel('#iterations')" - ], - "execution_count": 28, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Text(0.5, 0, '#iterations')" - ] - }, - "metadata": {}, - "execution_count": 28 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "eTEVZnXpQzFm" - }, - "source": [ - "" - ], - "execution_count": null, - "outputs": [] - } - ] -} \ No newline at end of file