Configuring cisst for Python

To use the cisst/SAW Python wrappers, you will need both SWIG and Python installed. If you plan to use the cisstVector data types as well you will need numpy.

Once you’ve installed all the prerequisites, set the following options during the cisst/SAW CMake configuration

  • CISST_BUILD_SHARED_LIBS: ON

  • CISST_HAS_SWIG_PYTHON: ON

When you’re done configuring cisst/SAW you should have the following variables set properly:

  • SWIG_DIR

  • SWIG_EXECUTABLE

  • PYTHON_EXECUTABLE

  • PYTHON_INCLUDE_DIR

  • PYTHON_LIBRARY

  • PYTHON_NUMPY_INCLUDE_DIR

Using the cisst Python wrappers

Most cisst libraries come with some Python wrappers generated using SWIG. To see if a library has been wrapped, check if the interface file cisstXyz/cisstXyz.i exists (e.g. cisstVector/cisstVector.i).

Before you can load any cisst Python wrappers, you should make sure your paths are set properly. Do to so, see compiling and running cisst/SAW.

Once your paths are set, you should be able to start your Python interpreter (e.g. ipython):

ipython

In Python:

import cisstCommonPython
dir(cisstCommonPython)
print(cisstCommonPython.cmnClassRegister_ToString())

cisstVector typemaps

This section is an introduction to writing functions that will use the cisstVector typemaps.

Who needs to read this section?

If you are writing C++ software that needs to be accessible by Python users and uses the cisstVector library, this document is for you. To use your C++ classes in Python, you will still need to use SWIG to wrap your class and methods. If your methods use any cisstVector types as parameters, the typemaps provided along the cisstVector library will convert the C++ vectors and matrices into Python numpy arrays. You will then be able to use the fast collection of numerical tools built on top of numpy.

In C++, there are many ways to pass or return data (by copy, reference, pointer … const or not, …) and not all of them make sense when using cisstVector containers. Furthermore, SWIG potentially requires a different typemap for each possible signature. As a consequence, there is a limited number of signatures supported by the cisst libraries.

Quick reference

Quickly find the recommended function signature to use

My data is a …

I want to … my container

Use this function signature

vctDynamicContainer<elementType>

Read

f(vctDynamicConstContainerRef<elementType>)

Read/write

f(vctDynamicContainerRef<elementType>)

Resize

f(vctDynamicContainer<elementType> &)

vctDynamicContainerRef<elementType>

Read

f(vctDynamicConstContainerRef<elementType>)

Read/write

f(vctDynamicContainerRef<elementType>)

Resize

Not allowed

vctDynamicConstContainerRef<elementType>

Read

f(vctDynamicConstContainerRef<elementType>)

Read/write

Not allowed

Resize

Not allowed

Conversions between container types

If the function’s parameter type is …

In C++, it accepts objects of type …

vctDynamicConstContainerRef

vctDynamicConstContainerRef, vctDynamicContainerRef, vctDynamicContainer

vctDynamicContainerRef

vctDynamicContainerRef, vctDynamicContainer

vctDynamicContainer

vctDynamicContainer

Additional function signatures

The cisstVector typemaps support four additional function signatures for read-only access to a container. Although they are of comparable or worse efficiency than the recommended read-only signature given above, they are provided for convenience. They all accomplish the exact same task as the recommended signature. When in doubt, use the recommended signature.

  • The first is: function(const vctDynamicConstContainerRef<elementType> &); In this signature, the CISST const reference is passed as a C++ const reference. This may be slightly more efficient than the recommended signature (since only a reference and not a copy of the vctDynamicConstContainerRef object is passed), but passing a CISST reference by C++ reference can become confusing. Also, the CISST reference containers are very lightweight, making the performance gain for this signature minimal.

  • The next signature is: function(const vctDynamicContainerRef<elementType> &); In this signature, a CISST non-const reference is passed by C++ const reference. The vctDynamicContainerRef class contains non-const methods, but as it is passed as a C++ const reference, the compiler will forbid their use; the end result is that the function will not be able to modify the container. Use of this signature is discouraged, as the parameter name is somewhat ambiguous.

  • The third signature is: function(vctDynamicContainer<elementType>); The entire container is passed by copy in this signature; as a result, the initial version will not be modified, although the function can internally modify the copied data. From a performance point of view, this means that the vector is copied at each function call. This signature also is not as flexible as the others (it doesn’t support calls such as function(myMatrix.Diagonal()) for instance, as the call to Diagonal(), like calls to many other container functions, returns a CISST reference; see Table 2 above). This signature should be used only when needed on rare occasions. [Note]

  • The final signature is: function(const vctDynamicContainer<elementType> &) This signature specifies that the function receives a container passed by C++ const reference. Functions with this signature won’t be as flexible as those written with the recommended signature, since CISST references can’t be used directly with this signature (see Table 2). Also, like the previous signature, SWIG copies the NumPy vector passed to this function at each call, so performance will take a hit.

Summary

In total, there are seven supported function signatures: five for read-only access, one for read and write access, and one for resize access. They are summarized below. Recommended signatures are in bold.

Access level

Supported function signatures

Allowed cisstVector types

Disallowed cisstVector types

Allowed NumPy types

Disallowed NumPy types

Read-only access

``f(vctDynamicConstContainerRef<elementType>)``

1, 2, 3

None

4, 5

None

f(const vctDynamicConstContainerRef<elementType> &)

1, 2, 3

None

4, 5

None

f(const vctDynamicContainerRef<elementType> &)

2, 3

1

4, 5

None

f(vctDynamicContainer<elementType>)

3

1, 2

4, 5

None

f(const vctDynamicContainer<elementType> &)

3

1, 2

4, 5

None

Write access (no resizing)

``f(vctDynamicContainerRef<elementType>)``

2, 3

1

5

4

Resize

``f(vctDynamicContainer<elementType> &)``

3

1, 2

5’

4, 5

Keys:

  • 1: vctDynamicConstContainerRef<elementType>

  • 2: vctDynamicContainerRef<elementType>

  • 3: vctDynamicContainer<elementType>

  • 4: Non-writable NumPy array

  • 5: Writable NumPy array

  • 5’: Writable NumPy array that owns it data and has no references on it

Note: Function signatures that are not supported

Two function signatures, function(vctDynamicConstContainerRef<elementType> &) and function(vctDynamicContainerRef<elementType> &), are explicitly not supported because they allow the user to change the container’s reference to point to arbitrary blocks of data. In particular, the reference could be changed to point to Python memory allocated without a reference count; if that memory is subsequently released, we would have a pointer to freed memory. We chose to avoid this case altogether by not supporting these two signatures.

Note: Safety checks performed by typemaps

The different typemaps provided perform a number of safety checks to make sure the input parameters (Python objects) can be used as cisst containers. If the checks fail, an exception is thrown.

  • Verify that the input is a NumPy array

  • Verify the dimension, i.e. 1 if a vector is expected, 2 for a matrix, etc …

  • Verify the type of elements, i.e. doubles, int, …

  • Verify the size (for fixed size containers only)

  • Verify if the container is writable when needed

  • Verify that the container owns its memory or has no reference on it