Skip to content

entropy module

Entropies and their derivatives.

EntropyHessianComponents = tuple[ThreeArrays, TwoArrays] module-attribute

combines the tuples of the values of the components of the hessians.

EntropyHessians = tuple[EntropyHessianMuMu, EntropyHessianMuR] module-attribute

combines the hessian functions.

EntropyFunctions dataclass

Defines the entropy used, via the derivative \(e_0 + e \cdot \alpha\)

Attributes:

Name Type Description
e0_fun MatchingFunction

required

parameter_dependent bool

if True, the entropy depends on parameters. Defaults to False

e_fun MatchingFunction | None

only in entropies that depend on parameters. Defaults to None

hessian str | None

defaults to "numeric" * if "provided", we provide the hessian of the entropy. * if "numerical", it is compute_d by central differences.

e0_derivative EntropyHessians | None

the derivative of e0_fun, if available. Defaults to None

e_derivative EntropyHessians | None

the derivative of e_fun, if available. Defaults to None

additional_parameters list | None

additional parameters that define the distribution of errors. Defaults to None

description str | None

some text describing the model. Defaults to None

Examples:

See entropy_choo_siow in choo_siow.py

Source code in cupid_matching/entropy.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@dataclass
class EntropyFunctions:
    """Defines the entropy used, via the derivative $e_0 + e \\cdot \\alpha$

    Attributes:
        e0_fun: required
        parameter_dependent:  if `True`, the entropy depends on parameters.
            Defaults to `False`
        e_fun: only in entropies that depend on parameters.
            Defaults to `None`
        hessian: defaults to `"numeric"`
            * if `"provided"`, we provide the hessian of the entropy.
            * if `"numerical"`, it is compute_d by central differences.
        e0_derivative: the derivative of `e0_fun`, if available.
            Defaults to `None`
        e_derivative: the derivative of `e_fun`, if available.
            Defaults to `None`
        additional_parameters: additional parameters
            that define the distribution of errors.
            Defaults to `None`
        description: some text describing the model.
            Defaults to `None`

    Examples:
        See `entropy_choo_siow` in `choo_siow.py`
    """

    e0_fun: MatchingFunction  # | MatchingFunctionParam
    e0_derivative: EntropyHessians | None = None
    additional_parameters: list | None = None
    description: str | None = None
    # e_fun: MatchingFunction | MatchingFunctionParam | None = None
    e_fun: MatchingFunction | None = None
    e_derivative: EntropyHessians | None = None
    hessian: str | None = "numerical"
    parameter_dependent: bool = False

    def __post_init__(self):
        if (
            (not self.parameter_dependent)
            and self.hessian == "provided"
            and self.e0_derivative is None
        ):
            bs_error_abort(
                "You claim to provide the hessian "
                + "but you did not provide the e0_derivative."
            )
        if self.parameter_dependent:
            if self.e_fun is None:
                bs_error_abort(
                    "Your entropy is parameter dependent "
                    + " but you did not provide the e_fun."
                )
            if self.hessian == "provided" and self.e_derivative is None:
                bs_error_abort(
                    "Your entropy is parameter dependent, "
                    + "you claim to provide the hessian,\n"
                    + " but I do not see the e_derivative."
                )

check_additional_parameters(number_required, additional_parameters)

checks that the correct number of additional parameters is passed in

Parameters:

Name Type Description Default
number_required int

number we want

required
additional_parameters list | None

the list of additional parameters passed in, if any

required
Source code in cupid_matching/entropy.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def check_additional_parameters(
    number_required: int, additional_parameters: list | None
) -> None:
    """checks that the correct number of additional parameters is passed in

    Args:
        number_required: number we want
        additional_parameters: the list of additional parameters passed in, if any
    """
    if number_required == 0:
        if additional_parameters is not None:
            bs_error_abort("no additional parameters should be passed.")
    else:
        if additional_parameters is None:
            bs_error_abort("additional parameters should be passed.")
        additional_parameters = cast(list, additional_parameters)
        if len(additional_parameters) != number_required:
            bs_error_abort(
                f"additional parameters should be a list of {number_required} elements."
            )

