Generalized range

T

tkpmep

I need to create ranges that can start and end with real numbers.
Searching this newsgroup brought me to a function that I then modified
as follows:

def myRange(iMin, iMax=None, iStep=1):
"""Extends range to real numbers. Wherever possible, use Python's
range .
In other cases, make the behavior follow the spirit of Python's
range """
epsilon = 1.e-8

if iMax == None and iStep == 1:
return range(int(iMin))

elif type(iMin).__name__.lower() in ('int', 'long') and \
type(iMax).__name__.lower() in ('int', 'long') and \
type(iStep).__name__.lower() in ('int', 'long') and iStep !=
0:
return range( iMin, iMax, iStep)

elif iMin <= iMax and iStep > 0:
return [ iMin+i*iStep for i in range( int(math.ceil((iMax -
iMin - epsilon)/iStep)) )]

elif iMin >= iMax and iStep < 0:
return [ iMin+i*iStep for i in range(-int(math.ceil((iMin -
iMax + epsilon)/iStep)) )]

else:
raise ValueError, 'Cannot construct a range with steps of size
' + str(iStep) + ' between ' + str(iMin) + ' and ' + str(iMax)


The one part of my implementation that has me a bit queasy (i.e.
works in my current application, but I can see it misbehaving
elsewhere) is the addition/subtraction of a fixed epsilon to ensure
that my rounding goes the right way. A clean implementation would
modify epsilon based on the achievable level of precision given the
particular values of iMax, iMin and iStep. I suspect this requires a
detailed understanding of the implementation of floating point
arithmetic, and would appreciate hearing any thoughts you might have
on gilding this lily.

Sincerely

Thomas Philips
 
S

Stargaming

I need to create ranges that can start and end with real numbers.
Searching this newsgroup brought me to a function that I then modified
as follows:

def myRange(iMin, iMax=None, iStep=1):
Just as a sidenote: it is not common to prefix your names with its type.
It could change at any time and min, max, step would be clear, too. IMO.
"""Extends range to real numbers. Wherever possible, use Python's
range .
In other cases, make the behavior follow the spirit of Python's
range """
If you want to stick to the "normal" range-implementation, myRange
should consider an one-argument-call as transmission of iMax.
epsilon = 1.e-8
I can't really say if your attempt using an epsilon-environment is good.
I think just increasing a "counter" from iMin to iMax should be fine,
achieving more precision by making computations fuzzy -- i don't know,
perhaps it's very good. I wouldn't do it.
If you like to care about precision, you should have a look at the
`decimal module said:
if iMax == None and iStep == 1:
return range(int(iMin))

elif type(iMin).__name__.lower() in ('int', 'long') and \
type(iMax).__name__.lower() in ('int', 'long') and \
type(iStep).__name__.lower() in ('int', 'long') and iStep !=
0:
return range( iMin, iMax, iStep)
Ouchie! *That* is a bad one. Checking for a name of an object is neither
safe nor good nor common. A better way would be directly comparing
type(yourobject) with int/long/float/anytype. See
http://docs.python.org/lib/comparisons.html for details on comparisons.
Another way of type-checking in python is doing something like ``if
isinstance(iMin, (int, long))``. Would work for subclasses, too.
elif iMin <= iMax and iStep > 0:
return [ iMin+i*iStep for i in range( int(math.ceil((iMax -
iMin - epsilon)/iStep)) )]

elif iMin >= iMax and iStep < 0:
return [ iMin+i*iStep for i in range(-int(math.ceil((iMin -
iMax + epsilon)/iStep)) )]
Will eat your memory. See below.
else:
raise ValueError, 'Cannot construct a range with steps of size
' + str(iStep) + ' between ' + str(iMin) + ' and ' + str(iMax)
In Python, it is common to use string interpolation instead. Like::
print 'Hello from %d to %d' % (iMin, iMax)
Read `String Formatting Operations
The one part of my implementation that has me a bit queasy (i.e.
works in my current application, but I can see it misbehaving
elsewhere) is the addition/subtraction of a fixed epsilon to ensure
that my rounding goes the right way. A clean implementation would
modify epsilon based on the achievable level of precision given the
particular values of iMax, iMin and iStep. I suspect this requires a
detailed understanding of the implementation of floating point
arithmetic,
I think so, too. That's why it is a bad idea, IMO.
and would appreciate hearing any thoughts you might have
on gilding this lily.

