miepom 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- miepom/__init__.py +1 -0
- miepom/backend.py +33 -0
- miepom/config.py +216 -0
- miepom/core.py +12 -0
- miepom/linalg.py +195 -0
- miepom/particles.py +283 -0
- miepom/plane.py +231 -0
- miepom/pom.py +211 -0
- miepom/sensor.py +289 -0
- miepom/sim.py +213 -0
- miepom/utils.py +35 -0
- miepom-0.2.0.dist-info/METADATA +59 -0
- miepom-0.2.0.dist-info/RECORD +16 -0
- miepom-0.2.0.dist-info/WHEEL +5 -0
- miepom-0.2.0.dist-info/licenses/LICENSE +21 -0
- miepom-0.2.0.dist-info/top_level.txt +1 -0
miepom/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
miepom/backend.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Backend switching for the MIEPOM library.
|
|
2
|
+
|
|
3
|
+
Backend switching allows the user to change the underlying computational library (e.g., NumPy, CuPy) used by the MIEPOM library.
|
|
4
|
+
Use the `set_backend(xp)` function to switch the backend, where `xp` is the desired computational library (e.g., `numpy`, `cupy`).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .sim import set_backend as _set_simulator_backend
|
|
8
|
+
from .linalg import set_backend as _set_linalg_backend
|
|
9
|
+
from .sensor import set_backend as _set_sensor_backend
|
|
10
|
+
from .pom import set_backend as _set_microscope_backend
|
|
11
|
+
from .config import set_backend as _set_config_backend
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
globals()["xp"] = np
|
|
16
|
+
|
|
17
|
+
def set_backend(xp):
|
|
18
|
+
"""Set the linear algebra backend for the MIEPOM library."""
|
|
19
|
+
_set_linalg_backend(xp)
|
|
20
|
+
_set_sensor_backend(xp)
|
|
21
|
+
_set_microscope_backend(xp)
|
|
22
|
+
_set_simulator_backend(xp)
|
|
23
|
+
_set_config_backend(xp)
|
|
24
|
+
globals()["xp"] = xp
|
|
25
|
+
print(f"MIEPOM backend set to {xp.__name__}")
|
|
26
|
+
|
|
27
|
+
def get_backend():
|
|
28
|
+
"""Get the name of the current linear algebra backend for the MIEPOM library."""
|
|
29
|
+
return globals()["xp"]
|
|
30
|
+
|
|
31
|
+
def get_backend_name():
|
|
32
|
+
"""Get the name of the current linear algebra backend for the MIEPOM library."""
|
|
33
|
+
return globals()["xp"].__name__
|
miepom/config.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Configuration settings for the MIEPOM library."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os, time
|
|
5
|
+
from .utils import print_line
|
|
6
|
+
|
|
7
|
+
globals()["xp"] = np
|
|
8
|
+
|
|
9
|
+
def set_backend(xp):
|
|
10
|
+
globals()["xp"] = xp
|
|
11
|
+
try:
|
|
12
|
+
import scipy as sp
|
|
13
|
+
USESCIPY = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
USESCIPY = False
|
|
16
|
+
try:
|
|
17
|
+
import numba as nb
|
|
18
|
+
USENUMBA = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
USENUMBA = False
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import mlx.core as mx
|
|
24
|
+
USEMLX = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
USEMLX = False
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
import cupy as cp
|
|
30
|
+
USECUPY = True
|
|
31
|
+
except ImportError:
|
|
32
|
+
USECUPY = False
|
|
33
|
+
|
|
34
|
+
VERBOSE = False if os.environ.get("MIEPOM_VERBOSE", "0") == "0" else True
|
|
35
|
+
|
|
36
|
+
# always use numpy as the default backend for the core object, but allow switching to other backends if available
|
|
37
|
+
DEFAULT_BACKEND = "numpy"
|
|
38
|
+
|
|
39
|
+
#: Computation precision. Note that if MLX (or CuPy) is used, it is always single precision
|
|
40
|
+
PRECISION = "single" if os.environ.get("MIEPOM_USE_DOUBLE", "0") == "0" else "double" # 'single' or 'double'
|
|
41
|
+
|
|
42
|
+
#: whether to use fast math (numba)
|
|
43
|
+
NBFASTMATH = False if os.environ.get("MIEPOM_FASTMATH", "0") == "0" else True
|
|
44
|
+
|
|
45
|
+
#: numba's compile target argument for vectorize and guvectorize functions.
|
|
46
|
+
NBTARGET = os.environ.get("MIEPOM_TARGET", "parallel")
|
|
47
|
+
if NBTARGET not in ["cpu", "parallel", "cuda"]:
|
|
48
|
+
raise ValueError(f"Invalid MIEPOM_TARGET value: {NBTARGET}. Must be one of 'cpu', 'parallel', or 'cuda'.")
|
|
49
|
+
|
|
50
|
+
#: if set to True, it assumes a vectorized version of the miepython library
|
|
51
|
+
VECTORIZED_MIE = False if os.environ.get("MIEPOM_VECTORIZED_MIE", "0") == "0" else True
|
|
52
|
+
|
|
53
|
+
# Computed constants below. Do not modify!
|
|
54
|
+
#-----------------------------------------
|
|
55
|
+
|
|
56
|
+
if USENUMBA:
|
|
57
|
+
import numba as nb
|
|
58
|
+
|
|
59
|
+
class nbtype():
|
|
60
|
+
def __getitem__(self, key):
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
import mlx.core as mx
|
|
65
|
+
mx_compile = mx.compile
|
|
66
|
+
mx_eval = mx.eval
|
|
67
|
+
except ImportError:
|
|
68
|
+
mx_compile = lambda f: f
|
|
69
|
+
mx_eval = lambda *args, **kwargs: None
|
|
70
|
+
|
|
71
|
+
def mx_compile_out(func):
|
|
72
|
+
"""A decorator to compile a function with mlx.core.compile. This is used to compile the sensor_image function with mlx.
|
|
73
|
+
It also handles the 'out' keyword argument for in-place computation.
|
|
74
|
+
"""
|
|
75
|
+
func = mx_compile(func)
|
|
76
|
+
def wrapper(*args, **kwargs):
|
|
77
|
+
if 'out' in kwargs:
|
|
78
|
+
out = kwargs.pop('out')
|
|
79
|
+
else:
|
|
80
|
+
out = None
|
|
81
|
+
if out is not None:
|
|
82
|
+
out[:] = func(*args, **kwargs)[:]
|
|
83
|
+
else:
|
|
84
|
+
out = func(*args, **kwargs)
|
|
85
|
+
return out
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
def mx_out(func):
|
|
89
|
+
def wrapper(*args, **kwargs):
|
|
90
|
+
if 'out' in kwargs:
|
|
91
|
+
out = kwargs.pop('out')
|
|
92
|
+
else:
|
|
93
|
+
out = None
|
|
94
|
+
if out is not None:
|
|
95
|
+
out[:] = func(*args, **kwargs)[:]
|
|
96
|
+
else:
|
|
97
|
+
out = func(*args, **kwargs)
|
|
98
|
+
return out
|
|
99
|
+
return wrapper
|
|
100
|
+
|
|
101
|
+
if USENUMBA:
|
|
102
|
+
vectorize = nb.vectorize
|
|
103
|
+
guvectorize = nb.guvectorize
|
|
104
|
+
jit = nb.jit
|
|
105
|
+
njit = nb.njit
|
|
106
|
+
else:
|
|
107
|
+
vectorize = lambda f,*args,**kwargs: f
|
|
108
|
+
guvectorize = lambda f,*args,**kwargs: f
|
|
109
|
+
jit = lambda f,*args,**kwargs: f
|
|
110
|
+
njit = lambda f,*args,**kwargs: f
|
|
111
|
+
NBFLOAT = nbtype()
|
|
112
|
+
NBCOMPLEX = nbtype()
|
|
113
|
+
NBUINT = nbtype()
|
|
114
|
+
NBBOOL = nbtype()
|
|
115
|
+
|
|
116
|
+
nbfloat = nbtype()
|
|
117
|
+
nbcomplex = nbtype()
|
|
118
|
+
nbuint = nbtype()
|
|
119
|
+
nbbool = nbtype()
|
|
120
|
+
|
|
121
|
+
os.environ["MIEPYTHON_CACHE"] = "0"
|
|
122
|
+
os.environ["MIEPYTHON_FASTMATH"] = "1"
|
|
123
|
+
os.environ["MIEPYTHON_TARGET"] = NBTARGET
|
|
124
|
+
|
|
125
|
+
if PRECISION == "single":
|
|
126
|
+
os.environ["MIEPYTHON_USE_DOUBLE"] = "0"
|
|
127
|
+
if USENUMBA:
|
|
128
|
+
NBFLOAT = nb.float32
|
|
129
|
+
NBCOMPLEX = nb.complex64
|
|
130
|
+
NBUINT = nb.uint16
|
|
131
|
+
NBBOOL = nb.boolean
|
|
132
|
+
|
|
133
|
+
nbfloat = nb.float32
|
|
134
|
+
nbcomplex = nb.complex64
|
|
135
|
+
nbuint = nb.uint16
|
|
136
|
+
nbbool = nb.boolean
|
|
137
|
+
|
|
138
|
+
CDTYPE = np.complex64
|
|
139
|
+
FDTYPE = np.float32
|
|
140
|
+
UDTYPE = "uint16"
|
|
141
|
+
|
|
142
|
+
cdtype = lambda: xp.complex64
|
|
143
|
+
fdtype = lambda: xp.float32
|
|
144
|
+
udtype = lambda: xp.uint16
|
|
145
|
+
|
|
146
|
+
elif PRECISION == "double":
|
|
147
|
+
os.environ["MIEPYTHON_USE_DOUBLE"] = "1"
|
|
148
|
+
if USENUMBA:
|
|
149
|
+
NBFLOAT = nb.float64
|
|
150
|
+
NBCOMPLEX = nb.complex128
|
|
151
|
+
NBUINT = nb.uint16
|
|
152
|
+
NBBOOL = nb.boolean
|
|
153
|
+
|
|
154
|
+
nbfloat = nb.float64
|
|
155
|
+
nbcomplex = nb.complex128
|
|
156
|
+
nbuint = nb.uint16
|
|
157
|
+
nbbool = nb.boolean
|
|
158
|
+
|
|
159
|
+
CDTYPE = np.complex128
|
|
160
|
+
FDTYPE = np.float64
|
|
161
|
+
UDTYPE = "uint16"
|
|
162
|
+
|
|
163
|
+
cdtype = lambda: xp.complex128
|
|
164
|
+
fdtype = lambda: xp.float64
|
|
165
|
+
udtype = lambda: xp.uint16
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError("Invalid precision name")
|
|
168
|
+
|
|
169
|
+
import miepython as mie
|
|
170
|
+
|
|
171
|
+
def print_settings():
|
|
172
|
+
print("Library settings")
|
|
173
|
+
print_line("-")
|
|
174
|
+
|
|
175
|
+
print(f"Using Numba: {USENUMBA}")
|
|
176
|
+
if USENUMBA:
|
|
177
|
+
print(f"Using fastmath: {NBFASTMATH}")
|
|
178
|
+
print(f"Numba target: '{NBTARGET}'")
|
|
179
|
+
print(f"Num threads: {nb.get_num_threads()}")
|
|
180
|
+
print(f"Precission: '{PRECISION}'")
|
|
181
|
+
print(f"Using mlx: {USEMLX}")
|
|
182
|
+
print(f"Using cupy: {USECUPY}")
|
|
183
|
+
print_line("=")
|
|
184
|
+
|
|
185
|
+
if VERBOSE:
|
|
186
|
+
print_settings()
|
|
187
|
+
|
|
188
|
+
print("numpy", np.__version__)
|
|
189
|
+
print("miepython", mie.__version__)
|
|
190
|
+
if USESCIPY:
|
|
191
|
+
print("scipy", sp.__version__)
|
|
192
|
+
if USECUPY:
|
|
193
|
+
print("cupy", cp.__version__)
|
|
194
|
+
if USEMLX:
|
|
195
|
+
print("mlx", mx.__version__)
|
|
196
|
+
if USENUMBA:
|
|
197
|
+
print("numba", nb.__version__)
|
|
198
|
+
|
|
199
|
+
print("-----------------------------------------------------------------------")
|
|
200
|
+
|
|
201
|
+
def get_array(x):
|
|
202
|
+
"""Convert array to numpy array if it is a cupy or mlx array, otherwise return the input as is.
|
|
203
|
+
This is useful for converting arrays to numpy for further processing or visualization."""
|
|
204
|
+
if xp.__name__ == 'mlx.core':
|
|
205
|
+
# Evaluate the eoutput using mlx.eval to ensure it is a computed array in the mlx backend.
|
|
206
|
+
# This is necessary because mlx uses lazy evaluation, and we want to ensure that the result is a concrete array.
|
|
207
|
+
mx_eval(x)
|
|
208
|
+
return np.asarray(x)
|
|
209
|
+
elif xp.__name__ == 'cupy':
|
|
210
|
+
return x.get() # Convert CuPy array to NumPy array
|
|
211
|
+
else:
|
|
212
|
+
#try conversion to numpy array, if it fails, return the input as is
|
|
213
|
+
try:
|
|
214
|
+
return np.asarray(x)
|
|
215
|
+
except Exception:
|
|
216
|
+
return x
|
miepom/core.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Core module for the MIEPOM library.
|
|
2
|
+
|
|
3
|
+
This module provides convenient access to the main components of the MIEPOM library, including particles, simulators, microscopes, sensors, and backend management.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .particles import *
|
|
7
|
+
from .sim import *
|
|
8
|
+
from .pom import *
|
|
9
|
+
from .sensor import *
|
|
10
|
+
from .backend import *
|
|
11
|
+
|
|
12
|
+
from .config import get_array
|
miepom/linalg.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Linear algebra utilities for the PPOM library."""
|
|
2
|
+
|
|
3
|
+
# Compiled numba code
|
|
4
|
+
from .config import USENUMBA, nbfloat,nbcomplex, nbuint, nbbool, NBFASTMATH, NBTARGET, vectorize, guvectorize, jit, njit, mx_compile, mx_compile_out, mx_compile
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
# Set the default linear algebra backend to numpy so that we can compile and run numba functions correctly.
|
|
8
|
+
globals()["xp"] = np
|
|
9
|
+
|
|
10
|
+
def backend_name():
|
|
11
|
+
"""Return the name of the current linear algebra backend."""
|
|
12
|
+
return globals()["xp"].__name__
|
|
13
|
+
|
|
14
|
+
def set_backend(xp):
|
|
15
|
+
globals()["xp"] = xp
|
|
16
|
+
|
|
17
|
+
# data packing and unpacking functions for boolean masks
|
|
18
|
+
|
|
19
|
+
# reference implementation of the pack function using numpy advanced indexing.
|
|
20
|
+
def _pack_np(data,mask):
|
|
21
|
+
return data[...,mask]
|
|
22
|
+
|
|
23
|
+
# numba implementation of the pack function, requires a dummy argument so that we can determine the output shape at compile time. This is a hack, but it works.
|
|
24
|
+
@guvectorize([(nbcomplex[:,:],nbbool[:,:],nbcomplex[:],nbcomplex[:])],
|
|
25
|
+
"(m,n),(m,n),(l)->(l)",boundscheck = False, target = NBTARGET, fastmath = NBFASTMATH)
|
|
26
|
+
def __pack_nb(a, mask, _dummy, out):
|
|
27
|
+
n = 0
|
|
28
|
+
for i in range(mask.shape[0]):
|
|
29
|
+
for j in range(mask.shape[1]):
|
|
30
|
+
if mask[i,j] == True:
|
|
31
|
+
if n < len(out):
|
|
32
|
+
out[n] = a[i,j]
|
|
33
|
+
n+=1
|
|
34
|
+
|
|
35
|
+
# numba wrapper to remove the dummy argument
|
|
36
|
+
def _pack_nb(a,mask, out = None):
|
|
37
|
+
if out is None:
|
|
38
|
+
out = np.empty(a.shape[:-2] + (np.sum(mask),), dtype = a.dtype)
|
|
39
|
+
return __pack_nb(a,mask,out,out = out)
|
|
40
|
+
|
|
41
|
+
# generic xp implementation that calls the numpy version and adds support for output array
|
|
42
|
+
def _pack_xp(data,mask, out = None):
|
|
43
|
+
if out is None:
|
|
44
|
+
return _pack_np(data,mask)
|
|
45
|
+
else:
|
|
46
|
+
out[:] = _pack_np(data,mask)
|
|
47
|
+
return out
|
|
48
|
+
|
|
49
|
+
# boolean indices are not supported in mlx, so we need to implement a custom pack function for mlx
|
|
50
|
+
def _pack_mx(data,mask, out = None):
|
|
51
|
+
if out is None:
|
|
52
|
+
#we convert to numpy array first, because mlx does not support boolean indexing
|
|
53
|
+
return xp.array(np.asarray(data)[...,mask])
|
|
54
|
+
else:
|
|
55
|
+
out[:] = np.asarray(data)[...,mask]
|
|
56
|
+
return out
|
|
57
|
+
|
|
58
|
+
def _unpack_np(mask,data, out = None):
|
|
59
|
+
if out is None:
|
|
60
|
+
out = xp.zeros(data.shape[:-1] + mask.shape, dtype = data.dtype)
|
|
61
|
+
out[...] = 0.
|
|
62
|
+
out[...,mask] = data
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
_unpack_xp = _unpack_np
|
|
66
|
+
|
|
67
|
+
# the mx version cannot use vectorize version because it does not support boolean indexing, so we need to implement a custom unpack function for mlx
|
|
68
|
+
def __unpack_mx(mask,data, out = None):
|
|
69
|
+
if out is None:
|
|
70
|
+
out = xp.zeros(mask.shape, dtype = data.dtype)
|
|
71
|
+
out[:] = 0.
|
|
72
|
+
out[mask] = data
|
|
73
|
+
return out
|
|
74
|
+
|
|
75
|
+
# the vectorized version of the mlx unpack function is used when the data is 2D, so that we can use vmap to speed up the computation.
|
|
76
|
+
def __unpack_mx_vec(mask,data, out = None):
|
|
77
|
+
if out is None:
|
|
78
|
+
return xp.vmap(__unpack_mx, in_axes=(None, 0), out_axes=0)(mask,data)
|
|
79
|
+
else:
|
|
80
|
+
out[:] = 0.
|
|
81
|
+
tmp = xp.vmap(__unpack_mx, in_axes=(None, 0), out_axes=0)(mask,data)
|
|
82
|
+
out[:] = tmp
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
def _unpack_mx(mask,data, out = None):
|
|
86
|
+
if data.ndim == 2:
|
|
87
|
+
return __unpack_mx_vec(mask,data, out = out)
|
|
88
|
+
else:
|
|
89
|
+
return __unpack_mx(mask,data, out = out)
|
|
90
|
+
|
|
91
|
+
@jit([(nbbool[:,:],nbcomplex[:],nbcomplex[:,:])])
|
|
92
|
+
def __unpack_nb(mask,data,out):
|
|
93
|
+
k = 0
|
|
94
|
+
for i in range(out.shape[0]):
|
|
95
|
+
for j in range(out.shape[1]):
|
|
96
|
+
if mask[i,j] == True:
|
|
97
|
+
out[i,j] = data[k]
|
|
98
|
+
k+=1
|
|
99
|
+
else:
|
|
100
|
+
out[i,j] = 0.
|
|
101
|
+
|
|
102
|
+
@guvectorize([(nbbool[:,:],nbcomplex[:],nbcomplex[:,:])], "(n,m),(k)->(n,m)", target = NBTARGET, fastmath = NBFASTMATH)
|
|
103
|
+
def _unpack_nb(mask,data,out):
|
|
104
|
+
__unpack_nb(mask,data,out)
|
|
105
|
+
|
|
106
|
+
def unpack(mask, data, out = None):
|
|
107
|
+
"""Unpack a 1D array into a 2D array at the locations specified by a boolean mask."""
|
|
108
|
+
if backend_name() == "mlx.core":
|
|
109
|
+
return _unpack_mx(mask,data, out = out)
|
|
110
|
+
elif backend_name() == "numba":
|
|
111
|
+
return _unpack_nb(mask,data, out = out)
|
|
112
|
+
else:
|
|
113
|
+
return _unpack_xp(mask,data, out)
|
|
114
|
+
|
|
115
|
+
def pack(data, mask, out = None):
|
|
116
|
+
"""Pack a 2D array into a 1D array at the locations specified by a boolean mask."""
|
|
117
|
+
if backend_name() == "mlx.core":
|
|
118
|
+
return _pack_mx(data, mask, out)
|
|
119
|
+
elif backend_name() == "numba":
|
|
120
|
+
return _pack_nb(data, mask, out)
|
|
121
|
+
else:
|
|
122
|
+
return _pack_xp(data, mask, out)
|
|
123
|
+
|
|
124
|
+
def _abs2_np(a):
|
|
125
|
+
return a.real**2 + a.imag**2
|
|
126
|
+
|
|
127
|
+
_abs2_nb = vectorize([nbfloat(nbcomplex)],target = NBTARGET,fastmath = NBFASTMATH)(_abs2_np)
|
|
128
|
+
|
|
129
|
+
def _abs2_mx(a, out = None):
|
|
130
|
+
if out is None:
|
|
131
|
+
return mx_compile(_abs2_np)(a)
|
|
132
|
+
else:
|
|
133
|
+
out[:] = mx_compile(_abs2_np)(a)
|
|
134
|
+
return out
|
|
135
|
+
|
|
136
|
+
def _abs2_xp(a, out = None):
|
|
137
|
+
x2 = np.real(a)**2
|
|
138
|
+
y = np.imag(a)
|
|
139
|
+
y2 = np.multiply(y, y, out = out)
|
|
140
|
+
return np.add(x2, y2, out = y2)
|
|
141
|
+
|
|
142
|
+
def abs2(a, out = None):
|
|
143
|
+
if backend_name() == "mlx.core":
|
|
144
|
+
return _abs2_mx(a, out)
|
|
145
|
+
elif backend_name() == "numba":
|
|
146
|
+
return _abs2_nb(a, out)
|
|
147
|
+
else:
|
|
148
|
+
return _abs2_xp(a, out)
|
|
149
|
+
|
|
150
|
+
_vecdot_xp = np.vecdot
|
|
151
|
+
|
|
152
|
+
def _vecdot_mx(a,b, out = None):
|
|
153
|
+
if out is None:
|
|
154
|
+
return mx_compile(_vecdot_xp)(a,b)
|
|
155
|
+
else:
|
|
156
|
+
out[:] = mx_compile(_vecdot_xp)(a,b)
|
|
157
|
+
return out
|
|
158
|
+
|
|
159
|
+
@guvectorize([(nbfloat[:],nbfloat[:],nbfloat[:])], "(n),(n)->()",
|
|
160
|
+
fastmath = NBFASTMATH, cache = True, target = NBTARGET)
|
|
161
|
+
def _vecdot_nb(a,b, out):
|
|
162
|
+
tmp = 0
|
|
163
|
+
for i in range(3):
|
|
164
|
+
tmp += a[i] * b[i]
|
|
165
|
+
out[0] = tmp
|
|
166
|
+
|
|
167
|
+
def vecdot(a,b, out = None):
|
|
168
|
+
if backend_name() == "mlx.core":
|
|
169
|
+
return _vecdot_mx(a,b, out)
|
|
170
|
+
elif backend_name() == "numba":
|
|
171
|
+
return _vecdot_nb(a,b, out)
|
|
172
|
+
else:
|
|
173
|
+
return _vecdot_xp(a,b, out)
|
|
174
|
+
|
|
175
|
+
def _exp_xp(x):
|
|
176
|
+
return xp.exp(x)
|
|
177
|
+
|
|
178
|
+
def _exp_mx(x, out = None):
|
|
179
|
+
if out is None:
|
|
180
|
+
return mx_compile(_exp_xp)(x)
|
|
181
|
+
else:
|
|
182
|
+
out[:] = mx_compile(_exp_xp)(x)
|
|
183
|
+
return out
|
|
184
|
+
|
|
185
|
+
_exp_nb = vectorize([nbcomplex(nbcomplex)], target = NBTARGET, fastmath = NBFASTMATH)(_exp_xp)
|
|
186
|
+
|
|
187
|
+
def exp(x):
|
|
188
|
+
"""Compute the complex exponential of a real array."""
|
|
189
|
+
if backend_name() == "mlx.core":
|
|
190
|
+
return _exp_mx(x)
|
|
191
|
+
elif backend_name() == "numba":
|
|
192
|
+
return _exp_nb(x)
|
|
193
|
+
else:
|
|
194
|
+
return _exp_xp(x)
|
|
195
|
+
|