Skip to content

Commit 8fb028b

Browse files
author
Alexander März
committed
Added tests
1 parent 3583fc9 commit 8fb028b

27 files changed

+1439
-0
lines changed

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""LightGBMLSS - An extension of LightGBM to probabilistic forecasting"""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""LightGBMLSS - An extension of LightGBM to probabilistic forecasting"""
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from ..utils import BaseTestClass, gen_test_data
2+
import numpy as np
3+
4+
5+
class TestClass(BaseTestClass):
6+
def test_calculate_start_values(self, dist_class, loss_fn):
7+
# Create data for testing
8+
_, target, _ = gen_test_data(dist_class)
9+
10+
# Set the loss function for testing
11+
dist_class.dist.loss_fn = loss_fn
12+
13+
# Call the objective_fn method
14+
loss, start_values = dist_class.dist.calculate_start_values(target)
15+
16+
# Assertions
17+
assert isinstance(loss, np.ndarray)
18+
assert not np.isnan(loss).any()
19+
assert not np.isinf(loss).any()
20+
21+
assert isinstance(start_values, np.ndarray)
22+
assert start_values.shape[0] == dist_class.dist.n_dist_param
23+
assert not np.isnan(start_values).any()
24+
assert not np.isinf(start_values).any()
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from ..utils import BaseTestClass, gen_test_data
2+
from typing import List
3+
import numpy as np
4+
import torch
5+
6+
7+
class TestClass(BaseTestClass):
8+
def test_compute_gradients_and_hessians(self, dist_class, loss_fn, stabilization):
9+
# Create data for testing
10+
params, target, weights, _ = gen_test_data(dist_class, weights=True)
11+
if dist_class.dist.univariate:
12+
target = torch.tensor(target)
13+
else:
14+
target = torch.tensor(target)[:, :dist_class.dist.n_targets]
15+
start_values = np.array([0.5 for _ in range(dist_class.dist.n_dist_param)])
16+
17+
# Set the loss function for testing
18+
dist_class.dist.loss_fn = loss_fn
19+
20+
# Set the stabilization for testing
21+
dist_class.dist.stabilization = stabilization
22+
23+
# Call the function
24+
predt, loss = dist_class.dist.get_params_loss(params, target, start_values, requires_grad=True)
25+
grad, hess = dist_class.dist.compute_gradients_and_hessians(loss, predt, weights)
26+
27+
# Assertions
28+
assert isinstance(predt, List)
29+
for i in range(len(predt)):
30+
assert isinstance(predt[i], torch.Tensor)
31+
assert not torch.isnan(predt[i]).any()
32+
assert not torch.isinf(predt[i]).any()
33+
assert isinstance(loss, torch.Tensor)
34+
assert not torch.isnan(loss).any()
35+
assert not torch.isinf(loss).any()
36+
37+
assert isinstance(grad, np.ndarray)
38+
assert isinstance(hess, np.ndarray)
39+
assert grad.shape == params.flatten().shape
40+
assert hess.shape == params.flatten().shape
41+
assert not np.isnan(grad).any()
42+
assert not np.isnan(hess).any()
43+
44+
def test_compute_gradients_and_hessians_crps(self, dist_class_crps, stabilization):
45+
# Create data for testing
46+
params, target, weights, _ = gen_test_data(dist_class_crps, weights=True)
47+
if dist_class_crps.dist.univariate:
48+
target = torch.tensor(target)
49+
else:
50+
target = torch.tensor(target)[:, :dist_class_crps.dist.n_targets]
51+
start_values = np.array([0.5 for _ in range(dist_class_crps.dist.n_dist_param)])
52+
53+
# Set the loss function for testing
54+
dist_class_crps.dist.loss_fn = "crps"
55+
56+
# Set the stabilization for testing
57+
dist_class_crps.dist.stabilization = stabilization
58+
59+
# Call the function
60+
predt, loss = dist_class_crps.dist.get_params_loss(params, target, start_values, requires_grad=True)
61+
grad, hess = dist_class_crps.dist.compute_gradients_and_hessians(loss, predt, weights)
62+
63+
# Assertions
64+
assert isinstance(predt, List)
65+
for i in range(len(predt)):
66+
assert isinstance(predt[i], torch.Tensor)
67+
assert not torch.isnan(predt[i]).any()
68+
assert not torch.isinf(predt[i]).any()
69+
assert isinstance(loss, torch.Tensor)
70+
assert not torch.isnan(loss).any()
71+
assert not torch.isinf(loss).any()
72+
73+
assert isinstance(grad, np.ndarray)
74+
assert isinstance(hess, np.ndarray)
75+
assert grad.shape == params.flatten().shape
76+
assert hess.shape == params.flatten().shape
77+
assert not np.isnan(grad).any()
78+
assert not np.isnan(hess).any()
79+
80+
def test_compute_gradients_and_hessians_nans(self, dist_class, loss_fn, stabilization):
81+
# Create data for testing
82+
params, target, weights, _ = gen_test_data(dist_class, weights=True)
83+
params[0, 0] = np.nan
84+
if dist_class.dist.univariate:
85+
target = torch.tensor(target)
86+
else:
87+
target = torch.tensor(target)[:, :dist_class.dist.n_targets]
88+
start_values = np.array([0.5 for _ in range(dist_class.dist.n_dist_param)])
89+
90+
# Set the loss function for testing
91+
dist_class.dist.loss_fn = loss_fn
92+
93+
# Set the stabilization for testing
94+
dist_class.dist.stabilization = stabilization
95+
96+
# Call the function
97+
predt, loss = dist_class.dist.get_params_loss(params, target, start_values, requires_grad=True)
98+
grad, hess = dist_class.dist.compute_gradients_and_hessians(loss, predt, weights)
99+
100+
# Assertions
101+
assert isinstance(predt, List)
102+
for i in range(len(predt)):
103+
assert isinstance(predt[i], torch.Tensor)
104+
assert not torch.isnan(predt[i]).any()
105+
assert not torch.isinf(predt[i]).any()
106+
assert isinstance(loss, torch.Tensor)
107+
assert not torch.isnan(loss).any()
108+
assert not torch.isinf(loss).any()
109+
110+
assert isinstance(grad, np.ndarray)
111+
assert isinstance(hess, np.ndarray)
112+
assert grad.shape == params.flatten().shape
113+
assert hess.shape == params.flatten().shape
114+
assert not np.isnan(grad).any()
115+
assert not np.isnan(hess).any()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from ..utils import BaseTestClass
2+
import torch
3+
4+
5+
class TestClass(BaseTestClass):
6+
def test_crps_score(self, dist_class_crps):
7+
# Create data for testing
8+
torch.manual_seed(123)
9+
n_obs = 10
10+
n_samples = 20
11+
y = torch.rand(n_obs, 1)
12+
yhat_dist = torch.rand(n_samples, n_obs)
13+
14+
# Call the function
15+
loss = dist_class_crps.dist.crps_score(y, yhat_dist)
16+
17+
# Assertions
18+
assert isinstance(loss, torch.Tensor)
19+
assert not torch.isnan(loss).any()
20+
assert not torch.isinf(loss).any()
21+
assert loss.shape == y.shape
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from ..utils import BaseTestClass
2+
3+
from lightgbmlss.distributions import Beta, Gaussian, StudentT, Gamma, Cauchy, LogNormal, Weibull, Gumbel, Laplace
4+
from lightgbmlss.distributions.SplineFlow import *
5+
from lightgbmlss.distributions.distribution_utils import DistributionClass as univariate_dist_class
6+
from lightgbmlss.distributions.flow_utils import NormalizingFlowClass as flow_dist_class
7+
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
class TestClass(BaseTestClass):
13+
####################################################################################################################
14+
# Univariate Distribution
15+
####################################################################################################################
16+
def test_univar_dist_select(self):
17+
# Create data for testing
18+
target = np.array([0.2, 0.4, 0.6, 0.8]).reshape(-1, 1)
19+
candidate_distributions = [Beta, Gaussian, StudentT, Gamma, Cauchy, LogNormal, Weibull, Gumbel, Laplace]
20+
21+
# Call the function
22+
dist_df = univariate_dist_class().dist_select(
23+
target, candidate_distributions, n_samples=10, plot=False
24+
).reset_index(drop=True)
25+
26+
# Assertions
27+
assert isinstance(dist_df, pd.DataFrame)
28+
assert not dist_df.isna().any().any()
29+
assert isinstance(dist_df["distribution"].values[0], str)
30+
assert np.issubdtype(dist_df["nll"].dtype, np.float64)
31+
assert not np.isnan(dist_df["nll"].values).any()
32+
assert not np.isinf(dist_df["nll"].values).any()
33+
34+
def test_univar_dist_select_plot(self):
35+
# Create data for testing
36+
target = np.array([0.2, 0.4, 0.6, 0.8]).reshape(-1, 1)
37+
candidate_distributions = [Beta, Gaussian, StudentT, Gamma, Cauchy, LogNormal, Weibull, Gumbel, Laplace]
38+
39+
# Call the function
40+
dist_df = univariate_dist_class().dist_select(
41+
target, candidate_distributions, n_samples=10, plot=True
42+
).reset_index(drop=True)
43+
44+
# Assertions
45+
assert isinstance(dist_df, pd.DataFrame)
46+
assert not dist_df.isna().any().any()
47+
assert isinstance(dist_df["distribution"].values[0], str)
48+
assert np.issubdtype(dist_df["nll"].dtype, np.float64)
49+
assert not np.isnan(dist_df["nll"].values).any()
50+
assert not np.isinf(dist_df["nll"].values).any()
51+
52+
####################################################################################################################
53+
# Normalizing Flows
54+
####################################################################################################################
55+
def test_flow_select(self):
56+
# Create data for testing
57+
target = np.array([0.2, 0.4, 0.6, 0.8]).reshape(-1, 1)
58+
bound = np.max([np.abs(target.min()), target.max()])
59+
target_support = "real"
60+
61+
candidate_flows = [
62+
SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="linear"),
63+
SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="quadratic")
64+
]
65+
66+
# Call the function
67+
dist_df = flow_dist_class().flow_select(
68+
target, candidate_flows, n_samples=10, plot=False
69+
).reset_index(drop=True)
70+
71+
# Assertions
72+
assert isinstance(dist_df, pd.DataFrame)
73+
assert not dist_df.isna().any().any()
74+
assert isinstance(dist_df["NormFlow"].values[0], str)
75+
assert np.issubdtype(dist_df["nll"].dtype, np.float64)
76+
assert not np.isnan(dist_df["nll"].values).any()
77+
assert not np.isinf(dist_df["nll"].values).any()
78+
79+
def test_flow_select_plot(self):
80+
# Create data for testing
81+
target = np.array([0.2, 0.4, 0.6, 0.8]).reshape(-1, 1)
82+
bound = np.max([np.abs(target.min()), target.max()])
83+
target_support = "real"
84+
85+
candidate_flows = [
86+
SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="linear"),
87+
SplineFlow(target_support=target_support, count_bins=2, bound=bound, order="quadratic")
88+
]
89+
90+
# Call the function
91+
dist_df = flow_dist_class().flow_select(
92+
target, candidate_flows, n_samples=10, plot=True
93+
).reset_index(drop=True)
94+
95+
# Assertions
96+
assert isinstance(dist_df, pd.DataFrame)
97+
assert not dist_df.isna().any().any()
98+
assert isinstance(dist_df["NormFlow"].values[0], str)
99+
assert np.issubdtype(dist_df["nll"].dtype, np.float64)
100+
assert not np.isnan(dist_df["nll"].values).any()
101+
assert not np.isinf(dist_df["nll"].values).any()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from ..utils import BaseTestClass
2+
import pandas as pd
3+
import numpy as np
4+
5+
6+
class TestClass(BaseTestClass):
7+
def test_draw_samples(self, dist_class):
8+
# Create data for testing
9+
predt_params = pd.DataFrame(np.array([0.5 for _ in range(dist_class.dist.n_dist_param)], dtype="float32")).T
10+
11+
# Call the function
12+
dist_samples = dist_class.dist.draw_samples(predt_params)
13+
14+
# Assertions
15+
if str(dist_class.dist).split(".")[2] != "Expectile":
16+
assert isinstance(dist_samples, (pd.DataFrame, type(None)))
17+
assert not dist_samples.isna().any().any()
18+
assert not np.isinf(dist_samples).any().any()
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from ..utils import BaseTestClass, gen_test_data
2+
from typing import List
3+
import numpy as np
4+
import torch
5+
6+
7+
class TestClass(BaseTestClass):
8+
def test_get_params_loss(self, dist_class, loss_fn, requires_grad):
9+
# Create data for testing
10+
predt, target, _ = gen_test_data(dist_class)
11+
target = torch.tensor(target)
12+
start_values = np.array([0.5 for _ in range(dist_class.dist.n_dist_param)])
13+
14+
# Set the loss function for testing
15+
dist_class.dist.loss_fn = loss_fn
16+
17+
# Call the function
18+
predt, loss = dist_class.dist.get_params_loss(predt, target, start_values, requires_grad)
19+
20+
# Assertions
21+
assert isinstance(predt, List)
22+
for i in range(len(predt)):
23+
assert isinstance(predt[i], torch.Tensor)
24+
assert not torch.isnan(predt[i]).any()
25+
assert not torch.isinf(predt[i]).any()
26+
assert isinstance(loss, torch.Tensor)
27+
assert not torch.isnan(loss).any()
28+
assert not torch.isinf(loss).any()
29+
30+
def test_get_params_loss_nans(self, dist_class, loss_fn, requires_grad):
31+
# Create data for testing
32+
predt, target, _ = gen_test_data(dist_class)
33+
predt[0, 0] = np.nan
34+
target = torch.tensor(target)
35+
start_values = np.array([0.5 for _ in range(dist_class.dist.n_dist_param)])
36+
37+
# Set the loss function for testing
38+
dist_class.dist.loss_fn = loss_fn
39+
40+
# Call the function
41+
predt, loss = dist_class.dist.get_params_loss(predt, target, start_values, requires_grad)
42+
43+
# Assertions
44+
assert isinstance(predt, List)
45+
for i in range(len(predt)):
46+
assert isinstance(predt[i], torch.Tensor)
47+
assert not torch.isnan(predt[i]).any()
48+
assert not torch.isinf(predt[i]).any()
49+
assert isinstance(loss, torch.Tensor)
50+
assert not torch.isnan(loss).any()
51+
assert not torch.isinf(loss).any()
52+
53+
def test_get_params_loss_crps(self, dist_class_crps, requires_grad):
54+
# Create data for testing
55+
predt, target, _ = gen_test_data(dist_class_crps)
56+
target = torch.tensor(target)
57+
start_values = np.array([0.5 for _ in range(dist_class_crps.dist.n_dist_param)])
58+
59+
# Set the loss function for testing
60+
dist_class_crps.dist.loss_fn = "crps"
61+
62+
# Call the function
63+
predt, loss = dist_class_crps.dist.get_params_loss(predt, target, start_values, requires_grad)
64+
65+
# Assertions
66+
assert isinstance(predt, List)
67+
for i in range(len(predt)):
68+
assert isinstance(predt[i], torch.Tensor)
69+
assert not torch.isnan(predt[i]).any()
70+
assert not torch.isinf(predt[i]).any()
71+
assert isinstance(loss, torch.Tensor)
72+
assert not torch.isnan(loss).any()
73+
assert not torch.isinf(loss).any()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from ..utils import BaseTestClass, gen_test_data
2+
import torch
3+
4+
5+
class TestClass(BaseTestClass):
6+
def test_loss_fn_start_values(self, dist_class, loss_fn):
7+
# Create data for testing
8+
_, target, _ = gen_test_data(dist_class)
9+
predt = [
10+
torch.tensor(0.5, dtype=torch.float64).reshape(-1, 1).requires_grad_(True) for _ in
11+
range(dist_class.dist.n_dist_param)
12+
]
13+
if dist_class.dist.univariate:
14+
target = torch.tensor(target)
15+
else:
16+
target = torch.tensor(target)[:, :dist_class.dist.n_targets]
17+
18+
# Set the loss function for testing
19+
dist_class.dist.loss_fn = loss_fn
20+
21+
# Call the function
22+
if hasattr(dist_class.dist, "base_dist"):
23+
pass
24+
else:
25+
loss = dist_class.dist.loss_fn_start_values(predt, target)
26+
# Assertions
27+
assert isinstance(loss, torch.Tensor)
28+
assert not torch.isnan(loss).any()
29+
assert not torch.isinf(loss).any()

0 commit comments

Comments
 (0)