def test_resulting_r_same_if_rate_is_constant_then_switches(self): schedule = np.zeros(self.n_samples) schedule[: self.n_partial] = self.rate wta = BioWTARegressor(self.n_models, self.n_features, rate=schedule) r = wta.transform(self.predictors, self.dependent) np.testing.assert_allclose(r[: self.n_partial], self.r_partial)
def setUp(self): self.n_models = 3 self.n_features = 4 self.rng = np.random.default_rng(9) def normalize_v(v: np.ndarray) -> np.ndarray: return v / np.sum(v) self.start_prob = normalize_v(self.rng.uniform(size=self.n_models)) self.trans_mat = [ normalize_v(self.rng.uniform(size=self.n_models)) for _ in range(self.n_models) ] self.kwargs = { "n_models": self.n_models, "n_features": self.n_features, "start_prob": self.start_prob, "trans_mat": self.trans_mat, } self.wta = BioWTARegressor(**self.kwargs) self.n_samples = 79 self.predictors = self.rng.normal(size=(self.n_samples, self.n_features)) self.dependent = self.rng.normal(size=self.n_samples)
def test_constructor_copies_weight_schedule(self): schedule = self.rate * np.ones(self.n_samples) wta = BioWTARegressor(self.n_models, self.n_features, rate=schedule) schedule[:] = 0 wta.transform(self.predictors, self.dependent) np.testing.assert_allclose(wta.weights_, self.wta_full.weights_)
def test_switching_rate_to_zero_fixes_weights(self): schedule = np.zeros(self.n_samples) schedule[: self.n_partial] = self.rate wta = BioWTARegressor(self.n_models, self.n_features, rate=schedule) wta.transform(self.predictors, self.dependent) np.testing.assert_allclose(wta.weights_, self.wta_partial.weights_)
def setUp(self): self.n_models = 3 self.n_features = 4 self.wta = BioWTARegressor(self.n_models, self.n_features) self.rng = np.random.default_rng(10) self.n_samples = 79 self.predictors = self.rng.normal(size=(self.n_samples, self.n_features)) self.dependent = self.rng.normal(size=self.n_samples)
def test_output_of_repeated_calls_to_transform_equivalent_to_single_call(self): n1 = 4 * self.n_samples // 7 r1 = self.wta.transform(self.predictors[:n1], self.dependent[:n1]) r2 = self.wta.transform(self.predictors[n1:], self.dependent[n1:]) wta_again = BioWTARegressor(**self.kwargs) r = wta_again.transform(self.predictors, self.dependent) np.testing.assert_allclose(r, np.vstack((r1, r2)))
def test_float_trans_mat(self): r1 = self.wta.transform(self.predictors, self.dependent) wta2 = BioWTARegressor( self.n_models, self.n_features, trans_mat=1 / self.n_models ) r2 = wta2.transform(self.predictors, self.dependent) np.testing.assert_allclose(r1, r2)
def setUp(self): self.n_models = 4 self.n_features = 5 self.wta = BioWTARegressor(self.n_models, self.n_features) self.rng = np.random.default_rng(1) self.n_samples = 85 self.predictors = self.rng.uniform(size=(self.n_samples, self.n_features)) self.dependent = self.rng.uniform(size=self.n_samples)
class TestBioWTARegressorLatentPriorTemperatureOne(unittest.TestCase): def setUp(self): self.n_models = 3 self.n_features = 4 self.temperature = 1.0 self.wta = BioWTARegressor( self.n_models, self.n_features, temperature=self.temperature ) self.rng = np.random.default_rng(10) self.n_samples = 79 self.predictors = self.rng.normal(size=(self.n_samples, self.n_features)) self.dependent = self.rng.normal(size=self.n_samples) def test_initial_r_value_changes_by_correct_amount_according_to_start_prob(self): r1 = self.wta.transform(self.predictors[[0]], self.dependent[[0]]) # change initial state distribution start_prob = (lambda v: v / np.sum(v))(self.rng.uniform(size=self.n_models)) wta2 = BioWTARegressor( self.n_models, self.n_features, temperature=self.temperature, start_prob=start_prob, ) r2 = wta2.transform(self.predictors[[0]], self.dependent[[0]]) diff_log_r = np.log(r2[0]) - np.log(r1[0]) np.testing.assert_allclose( diff_log_r - diff_log_r[0], np.log(start_prob) - np.log(start_prob[0]), ) def test_second_r_value_changes_by_correct_amount_according_to_trans_mat(self): r1 = self.wta.transform(self.predictors[:2], self.dependent[:2]) # change transition matrix trans_mat = [ (lambda v: v / np.sum(v))(self.rng.uniform(size=self.n_models)) for _ in range(self.n_models) ] wta2 = BioWTARegressor( self.n_models, self.n_features, temperature=self.temperature, trans_mat=trans_mat, ) r2 = wta2.transform(self.predictors[:2], self.dependent[:2]) np.testing.assert_allclose(r1[0], r2[0]) diff_log_r = np.log(r2[1]) - np.log(r1[1]) expected_diff_log_r = r2[0] @ np.log(trans_mat) np.testing.assert_allclose( diff_log_r - diff_log_r[0], expected_diff_log_r - expected_diff_log_r[0] )
def test_default_latent_state_transition_matrix_is_uniform(self): wta2 = BioWTARegressor( self.n_models, self.n_features, trans_mat=np.ones((self.n_models, self.n_models)) / self.n_models, ) r1 = self.wta.transform(self.predictors, self.dependent) r2 = wta2.transform(self.predictors, self.dependent) np.testing.assert_allclose(r1, r2)
def test_default_initial_latent_state_distribution_is_uniform(self): wta2 = BioWTARegressor( self.n_models, self.n_features, start_prob=np.ones(self.n_models) / self.n_models, ) r1 = self.wta.transform(self.predictors, self.dependent) r2 = wta2.transform(self.predictors, self.dependent) np.testing.assert_allclose(r1, r2)
def test_weight_change_from_repeated_transform_calls_equivalent_to_one_call(self): n1 = 2 * self.n_samples // 7 n2 = 3 * self.n_samples // 7 self.wta.transform(self.predictors[:n1], self.dependent[:n1]) self.wta.transform(self.predictors[n1 : n1 + n2], self.dependent[n1 : n1 + n2]) self.wta.transform(self.predictors[n1 + n2 :], self.dependent[n1 + n2 :]) wta_again = BioWTARegressor(**self.kwargs) wta_again.transform(self.predictors, self.dependent) np.testing.assert_allclose(self.wta.weights_, wta_again.weights_)
def test_changing_latent_state_transition_matrix_changes_output(self): trans_mat = [ (lambda v: v / np.sum(v))(self.rng.uniform(size=self.n_models)) for _ in range(self.n_models) ] wta2 = BioWTARegressor(self.n_models, self.n_features, trans_mat=trans_mat) r1 = self.wta.transform(self.predictors, self.dependent) r2 = wta2.transform(self.predictors, self.dependent) self.assertGreater(np.max(np.abs(r1 - r2)), 1e-3)
def test_monitor_as_object(self): names = ["weights_"] monitor = AttributeMonitor(names) self.wta.transform(self.predictors, self.dependent, monitor=monitor) wta_alt = BioWTARegressor(self.n_models, self.n_features) _, history_alt = wta_alt.transform( self.predictors, self.dependent, monitor=names ) np.testing.assert_allclose(monitor.history_.weights_, history_alt.weights_)
def test_no_warning_when_some_start_prob_are_zero(self): start_prob = np.ones(self.n_models) / (self.n_models - 2) start_prob[[0, 2]] = 0 wta = BioWTARegressor(self.n_models, self.n_features, start_prob=start_prob) with warnings.catch_warnings(record=True) as warn_list: # ensure warnings haven't been disabled warnings.simplefilter("always") wta.transform(self.predictors, self.dependent) # ensure no warnings have been triggered self.assertEqual(len(warn_list), 0)
def test_changing_initial_latent_state_distribution_changes_output(self): r1 = self.wta.transform(self.predictors, self.dependent) k = np.argmax(r1[0]) start_prob = self.rng.uniform(size=self.n_models) start_prob[k] /= 50 start_prob = start_prob / np.sum(start_prob) wta2 = BioWTARegressor(self.n_models, self.n_features, start_prob=start_prob) r2 = wta2.transform(self.predictors, self.dependent) self.assertGreater(np.max(np.abs(r1 - r2)), 1e-3)
def test_recent_loss_correct_if_timescale_is_not_zero(self): tau = 3.5 wta = BioWTARegressor(**self.kwargs, error_timescale=tau) _, history = wta.transform( self.predictors, self.dependent, monitor=["error_", "recent_loss_"] ) loss_exp = np.zeros((self.n_samples, self.n_models)) crt_loss = np.zeros(self.n_models) for i in range(self.n_samples): crt_loss += (history.error_[i] ** 2 - crt_loss) / tau loss_exp[i] = crt_loss np.testing.assert_allclose(history.recent_loss_, loss_exp)
def test_no_warning_when_some_trans_mat_are_zero(self): # disallow the system from performing some transition trans_mat = np.ones((self.n_models, self.n_models)) / self.n_models trans_mat[0] = np.ones(self.n_models) / (self.n_models - 1) trans_mat[0, 1] = 0 wta = BioWTARegressor(self.n_models, self.n_features, trans_mat=trans_mat) with warnings.catch_warnings(record=True) as warn_list: # ensure warnings haven't been disabled warnings.simplefilter("always") wta.transform(self.predictors, self.dependent) # ensure no warnings have been triggered self.assertEqual(len(warn_list), 0)
def test_log_r_prop_temperature(self): r = self.wta.transform(self.predictors[[0]], self.dependent[[0]]) temperature_again = 3.2 wta_again = BioWTARegressor( self.n_models, self.n_features, temperature=temperature_again ) r_again = wta_again.transform(self.predictors[[0]], self.dependent[[0]]) d_logr = np.log(r[0]) - np.log(r[0, 0]) d_logr_again = np.log(r_again[0]) - np.log(r_again[0, 0]) np.testing.assert_allclose( d_logr_again, d_logr * self.temperature / temperature_again )
def test_when_start_prob_large_for_a_state_then_that_state_gets_high_r(self): r1 = self.wta.transform(self.predictors[[0]], self.dependent[[0]]) k1 = np.argmax(r1[0]) # make another state win out k = 0 if k1 != 0 else 1 p_large = 0.999 start_prob = (1 - p_large) * np.ones(self.n_models) / (self.n_models - 1) start_prob[k] = p_large wta2 = BioWTARegressor(self.n_models, self.n_features, start_prob=start_prob) r2 = wta2.transform(self.predictors[[0]], self.dependent[[0]]) k2 = np.argmax(r2[0]) self.assertNotEqual(k1, k2) self.assertEqual(k2, k)
def test_initial_r_value_changes_by_correct_amount_according_to_start_prob(self): r1 = self.wta.transform(self.predictors[[0]], self.dependent[[0]]) # change initial state distribution start_prob = (lambda v: v / np.sum(v))(self.rng.uniform(size=self.n_models)) wta2 = BioWTARegressor( self.n_models, self.n_features, temperature=self.temperature, start_prob=start_prob, ) r2 = wta2.transform(self.predictors[[0]], self.dependent[[0]]) diff_log_r = np.log(r2[0]) - np.log(r1[0]) np.testing.assert_allclose( diff_log_r - diff_log_r[0], np.log(start_prob) - np.log(start_prob[0]), )
def make_bio_wta_with_stable_initial(*args, **kwargs) -> BioWTARegressor: """ Call the BioWTARegressor constructor, ensuring that the initial coefficients are chosen to correspond to stable AR processes. """ weights = [ make_random_arma(kwargs["n_features"], 0, rng=kwargs["rng"]).a for _ in range(kwargs["n_models"]) ] return BioWTARegressor(*args, weights=weights, **kwargs)
def test_weight_updates_use_instantaneous_error(self): wta0 = BioWTARegressor(**self.kwargs, error_timescale=1.0) wta1 = BioWTARegressor(**self.kwargs, error_timescale=2.3) weights0 = np.copy(wta0.weights_) np.testing.assert_allclose(wta1.weights_, weights0) wta0.transform(self.predictors[:1], self.dependent[:1]) wta1.transform(self.predictors[:1], self.dependent[:1]) self.assertGreater(np.max(np.abs(wta0.weights_ - weights0)), 1e-5) np.testing.assert_allclose(wta0.weights_, wta1.weights_)
def test_history_same_when_chunk_hint_changes(self): names = ["prediction_"] _, history = self.wta.transform( self.predictors, self.dependent, monitor=names, chunk_hint=1000, return_history=True, ) wta_alt = BioWTARegressor(self.n_models, self.n_features) _, history_alt = wta_alt.transform( self.predictors, self.dependent, monitor=names, chunk_hint=1, return_history=True, ) np.testing.assert_allclose(history.prediction_, history_alt.prediction_)
def setUp(self): self.n_models = 2 self.n_features = 3 self.rng = np.random.default_rng(0) self.n_samples = 53 self.predictors = self.rng.normal(size=(self.n_samples, self.n_features)) self.dependent = self.rng.normal(size=self.n_samples) self.rate = 0.005 self.wta_full = BioWTARegressor(self.n_models, self.n_features, rate=self.rate) self.r_full = self.wta_full.transform(self.predictors, self.dependent) self.n_partial = self.n_samples // 2 self.wta_partial = BioWTARegressor( self.n_models, self.n_features, rate=self.rate ) self.r_partial = self.wta_partial.transform( self.predictors[: self.n_partial], self.dependent[: self.n_partial] )
def test_last_value_of_rate_is_used_if_more_samples_than_len_rate(self): n = 3 * self.n_samples // 4 schedule_short = self.rng.uniform(0, self.rate, size=n) schedule = np.hstack( (schedule_short, (self.n_samples - n) * [schedule_short[-1]]) ) wta1 = BioWTARegressor(self.n_models, self.n_features, rate=schedule_short) wta2 = BioWTARegressor(self.n_models, self.n_features, rate=schedule) wta1.transform(self.predictors, self.dependent) wta2.transform(self.predictors, self.dependent) np.testing.assert_allclose(wta1.weights_, wta2.weights_)
def test_transition_with_zero_trans_mat_is_never_used(self): # first find most likely transition with uniform trans_mat wta1 = BioWTARegressor(self.n_models, self.n_features) r1 = wta1.transform(self.predictors, self.dependent) k1 = np.argmax(r1, axis=1) trans_count1 = np.zeros((self.n_models, self.n_models), dtype=int) for ki, kf in zip(k1, k1[1:]): trans_count1[ki, kf] += 1 kmli, kmlf = np.unravel_index(trans_count1.argmax(), trans_count1.shape) self.assertGreater(trans_count1[kmli, kmlf], 0) # next disallow the system performing that transition trans_mat = np.ones((self.n_models, self.n_models)) / self.n_models trans_mat[kmli] = np.ones(self.n_models) / (self.n_models - 1) trans_mat[kmli, kmlf] = 0 wta2 = BioWTARegressor(self.n_models, self.n_features, trans_mat=trans_mat) r2 = wta2.transform(self.predictors, self.dependent) k2 = np.argmax(r2, axis=1) trans_count2 = np.zeros((self.n_models, self.n_models), dtype=int) for ki, kf in zip(k2, k2[1:]): trans_count2[ki, kf] += 1 self.assertEqual(trans_count2[kmli, kmlf], 0)
def test_when_trans_mat_large_for_a_target_state_then_that_state_gets_high_r(self): # first figure out where we're starting r1 = self.wta.transform(self.predictors[:2], self.dependent[:2]) k1_start = np.argmax(r1[0]) k1 = np.argmax(r1[1]) # make another state win out k = 0 if k1 != 0 else 1 p0 = 0.999 trans_mat = np.ones((self.n_models, self.n_models)) / self.n_models trans_mat[k1_start, :] = (1 - p0) * np.ones(self.n_models) / (self.n_models - 1) trans_mat[k1_start, k] = p0 wta2 = BioWTARegressor(self.n_models, self.n_features, trans_mat=trans_mat) r2 = wta2.transform(self.predictors[:2], self.dependent[:2]) k2_start = np.argmax(r2[0]) k2 = np.argmax(r2[1]) self.assertEqual(k1_start, k2_start) self.assertNotEqual(k1, k2) self.assertEqual(k2, k)
def test_monitor_as_sequence(self): _, history = self.wta.transform( self.predictors, self.dependent, monitor=["weights_", "prediction_"], return_history=True, ) wta_again = BioWTARegressor(self.n_models, self.n_features) weights = [] predictions = [] for crt_x, crt_y in zip(self.predictors, self.dependent): weights.append(np.copy(wta_again.weights_)) crt_all_pred = np.dot(wta_again.weights_, crt_x) crt_r = wta_again.transform([crt_x], [crt_y]) crt_k = np.argmax(crt_r) crt_pred = crt_all_pred[crt_k] predictions.append(crt_pred) np.testing.assert_allclose(weights, history.weights_) np.testing.assert_allclose(predictions, history.prediction_)
def test_second_r_value_changes_by_correct_amount_according_to_trans_mat(self): r1 = self.wta.transform(self.predictors[:2], self.dependent[:2]) # change transition matrix trans_mat = [ (lambda v: v / np.sum(v))(self.rng.uniform(size=self.n_models)) for _ in range(self.n_models) ] wta2 = BioWTARegressor( self.n_models, self.n_features, temperature=self.temperature, trans_mat=trans_mat, ) r2 = wta2.transform(self.predictors[:2], self.dependent[:2]) np.testing.assert_allclose(r1[0], r2[0]) diff_log_r = np.log(r2[1]) - np.log(r1[1]) expected_diff_log_r = r2[0] @ np.log(trans_mat) np.testing.assert_allclose( diff_log_r - diff_log_r[0], expected_diff_log_r - expected_diff_log_r[0] )