-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🩹 False positive model validation fail when combining multiple defaul…
…t megacomplexes (#797) * 🩹 Fixed missing 'f' in front of f-string which rendered the error useless * 🩹 Deactivate instance check if megacomplex type is None Compatibility with legacy (0.4.0) model specs and combining multiple megacomplexes * 🩹 Fixed usage of 'glotaran_unique' in 'ensure_unique_megacomplexes' * 👌 Improved typing * 🧪 Added unittests for 'ensure_unique_megacomplexes' * 🩹 Fixed missing megacomplex definition not being reported as error * 🩹 Fixed megacomplex type being None * ♻️ Refactored 'ensure_unique_megacomplexes' using Counter and improved typing. Since 'DatasetModel' is never instantiated from the class definition, but 'create_dataset_model_type' creates a new class in a closure each time a new instance is needed, values across instances won't be overwritten. * ♻️ Moved typing information to stub file After a personal request of @joernweissenborn I removed the class attributes again and moved them to a pyi file * 🧹 Removed warning about missing megacomplex definition Since it is already reported as 'Missing Model Item'
- Loading branch information
Showing
4 changed files
with
184 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
from typing import Generator | ||
from typing import Hashable | ||
|
||
import numpy as np | ||
import xarray as xr | ||
|
||
from glotaran.model.megacomplex import Megacomplex | ||
from glotaran.model.model import Model | ||
from glotaran.parameter import Parameter | ||
|
||
def create_dataset_model_type(properties: dict[str, Any]) -> type[DatasetModel]: ... | ||
|
||
class DatasetModel: | ||
|
||
label: str | ||
megacomplex: list[str] | ||
megacomplex_scale: list[Parameter] | None | ||
global_megacomplex: list[str] | ||
global_megacomplex_scale: list[Parameter] | None | ||
scale: Parameter | None | ||
_coords: dict[Hashable, np.ndarray] | ||
def iterate_megacomplexes( | ||
self, | ||
) -> Generator[tuple[Parameter | int | None, Megacomplex | str], None, None]: ... | ||
def iterate_global_megacomplexes( | ||
self, | ||
) -> Generator[tuple[Parameter | int | None, Megacomplex | str], None, None]: ... | ||
def get_model_dimension(self) -> str: ... | ||
def finalize_data(self, dataset: xr.Dataset) -> None: ... | ||
def overwrite_model_dimension(self, model_dimension: str) -> None: ... | ||
def get_global_dimension(self) -> str: ... | ||
def overwrite_global_dimension(self, global_dimension: str) -> None: ... | ||
def swap_dimensions(self) -> None: ... | ||
def set_data(self, dataset: xr.Dataset) -> DatasetModel: ... | ||
def get_data(self) -> np.ndarray: ... | ||
def get_weight(self) -> np.ndarray | None: ... | ||
def index_dependent(self) -> bool: ... | ||
def overwrite_index_dependent(self, index_dependent: bool): ... | ||
def has_global_model(self) -> bool: ... | ||
def set_coordinates(self, coords: dict[str, np.ndarray]): ... | ||
def get_coordinates(self) -> dict[Hashable, np.ndarray]: ... | ||
def get_model_axis(self) -> np.ndarray: ... | ||
def get_global_axis(self) -> np.ndarray: ... | ||
def ensure_unique_megacomplexes(self, model: Model) -> list[str]: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
"""Tests for glotaran.model.dataset_model.DatasetModel""" | ||
from __future__ import annotations | ||
|
||
import pytest | ||
|
||
from glotaran.builtin.megacomplexes.baseline import BaselineMegacomplex | ||
from glotaran.builtin.megacomplexes.coherent_artifact import CoherentArtifactMegacomplex | ||
from glotaran.builtin.megacomplexes.damped_oscillation import DampedOscillationMegacomplex | ||
from glotaran.builtin.megacomplexes.decay import DecayMegacomplex | ||
from glotaran.builtin.megacomplexes.spectral import SpectralMegacomplex | ||
from glotaran.model.dataset_model import create_dataset_model_type | ||
from glotaran.model.model import default_dataset_properties | ||
|
||
|
||
class MockModel: | ||
"""Test Model only containing the megacomplex property. | ||
Multiple and different kinds of megacomplexes are defined | ||
but only a subset will be used by the DatsetModel. | ||
""" | ||
|
||
def __init__(self) -> None: | ||
self.megacomplex = { | ||
# not unique | ||
"d1": DecayMegacomplex(), | ||
"d2": DecayMegacomplex(), | ||
"d3": DecayMegacomplex(), | ||
"s1": SpectralMegacomplex(), | ||
"s2": SpectralMegacomplex(), | ||
"s3": SpectralMegacomplex(), | ||
"doa1": DampedOscillationMegacomplex(), | ||
"doa2": DampedOscillationMegacomplex(), | ||
# unique | ||
"b1": BaselineMegacomplex(), | ||
"b2": BaselineMegacomplex(), | ||
"c1": CoherentArtifactMegacomplex(), | ||
"c2": CoherentArtifactMegacomplex(), | ||
} | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"used_megacomplexes, expected_problems", | ||
( | ||
( | ||
["d1"], | ||
[], | ||
), | ||
( | ||
["d1", "d2", "d3"], | ||
[], | ||
), | ||
( | ||
["s1", "s2", "s3"], | ||
[], | ||
), | ||
( | ||
["d1", "d2", "d3", "s1", "s2", "s3", "doa1", "doa2", "b1", "c1"], | ||
[], | ||
), | ||
( | ||
["d1", "b1", "b2"], | ||
["Multiple instances of unique megacomplex type 'baseline' in dataset 'ds1'"], | ||
), | ||
( | ||
["d1", "c1", "c2"], | ||
["Multiple instances of unique megacomplex type 'coherent-artifact' in dataset 'ds1'"], | ||
), | ||
( | ||
["d1", "b1", "b2", "c1", "c2"], | ||
[ | ||
"Multiple instances of unique megacomplex type 'baseline' in dataset 'ds1'", | ||
"Multiple instances of unique megacomplex type " | ||
"'coherent-artifact' in dataset 'ds1'", | ||
], | ||
), | ||
), | ||
) | ||
def test_datasetmodel_ensure_unique_megacomplexes( | ||
used_megacomplexes: list[str], expected_problems: list[str] | ||
): | ||
"""Only report problems if multiple unique megacomplexes of the same type are used.""" | ||
dataset_model = create_dataset_model_type({**default_dataset_properties})() | ||
dataset_model.megacomplex = used_megacomplexes # type:ignore | ||
dataset_model.label = "ds1" # type:ignore | ||
problems = dataset_model.ensure_unique_megacomplexes(MockModel()) # type:ignore | ||
|
||
assert len(problems) == len(expected_problems) | ||
assert problems == expected_problems |