diff --git a/ezyrb/reducedordermodel.py b/ezyrb/reducedordermodel.py index 23a3a35..cb64e29 100644 --- a/ezyrb/reducedordermodel.py +++ b/ezyrb/reducedordermodel.py @@ -213,7 +213,7 @@ def n_reduction(self): @property def n_approximation(self): value_, class_ = self.approximation, Approximation - return len(value_) if isinstance(value_, class_) else 1 + return len(value_) if not isinstance(value_, class_) else 1 def fit_reduction(self): """ @@ -846,6 +846,10 @@ def approximation(self, value): else: self._approximation = value + @approximation.deleter + def approximation(self): + del self._approximation + @property def n_database(self): value_, class_ = self.database, Database @@ -859,7 +863,7 @@ def n_reduction(self): @property def n_approximation(self): value_, class_ = self.approximation, Approximation - return len(value_) if isinstance(value_, class_) else 1 + return len(value_) if not isinstance(value_, class_) else 1 def fit(self): r""" @@ -983,23 +987,34 @@ def test_error(self, test, norm=np.linalg.norm, relative=True): test snapshots. :rtype: numpy.ndarray """ - predicted_test = self.predict(test.parameters_matrix) - if relative: - return np.mean( - norm( - predicted_test.snapshots_matrix - test.snapshots_matrix, - axis=1, - ) - / norm(test.snapshots_matrix, axis=1) - ) - - return np.mean( - norm( - predicted_test.snapshots_matrix - test.snapshots_matrix, - axis=1, - ) - ) - + errors = {} + is_dict = isinstance(test,dict) + sample_key = list(test.keys())[0] if is_dict else None + params = test[sample_key].parameters_matrix if is_dict else test.parameters_matrix + predicted_test = self.predict(params) + + for key in predicted_test: + if is_dict: + if key in test: + db_key = key + elif isinstance(key, tuple) and key[0] in test: + db_key = key[0] + else: + db_key = sample_key + test_snaps = test[db_key].snapshots_matrix + else: + test_snaps = test.snapshots_matrix + + diff = predicted_test[key] - test_snaps + + if relative: + errors[key] = np.mean(norm(diff, axis=1)/norm(test_snaps, axis=1)) + else: + errors[key] = np.mean(norm(diff, axis=1)) + + + return errors + def kfold_cv_error( self, n_splits, *args, norm=np.linalg.norm, relative=True, **kwargs ): @@ -1021,23 +1036,27 @@ def kfold_cv_error( :return: the vector containing the errors corresponding to each fold. :rtype: numpy.ndarray """ - error = [] + errors = {k: [] for k in self.roms.keys()} kf = KFold(n_splits=n_splits) - for train_index, test_index in kf.split(self.database): - new_db = self.database[train_index] + db_len = len(list(self.database.values())[0]) + + for train_index, test_index in kf.split(range(db_len)): + new_db = {k: v[train_index] for k, v in self.database.items()} + test_db = {k: v[test_index] for k, v in self.database.items()} # TODO: Fix plugins handling - should pass: # plugins=[copy.deepcopy(p) for p in self.plugins] - rom = type(self)( + mrom = type(self)( new_db, copy.deepcopy(self.reduction), copy.deepcopy(self.approximation), ).fit(*args, **kwargs) - error.append( - rom.test_error(self.database[test_index], norm, relative) - ) + fold_errors = mrom.test_error(test_db, norm, relative) + + for k in errors: + errors[k].append(fold_errors[k]) - return np.array(error) + return {k: np.array(v) for k, v in errors.items()} def loo_error(self, *args, norm=np.linalg.norm, **kwargs): r""" @@ -1058,26 +1077,28 @@ def loo_error(self, *args, norm=np.linalg.norm, **kwargs): parametric points. :rtype: numpy.ndarray """ - error = np.zeros(len(self.database)) - db_range = list(range(len(self.database))) + db_len = len(list(self.database.values())[0]) + errors = {k: np.zeros(db_len) for k in self.roms.keys()} - for j in db_range: - indeces = np.array([True] * len(self.database)) + for j in range(db_len): + indeces = np.array([True] * db_len) indeces[j] = False - new_db = self.database[indeces] - test_db = self.database[~indeces] - # TODO: Fix plugins handling - should pass: - # plugins=[copy.deepcopy(p) for p in self.plugins] - rom = type(self)( + new_db = {k: v[indeces] for k, v in self.database.items()} + test_db = {k: v[~indeces] for k, v in self.database.items()} + + mrom = type(self)( new_db, copy.deepcopy(self.reduction), copy.deepcopy(self.approximation), - ).fit() + ).fit(*args, **kwargs) - error[j] = rom.test_error(test_db, norm=norm) + loo_errors = mrom.test_error(test_db, norm=norm, **kwargs) - return error + for k in errors: + errors[k][j] = loo_errors[k] + + return errors def optimal_mu(self, error=None, k=1): """ @@ -1097,26 +1118,32 @@ def optimal_mu(self, error=None, k=1): if error is None: error = self.loo_error() - mu = self.database.parameters_matrix + first_db = list(self.database.values())[0] + mu = first_db.parameters_matrix tria = Delaunay(mu) - error_on_simplex = np.array( - [ - np.sum(error[smpx]) * self._simplex_volume(mu[smpx]) - for smpx in tria.simplices - ] - ) + opt_mu_dict = {} - barycentric_point = [] - for index in np.argpartition(error_on_simplex, -k)[-k:]: - worst_tria_pts = mu[tria.simplices[index]] - worst_tria_err = error[tria.simplices[index]] - - barycentric_point.append( - np.average(worst_tria_pts, axis=0, weights=worst_tria_err) + for key, err in error.items(): + error_on_simplex = np.array( + [ + np.sum(err[smpx]) * self._simplex_volume(mu[smpx]) # Use 'err', not 'error' + for smpx in tria.simplices + ] ) - return np.asarray(barycentric_point) + barycentric_point = [] + for index in np.argpartition(error_on_simplex, -k)[-k:]: + worst_tria_pts = mu[tria.simplices[index]] + worst_tria_err = err[tria.simplices[index]] # Use 'err', not 'error' + + barycentric_point.append( + np.average(worst_tria_pts, axis=0, weights=worst_tria_err) + ) + + opt_mu_dict[key] = np.asarray(barycentric_point) + + return opt_mu_dict def _simplex_volume(self, vertices): """ @@ -1165,25 +1192,19 @@ def reduction_error(self, db=None, relative=True, eps=1e-12): >>> err_test_reduct = rom.reconstruction_error(db_test, relative=True) """ - errs = [] - if db is None: - db = self.database - snap = db.snapshots_matrix - snap_red = self.reduction.transform(snap.T) - snap_full = self.reduction.inverse_transform(snap_red).T - - E = snap - snap_full + errors = {} + for key, rom in self.roms.items(): + if db is None: + db_k = None + elif isinstance(db, dict): + db_key = key if key in db else (key[0] if isinstance(key, tuple) and key[0] in db else list(db.keys())[0]) + db_k = db[db_key] + else: + db_k = db - if relative: - num = np.linalg.norm(E, axis=1) - den = np.linalg.norm(snap, axis=1) + eps - - err = float(np.mean(num / den)) - else: - err = float(np.mean(np.linalg.norm(E, axis=1))) - errs.append(err) + errors[key] = rom.reduction_error(db=db_k, relative=relative, eps= eps) - return np.array(errs) + return errors def approximation_error(self, db=None, relative=True, eps=1e-12): """ @@ -1215,26 +1236,18 @@ def approximation_error(self, db=None, relative=True, eps=1e-12): >>> err_test_approx = rom.approximation_error(db_test, relative=True) """ - errs = [] - if db is None: - db = self.database - snap = db.snapshots_matrix - params_true = self.reduction.transform(snap.T).T + errors = {} - params = db.parameters_matrix + for key, rom in self.roms.items(): + if db is None: + db_k = None + elif isinstance(db, dict): + db_key = key if key in db else (key[0] if isinstance(key, tuple) and key[0] in db else list(db.keys())[0]) + db_k = db[db_key] + else: + db_k = db - params_approx = self.approximation.predict(params) + errors[key] = rom.approximation_error(db=db_k, relative=relative, eps=eps) - E = params_true - params_approx - - if relative: - num = np.linalg.norm(E, axis=1) - den = np.linalg.norm(params_true, axis=1) + eps - - err = float(np.mean(num / den)) - else: - err = float(np.mean(np.linalg.norm(E, axis=1))) - errs.append(err) - - return np.array(errs) + return errors \ No newline at end of file diff --git a/tests/test_parallel/test_reducedordermodel.py b/tests/test_parallel/test_reducedordermodel.py index 30d57ba..da6e25f 100644 --- a/tests/test_parallel/test_reducedordermodel.py +++ b/tests/test_parallel/test_reducedordermodel.py @@ -4,4 +4,5 @@ from ezyrb.parallel import ReducedOrderModel as ParallelROM ezyrb.ReducedOrderModel = ParallelROM -from tests.test_reducedordermodel import * +# Explicitly import ONLY the original base tests, not the new extended ones +from tests.test_reducedordermodel import TestReducedOrderModel, test_invariant_pod \ No newline at end of file diff --git a/tests/test_reducedordermodel.py b/tests/test_reducedordermodel.py index 415fa1e..2d287f2 100644 --- a/tests/test_reducedordermodel.py +++ b/tests/test_reducedordermodel.py @@ -1,9 +1,10 @@ import numpy as np - +import pytest from unittest import TestCase + from ezyrb import POD, GPR, RBF, Database from ezyrb import KNeighborsRegressor, RadiusNeighborsRegressor, Linear -from ezyrb import ReducedOrderModel as ROM +from ezyrb.reducedordermodel import ReducedOrderModel as ROM from ezyrb.reducedordermodel import MultiReducedOrderModel as MROM snapshots = np.load('tests/test_datasets/p_snapshots.npy').T @@ -11,6 +12,17 @@ pred_sol_gpr = np.load('tests/test_datasets/p_predsol_gpr.npy').T param = np.array([[-.5, -.5], [.5, -.5], [.5, .5], [-.5, .5]]) +def _make_db(): + return Database(param, snapshots.T) + +def _make_rom(rank=None, approx=None): + pod = POD() if rank is None else POD(rank=rank) + return ROM(_make_db(), pod, approx or RBF()).fit() + +def _make_mrom(): + """Minimal fitted MROM: one db, one POD, one RBF.""" + return MROM({"p": _make_db()}, {"pod": POD()}, {"rbf": RBF()}).fit() + class TestReducedOrderModel(TestCase): def test_constructor(self): @@ -202,9 +214,9 @@ def test_multi_db(self): assert isinstance(pred, dict) assert len(pred) == 2 + def test_invariant_pod(): pod = POD() - rbf = RBF() gpr = GPR() rnr = RadiusNeighborsRegressor() @@ -226,7 +238,6 @@ def test_invariant_pod(): atol=1e-8 ) - """ def test_optimal_mu(self): pod = POD() @@ -244,4 +255,491 @@ def test_optimal_mu(self): np.testing.assert_allclose(len_opt_mu, exact_len) len_k = [rom.optimal_mu(k=k).shape[0] for rom in roms] np.testing.assert_allclose(len_k, k) -""" \ No newline at end of file +""" + +class TestROMConstructorPlugins(TestCase): + + def test_constructor_with_non_empty_plugins(self): + class _P: + def fit_preprocessing(self, rom): pass + + plugin = _P() + rom = ROM(_make_db(), POD(), RBF(), plugins=[plugin]) + self.assertIn(plugin, rom.plugins) + + def test_execute_plugins_calls_correct_hooks(self): + stages = [] + + class _Tracker: + def fit_preprocessing(self, rom): + stages.append("fit_preprocessing") + def fit_postprocessing(self, rom): + stages.append("fit_postprocessing") + + ROM(_make_db(), POD(), RBF(), plugins=[_Tracker()]).fit() + self.assertIn("fit_preprocessing", stages) + self.assertIn("fit_postprocessing", stages) + + def test_execute_plugins_skips_missing_hooks(self): + class _NoHooks: pass + ROM(_make_db(), POD(), RBF(), plugins=[_NoHooks()]).fit() + + +class TestROMPropertySetters(TestCase): + + def test_database_setter_wrong_type(self): + with self.assertRaises(TypeError): + ROM(_make_db(), POD(), RBF()).database = "bad" + + def test_reduction_setter_wrong_type(self): + with self.assertRaises(TypeError): + ROM(_make_db(), POD(), RBF()).reduction = "bad" + + def test_approximation_setter_wrong_type(self): + with self.assertRaises(TypeError): + ROM(_make_db(), POD(), RBF()).approximation = "bad" + + +class TestROMPropertyDeleters(TestCase): + + def test_database_deleter(self): + rom = _make_rom() + del rom.database + self.assertFalse(hasattr(rom, "_database")) + + def test_reduction_deleter(self): + rom = _make_rom() + del rom.reduction + self.assertFalse(hasattr(rom, "_reduction")) + + def test_approximation_deleter(self): + rom = _make_rom() + del rom.approximation + self.assertFalse(hasattr(rom, "_approximation")) + + +class TestROMCountProperties(TestCase): + + def test_n_database_is_one(self): + self.assertEqual(_make_rom().n_database, 1) + + def test_n_reduction_is_one(self): + self.assertEqual(_make_rom().n_reduction, 1) + + def test_n_approximation_count(self): + self.assertEqual(_make_rom().n_approximation, 1) + + +class TestROMFitRuntimeErrors(TestCase): + + def test_fit_reduction_raises_when_attr_deleted(self): + rom = ROM(_make_db(), POD(), RBF()) + del rom.train_full_database + with self.assertRaises(RuntimeError): + rom.fit_reduction() + + def test_fit_approximation_raises_when_attr_deleted(self): + rom = ROM(_make_db(), POD(), RBF()) + del rom.train_reduced_database + with self.assertRaises(RuntimeError): + rom.fit_approximation() + + +class TestROMPredict(TestCase): + + def test_predict_database_input_returns_database(self): + rom = _make_rom() + result = rom.predict(_make_db()) + self.assertIsInstance(result, Database) + + def test_predict_tuple_input(self): + result = _make_rom().predict((-0.293344, -0.23120537)) + self.assertEqual(result.shape[0], 1) + + def test_predict_invalid_type_raises(self): + with self.assertRaises(TypeError): + _make_rom().predict({"bad": "input"}) + + +class TestROMClean(TestCase): + + def test_clean_sets_all_internal_dbs_to_none(self): + rom = _make_rom() + rom.clean() + for attr in ( + "train_full_database", "train_reduced_database", + "predict_full_database", "predict_reduced_database", + "test_full_database", "test_reduced_database", + "validation_full_database", "validation_reduced_database", + ): + self.assertIsNone(getattr(rom, attr), f"{attr} should be None") + + +class TestROMReduceDatabase(TestCase): + + def test_returns_database_with_same_row_count(self): + rom = _make_rom() + db = _make_db() + reduced = rom._reduce_database(db) + self.assertIsInstance(reduced, Database) + self.assertEqual(reduced.snapshots_matrix.shape[0], + db.snapshots_matrix.shape[0]) + + +class TestROMSavePartialFlags(TestCase): + + def test_save_without_reduction(self): + rom = _make_rom() + rom.save("rom_no_reduction.pkl", save_reduction=False) + self.assertFalse(hasattr(ROM.load("rom_no_reduction.pkl"), "_reduction")) + + def test_save_without_approx(self): + rom = _make_rom() + rom.save("rom_no_approx.pkl", save_approx=False) + self.assertFalse(hasattr(ROM.load("rom_no_approx.pkl"), "_approximation")) + + def test_save_all_flags_false(self): + rom = _make_rom() + rom.save("rom_skeleton.pkl", + save_db=False, save_reduction=False, save_approx=False) + loaded = ROM.load("rom_skeleton.pkl") + for attr in ("_database", "_reduction", "_approximation"): + self.assertFalse(hasattr(loaded, attr)) + + +class TestROMTestErrorAbsolute(TestCase): + + def test_absolute_error_is_non_negative(self): + err = _make_rom(rank=-1).test_error(_make_db(), relative=False) + self.assertGreaterEqual(err, 0.0) + + def test_relative_and_absolute_both_non_negative(self): + rom = _make_rom() + db = _make_db() + self.assertGreaterEqual(rom.test_error(db, relative=True), 0.0) + self.assertGreaterEqual(rom.test_error(db, relative=False), 0.0) + + +class TestROMKfoldCvError(TestCase): + + def test_length_equals_n_splits(self): + errors = ROM(_make_db(), POD(), GPR()).kfold_cv_error(n_splits=2) + self.assertEqual(len(errors), 2) + + def test_all_errors_non_negative(self): + errors = ROM(_make_db(), POD(), GPR()).kfold_cv_error(n_splits=2) + self.assertTrue(np.all(errors >= 0)) + + def test_absolute_mode(self): + errors = ROM(_make_db(), POD(), GPR()).kfold_cv_error( + n_splits=2, relative=False) + self.assertEqual(len(errors), 2) + self.assertTrue(np.all(errors >= 0)) + + +class TestROMOptimalMu(TestCase): + + def test_k1_returns_one_point(self): + opt = _make_rom().optimal_mu(k=1) + self.assertEqual(opt.shape, (1, param.shape[1])) + + def test_k2_returns_two_points(self): + opt = _make_rom().optimal_mu(k=2) + self.assertEqual(opt.shape[0], 2) + + def test_precomputed_error_gives_same_result(self): + rom = _make_rom() + error = rom.loo_error() + np.testing.assert_allclose(rom.optimal_mu(k=1), + rom.optimal_mu(error=error, k=1)) + + def test_simplex_volume_positive(self): + vol = _make_rom()._simplex_volume(param[:3]) + self.assertGreater(vol, 0.0) + + +class TestROMReductionError(TestCase): + + def test_default_relative(self): + err = _make_rom().reduction_error() + self.assertEqual(err.shape, (1,)) + self.assertGreaterEqual(err[0], 0.0) + + def test_absolute_branch(self): + err = _make_rom().reduction_error(relative=False) + self.assertGreaterEqual(err[0], 0.0) + + def test_explicit_db(self): + err = _make_rom().reduction_error(db=_make_db()) + self.assertEqual(err.shape, (1,)) + + def test_full_rank_error_is_small(self): + err = _make_rom(rank=-1).reduction_error() + self.assertLess(err[0], 1e-4) + + +class TestROMApproximationError(TestCase): + + def test_default_relative(self): + err = _make_rom().approximation_error() + self.assertEqual(err.shape, (1,)) + self.assertGreaterEqual(err[0], 0.0) + + def test_absolute_branch(self): + err = _make_rom().approximation_error(relative=False) + self.assertGreaterEqual(err[0], 0.0) + + def test_explicit_db(self): + err = _make_rom().approximation_error(db=_make_db()) + self.assertEqual(err.shape, (1,)) + + def test_interpolatory_method_near_zero(self): + np.testing.assert_almost_equal( + _make_rom().approximation_error()[0], 0.0, decimal=4) + + +class TestMROMInit(TestCase): + + def test_3arg_cartesian_product(self): + mrom = MROM({"p": _make_db()}, + {"pod": POD(), "pod2": POD(rank=1)}, + {"rbf": RBF()}) + self.assertEqual(len(mrom.roms), 2) + + def test_init_roms_dict_only(self): + db = _make_db() + roms = {"a": ROM(db, POD(), RBF()).fit(), + "b": ROM(db, POD(rank=1), RBF()).fit()} + mrom = MROM(roms) + self.assertIn("a", mrom.roms) + self.assertIn("a", mrom.database) + + def test_init_database_and_roms_dict(self): + db = _make_db() + mrom = MROM(db, {"a": ROM(db, POD(), RBF()).fit()}) + self.assertIn(0, mrom.database) + + def test_rom_plugin_appended_to_each_rom(self): + class _P: pass + plugin = _P() + mrom = MROM({"p": _make_db()}, {"pod": POD()}, {"rbf": RBF()}, + rom_plugin=plugin) + for rom_ in mrom.roms.values(): + self.assertIn(plugin, rom_.plugins) + + def test_global_plugins_stored_on_mrom(self): + class _GP: pass + gp = _GP() + mrom = MROM({"p": _make_db()}, {"pod": POD()}, {"rbf": RBF()}, + plugins=[gp]) + self.assertIn(gp, mrom.plugins) + + +class TestMROMPropertySetters(TestCase): + + def test_database_wrong_type(self): + with self.assertRaises(TypeError): + _make_mrom().database = 42 + + def test_database_plain_db_wraps_to_dict(self): + mrom = _make_mrom() + mrom.database = _make_db() + self.assertIn(0, mrom._database) + + def test_database_dict_stored_as_is(self): + d = {"x": _make_db()} + mrom = _make_mrom() + mrom.database = d + self.assertIs(mrom._database, d) + + def test_reduction_wrong_type(self): + with self.assertRaises(TypeError): + _make_mrom().reduction = "bad" + + def test_reduction_plain_wraps_to_dict(self): + mrom = _make_mrom() + mrom.reduction = POD() + self.assertIn(0, mrom._reduction) + + def test_approximation_wrong_type(self): + with self.assertRaises(TypeError): + _make_mrom().approximation = 3.14 + + def test_approximation_plain_wraps_to_dict(self): + mrom = _make_mrom() + mrom.approximation = RBF() + self.assertIn(0, mrom._approximation) + + +class TestMROMPropertyDeleters(TestCase): + + def test_database_deleter(self): + mrom = _make_mrom() + del mrom.database + self.assertFalse(hasattr(mrom, "_database")) + + def test_reduction_deleter(self): + mrom = _make_mrom() + del mrom.reduction + self.assertFalse(hasattr(mrom, "_reduction")) + + def test_approximation_deleter(self): + mrom = _make_mrom() + del mrom.approximation + self.assertFalse(hasattr(mrom, "_approximation")) + + +class TestMROMCountProperties(TestCase): + + def test_n_database(self): + mrom = _make_mrom() + self.assertEqual(mrom.n_database, len(mrom.database)) + + def test_n_reduction(self): + mrom = _make_mrom() + self.assertEqual(mrom.n_reduction, len(mrom.reduction)) + + def test_n_approximation(self): + mrom = _make_mrom() + self.assertEqual(mrom.n_approximation, len(mrom.approximation)) + + +class TestMROMPredict(TestCase): + + def test_list_input_returns_dict_of_arrays(self): + result = _make_mrom().predict([-.5, -.5]) + self.assertIsInstance(result, dict) + for v in result.values(): + self.assertIsInstance(v, np.ndarray) + + def test_database_input_returns_dict_of_databases(self): + result = _make_mrom().predict(_make_db()) + self.assertIsInstance(result, dict) + for v in result.values(): + self.assertIsInstance(v, Database) + + def test_invalid_type_raises_type_error(self): + with self.assertRaises(TypeError): + _make_mrom().predict({"bad": "input"}) + + def test_none_with_no_prior_db_raises_runtime_error(self): + mrom = _make_mrom() + mrom.predict_full_database = None + with self.assertRaises(RuntimeError): + mrom.predict(None) + + +class TestMROMFitIdempotent(TestCase): + + def test_second_fit_is_no_op_for_fitted_roms(self): + mrom = _make_mrom() + key = list(mrom.roms.keys())[0] + existing = mrom.roms[key].train_reduced_database + mrom.fit() + self.assertIs(mrom.roms[key].train_reduced_database, existing) + + +class TestMROMSaveLoad(TestCase): + + def test_roundtrip_predictions_match(self): + mrom = _make_mrom() + mrom.save("mrom_roundtrip.pkl") + loaded = MROM.load("mrom_roundtrip.pkl") + p = [-.3, -.3] + for k in mrom.predict(p): + np.testing.assert_allclose( + mrom.predict(p)[k], loaded.predict(p)[k], rtol=1e-5) + + def test_save_without_db(self): + mrom = _make_mrom() + mrom.save("mrom_no_db.pkl", save_db=False) + self.assertFalse(hasattr(MROM.load("mrom_no_db.pkl"), "_database")) + + def test_save_without_reduction(self): + mrom = _make_mrom() + mrom.save("mrom_no_red.pkl", save_reduction=False) + self.assertFalse(hasattr(MROM.load("mrom_no_red.pkl"), "_reduction")) + + def test_save_without_approx(self): + """Tests saving an MROM without the approximation model.""" + mrom = _make_mrom() + mrom.save("mrom_no_approx.pkl", save_approx=False) + loaded = MROM.load("mrom_no_approx.pkl") + self.assertFalse(hasattr(loaded, "_approximation")) + + +class TestMROMTestError(TestCase): + + def test_relative(self): + err = _make_mrom().test_error(_make_db(), relative=True) + self.assertIsInstance(err, dict) + + def test_absolute(self): + err = _make_mrom().test_error(_make_db(), relative=False) + self.assertIsInstance(err, dict) + + +class TestMROMKfoldCvError(TestCase): + + def test_kfold_cv_error(self): + err = MROM({"p": _make_db()}, {"pod": POD()}, {"gpr": GPR()}).kfold_cv_error( + n_splits=2) + self.assertIsInstance(err, dict) + + def test_kfold_cv_error_absolute(self): + err = MROM({"p": _make_db()}, {"pod": POD()}, {"gpr": GPR()}).kfold_cv_error( + n_splits=2, relative=False) + self.assertIsInstance(err, dict) + + +class TestMROMLooError(TestCase): + def test_loo_error(self): + err = MROM({"p": _make_db()}, {"pod": POD()}, {"rbf": RBF()}).loo_error() + self.assertIsInstance(err, dict) + + +class TestMROMOptimalMu(TestCase): + def test_optimal_mu_no_error(self): + opt = _make_mrom().optimal_mu(k=1) + self.assertIsInstance(opt, dict) + + def test_optimal_mu_precomputed_error(self): + opt = _make_mrom().optimal_mu(error={('p', 'pod', 'rbf'):np.array([0.1, 0.5, 0.2, 0.8])}, k=1) + self.assertIsInstance(opt, dict) + + def test_simplex_volume_positive(self): + self.assertGreater(_make_mrom()._simplex_volume(param[:3]), 0.0) + + +class TestMROMReductionError(TestCase): + + def test_default(self): + err = _make_mrom().reduction_error() + self.assertIsInstance(err, dict) + + def test_absolute(self): + err = _make_mrom().reduction_error(relative=False) + self.assertIsInstance(err, dict) + + def test_explicit_db(self): + err = _make_mrom().reduction_error(db=_make_db()) + self.assertIsInstance(err, dict) + + +class TestMROMApproximationError(TestCase): + + def test_default(self): + err = _make_mrom().approximation_error() + self.assertIsInstance(err, dict) + + def test_absolute(self): + err = _make_mrom().approximation_error(relative=False) + self.assertIsInstance(err, dict) + + def test_explicit_db(self): + err = _make_mrom().approximation_error(db=_make_db()) + self.assertIsInstance(err, dict) + +if __name__ == "__main__": + import unittest + unittest.main() \ No newline at end of file