entropy_gradient(entropy, muhat, alpha=None, additional_parameters=None)

Computes the derivative of the entropy wrt \(\mu\) at \((\mu, n, m, \alpha, p)\)

Parameters:

Name Type Description Default
entropy EntropyFunctions

the EntropyFunctions object

required
muhat Matching

a Matching

required
alpha np.ndarray | None

a vector of parameters of the derivative of the entropy, if any

None
additional_parameters list | None

a list of additional parameters p, if any

None

Returns:

Type Description
np.ndarray

the derivative of the entropy wrt \(\mu\)

np.ndarray

at \((\mu, n, m, \alpha, p)\).

Source code in cupid_matching/entropy.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def entropy_gradient(
    entropy: EntropyFunctions,
    muhat: Matching,
    alpha: np.ndarray | None = None,
    additional_parameters: list | None = None,
) -> np.ndarray:
    """Computes the derivative of the entropy wrt $\\mu$
     at $(\\mu, n, m, \\alpha, p)$

    Args:
        entropy: the `EntropyFunctions` object
        muhat: a Matching
        alpha: a vector of parameters of the derivative of the entropy, if any
        additional_parameters: a list of additional parameters `p`, if any

    Returns:
        the derivative of the entropy wrt $\\mu$
        at $(\\mu, n, m, \\alpha, p)$.
    """
    e0_vals = get_evals(entropy.e0_fun, muhat, additional_parameters)
    parameter_dependent = entropy.parameter_dependent
    if parameter_dependent:
        if alpha is None:
            bs_error_abort("alpha should be specified for this model")
        elif entropy.e_fun is None:
            bs_error_abort("we should have an e_fun in this model")
        else:
            e_vals = get_evals(entropy.e_fun, muhat, additional_parameters)
        return cast(np.ndarray, e0_vals + e_vals @ alpha)
    else:
        return cast(np.ndarray, e0_vals)

fill_hessianMuMu_from_components(hessian_components)

Fills the hessian of the entropy wrt \((\mu,\mu)\)

Parameters:

Name Type Description Default
hessian_components ThreeArrays

the three components of the hessian

required

Returns:

Type Description
np.ndarray

the (XY,XY) matrix of the hessian

Source code in cupid_matching/entropy.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
def fill_hessianMuMu_from_components(
    hessian_components: ThreeArrays,
) -> np.ndarray:
    """Fills the hessian of the entropy wrt $(\\mu,\\mu)$

    Args:
        hessian_components: the three components of the hessian

    Returns:
        the (XY,XY) matrix of the hessian
    """
    hess_x, hess_y, hess_xy = hessian_components
    X, Y = hess_xy.shape
    XY = X * Y
    hessian = np.zeros((XY, XY))

    i = 0
    ix = 0
    for x in range(X):
        for y in range(Y):
            hessian[i, ix : (ix + Y)] = hess_x[x, y, :]
            slice_y = slice(y, XY, Y)
            hessian[i, slice_y] = hess_y[x, y, :]
            hessian[i, i] = hess_xy[x, y]
            i += 1
        ix += Y

    return hessian

fill_hessianMuR_from_components(hessian_components)

Fills the hessian of the entropy wrt \((\mu,(n,m))\)

Parameters:

Name Type Description Default
hessian_components TwoArrays

the two components of the hessian

required

Returns:

Type Description
np.ndarray

the (XY,X+Y) matrix of the hessian

