11import numpy as np
22import pyest .gm as pygm
33from scipy .linalg import solve_triangular
4+ from scipy .integrate import dblquad
45
56
67def l2_dist (p1 , p2 ):
@@ -87,4 +88,106 @@ def madem(m, S, m_ref):
8788 """
8889 return np .linalg .norm (
8990 solve_triangular (S , m - m_ref , lower = True )
90- )
91+ )
92+
93+
94+ def integral_squared_error_2d (p1 , p2 , a , b , c , d , epsabs = 1.49e-2 , epsrel = 1.49e-2 ):
95+ ''' compute integral squared error between two 2D densities
96+
97+ Parameters
98+ ----------
99+ p1: callable
100+ first density, p1([x,y])
101+ p2: callable
102+ second density, p2([x,y])
103+ a, b : float
104+ The limits of integration in x: a<b
105+ c, d : float
106+ The limits of integration in y: c<d
107+ epsabs : float, optional
108+ absolute error tolerance for numerical integration
109+ epsrel : float, optional
110+ relative error tolerance for numerical integration
111+
112+
113+ Returns
114+ -------
115+ ise: foat
116+ kld
117+ int_error:
118+ numerical integration estimated error
119+
120+ See Also
121+ --------
122+ normalized_integral_squared_error_2d : compute normalized integral squared
123+ error between two 2D densities
124+ l2_dist : compute L2 distance (ISE) between two Gaussian mixtures
125+
126+ Notes
127+ -----
128+ This function is intended for use with generic callable densities and
129+ makes no assumptions about the form of the densities. If both p1 and p2
130+ are Gaussian mixtures, use l2_dist instead, which is exact and more
131+ efficient.
132+ '''
133+ integrand_fun = lambda y ,x : (p1 ([x ,y ])- p2 ([x ,y ]))** 2
134+ return dblquad (integrand_fun , a , b , c , d , epsabs = epsabs , epsrel = epsrel )
135+
136+
137+ def normalized_integral_squared_error_2d (p1 , p2 , a , b , c , d , epsabs = 1.49e-2 , epsrel = 1.49e-2 ):
138+ ''' compute normalized integral squared error between two 2D, densities
139+
140+ Parameters
141+ ----------
142+ p1: callable
143+ first density
144+ p2: callable
145+ second density
146+ a, b : float
147+ The limits of integration in x: a<b
148+ c, d : float
149+ The limits of integration in y: c<d
150+ epsabs : float, optional
151+ absolute error tolerance for numerical integration
152+ epsrel : float, optional
153+ relative error tolerance for numerical integration
154+
155+ Returns
156+ -------
157+ nise: float
158+ normalized integral squared error
159+ ise:
160+ integral squared error
161+ err:
162+ numerical integration estimated error in ISE computation
163+
164+ See Also
165+ --------
166+ integral_squared_error_2d : compute integral squared error between two
167+ 2D densities
168+ l2_dist : compute L2 distance (ISE) between two Gaussian mixtures
169+
170+ Notes
171+ -----
172+ This function is intended for use with generic callable densities and
173+ makes no assumptions about the form of the densities. If both p1 and p2
174+ are Gaussian mixtures, use metrics.l2_dist and gm.integral_squared_gm
175+ instead for the numerator and denominator terms separately, which is
176+ exact and more efficient.
177+
178+ '''
179+ ise , err = integral_squared_error_2d (p1 , p2 , a , b , c , d , epsabs = epsabs , epsrel = epsrel )
180+ # if p1 is a GaussianMixtureRv, use l2_dist
181+ if isinstance (p1 , pygm .GaussianMixture ):
182+ int_p1_sq = pygm .integral_squared_gm (p1 )
183+ else :
184+ p1_sq_integrand_fun = lambda y ,x : p1 ([x ,y ])** 2
185+ int_p1_sq = dblquad (p1_sq_integrand_fun , a , b , c , d , epsabs = epsabs , epsrel = epsrel )[0 ]
186+ if isinstance (p2 , pygm .GaussianMixture ):
187+ int_p2_sq = pygm .integral_squared_gm (p2 )
188+ else :
189+ p2_sq_integrand_fun = lambda y ,x : p2 ([x ,y ])** 2
190+ int_p2_sq = dblquad (p2_sq_integrand_fun , a , b , c , d , epsabs = epsabs , epsrel = epsrel )[0 ]
191+ nise = ise / (int_p1_sq + int_p2_sq )
192+
193+ return nise , ise , err
0 commit comments