amachine.am_fast.distance
1import cupy as cp 2import numpy as np 3from scipy.special import rel_entr 4 5# Relative Entropy Kernel 6# Ported from SciPy 1.17.1 7# https://github.com/scipy/scipy/blob/maintenance/1.17.x/scipy/special/_convex_analysis.pxd 8# Fixes overflow issue not addressed in cupyx implementation 9# https://github.com/scipy/scipy/issues/20710 10_rel_entr_kernel = cp.ElementwiseKernel( 11 'T x, T y', 12 'T z', 13 ''' 14 // Select the appropriate minimum normal value for the type at compile time. 15 // FLT_MIN ≈ 1.1755e-38 (float32 smallest normal) 16 // DBL_MIN ≈ 2.2251e-308 (float64 smallest normal) 17 const T type_min = (sizeof(T) == 4) ? (T)FLT_MIN : (T)DBL_MIN; 18 19 if (isnan(x) || isnan(y)) { 20 z = (T)CUDART_NAN; 21 } else if (x <= 0 || y <= 0) { 22 if (x == 0 && y >= 0) { 23 z = 0; 24 } else { 25 z = (T)CUDART_INF; 26 } 27 } else { 28 T ratio = x / y; 29 if (ratio > 0.5 && ratio < 2.0) { 30 z = x * log1p((x - y) / y); 31 } else if (ratio > type_min && ratio < (T)CUDART_INF) { 32 z = x * log(ratio); 33 } else { 34 z = x * (log(x) - log(y)); 35 } 36 } 37 ''', 38 'rel_entr_robust' 39) 40 41def jensenshannondivergence_cpu(p, q, axis=0, keepdims=False, base=None): 42 """ 43 Compute the Jensen-Shannon divergence between two probability arrays on GPU. 44 45 This is a port of scipy.spatial.distance.jensenshannon using a 46 numerically robust rel_entr kernel, except not taking the square root 47 48 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 49 """ 50 51 p = np.asarray(p) 52 q = np.asarray(q) 53 54 # Normalize inputs along the specified axis 55 p = p / np.sum(p, axis=axis, keepdims=True) 56 q = q / np.sum(q, axis=axis, keepdims=True) 57 58 # Calculate the midpoint distribution 59 m = (p + q) / 2.0 60 61 # Calculate relative entropy using the robust CUDA kernel 62 # Note: rel_entr results are in nats 63 left = rel_entr(p, m) 64 right = rel_entr(q, m) 65 66 # Sum across the specified axis 67 left_sum = np.sum(left, axis=axis, keepdims=keepdims) 68 right_sum = np.sum(right, axis=axis, keepdims=keepdims) 69 70 # JSDiv = (D_kl(P||M) + D_kl(Q||M)) / 2 71 js = (left_sum + right_sum) / 2.0 72 73 # Convert units if base is provided (e.g., base=2 for bits) 74 if base is not None: 75 js /= np.log(base) 76 77 return js / 2.0 78 79 80def jensenshannondivergence_gpu(p, q, axis=0, keepdims=False, base=None): 81 """ 82 Compute the Jensen-Shannon divergence between two probability arrays on GPU. 83 84 This is a port of scipy.spatial.distance.jensenshannon using a 85 numerically robust rel_entr kernel, except not taking the square root 86 87 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 88 """ 89 90 p = cp.asarray(p) 91 q = cp.asarray(q) 92 93 if p.dtype not in (cp.float32, cp.float64) or p.dtype != q.dtype : 94 raise TypeError( 95 f"Both arrays must be the same dtype and either float32 or float64, " 96 f"got p.dtype={p.dtype}, q.dtype={q.dtype}" 97 ) 98 99 # Normalize inputs along the specified axis 100 p = p / cp.sum(p, axis=axis, keepdims=True) 101 q = q / cp.sum(q, axis=axis, keepdims=True) 102 103 # Calculate the midpoint distribution 104 m = (p + q) / 2.0 105 106 # Calculate relative entropy using the robust CUDA kernel 107 # Note: rel_entr results are in nats 108 left = _rel_entr_kernel(p, m) 109 right = _rel_entr_kernel(q, m) 110 111 # Sum across the specified axis 112 left_sum = cp.sum(left, axis=axis, keepdims=keepdims) 113 right_sum = cp.sum(right, axis=axis, keepdims=keepdims) 114 115 # JSDiv = (D_kl(P||M) + D_kl(Q||M)) / 2 116 js = (left_sum + right_sum) / 2.0 117 118 # Convert units if base is provided (e.g., base=2 for bits) 119 if base is not None: 120 js /= cp.log(base) 121 122 return js / 2.0 123 124 125def jensenshannon_gpu(p, q, axis=0, keepdims=False, base=None): 126 """ 127 Compute the Jensen-Shannon distance between two probability arrays on GPU. 128 129 This is a port of scipy.spatial.distance.jensenshannon using a 130 numerically robust rel_entr kernel. 131 132 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 133 """ 134 js = jensenshannondivergence_gpu( p=p, q=q, axis=axis, keepdims=keepdims, base=base ) 135 136 return cp.sqrt(js / 2.0) 137
42def jensenshannondivergence_cpu(p, q, axis=0, keepdims=False, base=None): 43 """ 44 Compute the Jensen-Shannon divergence between two probability arrays on GPU. 45 46 This is a port of scipy.spatial.distance.jensenshannon using a 47 numerically robust rel_entr kernel, except not taking the square root 48 49 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 50 """ 51 52 p = np.asarray(p) 53 q = np.asarray(q) 54 55 # Normalize inputs along the specified axis 56 p = p / np.sum(p, axis=axis, keepdims=True) 57 q = q / np.sum(q, axis=axis, keepdims=True) 58 59 # Calculate the midpoint distribution 60 m = (p + q) / 2.0 61 62 # Calculate relative entropy using the robust CUDA kernel 63 # Note: rel_entr results are in nats 64 left = rel_entr(p, m) 65 right = rel_entr(q, m) 66 67 # Sum across the specified axis 68 left_sum = np.sum(left, axis=axis, keepdims=keepdims) 69 right_sum = np.sum(right, axis=axis, keepdims=keepdims) 70 71 # JSDiv = (D_kl(P||M) + D_kl(Q||M)) / 2 72 js = (left_sum + right_sum) / 2.0 73 74 # Convert units if base is provided (e.g., base=2 for bits) 75 if base is not None: 76 js /= np.log(base) 77 78 return js / 2.0
Compute the Jensen-Shannon divergence between two probability arrays on GPU.
This is a port of scipy.spatial.distance.jensenshannon using a numerically robust rel_entr kernel, except not taking the square root
https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290
81def jensenshannondivergence_gpu(p, q, axis=0, keepdims=False, base=None): 82 """ 83 Compute the Jensen-Shannon divergence between two probability arrays on GPU. 84 85 This is a port of scipy.spatial.distance.jensenshannon using a 86 numerically robust rel_entr kernel, except not taking the square root 87 88 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 89 """ 90 91 p = cp.asarray(p) 92 q = cp.asarray(q) 93 94 if p.dtype not in (cp.float32, cp.float64) or p.dtype != q.dtype : 95 raise TypeError( 96 f"Both arrays must be the same dtype and either float32 or float64, " 97 f"got p.dtype={p.dtype}, q.dtype={q.dtype}" 98 ) 99 100 # Normalize inputs along the specified axis 101 p = p / cp.sum(p, axis=axis, keepdims=True) 102 q = q / cp.sum(q, axis=axis, keepdims=True) 103 104 # Calculate the midpoint distribution 105 m = (p + q) / 2.0 106 107 # Calculate relative entropy using the robust CUDA kernel 108 # Note: rel_entr results are in nats 109 left = _rel_entr_kernel(p, m) 110 right = _rel_entr_kernel(q, m) 111 112 # Sum across the specified axis 113 left_sum = cp.sum(left, axis=axis, keepdims=keepdims) 114 right_sum = cp.sum(right, axis=axis, keepdims=keepdims) 115 116 # JSDiv = (D_kl(P||M) + D_kl(Q||M)) / 2 117 js = (left_sum + right_sum) / 2.0 118 119 # Convert units if base is provided (e.g., base=2 for bits) 120 if base is not None: 121 js /= cp.log(base) 122 123 return js / 2.0
Compute the Jensen-Shannon divergence between two probability arrays on GPU.
This is a port of scipy.spatial.distance.jensenshannon using a numerically robust rel_entr kernel, except not taking the square root
https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290
126def jensenshannon_gpu(p, q, axis=0, keepdims=False, base=None): 127 """ 128 Compute the Jensen-Shannon distance between two probability arrays on GPU. 129 130 This is a port of scipy.spatial.distance.jensenshannon using a 131 numerically robust rel_entr kernel. 132 133 https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290 134 """ 135 js = jensenshannondivergence_gpu( p=p, q=q, axis=axis, keepdims=keepdims, base=base ) 136 137 return cp.sqrt(js / 2.0)
Compute the Jensen-Shannon distance between two probability arrays on GPU.
This is a port of scipy.spatial.distance.jensenshannon using a numerically robust rel_entr kernel.
https://github.com/scipy/scipy/blob/v1.17.0/scipy/spatial/distance.py#L1205-L1290