Source code in cupid_matching/entropy.py
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
def fill_hessianMuR_from_components(
    hessian_components: TwoArrays,
) -> np.ndarray:
    """Fills the hessian of the entropy wrt $(\\mu,(n,m))$

    Args:
        hessian_components: the two components of the hessian

    Returns:
        the (XY,X+Y) matrix of the hessian
    """
    hess_nx, hess_my = hessian_components
    X, Y = hess_nx.shape[:2]
    XY = X * Y
    hessian = np.zeros((XY, X + Y))

    i = 0
    for x in range(X):
        iy = X
        for y in range(Y):
            hessian[i, x] = hess_nx[x, y]
            hessian[i, iy] = hess_my[x, y]
            i += 1
            iy += 1

    return hessian

numeric_hessian(entropy, muhat, alpha=None, additional_parameters=None)

Evaluates numerically the components of the hessians of the entropy wrt \((\mu,\mu)\) and \((\mu,(n,m))\)

Parameters:

Name Type Description Default
entropy EntropyFunctions

the EntropyFunctions object

required
muhat Matching

a Matching

required
alpha np.ndarray | None

a vector of parameters of the derivative of the entropy, if any

None
additional_parameters list | None

a list of additional parameters, if any

None

Returns:

Type Description
EntropyHessianComponents

the hessians of the entropy wrt \((\mu,\mu)\) and \((\mu,(n,m))\).

Source code in cupid_matching/entropy.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def numeric_hessian(
    entropy: EntropyFunctions,
    muhat: Matching,
    alpha: np.ndarray | None = None,
    additional_parameters: list | None = None,
) -> EntropyHessianComponents:
    """Evaluates numerically the components of the hessians of the entropy
    wrt $(\\mu,\\mu)$ and $(\\mu,(n,m))$

    Args:
        entropy: the `EntropyFunctions` object
        muhat: a Matching
        alpha: a vector of parameters of the derivative of the entropy, if any
        additional_parameters: a list of additional parameters, if any

    Returns:
        the hessians of the entropy wrt $(\\mu,\\mu)$ and $(\\mu,(n,m))$.
    """
    parameter_dependent = entropy.parameter_dependent
    # we create a derivative of entropy that is only a function of the Matching and the additional parameters
    if not parameter_dependent:
        entropy_deriv = partial(
            entropy_gradient,
            entropy,
        )
    else:
        entropy_deriv = partial(
            entropy_gradient,
            entropy,
            alpha=alpha,
        )
    muxyhat, _, _, n, m = muhat.unpack()
    X, Y = muxyhat.shape

    # make sure everything is floating point
    muxyhatf = muxyhat.copy().astype(float)
    nf = n.copy().astype(float)
    mf = m.copy().astype(float)
    muhatf = Matching(muxyhatf, nf, mf)

    # start with the hessian wrt (mu, mu)
    hessian_x = np.zeros((X, Y, Y))
    hessian_y = np.zeros((X, Y, X))
    hessian_xy = np.zeros((X, Y))
    for x in range(X):
        for y in range(Y):
            for t in range(Y):
                hessian_x[x, y, t] = _numeric_component(
                    muhatf,
                    x,
                    y,
                    t,
                    entropy_deriv,
                    additional_parameters,
                    direction="y",
                )
            for z in range(X):
                hessian_y[x, y, z] = _numeric_component(
                    muhatf, x, y, z, entropy_deriv, additional_parameters, direction="x"
                )
            hessian_xy[x, y] = _numeric_component(
                muhatf, x, y, 0, entropy_deriv, additional_parameters, direction="d"
            )
    components_mumu = (hessian_x, hessian_y, hessian_xy)

    # now the hessian wrt (mu, r)
    hessian_n = np.zeros((X, Y))
    hessian_m = np.zeros((X, Y))
    for x in range(X):
        for y in range(Y):
            hessian_n[x, y] = _numeric_component(
                muhatf, x, y, 0, entropy_deriv, additional_parameters, direction="n"
            )
            hessian_m[x, y] = _numeric_component(
                muhatf, x, y, 0, entropy_deriv, additional_parameters, direction="m"
            )

    components_mur = (hessian_n, hessian_m)

    return components_mumu, components_mur