Menu

[r4674]: / trunk / course / wrapping.lyx  Maximize  Restore  History

Download this file

673 lines (484 with data), 14.7 kB

#LyX 1.3 created this file. For more info see http://www.lyx.org/
\lyxformat 221
\textclass amsbook
\begin_preamble
\usepackage{ae,aecompl}
\usepackage{hyperref}
%\usepackage{html}
\usepackage{color}

\definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
% Use and configure listings package for nicely formatted code
\usepackage{listings}
\lstset{
  language=Python,
  basicstyle=\small\ttfamily,
  commentstyle=\ttfamily\color{blue},
  stringstyle=\ttfamily\color{orange},
  showstringspaces=false,
  breaklines=true,
  postbreak = \space\dots
}

% Some extra commands

\newcommand{\fig}[4]
{\begin{figure}[ht]
\begin{center}
\includegraphics[width=#1]{#2}
\caption{\label{#4} #3}
\end{center}
\end{figure}}

\newcommand{\matlab}[0]{matlab{\texttrademark}}
\newcommand{\fname}[1]{{\tt #1}}
\newcommand{\func}[1]{{\tt #1}}
\newcommand{\class}[1]{{\tt #1}}
\newcommand{\mpldoc}[1]{{\tt #1}}

\newcommand{\code}[1]{{\tt #1}}
\newcommand{\prompt}[1]{\code{>>> #1}}
\newcommand{\carg}[1]{\textit{#1}} % command argument
\newcommand{\val}[1]{\textit{#1}}
\newcommand{\rc}[1]{{\tt #1}}
\end_preamble
\language english
\inputencoding auto
\fontscheme default
\graphics default
\paperfontsize default
\spacing single 
\papersize Default
\paperpackage a4
\use_geometry 1
\use_amsmath 0
\use_natbib 0
\use_numerical_citations 0
\paperorientation portrait
\leftmargin 1.15in
\topmargin 1in
\rightmargin 1.15in
\bottommargin 1in
\secnumdepth 3
\tocdepth 3
\paragraph_separation indent
\defskip medskip
\quotes_language english
\quotes_times 2
\papercolumns 1
\papersides 1
\paperpagestyle default

\layout Chapter

Interfacing with external libraries
\begin_inset OptArg
collapsed true

\layout Standard

External libraries
\end_inset 


\layout Section

weave
\layout Comment

John - should we talk to Eric Jones about letting us use parts of his weave
 docs? There's a very old set of docs for weave, I could update some of
 it and this would make an excellent section.
 Weave is really amazing, but badly under-documented and hence unknown.
\layout Standard

Below is a listing of examples of weave use.
 This needs a lot of cleaning, as some of this code is very old and doesn't
 actually run with current weave.
 
\layout Standard


\begin_inset ERT
status Open

\layout Standard

\backslash 
lstinputlisting{examples/wrap_weave.py}
\end_inset 


\layout Section

swig 
\layout Section

f2py 
\layout Standard

This is a rough set of notes on how to use f2py.
 It does NOT substitute the official manual, but is rather meant to be used
 alongside with it.
 
\layout Standard

For any non-trivial poject involving f2py, one should also keep at hand
 Pierre Schnizer's excellent 'A short introduction to F2PY', available from
 http://fubphpc.tu-graz.ac.at/~pierre/f2py_tutorial.tar.gz 
\layout Subsection

Usage for the impatient 
\layout Standard

Start by building a scratch signature file automatically from your Fortran
 sources (in this case all, you can choose only those .f files you need):
 
\layout LyX-Code

f2py -m MODULENAME -h MODULENAME.pyf *.f 
\layout Standard

This writes the file MODULENAME.pyf, making the best guesses it can from
 the Fortran sources.
 It builds an interface for the module to be accessed as 'import adap1d'
 from python.
\layout Standard

You will then edit the .pyf file to fine-tune the python interface exhibited
 by the resulting extension.
 This means for example making unnecessary scratch areas or array dimensions
 hidden, or making certain parameters be optional and take a default value.
\layout Standard

Then, write your setup.py file using distutils, and list the .pyf file along
 with the Fortran sources it is meant to wrap.
 f2py will build the module for you automatically, respecting all the interface
 specifications you made in the .pyf file.
\layout Standard

This approach is ultimately far easier than trying to get all the declarations
 (especially dependencies) right through Cf2py directives in the Fortran
 sources.
 While that may seem appealing at first, experience seems to show that it's
 ultimately far more time-consuming and prone to subtle errors.
 Using this approach, the first f2py pass can do the bulk of the interface
 writing and only fine-tuning needs to be done manually.
 I would only recommend embedded Cf2py directives for very simple problems
 (where it works very well).
\layout Standard

The only drawback of this approach is that the interface and the original
 Fortran source lie in different files, which need to be kept in sync.
 This increases a bit the chances of forgetting to update the .pyf file if
 the Fortran interface changes (adding a parameter, for example).
 However, the benefit of having explicit, clear control over f2py's behavior
 far outweighs this concern.
 
\layout Subsection

Choosing a default compiler 
\layout Standard

Set the FC_VENDOR environment variable.
 This will then prevent f2py from testing all the compilers it knows about.
 
\layout Subsection

Using Cf2py directives 
\layout Standard

For simpler cases you may choose to go the route of Cf2py directives.
 Below are some tips and examples for this approach.
\layout Standard

Here's the signature of a simple Fortran routine: 
\begin_inset ERT
status Open

\layout Standard

\backslash 
lstinputlisting[language=Fortran]{snippets/wrap_sub_sig.f}
\end_inset 


\layout Standard

The above is correctly handled by f2py, but it can't know what is meant
 to be input/output and what the relations between the various variables
 are (such as integers which are array dimensions).
 If we add the following f2py directives, the generated python interface
 is a lot nicer: 
\begin_inset ERT
status Collapsed

\layout Standard

\backslash 
lstinputlisting[language=Fortran]{snippets/wrap_sub_sig_f2py.f}
\layout Standard

\end_inset 


\layout Standard

Some comments on the above:
\layout Itemize

The f2py directives should come immediately after the 'subroutine' line
 and before the Fortran variable lines.
 This allows the f2py dimension directives to override the Fortran var(*)
 directives.
\layout Itemize

If the Fortran code uses var(N) instead of var(*), the f2py directives can
 be placed after the Fortran declarations.
 This mode is preferred, as there is less redundancy overall.
 The result is much simpler: 
\layout Standard


\begin_inset ERT
status Collapsed

\layout Standard

\backslash 
lstinputlisting[language=Fortran]{snippets/wrap_sub_sig_f2py.f}
\layout Standard

\end_inset 


\layout Standard

Since python can automatically manage memory, it is possible to hide the
 need for manually passed 'work' areas.
 The C/python wrapper to the underlying fortran routine will allocate the
 memory for the needed work areas on the fly.
 This is done by specifying intent(hide,cache).
 'hide' tells f2py to remove the variable from the argument list and 'cache'
 tells it to auto-generate it.
\layout Standard

In cases where the allocation cost becomes a performance problem, one can
 remove the 'hide' part and make it an optional argument.
 In this case it will only be generated if not given.
 For this, the line above should be changed to:
\layout LyX-Code


\size small 
Cf2py   real *8 dimension(2*mm+2), intent(cache), optional, depend(mm) ::
 wrk 
\layout Standard

Note that this should only be done after proving that the scratch areas
 are causing a performance problem.
 The 
\family typewriter 
cache
\family default 
 directive causes f2py to keep cached copies of the scratch areas, so no
 unnecessary mallocs should be triggered.
\layout Standard

Since f2py relies on Numeric arrays, all dimensions can be determined from
 the arrays themselves and it is not necessary to pass them explicitly.
\layout Standard

With all this, the resulting f2py-generated docstring becomes: 
\layout LyX-Code

phipol - Function signature:
\layout LyX-Code

  phi = phipol(j,nodes,wei,x)
\layout LyX-Code

Required arguments:
\layout LyX-Code

  j : input int
\layout LyX-Code

  nodes : input rank-1 array('d') with bounds (mm)
\layout LyX-Code

  wei : input rank-1 array('d') with bounds (mm)
\layout LyX-Code

  x : input rank-1 array('d') with bounds (nn)
\layout LyX-Code

Return objects:
\layout LyX-Code

  phi : rank-1 array('d') with bounds (nn)
\layout Subsection

Debugging
\layout Standard

For debugging, use the --debug-capi option to f2py.
 This causes the extension modules to print detailed information while in
 operation.
 In distutils, this must be passed as an option in the f2py_options to the
 Extension constructor.
 
\layout Subsection

Wrapping C codes with f2py
\layout Standard

Below is Pearu Peterson's (the f2py author) response to a question about
 using f2py to wrap existing C codes.
 While SWIG provides similar functionality and weave is perfect for inlining
 C, f2py seems to be an incredibly simple and convenient tool for wrapping
 C libraries.
\layout Standard

Pearu's response follows: 
\layout Standard

For example, consider the following C file:
\layout LyX-Code

/* foo.c */
\layout LyX-Code

double foo(double *x, int n) {
\layout LyX-Code

  int i;
\layout LyX-Code

  double r = 0;
\layout LyX-Code

  for (i=0;i<n;++i)
\layout LyX-Code

    r += x[i];
\layout LyX-Code

  return r;
\layout LyX-Code

}
\layout LyX-Code

/* EOF foo.c */
\layout Standard

To wrap the C function foo() with f2py, create the following signature file
 bar.pyf: 
\layout LyX-Code

! -*- F90 -*-
\layout LyX-Code

python module bar
\layout LyX-Code

  interface
\layout LyX-Code

    real*8 function foo(x,n)
\layout LyX-Code

      intent(c) foo
\layout LyX-Code

      real*8 dimension(n),intent(in) :: x
\layout LyX-Code

      integer intent(c,hide),depend(x) :: n = len(x)
\layout LyX-Code

    end function foo
\layout LyX-Code

  end interface
\layout LyX-Code

end python module bar
\layout LyX-Code

! EOF bar.pyf
\layout Standard

(see usersguide for more info about intent(c)) and run
\layout LyX-Code

  f2py -c bar.pyf foo.c
\layout Standard

Finally, in Python:
\layout LyX-Code

>>> import bar
\layout LyX-Code

>>> bar.foo([1,2,3])
\layout LyX-Code

6.0
\layout Subsection

Passing offset arrays to Fortran routines
\layout Standard

It is possible to pass offset arrays (like pointers to the middle of other
 arrays) by using Numeric's slice notation.
\layout Standard

The print_dvec function below simply prints its argument as "print*,'x',x".
 We show some examples of how it behaves with both 1 and 2-d arrays: 
\layout LyX-Code

In [3]: x
\layout LyX-Code

Out[3]: array([ 2.8,  3.4,  4.1])
\layout LyX-Code

In [4]: tf.print_dvec(x)
\layout LyX-Code

 n 3
\layout LyX-Code

 x  2.8  3.4  4.1
\layout LyX-Code

In [5]: tf.print_dvec ?
\layout LyX-Code

Type:           fortran
\layout LyX-Code

String Form:    <fortran object at 0x8306fe8>
\layout LyX-Code

Namespace:      Currently not defined in user session.
\layout LyX-Code

Docstring:
\layout LyX-Code

    print_dvec - Function signature:
\layout LyX-Code

      print_dvec(x,[n])
\layout LyX-Code

    Required arguments:
\layout LyX-Code

      x : input rank-1 array('d') with bounds (n)
\layout LyX-Code

    Optional arguments:
\layout LyX-Code

      n := len(x) input int
\layout LyX-Code

In [6]: tf.print_dvec (x[1])
\layout LyX-Code

 n 1
\layout LyX-Code

 x  3.4
\layout LyX-Code

In [7]: tf.print_dvec (x[1:])
\layout LyX-Code

 n 2
\layout LyX-Code

 x  3.4  4.1
\layout LyX-Code

In [8]: A
\layout LyX-Code

Out[8]:
\layout LyX-Code

array([[ 3.5,  5.6,  8.2],
\layout LyX-Code

       [ 2.1,  4.5,  1.2],
\layout LyX-Code

       [ 6.3,  3.4,  3.1]])
\layout LyX-Code

In [9]: tf.print_dvec(A)
\layout LyX-Code

 n 9
\layout LyX-Code

 x  3.5  5.6  8.2  2.1  4.5  1.2  6.3  3.4  3.1
\layout LyX-Code

In [10]: A
\layout LyX-Code

Out[10]:
\layout LyX-Code

array([[ 3.5,  5.6,  8.2],
\layout LyX-Code

       [ 2.1,  4.5,  1.2],
\layout LyX-Code

       [ 6.3,  3.4,  3.1]])
\layout LyX-Code

In [11]: tf.print_dvec(A[1:])
\layout LyX-Code

 n 6
\layout LyX-Code

 x  2.1  4.5  1.2  6.3  3.4  3.1
\layout LyX-Code

In [12]: A[1:]
\layout LyX-Code

Out[12]:
\layout LyX-Code

array([[ 2.1,  4.5,  1.2],
\layout LyX-Code

       [ 6.3,  3.4,  3.1]])
\layout LyX-Code

In [13]: A[1:,1:]
\layout LyX-Code

Out[13]:
\layout LyX-Code

array([[ 4.5,  1.2],
\layout LyX-Code

       [ 3.4,  3.1]])
\layout LyX-Code

In [14]: tf.print_dvec(A[1:,1:])
\layout LyX-Code

 n 4
\layout LyX-Code

 x  4.5  1.2  3.4  3.1
\layout Subsection

On matrix ordering and in-memory copies
\layout Standard

Numeric (which f2py relies on) is C-based, and therefore its arrays are
 stored in row-major order.
 Fortran stores its arrays in column-major order.
 This means that copying issues must be dealt with.
 Below we reproduce some comments from Pearu on this topic given in the
 f2py mailing list in June/2002: 
\layout Quote

To avoid copying, you should create array that has internally Fortran data
 ordering.
 This is achived, for example, by reading/creating your data in Fortran
 ordering to Numeric array and then doing Numeric.transpose on that.
 Every f2py generated extension module provides also function 
\layout Quote

has_column_major_storage
\layout Quote

to check if an array is Fortran contiguous or not.
 If has_column_major_storage(arr) returns true then there will be no copying
 for the array arr if passed to f2py generated functions (assuming that
 the types are proper, of cource).
\layout Quote

Also note that copying done by f2py generated interface is carried out in
 C on the raw data and therefore it is extremely fast compared to if you
 would make a copy in Python, even when using Numeric.
 Tests with say 1000x1000 matrices show that there is no noticable performance
 hit when copying is carried out, in fact, sometimes making a copy may speed
 up things a bit -- I was quite surprised about that myself.
\layout Quote

So, I think, you should worry about copying only if the sizes of matrices
 are really large, say, larger than 5000x5000 and efficient memory usage
 is relevant.
 The time spent for copying is negligible even for large arrays provided
 that your computer has plenty of memory (>=256MB).
\layout Subsection

Distutils
\layout Standard

Below is an example setup.py file which generates a Python extension module
 from Fortran90 sources and a .pyf interface file generated by f2py and later
 fine tuned.
 
\layout Standard


\begin_inset ERT
status Open

\layout Standard

\backslash 
lstinputlisting{examples/wrap_f2py_setup.py}
\end_inset 


\layout Section

Others
\layout Standard

boost, pyrex, cxx
\layout Section

Distributing standalone applications
\begin_inset OptArg
collapsed true

\layout Standard

Standalone applications
\end_inset 


\layout Standard

py2exe, mcmillan installer
\the_end
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.