summaryrefslogtreecommitdiff
path: root/numpy/core/function_base.py
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2018-12-06 11:32:35 -0800
committerGitHub <noreply@github.com>2018-12-06 11:32:35 -0800
commit2970e41fcd33b20fecba5c089b24650836d24a7d (patch)
tree361e2f59b7dc7d63c70f41338c42d27dbb98b66c /numpy/core/function_base.py
parent45cef38cda80868355a920b5e94211dcf662ea07 (diff)
parentabf62624f7e2ea41029c0dbaeef9c2851429a07a (diff)
downloadnumpy-2970e41fcd33b20fecba5c089b24650836d24a7d.tar.gz
Merge pull request #12388 from mhvk/linspace-allow-array
ENH: allow arrays for start and stop in {lin,log,geom}space
Diffstat (limited to 'numpy/core/function_base.py')
-rw-r--r--numpy/core/function_base.py118
1 files changed, 80 insertions, 38 deletions
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
index 0fc56e70e..b68fd4068 100644
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -29,13 +29,14 @@ def _index_deprecate(i, stacklevel=2):
return i
-def _linspace_dispatcher(
- start, stop, num=None, endpoint=None, retstep=None, dtype=None):
+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):
+def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
+ axis=0):
"""
Return evenly spaced numbers over a specified interval.
@@ -44,11 +45,14 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
The endpoint of the interval can optionally be excluded.
+ .. versionchanged:: 1.16.0
+ Non-scalar `start` and `stop` are now supported.
+
Parameters
----------
- start : scalar
+ start : array_like
The starting value of the sequence.
- stop : scalar
+ 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
@@ -67,6 +71,13 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
.. 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
@@ -128,16 +139,15 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
if dtype is None:
dtype = dt
- y = _nx.arange(0, num, dtype=dt)
-
delta = stop - start
+ y = _nx.arange(0, num, dtype=dt).reshape((-1,) + (1,) * delta.ndim)
# 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.
- _mult_inplace = _nx.isscalar(delta)
+ _mult_inplace = _nx.isscalar(delta)
if num > 1:
step = delta / div
- if step == 0:
+ if _nx.any(step == 0):
# Special handling for denormal numbers, gh-5437
y /= div
if _mult_inplace:
@@ -160,19 +170,23 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
if endpoint and num > 1:
y[-1] = stop
+ if axis != 0:
+ y = _nx.moveaxis(y, 0, axis)
+
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):
+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):
+def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,
+ axis=0):
"""
Return numbers spaced evenly on a log scale.
@@ -180,11 +194,14 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
(`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 : float
+ start : array_like
``base ** start`` is the starting value of the sequence.
- stop : float
+ 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
@@ -201,6 +218,13 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
dtype : dtype
The type of the output array. If `dtype` is not given, infer the data
type from the other input arguments.
+ 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
-------
@@ -250,29 +274,33 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):
>>> plt.show()
"""
- y = linspace(start, stop, num=num, endpoint=endpoint)
+ 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)
+ return _nx.power(base, y).astype(dtype, copy=False)
-def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None):
+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):
+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 : scalar
+ start : array_like
The starting value of the sequence.
- stop : scalar
+ 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
@@ -285,6 +313,12 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
dtype : dtype
The type of the output array. If `dtype` is not given, infer the data
type from the other input arguments.
+ 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
-------
@@ -349,40 +383,48 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None):
>>> plt.show()
"""
- if start == 0 or stop == 0:
+ 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))
+ 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
- out_sign = 1
- if start.real == stop.real == 0:
- start, stop = start.imag, stop.imag
- out_sign = 1j * out_sign
- if _nx.sign(start) == _nx.sign(stop) == -1:
- start, stop = -start, -stop
- out_sign = -out_sign
-
- # Promote both arguments to the same dtype in case, for instance, one is
- # complex and another is negative and log would produce NaN otherwise
- start = start + (stop - stop)
- stop = stop + (start - start)
- if _nx.issubdtype(dtype, _nx.complexfloating):
- start = start + 0j
- stop = stop + 0j
+ 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 = out_sign * logspace(log_start, log_stop, num=num,
endpoint=endpoint, base=10.0, dtype=dtype)
+ if axis != 0:
+ result = _nx.moveaxis(result, 0, axis)
- return result.astype(dtype)
+ return result.astype(dtype, copy=False)
#always succeed