Sincerely

Thomas Philips

I'd recommend you to have a look into `generators
<http://docs.python.org/ref/yield.html>`_, it is what `xrange
<http://docs.python.org/lib/built-in-funcs.html#l2h-80>`_ uses. You
don't put all numbers into your memory ("precompute them") but behave a
little bit lazy and compute them whenever the user needs the next one. I
expect Google to have lots of example implementations of range as a
generator in python for you. :)

HTH,
Stargaming
 
M

Michael Hoffman

I need to create ranges that can start and end with real numbers.
Searching this newsgroup brought me to a function that I then modified
as follows:

def myRange(iMin, iMax=None, iStep=1):
"""Extends range to real numbers. Wherever possible, use Python's
range .
In other cases, make the behavior follow the spirit of Python's
range """
epsilon = 1.e-8

if iMax == None and iStep == 1:
return range(int(iMin))

elif type(iMin).__name__.lower() in ('int', 'long') and \
type(iMax).__name__.lower() in ('int', 'long') and \
type(iStep).__name__.lower() in ('int', 'long') and iStep !=
0:
return range( iMin, iMax, iStep)

elif iMin <= iMax and iStep > 0:
return [ iMin+i*iStep for i in range( int(math.ceil((iMax -
iMin - epsilon)/iStep)) )]

elif iMin >= iMax and iStep < 0:
return [ iMin+i*iStep for i in range(-int(math.ceil((iMin -
iMax + epsilon)/iStep)) )]

else:
raise ValueError, 'Cannot construct a range with steps of size
' + str(iStep) + ' between ' + str(iMin) + ' and ' + str(iMax)


The one part of my implementation that has me a bit queasy (i.e.
works in my current application, but I can see it misbehaving
elsewhere) is the addition/subtraction of a fixed epsilon to ensure
that my rounding goes the right way. A clean implementation would
modify epsilon based on the achievable level of precision given the
particular values of iMax, iMin and iStep. I suspect this requires a
detailed understanding of the implementation of floating point
arithmetic, and would appreciate hearing any thoughts you might have
on gilding this lily.

In addition to the comments of Stargaming, most of which I agree with, I
think you would be far better dropping the epsilon business and doing
something like:

# requires that minimum <= maximum; swap values if necessary
res = minimum
while res < maximum:
yield res
res += step
 
T

tkpmep

Thanks - you have covered a fair bit of gorund here - I will modify
myRange taking your suggestions into account. The one suggestion that
I'm going to have to think through is repeatedly incrementing res.

I deliberately did not use this as repeated addition can cause
rounding errors to accumulate, making the loop run a little longer or
shorter than necessary. I thought I would be far less likely to run
into rounding issues with a multiplicative construct - hence my use of
epsilon, and my question about an appropriate value for it
 
M

Michael Hoffman

Thanks - you have covered a fair bit of gorund here - I will modify
myRange taking your suggestions into account. The one suggestion that
I'm going to have to think through is repeatedly incrementing res.

I deliberately did not use this as repeated addition can cause
rounding errors to accumulate, making the loop run a little longer or
shorter than necessary. I thought I would be far less likely to run
into rounding issues with a multiplicative construct - hence my use of
epsilon, and my question about an appropriate value for it

You are right about rounding issues--with a sufficiently small step, the
way I have done it, it could become an infinite loop. But you can still
do it with multiplication, without using an epsilon constant. How about
something like this:

index = 0
while res < maximum:
yield minimum + (step * index)
index += 1
 
M

Michael Hoffman

Michael said:
> How about something like this:

