123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- import functools
- import warnings
- import operator
- import types
- from . import numeric as _nx
- from .numeric import result_type, NaN, asanyarray, ndim
- from numpy.core.multiarray import add_docstring
- from numpy.core import overrides
- __all__ = ['logspace', 'linspace', 'geomspace']
- array_function_dispatch = functools.partial(
- overrides.array_function_dispatch, module='numpy')
- def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None,
- dtype=None, axis=None):
- return (start, stop)
- @array_function_dispatch(_linspace_dispatcher)
- def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
- axis=0):
- """
- Return evenly spaced numbers over a specified interval.
- Returns `num` evenly spaced samples, calculated over the
- interval [`start`, `stop`].
- The endpoint of the interval can optionally be excluded.
- .. versionchanged:: 1.16.0
- Non-scalar `start` and `stop` are now supported.
- .. versionchanged:: 1.20.0
- Values are rounded towards ``-inf`` instead of ``0`` when an
- integer ``dtype`` is specified. The old behavior can
- still be obtained with ``np.linspace(start, stop, num).astype(int)``
- Parameters
- ----------
- start : array_like
- The starting value of the sequence.
- stop : array_like
- The end value of the sequence, unless `endpoint` is set to False.
- In that case, the sequence consists of all but the last of ``num + 1``
- evenly spaced samples, so that `stop` is excluded. Note that the step
- size changes when `endpoint` is False.
- num : int, optional
- Number of samples to generate. Default is 50. Must be non-negative.
- endpoint : bool, optional
- If True, `stop` is the last sample. Otherwise, it is not included.
- Default is True.
- retstep : bool, optional
- If True, return (`samples`, `step`), where `step` is the spacing
- between samples.
- dtype : dtype, optional
- The type of the output array. If `dtype` is not given, the data type
- is inferred from `start` and `stop`. The inferred dtype will never be
- an integer; `float` is chosen even if the arguments would produce an
- array of integers.
- .. versionadded:: 1.9.0
- axis : int, optional
- The axis in the result to store the samples. Relevant only if start
- or stop are array-like. By default (0), the samples will be along a
- new axis inserted at the beginning. Use -1 to get an axis at the end.
- .. versionadded:: 1.16.0
- Returns
- -------
- samples : ndarray
- There are `num` equally spaced samples in the closed interval
- ``[start, stop]`` or the half-open interval ``[start, stop)``
- (depending on whether `endpoint` is True or False).
- step : float, optional
- Only returned if `retstep` is True
- Size of spacing between samples.
- See Also
- --------
- arange : Similar to `linspace`, but uses a step size (instead of the
- number of samples).
- geomspace : Similar to `linspace`, but with numbers spaced evenly on a log
- scale (a geometric progression).
- logspace : Similar to `geomspace`, but with the end points specified as
- logarithms.
- :ref:`how-to-partition`
- Examples
- --------
- >>> np.linspace(2.0, 3.0, num=5)
- array([2. , 2.25, 2.5 , 2.75, 3. ])
- >>> np.linspace(2.0, 3.0, num=5, endpoint=False)
- array([2. , 2.2, 2.4, 2.6, 2.8])
- >>> np.linspace(2.0, 3.0, num=5, retstep=True)
- (array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
- Graphical illustration:
- >>> import matplotlib.pyplot as plt
- >>> N = 8
- >>> y = np.zeros(N)
- >>> x1 = np.linspace(0, 10, N, endpoint=True)
- >>> x2 = np.linspace(0, 10, N, endpoint=False)
- >>> plt.plot(x1, y, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.plot(x2, y + 0.5, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.ylim([-0.5, 1])
- (-0.5, 1)
- >>> plt.show()
- """
- num = operator.index(num)
- if num < 0:
- raise ValueError("Number of samples, %s, must be non-negative." % num)
- div = (num - 1) if endpoint else num
- # Convert float/complex array scalars to float, gh-3504
- # and make sure one can use variables that have an __array_interface__, gh-6634
- start = asanyarray(start) * 1.0
- stop = asanyarray(stop) * 1.0
- dt = result_type(start, stop, float(num))
- if dtype is None:
- dtype = dt
- integer_dtype = False
- else:
- integer_dtype = _nx.issubdtype(dtype, _nx.integer)
- delta = stop - start
- y = _nx.arange(0, num, dtype=dt).reshape((-1,) + (1,) * ndim(delta))
- # In-place multiplication y *= delta/div is faster, but prevents the multiplicant
- # from overriding what class is produced, and thus prevents, e.g. use of Quantities,
- # see gh-7142. Hence, we multiply in place only for standard scalar types.
- if div > 0:
- _mult_inplace = _nx.isscalar(delta)
- step = delta / div
- any_step_zero = (
- step == 0 if _mult_inplace else _nx.asanyarray(step == 0).any())
- if any_step_zero:
- # Special handling for denormal numbers, gh-5437
- y /= div
- if _mult_inplace:
- y *= delta
- else:
- y = y * delta
- else:
- if _mult_inplace:
- y *= step
- else:
- y = y * step
- else:
- # sequences with 0 items or 1 item with endpoint=True (i.e. div <= 0)
- # have an undefined step
- step = NaN
- # Multiply with delta to allow possible override of output class.
- y = y * delta
- y += start
- if endpoint and num > 1:
- y[-1] = stop
- if axis != 0:
- y = _nx.moveaxis(y, 0, axis)
- if integer_dtype:
- _nx.floor(y, out=y)
- if retstep:
- return y.astype(dtype, copy=False), step
- else:
- return y.astype(dtype, copy=False)
- def _logspace_dispatcher(start, stop, num=None, endpoint=None, base=None,
- dtype=None, axis=None):
- return (start, stop)
- @array_function_dispatch(_logspace_dispatcher)
- def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,
- axis=0):
- """
- Return numbers spaced evenly on a log scale.
- In linear space, the sequence starts at ``base ** start``
- (`base` to the power of `start`) and ends with ``base ** stop``
- (see `endpoint` below).
- .. versionchanged:: 1.16.0
- Non-scalar `start` and `stop` are now supported.
- Parameters
- ----------
- start : array_like
- ``base ** start`` is the starting value of the sequence.
- stop : array_like
- ``base ** stop`` is the final value of the sequence, unless `endpoint`
- is False. In that case, ``num + 1`` values are spaced over the
- interval in log-space, of which all but the last (a sequence of
- length `num`) are returned.
- num : integer, optional
- Number of samples to generate. Default is 50.
- endpoint : boolean, optional
- If true, `stop` is the last sample. Otherwise, it is not included.
- Default is True.
- base : array_like, optional
- The base of the log space. The step size between the elements in
- ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform.
- Default is 10.0.
- dtype : dtype
- The type of the output array. If `dtype` is not given, the data type
- is inferred from `start` and `stop`. The inferred type will never be
- an integer; `float` is chosen even if the arguments would produce an
- array of integers.
- axis : int, optional
- The axis in the result to store the samples. Relevant only if start
- or stop are array-like. By default (0), the samples will be along a
- new axis inserted at the beginning. Use -1 to get an axis at the end.
- .. versionadded:: 1.16.0
- Returns
- -------
- samples : ndarray
- `num` samples, equally spaced on a log scale.
- See Also
- --------
- arange : Similar to linspace, with the step size specified instead of the
- number of samples. Note that, when used with a float endpoint, the
- endpoint may or may not be included.
- linspace : Similar to logspace, but with the samples uniformly distributed
- in linear space, instead of log space.
- geomspace : Similar to logspace, but with endpoints specified directly.
- :ref:`how-to-partition`
- Notes
- -----
- Logspace is equivalent to the code
- >>> y = np.linspace(start, stop, num=num, endpoint=endpoint)
- ... # doctest: +SKIP
- >>> power(base, y).astype(dtype)
- ... # doctest: +SKIP
- Examples
- --------
- >>> np.logspace(2.0, 3.0, num=4)
- array([ 100. , 215.443469 , 464.15888336, 1000. ])
- >>> np.logspace(2.0, 3.0, num=4, endpoint=False)
- array([100. , 177.827941 , 316.22776602, 562.34132519])
- >>> np.logspace(2.0, 3.0, num=4, base=2.0)
- array([4. , 5.0396842 , 6.34960421, 8. ])
- Graphical illustration:
- >>> import matplotlib.pyplot as plt
- >>> N = 10
- >>> x1 = np.logspace(0.1, 1, N, endpoint=True)
- >>> x2 = np.logspace(0.1, 1, N, endpoint=False)
- >>> y = np.zeros(N)
- >>> plt.plot(x1, y, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.plot(x2, y + 0.5, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.ylim([-0.5, 1])
- (-0.5, 1)
- >>> plt.show()
- """
- y = linspace(start, stop, num=num, endpoint=endpoint, axis=axis)
- if dtype is None:
- return _nx.power(base, y)
- return _nx.power(base, y).astype(dtype, copy=False)
- def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None,
- axis=None):
- return (start, stop)
- @array_function_dispatch(_geomspace_dispatcher)
- def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
- """
- Return numbers spaced evenly on a log scale (a geometric progression).
- This is similar to `logspace`, but with endpoints specified directly.
- Each output sample is a constant multiple of the previous.
- .. versionchanged:: 1.16.0
- Non-scalar `start` and `stop` are now supported.
- Parameters
- ----------
- start : array_like
- The starting value of the sequence.
- stop : array_like
- The final value of the sequence, unless `endpoint` is False.
- In that case, ``num + 1`` values are spaced over the
- interval in log-space, of which all but the last (a sequence of
- length `num`) are returned.
- num : integer, optional
- Number of samples to generate. Default is 50.
- endpoint : boolean, optional
- If true, `stop` is the last sample. Otherwise, it is not included.
- Default is True.
- dtype : dtype
- The type of the output array. If `dtype` is not given, the data type
- is inferred from `start` and `stop`. The inferred dtype will never be
- an integer; `float` is chosen even if the arguments would produce an
- array of integers.
- axis : int, optional
- The axis in the result to store the samples. Relevant only if start
- or stop are array-like. By default (0), the samples will be along a
- new axis inserted at the beginning. Use -1 to get an axis at the end.
- .. versionadded:: 1.16.0
- Returns
- -------
- samples : ndarray
- `num` samples, equally spaced on a log scale.
- See Also
- --------
- logspace : Similar to geomspace, but with endpoints specified using log
- and base.
- linspace : Similar to geomspace, but with arithmetic instead of geometric
- progression.
- arange : Similar to linspace, with the step size specified instead of the
- number of samples.
- :ref:`how-to-partition`
- Notes
- -----
- If the inputs or dtype are complex, the output will follow a logarithmic
- spiral in the complex plane. (There are an infinite number of spirals
- passing through two points; the output will follow the shortest such path.)
- Examples
- --------
- >>> np.geomspace(1, 1000, num=4)
- array([ 1., 10., 100., 1000.])
- >>> np.geomspace(1, 1000, num=3, endpoint=False)
- array([ 1., 10., 100.])
- >>> np.geomspace(1, 1000, num=4, endpoint=False)
- array([ 1. , 5.62341325, 31.6227766 , 177.827941 ])
- >>> np.geomspace(1, 256, num=9)
- array([ 1., 2., 4., 8., 16., 32., 64., 128., 256.])
- Note that the above may not produce exact integers:
- >>> np.geomspace(1, 256, num=9, dtype=int)
- array([ 1, 2, 4, 7, 16, 32, 63, 127, 256])
- >>> np.around(np.geomspace(1, 256, num=9)).astype(int)
- array([ 1, 2, 4, 8, 16, 32, 64, 128, 256])
- Negative, decreasing, and complex inputs are allowed:
- >>> np.geomspace(1000, 1, num=4)
- array([1000., 100., 10., 1.])
- >>> np.geomspace(-1000, -1, num=4)
- array([-1000., -100., -10., -1.])
- >>> np.geomspace(1j, 1000j, num=4) # Straight line
- array([0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j])
- >>> np.geomspace(-1+0j, 1+0j, num=5) # Circle
- array([-1.00000000e+00+1.22464680e-16j, -7.07106781e-01+7.07106781e-01j,
- 6.12323400e-17+1.00000000e+00j, 7.07106781e-01+7.07106781e-01j,
- 1.00000000e+00+0.00000000e+00j])
- Graphical illustration of `endpoint` parameter:
- >>> import matplotlib.pyplot as plt
- >>> N = 10
- >>> y = np.zeros(N)
- >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=True), y + 1, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=False), y + 2, 'o')
- [<matplotlib.lines.Line2D object at 0x...>]
- >>> plt.axis([0.5, 2000, 0, 3])
- [0.5, 2000, 0, 3]
- >>> plt.grid(True, color='0.7', linestyle='-', which='both', axis='both')
- >>> plt.show()
- """
- start = asanyarray(start)
- stop = asanyarray(stop)
- if _nx.any(start == 0) or _nx.any(stop == 0):
- raise ValueError('Geometric sequence cannot include zero')
- dt = result_type(start, stop, float(num), _nx.zeros((), dtype))
- if dtype is None:
- dtype = dt
- else:
- # complex to dtype('complex128'), for instance
- dtype = _nx.dtype(dtype)
- # Promote both arguments to the same dtype in case, for instance, one is
- # complex and another is negative and log would produce NaN otherwise.
- # Copy since we may change things in-place further down.
- start = start.astype(dt, copy=True)
- stop = stop.astype(dt, copy=True)
- out_sign = _nx.ones(_nx.broadcast(start, stop).shape, dt)
- # Avoid negligible real or imaginary parts in output by rotating to
- # positive real, calculating, then undoing rotation
- if _nx.issubdtype(dt, _nx.complexfloating):
- all_imag = (start.real == 0.) & (stop.real == 0.)
- if _nx.any(all_imag):
- start[all_imag] = start[all_imag].imag
- stop[all_imag] = stop[all_imag].imag
- out_sign[all_imag] = 1j
- both_negative = (_nx.sign(start) == -1) & (_nx.sign(stop) == -1)
- if _nx.any(both_negative):
- _nx.negative(start, out=start, where=both_negative)
- _nx.negative(stop, out=stop, where=both_negative)
- _nx.negative(out_sign, out=out_sign, where=both_negative)
- log_start = _nx.log10(start)
- log_stop = _nx.log10(stop)
- result = logspace(log_start, log_stop, num=num,
- endpoint=endpoint, base=10.0, dtype=dtype)
- # Make sure the endpoints match the start and stop arguments. This is
- # necessary because np.exp(np.log(x)) is not necessarily equal to x.
- if num > 0:
- result[0] = start
- if num > 1 and endpoint:
- result[-1] = stop
- result = out_sign * result
- if axis != 0:
- result = _nx.moveaxis(result, 0, axis)
- return result.astype(dtype, copy=False)
- def _needs_add_docstring(obj):
- """
- Returns true if the only way to set the docstring of `obj` from python is
- via add_docstring.
- This function errs on the side of being overly conservative.
- """
- Py_TPFLAGS_HEAPTYPE = 1 << 9
- if isinstance(obj, (types.FunctionType, types.MethodType, property)):
- return False
- if isinstance(obj, type) and obj.__flags__ & Py_TPFLAGS_HEAPTYPE:
- return False
- return True
- def _add_docstring(obj, doc, warn_on_python):
- if warn_on_python and not _needs_add_docstring(obj):
- warnings.warn(
- "add_newdoc was used on a pure-python object {}. "
- "Prefer to attach it directly to the source."
- .format(obj),
- UserWarning,
- stacklevel=3)
- try:
- add_docstring(obj, doc)
- except Exception:
- pass
- def add_newdoc(place, obj, doc, warn_on_python=True):
- """
- Add documentation to an existing object, typically one defined in C
- The purpose is to allow easier editing of the docstrings without requiring
- a re-compile. This exists primarily for internal use within numpy itself.
- Parameters
- ----------
- place : str
- The absolute name of the module to import from
- obj : str
- The name of the object to add documentation to, typically a class or
- function name
- doc : {str, Tuple[str, str], List[Tuple[str, str]]}
- If a string, the documentation to apply to `obj`
- If a tuple, then the first element is interpreted as an attribute of
- `obj` and the second as the docstring to apply - ``(method, docstring)``
- If a list, then each element of the list should be a tuple of length
- two - ``[(method1, docstring1), (method2, docstring2), ...]``
- warn_on_python : bool
- If True, the default, emit `UserWarning` if this is used to attach
- documentation to a pure-python object.
- Notes
- -----
- This routine never raises an error if the docstring can't be written, but
- will raise an error if the object being documented does not exist.
- This routine cannot modify read-only docstrings, as appear
- in new-style classes or built-in functions. Because this
- routine never raises an error the caller must check manually
- that the docstrings were changed.
- Since this function grabs the ``char *`` from a c-level str object and puts
- it into the ``tp_doc`` slot of the type of `obj`, it violates a number of
- C-API best-practices, by:
- - modifying a `PyTypeObject` after calling `PyType_Ready`
- - calling `Py_INCREF` on the str and losing the reference, so the str
- will never be released
- If possible it should be avoided.
- """
- new = getattr(__import__(place, globals(), {}, [obj]), obj)
- if isinstance(doc, str):
- _add_docstring(new, doc.strip(), warn_on_python)
- elif isinstance(doc, tuple):
- attr, docstring = doc
- _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python)
- elif isinstance(doc, list):
- for attr, docstring in doc:
- _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python)
|