diff options
author | wfspotz@sandia.gov <wfspotz@sandia.gov@localhost> | 2007-11-29 19:52:29 +0000 |
---|---|---|
committer | wfspotz@sandia.gov <wfspotz@sandia.gov@localhost> | 2007-11-29 19:52:29 +0000 |
commit | 49a0503e47c9bb06d0bf7430b2958f7c23976234 (patch) | |
tree | f57e198c5c7cd44cedb44eb7b8387ac398ff3779 /numpy/doc/swig | |
parent | cbc162f466eac71102e807aa90742471187bebcb (diff) | |
download | numpy-49a0503e47c9bb06d0bf7430b2958f7c23976234.tar.gz |
* Added a new typemap suite, ARGOUTVIEW, which takes a view of a data
buffer and converts it to an output numpy array (1D, 2D and 3D, with
before- and after- dimension specifications.)
* Added new tests for the ARGOUTVIEW typemaps in the test directory
(Array1 and Array2 classes, both included in the Array python
extension module, tested in testArray.py).
* Updated the documentation to reflect the latest changes.
Diffstat (limited to 'numpy/doc/swig')
-rw-r--r-- | numpy/doc/swig/doc/numpy_swig.html | 122 | ||||
-rw-r--r-- | numpy/doc/swig/doc/numpy_swig.pdf | bin | 163324 -> 166822 bytes | |||
-rw-r--r-- | numpy/doc/swig/doc/numpy_swig.txt | 61 | ||||
-rw-r--r-- | numpy/doc/swig/doc/testing.pdf | bin | 72687 -> 72439 bytes | |||
-rw-r--r-- | numpy/doc/swig/numpy.i | 272 | ||||
-rw-r--r-- | numpy/doc/swig/test/Array.i | 107 | ||||
-rw-r--r-- | numpy/doc/swig/test/Array1.cxx | 131 | ||||
-rw-r--r-- | numpy/doc/swig/test/Array1.h | 55 | ||||
-rw-r--r-- | numpy/doc/swig/test/Array2.cxx | 168 | ||||
-rw-r--r-- | numpy/doc/swig/test/Array2.h | 63 | ||||
-rw-r--r-- | numpy/doc/swig/test/Makefile | 8 | ||||
-rwxr-xr-x | numpy/doc/swig/test/setup.py | 12 | ||||
-rwxr-xr-x | numpy/doc/swig/test/testArray.py | 285 |
13 files changed, 1137 insertions, 147 deletions
diff --git a/numpy/doc/swig/doc/numpy_swig.html b/numpy/doc/swig/doc/numpy_swig.html index 7da4f57fc..93a576001 100644 --- a/numpy/doc/swig/doc/numpy_swig.html +++ b/numpy/doc/swig/doc/numpy_swig.html @@ -5,7 +5,7 @@ <meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" /> <title>numpy.i: a SWIG Interface File for NumPy</title> <meta name="author" content="Bill Spotz" /> -<meta name="date" content="22 November, 2007" /> +<meta name="date" content="29 November, 2007" /> <style type="text/css"> /* @@ -302,7 +302,7 @@ ul.auto-toc { <tr class="field"><th class="docinfo-name">Institution:</th><td class="field-body">Sandia National Laboratories</td> </tr> <tr><th class="docinfo-name">Date:</th> -<td>22 November, 2007</td></tr> +<td>29 November, 2007</td></tr> </tbody> </table> <div class="contents topic"> @@ -314,28 +314,29 @@ ul.auto-toc { <li><a class="reference" href="#input-arrays" id="id4" name="id4">Input Arrays</a></li> <li><a class="reference" href="#in-place-arrays" id="id5" name="id5">In-Place Arrays</a></li> <li><a class="reference" href="#argout-arrays" id="id6" name="id6">Argout Arrays</a></li> -<li><a class="reference" href="#output-arrays" id="id7" name="id7">Output Arrays</a></li> -<li><a class="reference" href="#other-common-types-bool" id="id8" name="id8">Other Common Types: bool</a></li> -<li><a class="reference" href="#other-common-types-complex" id="id9" name="id9">Other Common Types: complex</a></li> +<li><a class="reference" href="#argoutview-arrays" id="id7" name="id7">Argoutview Arrays</a></li> +<li><a class="reference" href="#output-arrays" id="id8" name="id8">Output Arrays</a></li> +<li><a class="reference" href="#other-common-types-bool" id="id9" name="id9">Other Common Types: bool</a></li> +<li><a class="reference" href="#other-common-types-complex" id="id10" name="id10">Other Common Types: complex</a></li> </ul> </li> -<li><a class="reference" href="#numpy-array-scalars-and-swig" id="id10" name="id10">NumPy Array Scalars and SWIG</a><ul> -<li><a class="reference" href="#why-is-there-a-second-file" id="id11" name="id11">Why is There a Second File?</a></li> +<li><a class="reference" href="#numpy-array-scalars-and-swig" id="id11" name="id11">NumPy Array Scalars and SWIG</a><ul> +<li><a class="reference" href="#why-is-there-a-second-file" id="id12" name="id12">Why is There a Second File?</a></li> </ul> </li> -<li><a class="reference" href="#helper-functions" id="id12" name="id12">Helper Functions</a><ul> -<li><a class="reference" href="#macros" id="id13" name="id13">Macros</a></li> -<li><a class="reference" href="#routines" id="id14" name="id14">Routines</a></li> +<li><a class="reference" href="#helper-functions" id="id13" name="id13">Helper Functions</a><ul> +<li><a class="reference" href="#macros" id="id14" name="id14">Macros</a></li> +<li><a class="reference" href="#routines" id="id15" name="id15">Routines</a></li> </ul> </li> -<li><a class="reference" href="#beyond-the-provided-typemaps" id="id15" name="id15">Beyond the Provided Typemaps</a><ul> -<li><a class="reference" href="#a-common-example" id="id16" name="id16">A Common Example</a></li> -<li><a class="reference" href="#other-situations" id="id17" name="id17">Other Situations</a></li> -<li><a class="reference" href="#a-final-note" id="id18" name="id18">A Final Note</a></li> +<li><a class="reference" href="#beyond-the-provided-typemaps" id="id16" name="id16">Beyond the Provided Typemaps</a><ul> +<li><a class="reference" href="#a-common-example" id="id17" name="id17">A Common Example</a></li> +<li><a class="reference" href="#other-situations" id="id18" name="id18">Other Situations</a></li> +<li><a class="reference" href="#a-final-note" id="id19" name="id19">A Final Note</a></li> </ul> </li> -<li><a class="reference" href="#summary" id="id19" name="id19">Summary</a></li> -<li><a class="reference" href="#acknowledgements" id="id20" name="id20">Acknowledgements</a></li> +<li><a class="reference" href="#summary" id="id20" name="id20">Summary</a></li> +<li><a class="reference" href="#acknowledgements" id="id21" name="id21">Acknowledgements</a></li> </ul> </div> <div class="section"> @@ -636,7 +637,37 @@ cannot be avoided. Note that for these types of 1D typemaps, the <a class="reference" href="http://www.python.org">python</a> function will take a single argument representing <tt class="docutils literal"><span class="pre">DIM1</span></tt>.</p> </div> <div class="section"> -<h2><a class="toc-backref" href="#id7" id="output-arrays" name="output-arrays">Output Arrays</a></h2> +<h2><a class="toc-backref" href="#id7" id="argoutview-arrays" name="argoutview-arrays">Argoutview Arrays</a></h2> +<p>Argoutview arrays are for when your C code provides you with a view of +its internal data and does not require any memory to be allocated by +the user. This can be dangerous. There is almost no way to guarantee +that the internal data from the C code will remain in existence for +the entire lifetime of the <a class="reference" href="http://numpy.scipy.org">NumPy</a> array that encapsulates it. If +the user destroys the object that provides the view of the data before +destroying the <a class="reference" href="http://numpy.scipy.org">NumPy</a> array, then using that array my result in bad +memory references or segmentation faults. Nevertheless, there are +situations, working with large data sets, where you simply have no +other choice.</p> +<p>The C code to be wrapped for argoutview arrays are characterized by +pointers: pointers to the dimensions and double pointers to the data, +so that these values can be passed back to the user. The argoutview +typemap signatures are therefore</p> +<blockquote> +<ul class="simple"> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY1,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1</span> <span class="pre">)</span></tt></li> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1,</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY1</span> <span class="pre">)</span></tt></li> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY2,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM2</span> <span class="pre">)</span></tt></li> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM2,</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY2</span> <span class="pre">)</span></tt></li> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY3,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM2,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM3)</span></tt></li> +<li><tt class="docutils literal"><span class="pre">(</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM1,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM2,</span> <span class="pre">DIM_TYPE*</span> <span class="pre">DIM3,</span> <span class="pre">DATA_TYPE**</span> <span class="pre">ARGOUTVIEW_ARRAY3)</span></tt></li> +</ul> +</blockquote> +<p>Note that arrays with hard-coded dimensions are not supported. Thes +have to be allocated ahead of time, which defeats the purpose of the +typemap.</p> +</div> +<div class="section"> +<h2><a class="toc-backref" href="#id8" id="output-arrays" name="output-arrays">Output Arrays</a></h2> <p>The <tt class="docutils literal"><span class="pre">numpy.i</span></tt> interface file does not support typemaps for output arrays, for several reasons. First, C/C++ return arguments are limited to a single value. This prevents obtaining dimension @@ -656,7 +687,7 @@ function to be wrapped, either with <tt class="docutils literal"><span class="pr methods or <tt class="docutils literal"><span class="pre">%ignore</span></tt> and <tt class="docutils literal"><span class="pre">%rename</span></tt> for the case of functions.</p> </div> <div class="section"> -<h2><a class="toc-backref" href="#id8" id="other-common-types-bool" name="other-common-types-bool">Other Common Types: bool</a></h2> +<h2><a class="toc-backref" href="#id9" id="other-common-types-bool" name="other-common-types-bool">Other Common Types: bool</a></h2> <p>Note that C++ type <tt class="docutils literal"><span class="pre">bool</span></tt> is not supported in the list in the <a class="reference" href="#available-typemaps">Available Typemaps</a> section. NumPy bools are a single byte, while the C++ <tt class="docutils literal"><span class="pre">bool</span></tt> is four bytes (at least on my system). Therefore:</p> @@ -673,7 +704,7 @@ expansion:</p> but <a class="reference" href="#in-place-arrays">In-Place Arrays</a> might fail type-checking.</p> </div> <div class="section"> -<h2><a class="toc-backref" href="#id9" id="other-common-types-complex" name="other-common-types-complex">Other Common Types: complex</a></h2> +<h2><a class="toc-backref" href="#id10" id="other-common-types-complex" name="other-common-types-complex">Other Common Types: complex</a></h2> <p>Typemap conversions for complex floating-point types is also not supported automatically. This is because <a class="reference" href="http://www.python.org">python</a> and <a class="reference" href="http://numpy.scipy.org">NumPy</a> are written in C, which does not have native complex types. Both @@ -707,7 +738,7 @@ work.</p> </div> </div> <div class="section"> -<h1><a class="toc-backref" href="#id10" id="numpy-array-scalars-and-swig" name="numpy-array-scalars-and-swig">NumPy Array Scalars and SWIG</a></h1> +<h1><a class="toc-backref" href="#id11" id="numpy-array-scalars-and-swig" name="numpy-array-scalars-and-swig">NumPy Array Scalars and SWIG</a></h1> <p><a class="reference" href="http://www.swig.org">SWIG</a> has sophisticated type checking for numerical types. For example, if your C/C++ routine expects an integer as input, the code generated by <a class="reference" href="http://www.swig.org">SWIG</a> will check for both <a class="reference" href="http://www.python.org">python</a> integers and @@ -720,7 +751,7 @@ to pass this to a <a class="reference" href="http://www.swig.org">SWIG</a>-wrapp array scalar as an integer. (Often, this does in fact work -- it depends on whether <a class="reference" href="http://numpy.scipy.org">NumPy</a> recognizes the integer type you are using as inheriting from the <a class="reference" href="http://www.python.org">python</a> integer type on the platform you are -using. Often, this means that code that works on a 32-bit machine +using. Sometimes, this means that code that works on a 32-bit machine will fail on a 64-bit machine.)</p> <p>If you get a <a class="reference" href="http://www.python.org">python</a> error that looks like the following:</p> <pre class="literal-block"> @@ -730,7 +761,7 @@ TypeError: in method 'MyClass_MyMethod', argument 2 of type 'int' <a class="reference" href="http://numpy.scipy.org">NumPy</a> array, then you have stumbled upon this problem. The solution is to modify the <a class="reference" href="http://www.swig.org">SWIG</a> type conversion system to accept <a class="reference" href="http://numpy.scipy.org">Numpy</a> array scalars in addition to the standard integer types. -Fortunately, this capabilitiy has been provided to you. Simply copy +Fortunately, this capabilitiy has been provided for you. Simply copy the file:</p> <pre class="literal-block"> pyfragments.swg @@ -739,7 +770,7 @@ pyfragments.swg be fixed. It is suggested that you do this anyway, as it only increases the capabilities of your <a class="reference" href="http://www.python.org">python</a> interface.</p> <div class="section"> -<h2><a class="toc-backref" href="#id11" id="why-is-there-a-second-file" name="why-is-there-a-second-file">Why is There a Second File?</a></h2> +<h2><a class="toc-backref" href="#id12" id="why-is-there-a-second-file" name="why-is-there-a-second-file">Why is There a Second File?</a></h2> <p>The <a class="reference" href="http://www.swig.org">SWIG</a> type checking and conversion system is a complicated combination of C macros, <a class="reference" href="http://www.swig.org">SWIG</a> macros, <a class="reference" href="http://www.swig.org">SWIG</a> typemaps and <a class="reference" href="http://www.swig.org">SWIG</a> fragments. Fragments are a way to conditionally insert code into your @@ -760,12 +791,21 @@ in <tt class="docutils literal"><span class="pre">numpy.i</span></tt>, they woul </div> </div> <div class="section"> -<h1><a class="toc-backref" href="#id12" id="helper-functions" name="helper-functions">Helper Functions</a></h1> +<h1><a class="toc-backref" href="#id13" id="helper-functions" name="helper-functions">Helper Functions</a></h1> <p>The <tt class="docutils literal"><span class="pre">numpy.i</span></tt> file containes several macros and routines that it uses internally to build its typemaps. However, these functions may -be useful elsewhere in your interface file.</p> +be useful elsewhere in your interface file. These macros and routines +are implemented as fragments, as described briefly in the previous +section. If you try to use one or more of the following macros or +functions, but your compiler complains that it does not recognize the +symbol, then you need to force these fragments to appear in your code +using:</p> +<pre class="literal-block"> +%fragment("NumPy_Fragments"); +</pre> +<p>in your <a class="reference" href="http://www.swig.org">SWIG</a> interface file.</p> <div class="section"> -<h2><a class="toc-backref" href="#id13" id="macros" name="macros">Macros</a></h2> +<h2><a class="toc-backref" href="#id14" id="macros" name="macros">Macros</a></h2> <blockquote> <dl class="docutils"> <dt><strong>is_array(a)</strong></dt> @@ -797,7 +837,7 @@ order. Equivalent to <tt class="docutils literal"><span class="pre">(PyArray_IS </blockquote> </div> <div class="section"> -<h2><a class="toc-backref" href="#id14" id="routines" name="routines">Routines</a></h2> +<h2><a class="toc-backref" href="#id15" id="routines" name="routines">Routines</a></h2> <blockquote> <p><strong>pytype_string()</strong></p> <blockquote> @@ -957,11 +997,11 @@ string and return 0.</p> </div> </div> <div class="section"> -<h1><a class="toc-backref" href="#id15" id="beyond-the-provided-typemaps" name="beyond-the-provided-typemaps">Beyond the Provided Typemaps</a></h1> +<h1><a class="toc-backref" href="#id16" id="beyond-the-provided-typemaps" name="beyond-the-provided-typemaps">Beyond the Provided Typemaps</a></h1> <p>There are many C or C++ array/<a class="reference" href="http://numpy.scipy.org">NumPy</a> array situations not covered by a simple <tt class="docutils literal"><span class="pre">%include</span> <span class="pre">"numpy.i"</span></tt> and subsequent <tt class="docutils literal"><span class="pre">%apply</span></tt> directives.</p> <div class="section"> -<h2><a class="toc-backref" href="#id16" id="a-common-example" name="a-common-example">A Common Example</a></h2> +<h2><a class="toc-backref" href="#id17" id="a-common-example" name="a-common-example">A Common Example</a></h2> <p>Consider a reasonable prototype for a dot product function:</p> <pre class="literal-block"> double dot(int len, double* vec1, double* vec2); @@ -1017,7 +1057,7 @@ above for <tt class="docutils literal"><span class="pre">my_dot</span></tt> to g macro to perform this task.</p> </div> <div class="section"> -<h2><a class="toc-backref" href="#id17" id="other-situations" name="other-situations">Other Situations</a></h2> +<h2><a class="toc-backref" href="#id18" id="other-situations" name="other-situations">Other Situations</a></h2> <p>There are other wrapping situations in which <tt class="docutils literal"><span class="pre">numpy.i</span></tt> may be helpful when you encounter them.</p> <blockquote> @@ -1054,7 +1094,7 @@ developers of <tt class="docutils literal"><span class="pre">numpy.i</span></tt> </blockquote> </div> <div class="section"> -<h2><a class="toc-backref" href="#id18" id="a-final-note" name="a-final-note">A Final Note</a></h2> +<h2><a class="toc-backref" href="#id19" id="a-final-note" name="a-final-note">A Final Note</a></h2> <p>When you use the <tt class="docutils literal"><span class="pre">%apply</span></tt> directive, as is usually necessary to use <tt class="docutils literal"><span class="pre">numpy.i</span></tt>, it will remain in effect until you tell <a class="reference" href="http://www.swig.org">SWIG</a> that it shouldn't be. If the arguments to the functions or methods that you @@ -1072,7 +1112,7 @@ where you want them, and then clear them after you are done.</p> </div> </div> <div class="section"> -<h1><a class="toc-backref" href="#id19" id="summary" name="summary">Summary</a></h1> +<h1><a class="toc-backref" href="#id20" id="summary" name="summary">Summary</a></h1> <p>Out of the box, <tt class="docutils literal"><span class="pre">numpy.i</span></tt> provides typemaps that support conversion between <a class="reference" href="http://numpy.scipy.org">NumPy</a> arrays and C arrays:</p> <blockquote> @@ -1081,10 +1121,10 @@ between <a class="reference" href="http://numpy.scipy.org">NumPy</a> arrays and <tt class="docutils literal"><span class="pre">unsigned</span> <span class="pre">char</span></tt>, <tt class="docutils literal"><span class="pre">short</span></tt>, <tt class="docutils literal"><span class="pre">unsigned</span> <span class="pre">short</span></tt>, <tt class="docutils literal"><span class="pre">int</span></tt>, <tt class="docutils literal"><span class="pre">unsigned</span> <span class="pre">int</span></tt>, <tt class="docutils literal"><span class="pre">long</span></tt>, <tt class="docutils literal"><span class="pre">unsigned</span> <span class="pre">long</span></tt>, <tt class="docutils literal"><span class="pre">long</span> <span class="pre">long</span></tt>, <tt class="docutils literal"><span class="pre">unsigned</span> <span class="pre">long</span> <span class="pre">long</span></tt>, <tt class="docutils literal"><span class="pre">float</span></tt> and <tt class="docutils literal"><span class="pre">double</span></tt>.</li> -<li>That support 23 different argument signatures for each data type, +<li>That support 29 different argument signatures for each data type, including:<ul> <li>One-dimensional, two-dimensional and three-dimensional arrays.</li> -<li>Input-only, in-place, and argout behavior.</li> +<li>Input-only, in-place, argout and argoutview behavior.</li> <li>Hard-coded dimensions, data-buffer-then-dimensions specification, and dimensions-then-data-buffer specification.</li> </ul> @@ -1096,31 +1136,31 @@ wrapper developers, including:</p> <blockquote> <ul class="simple"> <li>A <a class="reference" href="http://www.swig.org">SWIG</a> macro (<tt class="docutils literal"><span class="pre">%numpy_typemaps</span></tt>) with three arguments for -implementing the 23 argument signatures for the user's choice of +implementing the 29 argument signatures for the user's choice of (1) C data type, (2) <a class="reference" href="http://numpy.scipy.org">NumPy</a> data type (assuming they match), and (3) dimension type.</li> -<li>Seven C macros and eleven C functions that can be used to write +<li>Eight C macros and twelve C functions that can be used to write specialized typemaps, extensions, or inlined functions that handle cases not covered by the provided typemaps.</li> </ul> </blockquote> </div> <div class="section"> -<h1><a class="toc-backref" href="#id20" id="acknowledgements" name="acknowledgements">Acknowledgements</a></h1> +<h1><a class="toc-backref" href="#id21" id="acknowledgements" name="acknowledgements">Acknowledgements</a></h1> <p>Many people have worked to glue <a class="reference" href="http://www.swig.org">SWIG</a> and <a class="reference" href="http://numpy.scipy.org">NumPy</a> together (as well as <a class="reference" href="http://www.swig.org">SWIG</a> and the predecessors of <a class="reference" href="http://numpy.scipy.org">NumPy</a>, Numeric and numarray). The effort to standardize this work into <tt class="docutils literal"><span class="pre">numpy.i</span></tt> began at the 2005 <a class="reference" href="http://scipy.org">SciPy</a> Conference with a conversation between Fernando Perez and myself. Fernando collected helper functions and typemaps from Eric Jones, Michael Hunter, Anna Omelchenko and Michael -Sanner. Sebastian Hasse has also provided additional error checking -and use cases. The work of these contributors has made this end -result possible.</p> +Sanner. Sebastian Hasse and Georg Holzmann have also provided +additional error checking and use cases. The work of these +contributors has made this end result possible.</p> </div> </div> <div class="footer"> <hr class="footer" /> -Generated on: 2007-11-22 18:34 UTC. +Generated on: 2007-11-29 19:48 UTC. Generated by <a class="reference" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. </div> diff --git a/numpy/doc/swig/doc/numpy_swig.pdf b/numpy/doc/swig/doc/numpy_swig.pdf Binary files differindex 81da6aa93..7797c00b3 100644 --- a/numpy/doc/swig/doc/numpy_swig.pdf +++ b/numpy/doc/swig/doc/numpy_swig.pdf diff --git a/numpy/doc/swig/doc/numpy_swig.txt b/numpy/doc/swig/doc/numpy_swig.txt index 84c583029..fd140ead3 100644 --- a/numpy/doc/swig/doc/numpy_swig.txt +++ b/numpy/doc/swig/doc/numpy_swig.txt @@ -4,7 +4,7 @@ :Author: Bill Spotz :Institution: Sandia National Laboratories -:Date: 22 November, 2007 +:Date: 29 November, 2007 .. contents:: @@ -314,6 +314,36 @@ or 3D. This because of a quirk with the `SWIG`_ typemap syntax and cannot be avoided. Note that for these types of 1D typemaps, the `python`_ function will take a single argument representing ``DIM1``. +Argoutview Arrays +----------------- + +Argoutview arrays are for when your C code provides you with a view of +its internal data and does not require any memory to be allocated by +the user. This can be dangerous. There is almost no way to guarantee +that the internal data from the C code will remain in existence for +the entire lifetime of the `NumPy`_ array that encapsulates it. If +the user destroys the object that provides the view of the data before +destroying the `NumPy`_ array, then using that array my result in bad +memory references or segmentation faults. Nevertheless, there are +situations, working with large data sets, where you simply have no +other choice. + +The C code to be wrapped for argoutview arrays are characterized by +pointers: pointers to the dimensions and double pointers to the data, +so that these values can be passed back to the user. The argoutview +typemap signatures are therefore + + * ``( DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 )`` + * ``( DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1 )`` + * ``( DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2 )`` + * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2 )`` + * ``( DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)`` + * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)`` + +Note that arrays with hard-coded dimensions are not supported. Thes +have to be allocated ahead of time, which defeats the purpose of the +typemap. + Output Arrays ------------- @@ -402,7 +432,7 @@ to pass this to a `SWIG`_-wrapped C/C++ function that expects an array scalar as an integer. (Often, this does in fact work -- it depends on whether `NumPy`_ recognizes the integer type you are using as inheriting from the `python`_ integer type on the platform you are -using. Often, this means that code that works on a 32-bit machine +using. Sometimes, this means that code that works on a 32-bit machine will fail on a 64-bit machine.) If you get a `python`_ error that looks like the following:: @@ -413,7 +443,7 @@ and the argument you are passing is an integer extracted from a `NumPy`_ array, then you have stumbled upon this problem. The solution is to modify the `SWIG`_ type conversion system to accept `Numpy`_ array scalars in addition to the standard integer types. -Fortunately, this capabilitiy has been provided to you. Simply copy +Fortunately, this capabilitiy has been provided for you. Simply copy the file:: pyfragments.swg @@ -449,7 +479,16 @@ Helper Functions The ``numpy.i`` file containes several macros and routines that it uses internally to build its typemaps. However, these functions may -be useful elsewhere in your interface file. +be useful elsewhere in your interface file. These macros and routines +are implemented as fragments, as described briefly in the previous +section. If you try to use one or more of the following macros or +functions, but your compiler complains that it does not recognize the +symbol, then you need to force these fragments to appear in your code +using:: + + %fragment("NumPy_Fragments"); + +in your `SWIG`_ interface file. Macros ------ @@ -806,12 +845,12 @@ between `NumPy`_ arrays and C arrays: ``unsigned int``, ``long``, ``unsigned long``, ``long long``, ``unsigned long long``, ``float`` and ``double``. - * That support 23 different argument signatures for each data type, + * That support 29 different argument signatures for each data type, including: + One-dimensional, two-dimensional and three-dimensional arrays. - + Input-only, in-place, and argout behavior. + + Input-only, in-place, argout and argoutview behavior. + Hard-coded dimensions, data-buffer-then-dimensions specification, and dimensions-then-data-buffer specification. @@ -820,11 +859,11 @@ The ``numpy.i`` interface file also provides additional tools for wrapper developers, including: * A `SWIG`_ macro (``%numpy_typemaps``) with three arguments for - implementing the 23 argument signatures for the user's choice of + implementing the 29 argument signatures for the user's choice of (1) C data type, (2) `NumPy`_ data type (assuming they match), and (3) dimension type. - * Seven C macros and eleven C functions that can be used to write + * Eight C macros and twelve C functions that can be used to write specialized typemaps, extensions, or inlined functions that handle cases not covered by the provided typemaps. @@ -837,6 +876,6 @@ The effort to standardize this work into ``numpy.i`` began at the 2005 `SciPy <http://scipy.org>`_ Conference with a conversation between Fernando Perez and myself. Fernando collected helper functions and typemaps from Eric Jones, Michael Hunter, Anna Omelchenko and Michael -Sanner. Sebastian Hasse has also provided additional error checking -and use cases. The work of these contributors has made this end -result possible. +Sanner. Sebastian Hasse and Georg Holzmann have also provided +additional error checking and use cases. The work of these +contributors has made this end result possible. diff --git a/numpy/doc/swig/doc/testing.pdf b/numpy/doc/swig/doc/testing.pdf Binary files differindex 7029c75fc..9ffcf7575 100644 --- a/numpy/doc/swig/doc/testing.pdf +++ b/numpy/doc/swig/doc/testing.pdf diff --git a/numpy/doc/swig/numpy.i b/numpy/doc/swig/numpy.i index 626067d79..fc918f1ed 100644 --- a/numpy/doc/swig/numpy.i +++ b/numpy/doc/swig/numpy.i @@ -9,14 +9,6 @@ #include <numpy/arrayobject.h> %} -/* The following code originally appeared in - * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was - * translated from C++ to C by John Hunter. Bill Spotz has modified - * it slightly to fix some minor bugs, upgrade from Numeric to numpy - * (all versions), add some comments and functionality, and convert - * from direct code insertion to SWIG fragments. - */ - /**********************************************************************/ %fragment("NumPy_Backward_Compatibility", "header") @@ -73,6 +65,14 @@ /**********************************************************************/ +/* The following code originally appeared in + * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was + * translated from C++ to C by John Hunter. Bill Spotz has modified + * it to fix some minor bugs, upgrade from Numeric to numpy (all + * versions), add some comments and functionality, and convert from + * direct code insertion to SWIG fragments. + */ + %fragment("NumPy_Macros", "header") { /* Macros to extract array attributes. @@ -406,7 +406,7 @@ /* %numpy_typemaps() macro * - * This macro defines a family of 23 typemaps that allow C arguments + * This macro defines a family of 29 typemaps that allow C arguments * of the form * * (DATA_TYPE IN_ARRAY1[ANY]) @@ -441,95 +441,53 @@ * * (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) * + * (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) + * (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) + * + * (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) + * + * (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) + * * where "DATA_TYPE" is any type supported by the NumPy module, and * "DIM_TYPE" is any int-like type suitable for specifying dimensions. * In python, the dimensions will not need to be specified (except for * the "DATA_TYPE* ARGOUT_ARRAY1" typemaps). The IN_ARRAYs can be a * numpy array or any sequence that can be converted to a numpy array * of the specified type. The INPLACE_ARRAYs must be numpy arrays of - * the appropriate type. The ARGOUT_ARRAYs will be returned as numpy - * arrays of the appropriate type. + * the appropriate type. The ARGOUT_ARRAYs will be returned as new + * numpy arrays of the appropriate type. * * These typemaps can be applied to existing functions using the - * %apply directive: - * - * %apply (double IN_ARRAY1[ANY]) {(double vector[ANY])}; - * double length(double vector[3]); + * %apply directive. For example: * * %apply (double* IN_ARRAY1, int DIM1) {(double* series, int length)}; * double prod(double* series, int length); * - * %apply (int DIM1, double* IN_ARRAY1) {(int length, double* series)} - * double sum(int length, double* series) - * - * %apply (double IN_ARRAY2[ANY][ANY]) {(double matrix[2][2])}; - * double det(double matrix[2][2]); - * - * %apply (double* IN_ARRAY2, int DIM1, int DIM2) {(double* matrix, int rows, int cols)}; - * double max(double* matrix, int rows, int cols); - * - * %apply (int DIM1, int DIM2, double* IN_ARRAY2) {(int rows, int cols, double* matrix)} - * double min(int length, double* series) - * - * %apply (double INPLACE_ARRAY1[ANY]) {(double vector[3])}; - * void reverse(double vector[3]); - * - * %apply (double* INPLACE_ARRAY1, int DIM1) {(double* series, int length)}; - * void ones(double* series, int length); - * - * %apply (int DIM1, double* INPLACE_ARRAY1) {(int length, double* series)} - * double zeros(int length, double* series) + * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2) + * {(int rows, int cols, double* matrix )}; + * void floor(int rows, int cols, double* matrix, double f); * - * %apply (double INPLACE_ARRAY2[ANY][ANY]) {(double matrix[3][3])}; - * void scale(double matrix[3][3]); - * - * %apply (double* INPLACE_ARRAY2, int DIM1, int DIM2) {(double* matrix, int rows, int cols)}; - * void floor(double* matrix, int rows, int cols); - * - * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2) {(int rows, int cols, double* matrix)}; - * void ceil(int rows, int cols, double* matrix); - * - * %apply (double IN_ARRAY1[ANY] ) {(double vector[ANY])}; - * %apply (double ARGOUT_ARRAY1[ANY]) {(double even[ 3])}; - * %apply (double ARGOUT_ARRAY1[ANY]) {(double odd[ 3])}; - * void eoSplit(double vector[3], double even[3], double odd[3]); - * - * %apply (double* ARGOUT_ARRAY1, int DIM1) {(double* twoVec, int size)}; - * void twos(double* twoVec, int size); - * - * %apply (int DIM1, double* ARGOUT_ARRAY1) {(int size, double* threeVec)}; - * void threes(int size, double* threeVec); - * - * %apply (double IN_ARRAY2[ANY][ANY]) {(double matrix[2][2])}; - * %apply (double ARGOUT_ARRAY2[ANY][ANY]) {(double upper[ 3][3])}; - * %apply (double ARGOUT_ARRAY2[ANY][ANY]) {(double lower[ 3][3])}; - * void luSplit(double matrix[3][3], double upper[3][3], double lower[3][3]); + * %apply (double IN_ARRAY3[ANY][ANY][ANY]) + * {(double tensor[2][2][2] )}; + * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) + * {(double low[2][2][2] )}; + * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) + * {(double upp[2][2][2] )}; + * void luSplit(double tensor[2][2][2], + * double low[2][2][2], + * double upp[2][2][2] ); * * or directly with * - * double length(double IN_ARRAY1[ANY]); * double prod(double* IN_ARRAY1, int DIM1); - * double sum( int DIM1, double* IN_ARRAY1) - * - * double det(double IN_ARRAY2[ANY][ANY]); - * double max(double* IN_ARRAY2, int DIM1, int DIM2); - * double min(int DIM1, int DIM2, double* IN_ARRAY2) - * - * void reverse(double INPLACE_ARRAY1[ANY]); - * void ones( double* INPLACE_ARRAY1, int DIM1); - * void zeros(int DIM1, double* INPLACE_ARRAY1) * - * void scale(double INPLACE_ARRAY2[ANY][ANY]); - * void floor(double* INPLACE_ARRAY2, int DIM1, int DIM2, double floor); - * void ceil( int DIM1, int DIM2, double* INPLACE_ARRAY2, double ceil ); + * void floor(int DIM1, int DIM2, double* INPLACE_ARRAY2, double f); * - * void eoSplit(double IN_ARRAY1[ANY], double ARGOUT_ARRAY1[ANY], - * double ARGOUT_ARRAY1[ANY]); - * void twos(double* ARGOUT_ARRAY1, int DIM1) - * void threes(int DIM1, double* ARGOUT_ARRAY1) - * - * void luSplit(double IN_ARRAY2[ANY][ANY], double ARGOUT_ARRAY2[ANY][ANY], - * double ARGOUT_ARRAY2[ANY][ANY]); + * void luSplit(double IN_ARRAY3[ANY][ANY][ANY], + * double ARGOUT_ARRAY3[ANY][ANY][ANY], + * double ARGOUT_ARRAY3[ANY][ANY][ANY]); */ %define %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE) @@ -1107,8 +1065,140 @@ $result = SWIG_Python_AppendOutput($result,array$argnum); } -%enddef /* %numpy_typemaps() macro */ +/*****************************/ +/* Argoutview Array Typemaps */ +/*****************************/ +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 ) + (DATA_TYPE* data_temp , DIM_TYPE dim_temp) +{ + $1 = &data_temp; + $2 = &dim_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) + (PyObject* array = NULL) +{ + npy_intp dims[1] = { *$2 }; + array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEW_ARRAY1) + (DIM_TYPE dim_temp, DATA_TYPE* data_temp ) +{ + $1 = &dim_temp; + $2 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) + (PyObject* array = NULL) +{ + npy_intp dims[1] = { *$1 }; + array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) + (DATA_TYPE* data_temp , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + (PyObject* array = NULL) +{ + npy_intp dims[2] = { *$2, *$3 }; + array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_ARRAY2) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) + (PyObject* array = NULL) +{ + npy_intp dims[2] = { *$1, *$2 }; + array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + (DATA_TYPE* data_temp, DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + (PyObject* array = NULL) +{ + npy_intp dims[3] = { *$2, *$3, *$4 }; + array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, + DATA_TYPE** ARGOUTVIEW_ARRAY3) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) + (PyObject* array = NULL) +{ + npy_intp dims[3] = { *$1, *$2, *$3 }; + array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$3)); + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,array); +} + +%enddef /* %numpy_typemaps() macro */ +/* *************************************************************** */ /* Concrete instances of the %numpy_typemaps() macro: Each invocation * below applies all of the typemaps above to the specified data type. @@ -1129,26 +1219,26 @@ /* *************************************************************** * The follow macro expansion does not work, because C++ bool is 4 * bytes and NPY_BOOL is 1 byte - */ -/*%numpy_typemaps(bool, NPY_BOOL, int) + * + * %numpy_typemaps(bool, NPY_BOOL, int) */ /* *************************************************************** * On my Mac, I get the following warning for this macro expansion: * 'swig/python detected a memory leak of type 'long double *', no destructor found.' - */ -/*%numpy_typemaps(long double, NPY_LONGDOUBLE, int) + * + * %numpy_typemaps(long double, NPY_LONGDOUBLE, int) */ /* *************************************************************** - * Swig complains about a syntax error for the following macros + * Swig complains about a syntax error for the following macro * expansions: - */ -/*%numpy_typemaps(complex float, NPY_CFLOAT , int) - */ -/*%numpy_typemaps(complex double, NPY_CDOUBLE, int) - */ -/*%numpy_typemaps(complex long double, NPY_CLONGDOUBLE) + * + * %numpy_typemaps(complex float, NPY_CFLOAT , int) + * + * %numpy_typemaps(complex double, NPY_CDOUBLE, int) + * + * %numpy_typemaps(complex long double, NPY_CLONGDOUBLE, int) */ #endif /* SWIGPYTHON */ diff --git a/numpy/doc/swig/test/Array.i b/numpy/doc/swig/test/Array.i new file mode 100644 index 000000000..d56dd2d1c --- /dev/null +++ b/numpy/doc/swig/test/Array.i @@ -0,0 +1,107 @@ +// -*- c++ -*- + +%module Array + +%{ +#define SWIG_FILE_WITH_INIT +#include "Array1.h" +#include "Array2.h" +%} + +// Get the NumPy typemaps +%include "../numpy.i" + + // Get the STL typemaps +%include "stl.i" + +// Handle standard exceptions +%include "exception.i" +%exception +{ + try + { + $action + } + catch (const std::invalid_argument& e) + { + SWIG_exception(SWIG_ValueError, e.what()); + } + catch (const std::out_of_range& e) + { + SWIG_exception(SWIG_IndexError, e.what()); + } +} +%init %{ + import_array(); +%} + +// Global ignores +%ignore *::operator=; +%ignore *::operator[]; + +// Apply the 1D NumPy typemaps +%apply (int DIM1 , long* INPLACE_ARRAY1) + {(int length, long* data )}; +%apply (long** ARGOUTVIEW_ARRAY1, int* DIM1 ) + {(long** data , int* length)}; + +// Apply the 2D NumPy typemaps +%apply (int DIM1 , int DIM2 , long* INPLACE_ARRAY2) + {(int nrows, int ncols, long* data )}; +%apply (int* DIM1 , int* DIM2 , long** ARGOUTVIEW_ARRAY2) + {(int* nrows, int* ncols, long** data )}; +// Note: the %apply for INPLACE_ARRAY2 above gets successfully applied +// to the constructor Array2(int nrows, int ncols, long* data), but +// does not get applied to the method Array2::resize(int nrows, int +// ncols, long* data). I have no idea why. For this reason the test +// for Apply2.resize(numpy.ndarray) in testArray.py is commented out. + +// Array1 support +%include "Array1.h" +%extend Array1 +{ + void __setitem__(int i, long v) + { + self->operator[](i) = v; + } + + long __getitem__(int i) + { + return self->operator[](i); + } + + int __len__() + { + return self->length(); + } + + std::string __str__() + { + return self->asString(); + } +} + +// Array2 support +%include "Array2.h" +%extend Array2 +{ + void __setitem__(int i, Array1 & v) + { + self->operator[](i) = v; + } + + Array1 & __getitem__(int i) + { + return self->operator[](i); + } + + int __len__() + { + return self->nrows() * self->ncols(); + } + + std::string __str__() + { + return self->asString(); + } +} diff --git a/numpy/doc/swig/test/Array1.cxx b/numpy/doc/swig/test/Array1.cxx new file mode 100644 index 000000000..0c09e02f9 --- /dev/null +++ b/numpy/doc/swig/test/Array1.cxx @@ -0,0 +1,131 @@ +#include "Array1.h" +#include <iostream> +#include <sstream> + +// Default/length/array constructor +Array1::Array1(int length, long* data) : + _ownData(false), _length(0), _buffer(0) +{ + resize(length, data); +} + +// Copy constructor +Array1::Array1(const Array1 & source) : + _length(source._length) +{ + allocateMemory(); + *this = source; +} + +// Destructor +Array1::~Array1() +{ + deallocateMemory(); +} + +// Assignment operator +Array1 & Array1::operator=(const Array1 & source) +{ + int len = _length < source._length ? _length : source._length; + for (int i=0; i < len; ++i) + { + (*this)[i] = source[i]; + } + return *this; +} + +// Equals operator +bool Array1::operator==(const Array1 & other) const +{ + if (_length != other._length) return false; + for (int i=0; i < _length; ++i) + { + if ((*this)[i] != other[i]) return false; + } + return true; +} + +// Length accessor +int Array1::length() const +{ + return _length; +} + +// Resize array +void Array1::resize(int length, long* data) +{ + if (length < 0) throw std::invalid_argument("Array1 length less than 0"); + if (length == _length) return; + deallocateMemory(); + _length = length; + if (!data) + { + allocateMemory(); + } + else + { + _ownData = false; + _buffer = data; + } +} + +// Set item accessor +long & Array1::operator[](int i) +{ + if (i < 0 || i >= _length) throw std::out_of_range("Array1 index out of range"); + return _buffer[i]; +} + +// Get item accessor +const long & Array1::operator[](int i) const +{ + if (i < 0 || i >= _length) throw std::out_of_range("Array1 index out of range"); + return _buffer[i]; +} + +// String output +std::string Array1::asString() const +{ + std::stringstream result; + result << "["; + for (int i=0; i < _length; ++i) + { + result << " " << _buffer[i]; + if (i < _length-1) result << ","; + } + result << " ]"; + return result.str(); +} + +// Get view +void Array1::view(long** data, int* length) const +{ + *data = _buffer; + *length = _length; +} + +// Private methods + void Array1::allocateMemory() + { + if (_length == 0) + { + _ownData = false; + _buffer = 0; + } + else + { + _ownData = true; + _buffer = new long[_length]; + } + } + + void Array1::deallocateMemory() + { + if (_ownData && _length && _buffer) + { + delete [] _buffer; + } + _ownData = false; + _length = 0; + _buffer = 0; + } diff --git a/numpy/doc/swig/test/Array1.h b/numpy/doc/swig/test/Array1.h new file mode 100644 index 000000000..754c248fc --- /dev/null +++ b/numpy/doc/swig/test/Array1.h @@ -0,0 +1,55 @@ +#ifndef ARRAY1_H +#define ARRAY1_H + +#include <stdexcept> +#include <string> + +class Array1 +{ +public: + + // Default/length/array constructor + Array1(int length = 0, long* data = 0); + + // Copy constructor + Array1(const Array1 & source); + + // Destructor + ~Array1(); + + // Assignment operator + Array1 & operator=(const Array1 & source); + + // Equals operator + bool operator==(const Array1 & other) const; + + // Length accessor + int length() const; + + // Resize array + void resize(int length, long* data = 0); + + // Set item accessor + long & operator[](int i); + + // Get item accessor + const long & operator[](int i) const; + + // String output + std::string asString() const; + + // Get view + void view(long** data, int* length) const; + +private: + // Members + bool _ownData; + int _length; + long * _buffer; + + // Methods + void allocateMemory(); + void deallocateMemory(); +}; + +#endif diff --git a/numpy/doc/swig/test/Array2.cxx b/numpy/doc/swig/test/Array2.cxx new file mode 100644 index 000000000..e3558f786 --- /dev/null +++ b/numpy/doc/swig/test/Array2.cxx @@ -0,0 +1,168 @@ +#include "Array2.h" +#include <sstream> + +// Default constructor +Array2::Array2() : + _ownData(false), _nrows(0), _ncols(), _buffer(0), _rows(0) +{ } + +// Size/array constructor +Array2::Array2(int nrows, int ncols, long* data) : + _ownData(false), _nrows(0), _ncols(), _buffer(0), _rows(0) +{ + resize(nrows, ncols, data); +} + +// Copy constructor +Array2::Array2(const Array2 & source) : + _nrows(source._nrows), _ncols(source._ncols) +{ + _ownData = true; + allocateMemory(); + *this = source; +} + +// Destructor +Array2::~Array2() +{ + deallocateMemory(); +} + +// Assignment operator +Array2 & Array2::operator=(const Array2 & source) +{ + int nrows = _nrows < source._nrows ? _nrows : source._nrows; + int ncols = _ncols < source._ncols ? _ncols : source._ncols; + for (int i=0; i < nrows; ++i) + { + for (int j=0; j < ncols; ++j) + { + (*this)[i][j] = source[i][j]; + } + } + return *this; +} + +// Equals operator +bool Array2::operator==(const Array2 & other) const +{ + if (_nrows != other._nrows) return false; + if (_ncols != other._ncols) return false; + for (int i=0; i < _nrows; ++i) + { + for (int j=0; j < _ncols; ++j) + { + if ((*this)[i][j] != other[i][j]) return false; + } + } + return true; +} + +// Length accessors +int Array2::nrows() const +{ + return _nrows; +} + +int Array2::ncols() const +{ + return _ncols; +} + +// Resize array +void Array2::resize(int nrows, int ncols, long* data) +{ + if (nrows < 0) throw std::invalid_argument("Array2 nrows less than 0"); + if (ncols < 0) throw std::invalid_argument("Array2 ncols less than 0"); + if (nrows == _nrows && ncols == _ncols) return; + deallocateMemory(); + _nrows = nrows; + _ncols = ncols; + if (!data) + { + allocateMemory(); + } + else + { + _ownData = false; + _buffer = data; + allocateRows(); + } +} + +// Set item accessor +Array1 & Array2::operator[](int i) +{ + if (i < 0 || i > _nrows) throw std::out_of_range("Array2 row index out of range"); + return _rows[i]; +} + +// Get item accessor +const Array1 & Array2::operator[](int i) const +{ + if (i < 0 || i > _nrows) throw std::out_of_range("Array2 row index out of range"); + return _rows[i]; +} + +// String output +std::string Array2::asString() const +{ + std::stringstream result; + result << "[ "; + for (int i=0; i < _nrows; ++i) + { + if (i > 0) result << " "; + result << (*this)[i].asString(); + if (i < _nrows-1) result << "," << std::endl; + } + result << " ]" << std::endl; + return result.str(); +} + +// Get view +void Array2::view(int* nrows, int* ncols, long** data) const +{ + *nrows = _nrows; + *ncols = _ncols; + *data = _buffer; +} + +// Private methods +void Array2::allocateMemory() +{ + if (_nrows * _ncols == 0) + { + _ownData = false; + _buffer = 0; + _rows = 0; + } + else + { + _ownData = true; + _buffer = new long[_nrows*_ncols]; + allocateRows(); + } +} + +void Array2::allocateRows() +{ + _rows = new Array1[_nrows]; + for (int i=0; i < _nrows; ++i) + { + _rows[i].resize(_ncols, &_buffer[i*_ncols]); + } +} + +void Array2::deallocateMemory() +{ + if (_ownData && _nrows*_ncols && _buffer) + { + delete [] _rows; + delete [] _buffer; + } + _ownData = false; + _nrows = 0; + _ncols = 0; + _buffer = 0; + _rows = 0; +} diff --git a/numpy/doc/swig/test/Array2.h b/numpy/doc/swig/test/Array2.h new file mode 100644 index 000000000..a6e5bfc30 --- /dev/null +++ b/numpy/doc/swig/test/Array2.h @@ -0,0 +1,63 @@ +#ifndef ARRAY2_H +#define ARRAY2_H + +#include "Array1.h" +#include <stdexcept> +#include <string> + +class Array2 +{ +public: + + // Default constructor + Array2(); + + // Size/array constructor + Array2(int nrows, int ncols, long* data=0); + + // Copy constructor + Array2(const Array2 & source); + + // Destructor + ~Array2(); + + // Assignment operator + Array2 & operator=(const Array2 & source); + + // Equals operator + bool operator==(const Array2 & other) const; + + // Length accessors + int nrows() const; + int ncols() const; + + // Resize array + void resize(int ncols, int nrows, long* data=0); + + // Set item accessor + Array1 & operator[](int i); + + // Get item accessor + const Array1 & operator[](int i) const; + + // String output + std::string asString() const; + + // Get view + void view(int* nrows, int* ncols, long** data) const; + +private: + // Members + bool _ownData; + int _nrows; + int _ncols; + long * _buffer; + Array1 * _rows; + + // Methods + void allocateMemory(); + void allocateRows(); + void deallocateMemory(); +}; + +#endif diff --git a/numpy/doc/swig/test/Makefile b/numpy/doc/swig/test/Makefile index e6768b52c..b980cce78 100644 --- a/numpy/doc/swig/test/Makefile +++ b/numpy/doc/swig/test/Makefile @@ -1,11 +1,12 @@ # SWIG -INTERFACES = Vector.i Matrix.i Tensor.i +INTERFACES = Array.i Vector.i Matrix.i Tensor.i WRAPPERS = $(INTERFACES:.i=_wrap.cxx) PROXIES = $(INTERFACES:.i=.py ) # Default target: build the tests .PHONY : all -all: $(WRAPPERS) Vector.cxx Vector.h Matrix.cxx Matrix.h Tensor.cxx Tensor.h +all: $(WRAPPERS) Array1.cxx Array1.h Vector.cxx Vector.h Matrix.cxx Matrix.h \ + Tensor.cxx Tensor.h ./setup.py build # Test target: run the tests @@ -14,10 +15,13 @@ test: all python testVector.py python testMatrix.py python testTensor.py + python testArray.py # Rule: %.i -> %_wrap.cxx %_wrap.cxx: %.i %.h ../numpy.i swig -c++ -python $< +%_wrap.cxx: %.i %1.h %2.h ../numpy.i + swig -c++ -python $< # Clean target .PHONY : clean diff --git a/numpy/doc/swig/test/setup.py b/numpy/doc/swig/test/setup.py index 4e6826cc1..948b8ec7e 100755 --- a/numpy/doc/swig/test/setup.py +++ b/numpy/doc/swig/test/setup.py @@ -13,6 +13,14 @@ try: except AttributeError: numpy_include = numpy.get_numpy_include() +# Array extension module +_Array = Extension("_Array", + ["Array_wrap.cxx", + "Array1.cxx", + "Array2.cxx"], + include_dirs = [numpy_include], + ) + # _Vector extension module _Vector = Extension("_Vector", ["Vector_wrap.cxx", @@ -38,6 +46,6 @@ _Tensor = Extension("_Tensor", setup(name = "NumpyTypemapTests", description = "Functions that work on arrays", author = "Bill Spotz", - py_modules = ["Vector", "Matrix", "Tensor"], - ext_modules = [_Vector , _Matrix , _Tensor ] + py_modules = ["Array", "Vector", "Matrix", "Tensor"], + ext_modules = [_Array , _Vector , _Matrix , _Tensor ] ) diff --git a/numpy/doc/swig/test/testArray.py b/numpy/doc/swig/test/testArray.py new file mode 100755 index 000000000..a22692465 --- /dev/null +++ b/numpy/doc/swig/test/testArray.py @@ -0,0 +1,285 @@ +#! /usr/bin/env python + +# System imports +from distutils.util import get_platform +import os +import sys +import unittest + +# Import NumPy +import numpy as N +major, minor = [ int(d) for d in N.__version__.split(".")[:2] ] +if major == 0: BadListError = TypeError +else: BadListError = ValueError + +# Add the distutils-generated build directory to the python search path and then +# import the extension module +libDir = "lib.%s-%s" % (get_platform(), sys.version[:3]) +sys.path.insert(0,os.path.join("build", libDir)) +import Array + +###################################################################### + +class Array1TestCase(unittest.TestCase): + + def setUp(self): + self.length = 5 + self.array1 = Array.Array1(self.length) + + def testConstructor0(self): + "Test Array1 default constructor" + a = Array.Array1() + self.failUnless(isinstance(a, Array.Array1)) + self.failUnless(len(a) == 0) + + def testConstructor1(self): + "Test Array1 length constructor" + self.failUnless(isinstance(self.array1, Array.Array1)) + + def testConstructor2(self): + "Test Array1 array constructor" + na = N.arange(self.length) + aa = Array.Array1(na) + self.failUnless(isinstance(aa, Array.Array1)) + + def testConstructor3(self): + "Test Array1 copy constructor" + for i in range(self.array1.length()): self.array1[i] = i + arrayCopy = Array.Array1(self.array1) + self.failUnless(arrayCopy == self.array1) + + def testConstructorBad(self): + "Test Array1 length constructor, negative" + self.assertRaises(ValueError, Array.Array1, -4) + + def testLength(self): + "Test Array1 length method" + self.failUnless(self.array1.length() == self.length) + + def testLen(self): + "Test Array1 __len__ method" + self.failUnless(len(self.array1) == self.length) + + def testResize0(self): + "Test Array1 resize method, length" + newLen = 2 * self.length + self.array1.resize(newLen) + self.failUnless(len(self.array1) == newLen) + + def testResize1(self): + "Test Array1 resize method, array" + a = N.zeros((2*self.length,), dtype='l') + self.array1.resize(a) + self.failUnless(len(self.array1) == len(a)) + + def testResizeBad(self): + "Test Array1 resize method, negative length" + self.assertRaises(ValueError, self.array1.resize, -5) + + def testSetGet(self): + "Test Array1 __setitem__, __getitem__ methods" + n = self.length + for i in range(n): + self.array1[i] = i*i + for i in range(n): + self.failUnless(self.array1[i] == i*i) + + def testSetBad1(self): + "Test Array1 __setitem__ method, negative index" + self.assertRaises(IndexError, self.array1.__setitem__, -1, 0) + + def testSetBad2(self): + "Test Array1 __setitem__ method, out-of-range index" + self.assertRaises(IndexError, self.array1.__setitem__, self.length+1, 0) + + def testGetBad1(self): + "Test Array1 __getitem__ method, negative index" + self.assertRaises(IndexError, self.array1.__getitem__, -1) + + def testGetBad2(self): + "Test Array1 __getitem__ method, out-of-range index" + self.assertRaises(IndexError, self.array1.__getitem__, self.length+1) + + def testAsString(self): + "Test Array1 asString method" + for i in range(self.array1.length()): self.array1[i] = i+1 + self.failUnless(self.array1.asString() == "[ 1, 2, 3, 4, 5 ]") + + def testStr(self): + "Test Array1 __str__ method" + for i in range(self.array1.length()): self.array1[i] = i-2 + self.failUnless(str(self.array1) == "[ -2, -1, 0, 1, 2 ]") + + def testView(self): + "Test Array1 view method" + for i in range(self.array1.length()): self.array1[i] = i+1 + a = self.array1.view() + self.failUnless(isinstance(a, N.ndarray)) + self.failUnless(len(a) == self.length) + self.failUnless((a == [1,2,3,4,5]).all()) + +###################################################################### + +class Array2TestCase(unittest.TestCase): + + def setUp(self): + self.nrows = 5 + self.ncols = 4 + self.array2 = Array.Array2(self.nrows, self.ncols) + + def testConstructor0(self): + "Test Array2 default constructor" + a = Array.Array2() + self.failUnless(isinstance(a, Array.Array2)) + self.failUnless(len(a) == 0) + + def testConstructor1(self): + "Test Array2 nrows, ncols constructor" + self.failUnless(isinstance(self.array2, Array.Array2)) + + def testConstructor2(self): + "Test Array2 array constructor" + na = N.zeros((3,4), dtype="l") + aa = Array.Array2(na) + self.failUnless(isinstance(aa, Array.Array2)) + + def testConstructor3(self): + "Test Array2 copy constructor" + for i in range(self.nrows): + for j in range(self.ncols): + self.array2[i][j] = i * j + arrayCopy = Array.Array2(self.array2) + self.failUnless(arrayCopy == self.array2) + + def testConstructorBad1(self): + "Test Array2 nrows, ncols constructor, negative nrows" + self.assertRaises(ValueError, Array.Array2, -4, 4) + + def testConstructorBad2(self): + "Test Array2 nrows, ncols constructor, negative ncols" + self.assertRaises(ValueError, Array.Array2, 4, -4) + + def testNrows(self): + "Test Array2 nrows method" + self.failUnless(self.array2.nrows() == self.nrows) + + def testNcols(self): + "Test Array2 ncols method" + self.failUnless(self.array2.ncols() == self.ncols) + + def testLen(self): + "Test Array2 __len__ method" + self.failUnless(len(self.array2) == self.nrows*self.ncols) + + def testResize0(self): + "Test Array2 resize method, size" + newRows = 2 * self.nrows + newCols = 2 * self.ncols + self.array2.resize(newRows, newCols) + self.failUnless(len(self.array2) == newRows * newCols) + + #def testResize1(self): + # "Test Array2 resize method, array" + # a = N.zeros((2*self.nrows, 2*self.ncols), dtype='l') + # self.array2.resize(a) + # self.failUnless(len(self.array2) == len(a)) + + def testResizeBad1(self): + "Test Array2 resize method, negative nrows" + self.assertRaises(ValueError, self.array2.resize, -5, 5) + + def testResizeBad2(self): + "Test Array2 resize method, negative ncols" + self.assertRaises(ValueError, self.array2.resize, 5, -5) + + def testSetGet1(self): + "Test Array2 __setitem__, __getitem__ methods" + m = self.nrows + n = self.ncols + array1 = [ ] + a = N.arange(n, dtype="l") + for i in range(m): + array1.append(Array.Array1(i*a)) + for i in range(m): + self.array2[i] = array1[i] + for i in range(m): + self.failUnless(self.array2[i] == array1[i]) + + def testSetGet2(self): + "Test Array2 chained __setitem__, __getitem__ methods" + m = self.nrows + n = self.ncols + for i in range(m): + for j in range(n): + self.array2[i][j] = i*j + for i in range(m): + for j in range(n): + self.failUnless(self.array2[i][j] == i*j) + + def testSetBad1(self): + "Test Array2 __setitem__ method, negative index" + a = Array.Array1(self.ncols) + self.assertRaises(IndexError, self.array2.__setitem__, -1, a) + + def testSetBad2(self): + "Test Array2 __setitem__ method, out-of-range index" + a = Array.Array1(self.ncols) + self.assertRaises(IndexError, self.array2.__setitem__, self.nrows+1, a) + + def testGetBad1(self): + "Test Array2 __getitem__ method, negative index" + self.assertRaises(IndexError, self.array2.__getitem__, -1) + + def testGetBad2(self): + "Test Array2 __getitem__ method, out-of-range index" + self.assertRaises(IndexError, self.array2.__getitem__, self.nrows+1) + + def testAsString(self): + "Test Array2 asString method" + result = """\ +[ [ 0, 1, 2, 3 ], + [ 1, 2, 3, 4 ], + [ 2, 3, 4, 5 ], + [ 3, 4, 5, 6 ], + [ 4, 5, 6, 7 ] ] +""" + for i in range(self.nrows): + for j in range(self.ncols): + self.array2[i][j] = i+j + self.failUnless(self.array2.asString() == result) + + def testStr(self): + "Test Array2 __str__ method" + result = """\ +[ [ 0, -1, -2, -3 ], + [ 1, 0, -1, -2 ], + [ 2, 1, 0, -1 ], + [ 3, 2, 1, 0 ], + [ 4, 3, 2, 1 ] ] +""" + for i in range(self.nrows): + for j in range(self.ncols): + self.array2[i][j] = i-j + self.failUnless(str(self.array2) == result) + + def testView(self): + "Test Array2 view method" + a = self.array2.view() + self.failUnless(isinstance(a, N.ndarray)) + self.failUnless(len(a) == self.nrows) + +###################################################################### + +if __name__ == "__main__": + + # Build the test suite + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(Array1TestCase)) + suite.addTest(unittest.makeSuite(Array2TestCase)) + + # Execute the test suite + print "Testing Classes of Module Array" + print "NumPy version", N.__version__ + print + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(len(result.errors) + len(result.failures)) |