index = 0
while res < maximum:
yield minimum + (step * index)
index += 1

Well it really would have to be something LIKE that since I never
defined res. Let's try that again:

index = 0
res = minimum
while res < maximum:
yield res
res = minimum + (step * index)
index += 1
 
A

Alex Martelli

Michael Hoffman said:
You are right about rounding issues--with a sufficiently small step, the
way I have done it, it could become an infinite loop. But you can still
do it with multiplication, without using an epsilon constant. How about
something like this:

index = 0
while res < maximum:
yield minimum + (step * index)
index += 1

Absolutely (with your later correction of actually assigning res), MUCH
better than the misguided attempts based on repeatedly adding 'step'.

I've taught "numerical computing" in university, and I would have had to
fail anybody who'd misunderstood floating-point computations badly
enough to try that "+=step" idea (including, sigh, the coders of several
Fortran compilers who were popular at that time).

These days, I _heartily_ recommend "Real Computing Made Real" by Foreman
Acton -- the best programming book without one line of code in any
language (it's all formulas and graphs). Anybody who has trouble
following it must understand they should NOT use floating point numbers
until they've gotten the prereqs (which aren't all that deep:
engineering-undergrad-level algebra and calculus, plus
<http://docs.sun.com/source/806-3568/ncg_goldberg.html> and a couple
weeks' worth of refresher courses on fundamental algorithms such as
Newton's root-finding approach and the like -- I shudder to think how
many people with CS bachelor degrees and even post-grad degrees are just
never taught these fundamentals, yet end up having to program _some_
floating point computations, mistakenly thinking they can...).


Alex
 
B

Beliavsky

Absolutely (with your later correction of actually assigning res), MUCH
better than the misguided attempts based on repeatedly adding 'step'.

I've taught "numerical computing" in university, and I would have had to
fail anybody who'd misunderstood floating-point computations badly
enough to try that "+=step" idea (including, sigh, the coders of several Fortran compilers who were popular at that time).

You may be referring to the Fortran DO loop with a REAL loop variable,
for example

do x=1.5,3.5,0.5
print*,x
end do

This was part of standard Fortran 77, so one should blame the
standards committee, not the compiler writers. Very few features of
F77 were deleted in Fortran 95, but this was one of them. In practice,
nothing gets deleted from commercial Fortran compilers.

At the SciPy site http://www.scipy.org/Cookbook/OptimizationAndFitDemo1
there is some code

1 from enthought.chaco.wx import plt
2 from scipy import arange, optimize, special
3
4 plt.figure()
5 plt.hold()
6 w = []
7 z = []
8 x = arange(0,10,.01)
9
10 for k in arange(1,5,.5):
11 y = special.jv(k,x)
12 plt.plot(x,y)
13 f = lambda x: -special.jv(k,x)
14 x_max = optimize.fminbound(f,0,6)
15 w.append(x_max)
16 z.append(special.jv(k,x_max))
17
18 plt.plot(w,z, 'ro')
19 from scipy import interpolate
20 t = interpolate.splrep(w, z, k=3)
21 s_fit3 = interpolate.splev(x,t)
22 plt.plot(x,s_fit3, 'g-')
23 t5 = interpolate.splrep(w, z, k=5)
24 s_fit5 = interpolate.splev(x,t5)
25 plt.plot(x,s_fit5, 'y-')

I think the use of arange with a non-integer increment is poor style,
for reasons discussed in this thread.
 
A

Alex Martelli

Beliavsky said:
You may be referring to the Fortran DO loop with a REAL loop variable,
for example

do x=1.5,3.5,0.5
print*,x
end do

This was part of standard Fortran 77, so one should blame the
standards committee, not the compiler writers. Very few features of

I was thinking of "Fortran IV" aka Fortran '66, where as I recall per
the standard you were _supposed_ to only use integers in a DO, but
several compilers supplied real loop variables as an extension, and got
its implementation wrong.


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,234
Latest member
SkyeWeems

Latest Threads

Top