11import numpy as np
2+ import pytest
23import sparsediffpy as sp
34from tests .utils import NumericalDerivativeChecker , random_positive_point
45
56
6- def test_rel_entr_jacobian (scope , rng ):
7+ # --- Vector-vector (both same shape) ---
8+
9+ def test_rel_entr_vector_jacobian (scope , rng ):
710 x = scope .Variable (3 , 1 )
811 y = scope .Variable (3 , 1 )
912 f = sp .rel_entr (x , y )
@@ -12,7 +15,7 @@ def test_rel_entr_jacobian(scope, rng):
1215 checker .check_jacobian (random_positive_point (scope , rng ))
1316
1417
15- def test_rel_entr_hessian (scope , rng ):
18+ def test_rel_entr_vector_hessian (scope , rng ):
1619 x = scope .Variable (3 , 1 )
1720 y = scope .Variable (3 , 1 )
1821 f = sp .rel_entr (x , y )
@@ -22,7 +25,7 @@ def test_rel_entr_hessian(scope, rng):
2225 checker .check_hessian (x0 , rng .standard_normal (3 ))
2326
2427
25- def test_rel_entr_forward (scope , rng ):
28+ def test_rel_entr_vector_forward (scope , rng ):
2629 x = scope .Variable (3 , 1 )
2730 y = scope .Variable (3 , 1 )
2831 f = sp .rel_entr (x , y )
@@ -32,3 +35,80 @@ def test_rel_entr_forward(scope, rng):
3235 y_val = y .value
3336 expected = x_val * np .log (x_val / y_val )
3437 np .testing .assert_allclose (fn .forward (), expected )
38+
39+
40+ # --- Scalar x, vector y ---
41+
42+ def test_rel_entr_scalar_vector_jacobian (scope , rng ):
43+ x = scope .Variable (1 , 1 )
44+ y = scope .Variable (3 , 1 )
45+ f = sp .rel_entr (x , y )
46+ assert f .shape == (3 , 1 )
47+ fn = sp .compile (f )
48+ checker = NumericalDerivativeChecker (fn , scope )
49+ checker .check_jacobian (random_positive_point (scope , rng ))
50+
51+
52+ def test_rel_entr_scalar_vector_hessian (scope , rng ):
53+ x = scope .Variable (1 , 1 )
54+ y = scope .Variable (3 , 1 )
55+ f = sp .rel_entr (x , y )
56+ fn = sp .compile (f )
57+ checker = NumericalDerivativeChecker (fn , scope )
58+ x0 = random_positive_point (scope , rng )
59+ checker .check_hessian (x0 , rng .standard_normal (3 ))
60+
61+
62+ def test_rel_entr_scalar_vector_forward (scope , rng ):
63+ x = scope .Variable (1 , 1 )
64+ y = scope .Variable (3 , 1 )
65+ f = sp .rel_entr (x , y )
66+ fn = sp .compile (f )
67+ x0 = random_positive_point (scope , rng )
68+ x_val = x .value [0 ]
69+ y_val = y .value
70+ expected = x_val * np .log (x_val / y_val )
71+ np .testing .assert_allclose (fn .forward (), expected )
72+
73+
74+ # --- Vector x, scalar y ---
75+
76+ def test_rel_entr_vector_scalar_jacobian (scope , rng ):
77+ x = scope .Variable (3 , 1 )
78+ y = scope .Variable (1 , 1 )
79+ f = sp .rel_entr (x , y )
80+ assert f .shape == (3 , 1 )
81+ fn = sp .compile (f )
82+ checker = NumericalDerivativeChecker (fn , scope )
83+ checker .check_jacobian (random_positive_point (scope , rng ))
84+
85+
86+ def test_rel_entr_vector_scalar_hessian (scope , rng ):
87+ x = scope .Variable (3 , 1 )
88+ y = scope .Variable (1 , 1 )
89+ f = sp .rel_entr (x , y )
90+ fn = sp .compile (f )
91+ checker = NumericalDerivativeChecker (fn , scope )
92+ x0 = random_positive_point (scope , rng )
93+ checker .check_hessian (x0 , rng .standard_normal (3 ))
94+
95+
96+ def test_rel_entr_vector_scalar_forward (scope , rng ):
97+ x = scope .Variable (3 , 1 )
98+ y = scope .Variable (1 , 1 )
99+ f = sp .rel_entr (x , y )
100+ fn = sp .compile (f )
101+ x0 = random_positive_point (scope , rng )
102+ x_val = x .value
103+ y_val = y .value [0 ]
104+ expected = x_val * np .log (x_val / y_val )
105+ np .testing .assert_allclose (fn .forward (), expected )
106+
107+
108+ # --- Shape mismatch ---
109+
110+ def test_rel_entr_incompatible_shapes (scope ):
111+ x = scope .Variable (3 , 1 )
112+ y = scope .Variable (2 , 1 )
113+ with pytest .raises (ValueError , match = "shapes must match or one must be scalar" ):
114+ sp .rel_entr (x , y )
0 commit comments