1import functools
2import inspect
3import operator
4import types
5import warnings
6
7import numpy as np
8from numpy._core import overrides
9from numpy._core._multiarray_umath import _array_converter
10from numpy._core.multiarray import add_docstring
11
12from . import numeric as _nx
13from .numeric import asanyarray, nan, ndim, result_type
14
15__all__ = ['logspace', 'linspace', 'geomspace']
16
17
18array_function_dispatch = functools.partial(
19 overrides.array_function_dispatch, module='numpy')
20
21
22def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None,
23 dtype=None, axis=None, *, device=None):
24 return (start, stop)
25
26
27@array_function_dispatch(_linspace_dispatcher)
28def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
29 axis=0, *, device=None):
30 """
31 Return evenly spaced numbers over a specified interval.
32
33 Returns `num` evenly spaced samples, calculated over the
34 interval [`start`, `stop`].
35
36 The endpoint of the interval can optionally be excluded.
37
38 .. versionchanged:: 1.20.0
39 Values are rounded towards ``-inf`` instead of ``0`` when an
40 integer ``dtype`` is specified. The old behavior can
41 still be obtained with ``np.linspace(start, stop, num).astype(int)``
42
43 Parameters
44 ----------
45 start : array_like
46 The starting value of the sequence.
47 stop : array_like
48 The end value of the sequence, unless `endpoint` is set to False.
49 In that case, the sequence consists of all but the last of ``num + 1``
50 evenly spaced samples, so that `stop` is excluded. Note that the step
51 size changes when `endpoint` is False.
52 num : int, optional
53 Number of samples to generate. Default is 50. Must be non-negative.
54 endpoint : bool, optional
55 If True, `stop` is the last sample. Otherwise, it is not included.
56 Default is True.
57 retstep : bool, optional
58 If True, return (`samples`, `step`), where `step` is the spacing
59 between samples.
60 dtype : dtype, optional
61 The type of the output array. If `dtype` is not given, the data type
62 is inferred from `start` and `stop`. The inferred dtype will never be
63 an integer; `float` is chosen even if the arguments would produce an
64 array of integers.
65 axis : int, optional
66 The axis in the result to store the samples. Relevant only if start
67 or stop are array-like. By default (0), the samples will be along a
68 new axis inserted at the beginning. Use -1 to get an axis at the end.
69 device : str, optional
70 The device on which to place the created array. Default: None.
71 For Array-API interoperability only, so must be ``"cpu"`` if passed.
72
73 .. versionadded:: 2.0.0
74
75 Returns
76 -------
77 samples : ndarray
78 There are `num` equally spaced samples in the closed interval
79 ``[start, stop]`` or the half-open interval ``[start, stop)``
80 (depending on whether `endpoint` is True or False).
81 step : float, optional
82 Only returned if `retstep` is True
83
84 Size of spacing between samples.
85
86
87 See Also
88 --------
89 arange : Similar to `linspace`, but uses a step size (instead of the
90 number of samples).
91 geomspace : Similar to `linspace`, but with numbers spaced evenly on a log
92 scale (a geometric progression).
93 logspace : Similar to `geomspace`, but with the end points specified as
94 logarithms.
95 :ref:`how-to-partition`
96
97 Examples
98 --------
99 >>> import numpy as np
100 >>> np.linspace(2.0, 3.0, num=5)
101 array([2. , 2.25, 2.5 , 2.75, 3. ])
102 >>> np.linspace(2.0, 3.0, num=5, endpoint=False)
103 array([2. , 2.2, 2.4, 2.6, 2.8])
104 >>> np.linspace(2.0, 3.0, num=5, retstep=True)
105 (array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
106
107 Graphical illustration:
108
109 >>> import matplotlib.pyplot as plt
110 >>> N = 8
111 >>> y = np.zeros(N)
112 >>> x1 = np.linspace(0, 10, N, endpoint=True)
113 >>> x2 = np.linspace(0, 10, N, endpoint=False)
114 >>> plt.plot(x1, y, 'o')
115 [<matplotlib.lines.Line2D object at 0x...>]
116 >>> plt.plot(x2, y + 0.5, 'o')
117 [<matplotlib.lines.Line2D object at 0x...>]
118 >>> plt.ylim([-0.5, 1])
119 (-0.5, 1)
120 >>> plt.show()
121
122 """
123 num = operator.index(num)
124 if num < 0:
125 raise ValueError(
126 f"Number of samples, {num}, must be non-negative."
127 )
128 div = (num - 1) if endpoint else num
129
130 conv = _array_converter(start, stop)
131 start, stop = conv.as_arrays()
132 dt = conv.result_type(ensure_inexact=True)
133
134 if dtype is None:
135 dtype = dt
136 integer_dtype = False
137 else:
138 integer_dtype = _nx.issubdtype(dtype, _nx.integer)
139
140 # Use `dtype=type(dt)` to enforce a floating point evaluation:
141 delta = np.subtract(stop, start, dtype=type(dt))
142 y = _nx.arange(
143 0, num, dtype=dt, device=device
144 ).reshape((-1,) + (1,) * ndim(delta))
145
146 # In-place multiplication y *= delta/div is faster, but prevents
147 # the multiplicant from overriding what class is produced, and thus
148 # prevents, e.g. use of Quantities, see gh-7142. Hence, we multiply
149 # in place only for standard scalar types.
150 if div > 0:
151 _mult_inplace = _nx.isscalar(delta)
152 step = delta / div
153 any_step_zero = (
154 step == 0 if _mult_inplace else _nx.asanyarray(step == 0).any())
155 if any_step_zero:
156 # Special handling for denormal numbers, gh-5437
157 y /= div
158 if _mult_inplace:
159 y *= delta
160 else:
161 y = y * delta
162 elif _mult_inplace:
163 y *= step
164 else:
165 y = y * step
166 else:
167 # sequences with 0 items or 1 item with endpoint=True (i.e. div <= 0)
168 # have an undefined step
169 step = nan
170 # Multiply with delta to allow possible override of output class.
171 y = y * delta
172
173 y += start
174
175 if endpoint and num > 1:
176 y[-1, ...] = stop
177
178 if axis != 0:
179 y = _nx.moveaxis(y, 0, axis)
180
181 if integer_dtype:
182 _nx.floor(y, out=y)
183
184 y = conv.wrap(y.astype(dtype, copy=False))
185 if retstep:
186 return y, step
187 else:
188 return y
189
190
191def _logspace_dispatcher(start, stop, num=None, endpoint=None, base=None,
192 dtype=None, axis=None):
193 return (start, stop, base)
194
195
196@array_function_dispatch(_logspace_dispatcher)
197def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,
198 axis=0):
199 """
200 Return numbers spaced evenly on a log scale.
201
202 In linear space, the sequence starts at ``base ** start``
203 (`base` to the power of `start`) and ends with ``base ** stop``
204 (see `endpoint` below).
205
206 .. versionchanged:: 1.25.0
207 Non-scalar 'base` is now supported
208
209 Parameters
210 ----------
211 start : array_like
212 ``base ** start`` is the starting value of the sequence.
213 stop : array_like
214 ``base ** stop`` is the final value of the sequence, unless `endpoint`
215 is False. In that case, ``num + 1`` values are spaced over the
216 interval in log-space, of which all but the last (a sequence of
217 length `num`) are returned.
218 num : integer, optional
219 Number of samples to generate. Default is 50.
220 endpoint : boolean, optional
221 If true, `stop` is the last sample. Otherwise, it is not included.
222 Default is True.
223 base : array_like, optional
224 The base of the log space. The step size between the elements in
225 ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform.
226 Default is 10.0.
227 dtype : dtype
228 The type of the output array. If `dtype` is not given, the data type
229 is inferred from `start` and `stop`. The inferred type will never be
230 an integer; `float` is chosen even if the arguments would produce an
231 array of integers.
232 axis : int, optional
233 The axis in the result to store the samples. Relevant only if start,
234 stop, or base are array-like. By default (0), the samples will be
235 along a new axis inserted at the beginning. Use -1 to get an axis at
236 the end.
237
238 Returns
239 -------
240 samples : ndarray
241 `num` samples, equally spaced on a log scale.
242
243 See Also
244 --------
245 arange : Similar to linspace, with the step size specified instead of the
246 number of samples. Note that, when used with a float endpoint, the
247 endpoint may or may not be included.
248 linspace : Similar to logspace, but with the samples uniformly distributed
249 in linear space, instead of log space.
250 geomspace : Similar to logspace, but with endpoints specified directly.
251 :ref:`how-to-partition`
252
253 Notes
254 -----
255 If base is a scalar, logspace is equivalent to the code
256
257 >>> y = np.linspace(start, stop, num=num, endpoint=endpoint)
258 ... # doctest: +SKIP
259 >>> power(base, y).astype(dtype)
260 ... # doctest: +SKIP
261
262 Examples
263 --------
264 >>> import numpy as np
265 >>> np.logspace(2.0, 3.0, num=4)
266 array([ 100. , 215.443469 , 464.15888336, 1000. ])
267 >>> np.logspace(2.0, 3.0, num=4, endpoint=False)
268 array([100. , 177.827941 , 316.22776602, 562.34132519])
269 >>> np.logspace(2.0, 3.0, num=4, base=2.0)
270 array([4. , 5.0396842 , 6.34960421, 8. ])
271 >>> np.logspace(2.0, 3.0, num=4, base=[2.0, 3.0], axis=-1)
272 array([[ 4. , 5.0396842 , 6.34960421, 8. ],
273 [ 9. , 12.98024613, 18.72075441, 27. ]])
274
275 Graphical illustration:
276
277 >>> import matplotlib.pyplot as plt
278 >>> N = 10
279 >>> x1 = np.logspace(0.1, 1, N, endpoint=True)
280 >>> x2 = np.logspace(0.1, 1, N, endpoint=False)
281 >>> y = np.zeros(N)
282 >>> plt.plot(x1, y, 'o')
283 [<matplotlib.lines.Line2D object at 0x...>]
284 >>> plt.plot(x2, y + 0.5, 'o')
285 [<matplotlib.lines.Line2D object at 0x...>]
286 >>> plt.ylim([-0.5, 1])
287 (-0.5, 1)
288 >>> plt.show()
289
290 """
291 if not isinstance(base, (float, int)) and np.ndim(base):
292 # If base is non-scalar, broadcast it with the others, since it
293 # may influence how axis is interpreted.
294 ndmax = np.broadcast(start, stop, base).ndim
295 start, stop, base = (
296 np.array(a, copy=None, subok=True, ndmin=ndmax)
297 for a in (start, stop, base)
298 )
299 base = np.expand_dims(base, axis=axis)
300 y = linspace(start, stop, num=num, endpoint=endpoint, axis=axis)
301 if dtype is None:
302 return _nx.power(base, y)
303 return _nx.power(base, y).astype(dtype, copy=False)
304
305
306def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None,
307 axis=None):
308 return (start, stop)
309
310
311@array_function_dispatch(_geomspace_dispatcher)
312def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
313 """
314 Return numbers spaced evenly on a log scale (a geometric progression).
315
316 This is similar to `logspace`, but with endpoints specified directly.
317 Each output sample is a constant multiple of the previous.
318
319 Parameters
320 ----------
321 start : array_like
322 The starting value of the sequence.
323 stop : array_like
324 The final value of the sequence, unless `endpoint` is False.
325 In that case, ``num + 1`` values are spaced over the
326 interval in log-space, of which all but the last (a sequence of
327 length `num`) are returned.
328 num : integer, optional
329 Number of samples to generate. Default is 50.
330 endpoint : boolean, optional
331 If true, `stop` is the last sample. Otherwise, it is not included.
332 Default is True.
333 dtype : dtype
334 The type of the output array. If `dtype` is not given, the data type
335 is inferred from `start` and `stop`. The inferred dtype will never be
336 an integer; `float` is chosen even if the arguments would produce an
337 array of integers.
338 axis : int, optional
339 The axis in the result to store the samples. Relevant only if start
340 or stop are array-like. By default (0), the samples will be along a
341 new axis inserted at the beginning. Use -1 to get an axis at the end.
342
343 Returns
344 -------
345 samples : ndarray
346 `num` samples, equally spaced on a log scale.
347
348 See Also
349 --------
350 logspace : Similar to geomspace, but with endpoints specified using log
351 and base.
352 linspace : Similar to geomspace, but with arithmetic instead of geometric
353 progression.
354 arange : Similar to linspace, with the step size specified instead of the
355 number of samples.
356 :ref:`how-to-partition`
357
358 Notes
359 -----
360 If the inputs or dtype are complex, the output will follow a logarithmic
361 spiral in the complex plane. (There are an infinite number of spirals
362 passing through two points; the output will follow the shortest such path.)
363
364 Examples
365 --------
366 >>> import numpy as np
367 >>> np.geomspace(1, 1000, num=4)
368 array([ 1., 10., 100., 1000.])
369 >>> np.geomspace(1, 1000, num=3, endpoint=False)
370 array([ 1., 10., 100.])
371 >>> np.geomspace(1, 1000, num=4, endpoint=False)
372 array([ 1. , 5.62341325, 31.6227766 , 177.827941 ])
373 >>> np.geomspace(1, 256, num=9)
374 array([ 1., 2., 4., 8., 16., 32., 64., 128., 256.])
375
376 Note that the above may not produce exact integers:
377
378 >>> np.geomspace(1, 256, num=9, dtype=int)
379 array([ 1, 2, 4, 7, 16, 32, 63, 127, 256])
380 >>> np.around(np.geomspace(1, 256, num=9)).astype(int)
381 array([ 1, 2, 4, 8, 16, 32, 64, 128, 256])
382
383 Negative, decreasing, and complex inputs are allowed:
384
385 >>> np.geomspace(1000, 1, num=4)
386 array([1000., 100., 10., 1.])
387 >>> np.geomspace(-1000, -1, num=4)
388 array([-1000., -100., -10., -1.])
389 >>> np.geomspace(1j, 1000j, num=4) # Straight line
390 array([0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j])
391 >>> np.geomspace(-1+0j, 1+0j, num=5) # Circle
392 array([-1.00000000e+00+1.22464680e-16j, -7.07106781e-01+7.07106781e-01j,
393 6.12323400e-17+1.00000000e+00j, 7.07106781e-01+7.07106781e-01j,
394 1.00000000e+00+0.00000000e+00j])
395
396 Graphical illustration of `endpoint` parameter:
397
398 >>> import matplotlib.pyplot as plt
399 >>> N = 10
400 >>> y = np.zeros(N)
401 >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=True), y + 1, 'o')
402 [<matplotlib.lines.Line2D object at 0x...>]
403 >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=False), y + 2, 'o')
404 [<matplotlib.lines.Line2D object at 0x...>]
405 >>> plt.axis([0.5, 2000, 0, 3])
406 [0.5, 2000, 0, 3]
407 >>> plt.grid(True, color='0.7', linestyle='-', which='both', axis='both')
408 >>> plt.show()
409
410 """
411 start = asanyarray(start)
412 stop = asanyarray(stop)
413 if _nx.any(start == 0) or _nx.any(stop == 0):
414 raise ValueError('Geometric sequence cannot include zero')
415
416 dt = result_type(start, stop, float(num), _nx.zeros((), dtype))
417 if dtype is None:
418 dtype = dt
419 else:
420 # complex to dtype('complex128'), for instance
421 dtype = _nx.dtype(dtype)
422
423 # Promote both arguments to the same dtype in case, for instance, one is
424 # complex and another is negative and log would produce NaN otherwise.
425 # Copy since we may change things in-place further down.
426 start = start.astype(dt, copy=True)
427 stop = stop.astype(dt, copy=True)
428
429 # Allow negative real values and ensure a consistent result for complex
430 # (including avoiding negligible real or imaginary parts in output) by
431 # rotating start to positive real, calculating, then undoing rotation.
432 out_sign = _nx.sign(start)
433 start /= out_sign
434 stop = stop / out_sign
435
436 log_start = _nx.log10(start)
437 log_stop = _nx.log10(stop)
438 result = logspace(log_start, log_stop, num=num,
439 endpoint=endpoint, base=10.0, dtype=dt)
440
441 # Make sure the endpoints match the start and stop arguments. This is
442 # necessary because np.exp(np.log(x)) is not necessarily equal to x.
443 if num > 0:
444 result[0] = start
445 if num > 1 and endpoint:
446 result[-1] = stop
447
448 result *= out_sign
449
450 if axis != 0:
451 result = _nx.moveaxis(result, 0, axis)
452
453 return result.astype(dtype, copy=False)
454
455
456def _needs_add_docstring(obj):
457 """
458 Returns true if the only way to set the docstring of `obj` from python is
459 via add_docstring.
460
461 This function errs on the side of being overly conservative.
462 """
463 Py_TPFLAGS_HEAPTYPE = 1 << 9
464
465 if isinstance(obj, (types.FunctionType, types.MethodType, property)):
466 return False
467
468 if isinstance(obj, type) and obj.__flags__ & Py_TPFLAGS_HEAPTYPE:
469 return False
470
471 return True
472
473
474def _add_docstring(obj, doc, warn_on_python):
475 if warn_on_python and not _needs_add_docstring(obj):
476 warnings.warn(
477 f"add_newdoc was used on a pure-python object {obj}. "
478 "Prefer to attach it directly to the source.",
479 UserWarning,
480 stacklevel=3)
481
482 doc = inspect.cleandoc(doc)
483
484 try:
485 add_docstring(obj, doc)
486 except Exception:
487 pass
488
489
490def add_newdoc(place, obj, doc, warn_on_python=True):
491 """
492 Add documentation to an existing object, typically one defined in C
493
494 The purpose is to allow easier editing of the docstrings without requiring
495 a re-compile. This exists primarily for internal use within numpy itself.
496
497 Parameters
498 ----------
499 place : str
500 The absolute name of the module to import from
501 obj : str | None
502 The name of the object to add documentation to, typically a class or
503 function name.
504 doc : str | tuple[str, str] | list[tuple[str, str]]
505 If a string, the documentation to apply to `obj`
506
507 If a tuple, then the first element is interpreted as an attribute
508 of `obj` and the second as the docstring to apply -
509 ``(method, docstring)``
510
511 If a list, then each element of the list should be a tuple of length
512 two - ``[(method1, docstring1), (method2, docstring2), ...]``
513 warn_on_python : bool
514 If True, the default, emit `UserWarning` if this is used to attach
515 documentation to a pure-python object.
516
517 Notes
518 -----
519 This routine never raises an error if the docstring can't be written, but
520 will raise an error if the object being documented does not exist.
521
522 This routine cannot modify read-only docstrings, as appear
523 in new-style classes or built-in functions. Because this
524 routine never raises an error the caller must check manually
525 that the docstrings were changed.
526
527 Since this function grabs the ``char *`` from a c-level str object and puts
528 it into the ``tp_doc`` slot of the type of `obj`, it violates a number of
529 C-API best-practices, by:
530
531 - modifying a `PyTypeObject` after calling `PyType_Ready`
532 - calling `Py_INCREF` on the str and losing the reference, so the str
533 will never be released
534
535 If possible it should be avoided.
536 """
537 new = getattr(__import__(place, globals(), {}, [obj]), obj)
538 if isinstance(doc, str):
539 if "${ARRAY_FUNCTION_LIKE}" in doc:
540 doc = overrides.get_array_function_like_doc(new, doc)
541 _add_docstring(new, doc, warn_on_python)
542 elif isinstance(doc, tuple):
543 attr, docstring = doc
544 _add_docstring(getattr(new, attr), docstring, warn_on_python)
545 elif isinstance(doc, list):
546 for attr, docstring in doc:
547 _add_docstring(getattr(new, attr), docstring, warn_on_python)