#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Definition of RSA Model class and subclasses
"""
import numpy as np
from rsatoolbox.rdm import RDMs
from rsatoolbox.rdm import rdms_from_dict
from rsatoolbox.util.rdm_utils import batch_to_vectors
from .fitter import fit_mock, fit_optimize, fit_select, fit_interpolate
[docs]class Model:
"""
Abstract model class.
Defines members that every class needs to have, but does not implement any
interesting behavior. Inherit from this class to define specific model
types
"""
def __init__(self, name):
self.name = name
self.n_param = 0
self.default_fitter = fit_mock
self.rdm_obj = None
[docs] def predict(self, theta=None):
""" Returns the predicted rdm vector
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
numpy.ndarray: rdm vector
"""
raise NotImplementedError(
"Predict function not implemented in used model class!")
[docs] def predict_rdm(self, theta=None):
""" Returns the predicted rdm as an object
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
numpy.ndarray: rdm object
"""
raise NotImplementedError(
"Predict rdm function not implemented in used model class!")
[docs] def fit(self, data, method='cosine', pattern_idx=None,
pattern_descriptor=None, sigma_k=None):
""" fit the model to a RDM object data
Args:
data(RDM object): the RDMs to be fit with the model
method(String): how to measure rdm_similarity
patterrn_idx: which patterns to use
pattern_descriptor: which part of the dict to use to interpret
pattern_idx
Returns:
theta(numpy.ndarray): parameter vector (one dimensional)
"""
return self.default_fitter(self, data, method=method,
pattern_idx=pattern_idx,
pattern_descriptor=pattern_descriptor,
sigma_k=sigma_k)
[docs] def to_dict(self):
""" Converts the model into a dictionary, which can be used for saving
Returns:
model_dict(dict): A dictionary containting all data needed to
recreate the object
"""
model_dict = {}
if self.rdm_obj:
model_dict['rdm'] = self.rdm_obj.to_dict()
else:
model_dict['rdm'] = None
model_dict['name'] = self.name
model_dict['type'] = type(self).__name__
return model_dict
[docs]class ModelFixed(Model):
def __init__(self, name, rdm):
"""
Fixed model
This is a parameter-free model that simply predicts a fixed RDM
It takes rdm object, a vector or a matrix as input to define the RDM
Args:
Name(String): Model name
rdm(rsatoolbox.rdm.RDMs): rdms in one object
"""
Model.__init__(self, name)
if isinstance(rdm, RDMs):
self.rdm_obj = rdm
self.rdm = np.mean(rdm.get_vectors(), axis=0)
self.n_cond = rdm.n_cond
elif rdm.ndim == 1: # User passed a vector
self.rdm_obj = RDMs(np.array([rdm]))
self.n_cond = (1 + np.sqrt(1 + 8 * rdm.size)) / 2
if self.n_cond % 1 != 0:
raise NameError(
"RDM vector needs to have size of ncond*(ncond-1)/2")
self.rdm = rdm
else: # User passed a matrix
self.rdm_obj = RDMs(np.array([rdm]))
self.rdm, _, self.n_cond = batch_to_vectors(np.array([rdm]))
self.rdm = self.rdm[0]
self.n_param = 0
self.default_fitter = fit_mock
self.rdm_obj.pattern_descriptors['index'] = np.arange(self.n_cond)
[docs] def predict(self, theta=None):
""" Returns the predicted rdm vector
For the fixed model there are no parameters. theta is ignored.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rdm vector
"""
return self.rdm
[docs] def predict_rdm(self, theta=None):
""" Returns the predicted rdm vector
For the fixed model there are no parameters.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rsatoolbox.rdm.RDMs: rdm object
"""
return self.rdm_obj
[docs]class ModelSelect(Model):
"""
Selection model
This model has a set of RDMs and selects one of them as its prediction.
theta should here be an integer index
"""
# Model Constructor
def __init__(self, name, rdm):
Model.__init__(self, name)
if isinstance(rdm, RDMs):
self.rdm_obj = rdm
self.rdm = rdm.get_vectors()
self.n_cond = rdm.n_cond
elif rdm.ndim == 2: # User supplied vectors
self.rdm_obj = RDMs(rdm)
self.n_cond = (1 + np.sqrt(1 + 8 * rdm.shape[1])) / 2
if self.n_cond % 1 != 0:
raise NameError(
"RDM vector needs to have size of ncond*(ncond-1)/2")
self.rdm = rdm
else: # User passed matrixes
self.rdm_obj = RDMs(rdm)
self.rdm, _, self.n_cond = batch_to_vectors(rdm)
self.n_param = 1
self.n_rdm = self.rdm_obj.n_rdm
self.default_fitter = fit_select
[docs] def predict(self, theta=0):
""" Returns the predicted rdm vector
For the fixed model there are no parameters. theta is ignored.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rdm vector
"""
return self.rdm[theta]
[docs] def predict_rdm(self, theta=0):
""" Returns the predicted rdm vector
For the fixed model there are no parameters.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rsatoolbox.rdm.RDMs: rdm object
"""
return self.rdm_obj[theta]
[docs]class ModelWeighted(Model):
"""
weighted Model
models the RDM as a weighted sum of a set of RDMs
"""
# Model Constructor
def __init__(self, name, rdm):
Model.__init__(self, name)
if isinstance(rdm, RDMs):
self.rdm_obj = rdm
self.rdm = rdm.get_vectors()
self.n_cond = rdm.n_cond
elif rdm.ndim == 2: # User supplied vectors
self.rdm_obj = RDMs(rdm)
self.n_cond = (1 + np.sqrt(1 + 8 * rdm.shape[1])) / 2
if self.n_cond % 1 != 0:
raise NameError(
"RDM vector needs to have size of ncond*(ncond-1)/2")
self.rdm = rdm
else: # User passed matrixes
self.rdm_obj = RDMs(rdm)
self.rdm, _, self.n_cond = batch_to_vectors(rdm)
self.n_param = self.rdm_obj.n_rdm
self.n_rdm = self.rdm_obj.n_rdm
self.default_fitter = fit_optimize
[docs] def predict(self, theta=None):
""" Returns the predicted rdm vector
theta are the weights for the different rdms
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rdm vector
"""
if theta is None:
theta = np.ones(self.n_rdm)
theta = np.array(theta)
return np.matmul(self.rdm.T, theta.reshape(-1))
[docs] def predict_rdm(self, theta=None):
""" Returns the predicted rdm vector
For the fixed model there are no parameters.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rsatoolbox.rdm.RDMs: rdm object
"""
if theta is None:
theta = np.ones(self.n_rdm)
theta = np.array(theta)
dissimilarities = np.matmul(self.rdm.T, theta.reshape(-1))
rdms = RDMs(
dissimilarities.reshape(1, -1),
dissimilarity_measure=self.rdm_obj.dissimilarity_measure,
descriptors=self.rdm_obj.descriptors,
pattern_descriptors=self.rdm_obj.pattern_descriptors)
return rdms
[docs]class ModelInterpolate(Model):
"""
inpterpolation Model
models the RDM as an interpolation between 2 neigboring rdms
"""
# Model Constructor
def __init__(self, name, rdm):
Model.__init__(self, name)
if isinstance(rdm, RDMs):
self.rdm_obj = rdm
self.rdm = rdm.get_vectors()
self.n_cond = rdm.n_cond
elif rdm.ndim == 2: # User supplied vectors
self.rdm_obj = RDMs(rdm)
self.n_cond = (1 + np.sqrt(1 + 8 * rdm.shape[1])) / 2
if self.n_cond % 1 != 0:
raise NameError(
"RDM vector needs to have size of ncond*(ncond-1)/2")
self.rdm = rdm
else: # User passed matrixes
self.rdm_obj = RDMs(rdm)
self.rdm, _, self.n_cond = batch_to_vectors(rdm)
self.n_param = self.rdm_obj.n_rdm
self.n_rdm = self.rdm_obj.n_rdm
self.default_fitter = fit_interpolate
[docs] def predict(self, theta=None):
""" Returns the predicted rdm vector
theta are the weights for the different rdms
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rdm vector
"""
if theta is None:
theta = np.zeros(self.n_rdm)
theta[0] = 0.5
theta[1] = 0.5
theta = np.array(theta)
return np.matmul(self.rdm.T, theta.reshape(-1))
[docs] def predict_rdm(self, theta=None):
""" Returns the predicted rdm vector
For the fixed model there are no parameters.
Args:
theta(numpy.ndarray): the model parameter vector (one dimensional)
Returns:
rsatoolbox.rdm.RDMs: rdm object
"""
if theta is None:
theta = np.ones(self.n_rdm)
theta = np.maximum(theta, 0)
theta = np.array(theta)
dissimilarities = np.matmul(self.rdm.T, theta.reshape(-1))
rdms = RDMs(
dissimilarities.reshape(1, -1),
dissimilarity_measure=self.rdm_obj.dissimilarity_measure,
descriptors=self.rdm_obj.descriptors,
pattern_descriptors=self.rdm_obj.pattern_descriptors)
return rdms
[docs]def model_from_dict(model_dict):
""" recreates a model object from a dictionary
Args:
model_dict(dict): The dictionary to be turned into a model
Returns
model(Model): The recreated model
"""
if model_dict['rdm']:
rdm_obj = rdms_from_dict(model_dict['rdm'])
if model_dict['type'] == 'Model':
model = Model(model_dict['name'])
elif model_dict['type'] == 'ModelFixed':
model = ModelFixed(model_dict['name'], rdm_obj)
elif model_dict['type'] == 'ModelSelect':
model = ModelSelect(model_dict['name'], rdm_obj)
elif model_dict['type'] == 'ModelWeighted':
model = ModelWeighted(model_dict['name'], rdm_obj)
elif model_dict['type'] == 'ModelInterpolate':
model = ModelInterpolate(model_dict['name'], rdm_obj)
return model