Skip to content

Commit

Permalink
Fix crash when user mixes types in arguements to independent and adfun.
Browse files Browse the repository at this point in the history
doc.omh: change version number.
install.omh: change version number.
whats_new_10.omh: user's view of the changes.
__init__.py: remove duplicate copy of indpendent(x) (also in adfun.py).
adfun.py: check for mixed types in independent and adfun arguments.
test_more.py: test check for mixed types in independent and adfun arguments.
  • Loading branch information
bradbell committed Dec 12, 2010
1 parent ab93395 commit 95a7646
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 54 deletions.
2 changes: 1 addition & 1 deletion doc.omh
Expand Up @@ -33,7 +33,7 @@ $hiliteseq%
%.%reverse%(% reverse
%$$

$section pycppad-20101205: A Python Algorithm Derivative Package$$
$section pycppad-20101212: A Python Algorithm Derivative Package$$

$index AD, python$$
$index python, AD$$
Expand Down
22 changes: 11 additions & 11 deletions omh/install.omh
Expand Up @@ -53,8 +53,8 @@ $index download, pycppad$$
$index pycppad, download$$
Download the file
$href%
http://www.seanet.com/~bradbell/pycppad-20101205.tar.gz%
pycppad-20101205.tar.gz
http://www.seanet.com/~bradbell/pycppad-20101212.tar.gz%
pycppad-20101212.tar.gz
%$$
and store it on your machine.

Expand All @@ -65,14 +65,14 @@ $index extract, pycppad$$
$index pycppad, extract$$
On unix, you could use the command
$codep
tar -xvzf pycppad-20101205.tar.gz
tar -xvzf pycppad-20101212.tar.gz
$$
which would create the directory $code pycppad-20101205$$.
which would create the directory $code pycppad-20101212$$.

$head Required Setup Information$$
The value of the following setup variables, in the file
$codep
pycppad-20101205/setup.py
pycppad-20101212/setup.py
$$
must be set to agree with your system:
$code
Expand All @@ -89,7 +89,7 @@ $index pycppad, build$$
$subhead With Debugging$$
$index debugging, build$$
$index build, debugging$$
Change into the directory $code pycppad-20101205$$ and execute the command
Change into the directory $code pycppad-20101212$$ and execute the command
$codep
./setup.py build_ext --inplace --debug --undef NDEBUG
$$
Expand Down Expand Up @@ -162,7 +162,7 @@ $index test, pycppad$$
$index pycppad, test$$
You can test of all the
$cref/examples/example/$$ in the $code pycppad$$ documentation.
Change into the directory $code pycppad-20101205$$ and execute the command
Change into the directory $code pycppad-20101212$$ and execute the command
$codep
python test_example.py
$$
Expand All @@ -179,7 +179,7 @@ You may or may not preform this step:
$pre

$$
Change into the directory $code pycppad-20101205$$ and execute the command
Change into the directory $code pycppad-20101212$$ and execute the command
$codei%
python setup.py install --prefix=%prefix%
%$$
Expand All @@ -206,7 +206,7 @@ an uninstall command. You can uninstall the $code pycppad$$ package
by removing the entries
$codei%
%prefix%/lib/python%major%.%minor%/site-packages/pycppad
%prefix%/lib/python%major%.%minor%/site-packages/pycppad-20101205.egg-info
%prefix%/lib/python%major%.%minor%/site-packages/pycppad-20101212.egg-info
%prefix%/share/doc/pycppad
%$$
where $icode major$$ and $icode minor$$
Expand All @@ -225,7 +225,7 @@ $codei%
%$$
unless the distribution directory
$codep
pycppad-20101205
pycppad-20101212
$$
is in your python path.
If you have installed $code pycppad$$,
Expand Down Expand Up @@ -257,7 +257,7 @@ to your $code @HOME/.bashrc$$ file.
$head pycppad Documentation$$
The documentation for $code pycppad$$ starts out in the directory
$codep
pycppad-20101205/doc
pycppad-20101212/doc
$$
During the installation process, it is copied to the directory
$codei%
Expand Down
10 changes: 10 additions & 0 deletions omh/whats_new_10.omh
@@ -1,6 +1,7 @@
$begin whats_new_10$$
$dollar @$$
$spell
adfun
cygwin
tarball
inplace
Expand All @@ -22,6 +23,15 @@ $table
$tref whats_new_09$$
$tend

$head 12-12$$
The $cref/independent(x)/independent/$$
and $cref/adfun(x,y)/adfun/$$ functions
require the elements of $icode x$$ and $icode y$$ to all have
the same type.
If this was not the case, $code pycppad$$ might crash with out
a useful error message.
This has been fixed.

$head 12-05$$
First version that works (and installs properly) under MS windows using the
$href%www.cygwin.com%cygwin%$$ system; see special
Expand Down
18 changes: 0 additions & 18 deletions pycppad/__init__.py
Expand Up @@ -218,24 +218,6 @@ def value(a_x) :
msg += 'value(a_x): only implemented where a_x is an a_float, a2float,\n'
msg += 'or an array of a_float or a2float'
raise NotImplementedError(msg)

def independent(x) :
"""
a_x = independent(x): create independent variable vector a_x, equal to x,
and start recording operations that use the class corresponding to ad( x[0] ).
"""
if not isinstance(x, numpy.ndarray) :
raise NotImplementedError('independent(x): x is not of type numpy.array')
x0 = x[0]
if isinstance(x0, int) or isinstance(x0, float) :
return cppad_.independent(x, 1) # level = 1
elif isinstance(x0, a_float) :
return cppad_.independent(x, 2) # level = 2
else:
print "type(x[j]) = ", type(x0)
raise NotImplementedError(
'independent(x): only implemented where x[j] is float or a_float'
)

from adfun import *
from runge_kutta_4 import *
Expand Down
92 changes: 68 additions & 24 deletions pycppad/adfun.py
Expand Up @@ -158,24 +158,48 @@
import cppad_
from cppad_ import abort_recording
import numpy

def independent(x) :
"""
a_x = independent(x): create independent variable vector a_x, equal to x,
and start recording operations that use the class corresponding to ad( x[0] ).
"""
#
# It would be better faster if all this type checking were done in the C++
#
if not isinstance(x, numpy.ndarray) :
raise NotImplementedError('independent(x): x is not of type numpy.array')
#
x0 = x[0]
if isinstance(x0, int) or isinstance(x0, float) :
if isinstance(x0, int) :
for j in range( len(x) ) :
if not isinstance(x[j], int) :
other = 'x[' + str(j) + '] is ' + type(x[j]).__name__
msg = 'independent(x): mixed types x[0] is int and ' + other
raise NotImplementedError(msg)
x = numpy.array(x, dtype=int) # incase dtype of x is object
return cppad_.independent(x, 1) # level = 1
#
if isinstance(x0, float) :
for j in range( len(x) ) :
if not isinstance(x[j], float) :
other = 'x[' + str(j) + '] is ' + type(x[j]).__name__
msg = 'independent(x): mixed types x[0] is float and ' + other
raise NotImplementedError(msg)
x = numpy.array(x, dtype=float) # incase dtype of x is object
return cppad_.independent(x, 1) # level = 1
elif isinstance(x0, cppad_.a_float) :
#
if isinstance(x0, cppad_.a_float) :
for j in range( len(x) ) :
if not isinstance(x[j], cppad_.a_float) :
other = 'x[' + str(j) + '] is ' + type(x[j]).__name__
msg = 'independent(x): mixed types x[0] is a_float and ' + other
raise NotImplementedError(msg)
return cppad_.independent(x, 2) # level = 2
else:
print "type(x[j]) = ", type(x0)
raise NotImplementedError(
'independent(x): only implemented where x[j] is float or a_float'
)
#
msg = 'independent(x): x[0] has type' + type(x0).__name__ + '\n'
msg = 'only implemented where x[j] is int, float, or a_float'
raise NotImplementedError(msg)

class adfun_float(cppad_.adfun_float) :
"""
Expand Down Expand Up @@ -205,23 +229,43 @@ def adfun(x,y) :
x: a numpy one dimnesional array containing the independent variable vector.
y: a vector with same type as x and containing the dependent variable vector.
"""
if not isinstance(x, numpy.ndarray) or not isinstance(x, numpy.ndarray) :
#
# It would be better faster if all this type checking were done in the C++
#
if not isinstance(x, numpy.ndarray) or not isinstance(y, numpy.ndarray) :
raise NotImplementedError('adfun(x, y): x or y is not of type numpy.array')
#
x0 = x[0]
y0 = y[0]
if isinstance(x0, cppad_.a_float) :
if isinstance(y0, cppad_.a_float) :
return adfun_float(x, y)
else :
raise NotImplementedError(
'adfun(x, y): x[j] and y[j] have different elements types')
elif isinstance(x0, cppad_.a2float) :
if isinstance(y0, cppad_.a2float) :
return adfun_a_float(x, y)
else :
raise NotImplementedError(
'adfun(x, y): x[j] and y[j] have different elements types')
else :
raise NotImplementedError(
'adfun(x, y): elements of x and y are not a_float or a2dobule')

for j in range( len(x) ) :
if not isinstance(x[j], cppad_.a_float) :
other = 'x[' + str(j) + '] is ' + type(x[j]).__name__
msg = 'independent(x): mixed types x[0] is a_float and ' + other
raise NotImplementedError(msg)
#
for i in range( len(y) ) :
if not isinstance(y[i], cppad_.a_float) :
other = 'y[' + str(i) + '] is ' + type(y[i]).__name__
msg = 'independent(x): mixed types x[0] is a_float and ' + other
raise NotImplementedError(msg)
#
return adfun_float(x, y)
#
if isinstance(x0, cppad_.a2float) :
for j in range( len(x) ) :
if not isinstance(x[j], cppad_.a2float) :
other = 'x[' + str(j) + '] is ' + type(x[j]).__name__
msg = 'independent(x): mixed types x[0] is a2float and ' + other
raise NotImplementedError(msg)
#
for i in range( len(y) ) :
if not isinstance(y[i], cppad_.a2float) :
other = 'y[' + str(i) + '] is ' + type(y[i]).__name__
msg = 'independent(x): mixed types x[0] is a2float and ' + other
raise NotImplementedError(msg)
#
return adfun_a_float(x, y)
#
raise NotImplementedError(
'adfun(x, y): elements of x and y are not a_float or a2float')
22 changes: 22 additions & 0 deletions test_more.py
Expand Up @@ -546,6 +546,28 @@ def fun(x):
H = f.hessian(x, w)
assert numpy.prod( A == H )

def pycppad_test_mixed_element_types():
x = numpy.array( [ 1 , 2. ], dtype=object )
ok = False
try :
a_x = independent(x)
except NotImplementedError :
x[0] = 0.
a_x = independent(x)
ok = True
assert ok
#
a_y = numpy.array( [ ad(0) , 1. ], dtype=object )
ok = False
try :
f = adfun(a_x, a_y)
except NotImplementedError :
a_y[1] = ad(1)
f = adfun(a_x, a_y)
ok = True
assert ok
#

import sys
if __name__ == "__main__" :
number_ok = 0
Expand Down

0 comments on commit 95a7646

Please sign in to comment.