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