Skip to content

Commit 5edb1db

Browse files
committed
Uploaded initial wave of new bopdmd.py tests
1 parent 97c4810 commit 5edb1db

File tree

1 file changed

+256
-2
lines changed

1 file changed

+256
-2
lines changed

tests/test_bopdmd.py

Lines changed: 256 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import numpy as np
2-
from pytest import raises
2+
from pytest import raises, warns
33
from scipy.integrate import solve_ivp
44

55
from pydmd.bopdmd import BOPDMD
66

7-
87
def simulate_z(t_eval):
98
"""
109
Given a time vector t_eval = t1, t2, ..., evaluates and returns
@@ -331,3 +330,258 @@ def make_imag(x):
331330
bopdmd1 = BOPDMD(svd_rank=2, eig_constraints={"imag"}).fit(Z, t)
332331
bopdmd2 = BOPDMD(svd_rank=2, eig_constraints=make_imag).fit(Z, t)
333332
np.testing.assert_array_equal(bopdmd1.eigs, bopdmd2.eigs)
333+
334+
335+
def test_plot_mode_uq():
336+
"""
337+
Test that a basic call to plot_mode_uq is successful.
338+
"""
339+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=0.8)
340+
bopdmd.fit(Z, t)
341+
bopdmd.plot_mode_uq()
342+
343+
344+
def test_plot_eig_uq():
345+
"""
346+
Test that a basic call to plot_eig_uq is successful.
347+
"""
348+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=0.8)
349+
bopdmd.fit(Z, t)
350+
bopdmd.plot_eig_uq()
351+
352+
353+
def test_plot_error():
354+
"""
355+
Test that UQ plotters fail if bagging wasn't used.
356+
"""
357+
bopdmd = BOPDMD(svd_rank=2, num_trials=0)
358+
bopdmd.fit(Z, t)
359+
360+
with raises(ValueError):
361+
bopdmd.plot_mode_uq()
362+
363+
with raises(ValueError):
364+
bopdmd.plot_eig_uq()
365+
366+
367+
def test_varpro_opts_warn():
368+
"""
369+
Test that errors or warnings are correctly thrown if invalid
370+
or poorly-chosen variable projection parameters are given.
371+
The `tol` parameter is specifically tested.
372+
"""
373+
with raises(TypeError):
374+
bopdmd = BOPDMD(varpro_opts_dict={"tol": None})
375+
bopdmd.fit(Z, t)
376+
377+
with warns():
378+
bopdmd = BOPDMD(varpro_opts_dict={"tol": -1.0})
379+
bopdmd.fit(Z, t)
380+
381+
with warns():
382+
bopdmd = BOPDMD(varpro_opts_dict={"tol": np.inf})
383+
bopdmd.fit(Z, t)
384+
385+
386+
def test_varpro_opts_print():
387+
"""
388+
Test that variable projection parameters can be printed after fitting.
389+
"""
390+
bopdmd = BOPDMD(svd_rank=2)
391+
392+
with raises(ValueError):
393+
bopdmd.print_varpro_opts()
394+
395+
bopdmd.fit(Z, t)
396+
bopdmd.print_varpro_opts()
397+
398+
399+
def test_verbose_outputs_1():
400+
"""
401+
Test variable projection verbosity for optimized DMD.
402+
"""
403+
bopdmd = BOPDMD(
404+
svd_rank=2,
405+
num_trials=0,
406+
varpro_opts_dict={"verbose": True},
407+
)
408+
bopdmd.fit(Z, t)
409+
410+
411+
def test_verbose_outputs_2():
412+
"""
413+
Test variable projection verbosity for BOP-DMD.
414+
"""
415+
bopdmd = BOPDMD(
416+
svd_rank=2,
417+
num_trials=10,
418+
trial_size=0.8,
419+
varpro_opts_dict={"verbose": True},
420+
)
421+
bopdmd.fit(Z, t)
422+
423+
424+
def test_verbose_outputs_3():
425+
"""
426+
Test variable projection verbosity for BOP-DMD without bad bags.
427+
"""
428+
bopdmd = BOPDMD(
429+
svd_rank=2,
430+
num_trials=10,
431+
trial_size=0.8,
432+
varpro_opts_dict={"verbose": True, "tol": 1.0},
433+
remove_bad_bags=True,
434+
)
435+
bopdmd.fit(Z, t)
436+
437+
438+
def test_bag_int():
439+
"""
440+
Test that trial_size can be a valid integer value.
441+
"""
442+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=3200)
443+
bopdmd.fit(Z, t)
444+
445+
446+
def test_bag_error():
447+
"""
448+
Test that errors are thrown if invalid bagging parameters are given.
449+
"""
450+
# Error should raise if trial_size isn't a positive integer...
451+
with raises(ValueError):
452+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=-1)
453+
bopdmd.fit(Z, t)
454+
455+
# ...or if it isn't a float in the range (0.0, 1.0).
456+
with raises(ValueError):
457+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=2.0)
458+
bopdmd.fit(Z, t)
459+
460+
# Error should raise if the requested trial size is too big...
461+
with raises(ValueError):
462+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=4001)
463+
bopdmd.fit(Z, t)
464+
465+
# ...or if the requested trial size is too small.
466+
with raises(ValueError):
467+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=1e-6)
468+
bopdmd.fit(Z, t)
469+
470+
471+
def test_mode_prox():
472+
"""
473+
Test that the mode_prox function is applied as expected.
474+
"""
475+
def dummy_prox(X):
476+
return X + 1.0
477+
478+
# Test that the function is applied in the use_proj=False case.
479+
bopdmd = BOPDMD(svd_rank=2, use_proj=False, mode_prox=dummy_prox)
480+
bopdmd.fit(Z, t)
481+
482+
# Test that the function is applied in the use_proj=True case.
483+
bopdmd = BOPDMD(svd_rank=2, use_proj=True, mode_prox=dummy_prox)
484+
bopdmd.fit(Z, t)
485+
486+
# Compare use_proj=True results to the no prox case.
487+
bopdmd_noprox = BOPDMD(svd_rank=2, use_proj=True)
488+
bopdmd_noprox.fit(Z, t)
489+
np.testing.assert_allclose(bopdmd.modes, dummy_prox(bopdmd_noprox.modes))
490+
491+
492+
def test_init_alpha_initializer():
493+
"""
494+
Test that the eigenvalues are accurately initialized by default.
495+
"""
496+
bopdmd = BOPDMD(svd_rank=2)
497+
498+
# Initial eigs shouldn't be defined yet.
499+
with raises(RuntimeError):
500+
_ = bopdmd.init_alpha
501+
502+
# After fitting, the initial eigs used should be fairly accurate.
503+
bopdmd.fit(Z, t)
504+
np.testing.assert_allclose(
505+
sort_imag(bopdmd.init_alpha),
506+
expected_eigs,
507+
rtol=0.01,
508+
)
509+
510+
511+
def test_proj_basis_initializer():
512+
"""
513+
Test that the projection basis is accurately initialized by default.
514+
"""
515+
bopdmd = BOPDMD(svd_rank=2)
516+
517+
# Projection basis shouldn't be defined yet.
518+
with raises(RuntimeError):
519+
_ = bopdmd.proj_basis
520+
521+
# After fitting, the projection basis used should be accurate.
522+
bopdmd.fit(Z, t)
523+
np.testing.assert_array_equal(bopdmd.proj_basis, np.linalg.svd(Z)[0])
524+
525+
526+
def test_svd_rank():
527+
"""
528+
Test svd_rank getter and setter.
529+
"""
530+
bopdmd = BOPDMD(svd_rank=2)
531+
assert bopdmd.svd_rank == 2
532+
533+
bopdmd.svd_rank = 0
534+
assert bopdmd.svd_rank == 0
535+
536+
537+
def test_init_alpha():
538+
"""
539+
Test init_alpha getter and setter.
540+
"""
541+
dummy_eigs = 2.0 * expected_eigs
542+
bopdmd = BOPDMD(init_alpha=dummy_eigs)
543+
np.testing.assert_array_equal(bopdmd.init_alpha, dummy_eigs)
544+
545+
bopdmd.init_alpha = 2.0 * dummy_eigs
546+
np.testing.assert_array_equal(bopdmd.init_alpha, 2.0 * dummy_eigs)
547+
548+
549+
def test_proj_basis():
550+
"""
551+
Test proj_basis getter and setter.
552+
"""
553+
dummy_basis = 2.0 * np.linalg.svd(Z)[0]
554+
bopdmd = BOPDMD(proj_basis=dummy_basis)
555+
np.testing.assert_array_equal(bopdmd.proj_basis, dummy_basis)
556+
557+
bopdmd.proj_basis = 2.0 * dummy_basis
558+
np.testing.assert_array_equal(bopdmd.proj_basis, 2.0 * dummy_basis)
559+
560+
561+
def test_default_initializers():
562+
"""
563+
Test that default initialization does not impact custom inputs.
564+
"""
565+
# Define the dummy init_alpha and proj_basis to NOT be the defaults.
566+
dummy_eigs = 2.0 * expected_eigs
567+
dummy_basis = 2.0 * np.linalg.svd(Z)[0]
568+
bopdmd = BOPDMD(
569+
svd_rank=2,
570+
init_alpha=dummy_eigs,
571+
proj_basis=dummy_basis,
572+
)
573+
bopdmd.fit(Z, t)
574+
np.testing.assert_array_equal(bopdmd.init_alpha, dummy_eigs)
575+
np.testing.assert_array_equal(bopdmd.proj_basis, dummy_basis)
576+
577+
578+
def test_std_shape():
579+
"""
580+
Test the shapes of the standard deviation attributes.
581+
"""
582+
bopdmd = BOPDMD(svd_rank=2, num_trials=10, trial_size=0.8)
583+
bopdmd.fit(Z, t)
584+
585+
assert bopdmd.eigenvalues_std.shape == bopdmd.eigs.shape
586+
assert bopdmd.modes_std.shape == bopdmd.modes.shape
587+
assert bopdmd.amplitudes_std.shape == bopdmd.amplitudes.shape

0 commit comments

Comments
 (0)