Skip to content

Commit 22a73f9

Browse files
Merge pull request #9666 from JuliaLang/sk/linspace
linspace: try to "lift" linspace the float ranges are [close #9637]
2 parents ae23430 + aa78037 commit 22a73f9

10 files changed

+192
-61
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ Library improvements
245245
* `code_llvm` now outputs stripped IR without debug info or other attached metadata.
246246
Use `code_llvm_raw` for the unstripped output ([#10747]).
247247

248+
* `linspace` now returns a `LinSpace` object which lazily computes linear interpolation of values between the start and stop values. It "lifts" endpoints which are approximately rational in the same manner as the `colon` operator.
249+
248250
Deprecated or removed
249251
---------------------
250252

@@ -309,6 +311,8 @@ Deprecated or removed
309311

310312
* the --int-literals compiler option is no longer accepted.
311313

314+
* Instead of `linrange` use `linspace`.
315+
312316
Julia v0.3.0 Release Notes
313317
==========================
314318

base/array.jl

-22
Original file line numberDiff line numberDiff line change
@@ -246,28 +246,6 @@ function one{T}(x::AbstractMatrix{T})
246246
eye(T, m)
247247
end
248248

249-
linspace(start::Integer, stop::Integer, n::Integer) =
250-
linspace(float(start), float(stop), n)
251-
function linspace(start::Real, stop::Real, n::Integer)
252-
(start, stop) = promote(start, stop)
253-
T = typeof(start)
254-
a = Array(T, Int(n))
255-
if n == 1
256-
a[1] = start
257-
return a
258-
end
259-
n -= 1
260-
S = promote_type(T, Float64)
261-
for i=0:n
262-
a[i+1] = start*(convert(S, (n-i))/n) + stop*(convert(S, i)/n)
263-
end
264-
a
265-
end
266-
linspace(start::Real, stop::Real) = linspace(start, stop, 100)
267-
268-
logspace(start::Real, stop::Real, n::Integer) = 10.^linspace(start, stop, n)
269-
logspace(start::Real, stop::Real) = logspace(start, stop, 50)
270-
271249
## Conversions ##
272250

273251
convert{T,n}(::Type{Array{T}}, x::Array{T,n}) = x

base/deprecated.jl

+2
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,5 @@ export float32_isvalid, float64_isvalid
522522
@deprecate parseint(s,base) parse(Int, s, base)
523523
@deprecate parseint(T::Type, s) parse(T, s)
524524
@deprecate parseint(T::Type, s, base) parse(T, s, base)
525+
526+
@deprecate linrange linspace

base/exports.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export
6464
IO,
6565
IOBuffer,
6666
IOStream,
67+
LinSpace,
6768
LocalProcess,
6869
LowerTriangular,
6970
MathConst,
@@ -521,7 +522,6 @@ export
521522
issorted,
522523
last,
523524
levicivita,
524-
linrange,
525525
linspace,
526526
logspace,
527527
mapslices,

base/range.jl

+94-8
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,81 @@ range(a::FloatingPoint, st::FloatingPoint, len::Integer) = FloatRange(a,st,len,o
163163
range(a::Real, st::FloatingPoint, len::Integer) = FloatRange(float(a), st, len, one(st))
164164
range(a::FloatingPoint, st::Real, len::Integer) = FloatRange(a, float(st), len, one(a))
165165

166-
linrange(a::Real, b::Real, len::Integer) =
167-
len >= 2 ? range(a, (b-a)/(len-1), len) :
168-
len == 1 && a == b ? range(a, zero((b-a)/(len-1)), 1) :
169-
throw(ArgumentError("invalid range length"))
166+
## linspace and logspace
167+
168+
immutable LinSpace{T<:FloatingPoint} <: Range{T}
169+
start::T
170+
stop::T
171+
len::T
172+
divisor::T
173+
end
174+
175+
function linspace{T<:FloatingPoint}(start::T, stop::T, len::T)
176+
len == round(len) || throw(InexactError())
177+
0 <= len || error("linspace($start, $stop, $len): negative length")
178+
if len == 0
179+
n = convert(T, 2)
180+
if isinf(n*start) || isinf(n*stop)
181+
start /= n; stop /= n; n = one(T)
182+
end
183+
return LinSpace(-start, -stop, -one(T), n)
184+
end
185+
if len == 1
186+
start == stop || error("linspace($start, $stop, $len): endpoints differ")
187+
return LinSpace(-start, -start, zero(T), one(T))
188+
end
189+
n = convert(T, len - 1)
190+
len - n == 1 || error("linspace($start, $stop, $len): too long for $T")
191+
a0, b = rat(start)
192+
a = convert(T,a0)
193+
if a/convert(T,b) == start
194+
c0, d = rat(stop)
195+
c = convert(T,c0)
196+
if c/convert(T,d) == stop
197+
e = lcm(b,d)
198+
a *= div(e,b)
199+
c *= div(e,d)
200+
s = convert(T,n*e)
201+
if isinf(a*n) || isinf(c*n)
202+
s, p = frexp(s)
203+
p = oftype(s,2)^p
204+
a /= p; c /= p
205+
end
206+
if a*n/s == start && c*n/s == stop
207+
return LinSpace(a, c, len, s)
208+
end
209+
end
210+
end
211+
a, c, s = start, stop, n
212+
if isinf(a*n) || isinf(c*n)
213+
s, p = frexp(s)
214+
p = oftype(s,2)^p
215+
a /= p; c /= p
216+
end
217+
if a*n/s == start && c*n/s == stop
218+
return LinSpace(a, c, len, s)
219+
end
220+
error("linspace($start, $stop, $len): cannot be constructed")
221+
end
222+
function linspace{T<:FloatingPoint}(start::T, stop::T, len::Real)
223+
T_len = convert(T, len)
224+
T_len == len || throw(InexactError())
225+
linspace(start, stop, T_len)
226+
end
227+
linspace(start::Real, stop::Real, len::Real=50) =
228+
linspace(promote(FloatingPoint(start), FloatingPoint(stop))..., len)
229+
230+
function show(io::IO, r::LinSpace)
231+
print(io, "linspace(")
232+
show(io, first(r))
233+
print(io, ',')
234+
show(last(r))
235+
print(io, ',')
236+
show(length(r))
237+
print(io, ')')
238+
end
239+
240+
logspace(start::Real, stop::Real, n::Integer=50) = 10.^linspace(start, stop, n)
170241

171242
## interface implementations
172243

@@ -178,18 +249,21 @@ size(r::Range) = (length(r),)
178249
isempty(r::StepRange) =
179250
(r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start))
180251
isempty(r::UnitRange) = r.start > r.stop
181-
isempty(r::FloatRange) = length(r)==0
252+
isempty(r::FloatRange) = length(r) == 0
253+
isempty(r::LinSpace) = length(r) == 0
182254

183255
step(r::StepRange) = r.step
184256
step(r::UnitRange) = 1
185257
step(r::FloatRange) = r.step/r.divisor
258+
step{T}(r::LinSpace{T}) = ifelse(r.len <= 0, convert(T,NaN), (r.stop-r.start)/r.divisor)
186259

187260
function length(r::StepRange)
188261
n = Integer(div(r.stop+r.step - r.start, r.step))
189262
isempty(r) ? zero(n) : n
190263
end
191264
length(r::UnitRange) = Integer(r.stop - r.start + 1)
192265
length(r::FloatRange) = Integer(r.len)
266+
length(r::LinSpace) = Integer(r.len + signbit(r.len - 1))
193267

194268
function length{T<:Union(Int,UInt,Int64,UInt64)}(r::StepRange{T})
195269
isempty(r) && return zero(T)
@@ -220,11 +294,13 @@ let smallint = (Int === Int64 ?
220294
end
221295

222296
first{T}(r::OrdinalRange{T}) = convert(T, r.start)
223-
first(r::FloatRange) = r.start/r.divisor
297+
first{T}(r::FloatRange{T}) = convert(T, r.start/r.divisor)
298+
first{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.start/r.divisor)
224299

225300
last{T}(r::StepRange{T}) = r.stop
226301
last(r::UnitRange) = r.stop
227302
last{T}(r::FloatRange{T}) = convert(T, (r.start + (r.len-1)*r.step)/r.divisor)
303+
last{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.stop/r.divisor)
228304

229305
minimum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r)
230306
maximum(r::UnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r)
@@ -241,8 +317,14 @@ copy(r::Range) = r
241317
## iteration
242318

243319
start(r::FloatRange) = 0
244-
next{T}(r::FloatRange{T}, i::Int) = (convert(T, (r.start + i*r.step)/r.divisor), i+1)
245-
done(r::FloatRange, i::Int) = (length(r) <= i)
320+
done(r::FloatRange, i::Int) = length(r) <= i
321+
next{T}(r::FloatRange{T}, i::Int) =
322+
(convert(T, (r.start + i*r.step)/r.divisor), i+1)
323+
324+
start(r::LinSpace) = 1
325+
done(r::LinSpace, i::Int) = length(r) < i
326+
next{T}(r::LinSpace{T}, i::Int) =
327+
(convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor), i+1)
246328

247329
# NOTE: For ordinal ranges, we assume start+step might be from a
248330
# lifted domain (e.g. Int8+Int8 => Int); use that for iterating.
@@ -268,6 +350,10 @@ function getindex{T}(r::FloatRange{T}, i::Integer)
268350
1 <= i <= length(r) || throw(BoundsError())
269351
convert(T, (r.start + (i-1)*r.step)/r.divisor)
270352
end
353+
function getindex{T}(r::LinSpace{T}, i::Integer)
354+
1 <= i <= length(r) || throw(BoundsError())
355+
convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor)
356+
end
271357

272358
function check_indexingrange(s, r)
273359
sl = length(s)

doc/manual/arrays.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Function Description
8989
distributed random values
9090
:func:`eye(n) <eye>` ``n``-by-``n`` identity matrix
9191
:func:`eye(m, n) <eye>` ``m``-by-``n`` identity matrix
92-
:func:`linspace(start, stop, n) <linspace>` vector of ``n`` linearly-spaced elements from ``start`` to ``stop``
92+
:func:`linspace(start, stop, n) <linspace>` range of ``n`` linearly spaced elements from ``start`` to ``stop``
9393
:func:`fill!(A, x) <fill!>` fill the array ``A`` with the value ``x``
9494
:func:`fill(x, dims) <fill>` create an array filled with the value ``x``
9595
=================================================== =====================================================================

doc/manual/noteworthy-differences.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ some noteworthy differences that may trip up Julia users accustomed to MATLAB:
4444
the syntax ``[a b; c d]`` is used to avoid confusion. In Julia v0.4, the
4545
concatenation syntax ``[x, [y, z]]`` is deprecated in favor of ``[x; [y, z]]``.
4646
- In Julia, ``a:b`` and ``a:b:c`` construct :obj:`Range` objects. To construct
47-
a full vector like in MATLAB, use :func:`collect(a:b) <collect>` or
48-
:func:`linspace`.
47+
a full vector like in MATLAB, use :func:`collect(a:b) <collect>`.
4948
- Functions in Julia return values from their last expression or the ``return``
5049
keyword instead of listing the names of variables to return in the function
5150
definition (see :ref:`man-return-keyword` for details).

doc/stdlib/arrays.rst

+2-3
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,11 @@ Constructors
169169

170170
.. function:: linspace(start, stop, n=100)
171171

172-
Construct a vector of ``n`` linearly-spaced elements from ``start`` to ``stop``.
173-
See also: :func:`linrange` that constructs a range object.
172+
Construct a range of ``n`` linearly spaced elements from ``start`` to ``stop``.
174173

175174
.. function:: logspace(start, stop, n=50)
176175

177-
Construct a vector of ``n`` logarithmically-spaced numbers from ``10^start`` to ``10^stop``.
176+
Construct a vector of ``n`` logarithmically spaced numbers from ``10^start`` to ``10^stop``.
178177

179178
Mathematical operators and functions
180179
------------------------------------

doc/stdlib/math.rst

-4
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,6 @@ Mathematical Operators
189189

190190
Construct a range by length, given a starting value and optional step (defaults to 1).
191191

192-
.. function:: linrange(start, end, length)
193-
194-
Construct a range by length, given a starting and ending value.
195-
196192
.. _==:
197193
.. function:: ==(x, y)
198194

0 commit comments

Comments
 (0)