Monthly Archives: March 2010

When a Spring is not a spring

TraitsUI has a lot of layout magic, which is usually good enough, but sometimes you need to apply a little extra effort to get your GUI layout correct. One trick I often employ is to use springs between Items to align them. That works most of the time, but on occasion I need a fixed width spacer.

Lets look at the spring code:
[sourcecode language=”python”]
class Spring ( Item ):
# An item that is a layout “spring”.

# Name of the trait the item is editing
name = ‘spring’

# Should a label be displayed?
show_label = Bool( False )

# Editor to use for the item
editor = Instance( ‘enthought.traits.ui.api.NullEditor’, () )

# Should the item use extra space along its Group’s
# layout orientation?
springy = True

spring = Spring()
[/sourcecode]

So what would happen if we tried to create a Spring with the width set and springy disabled? We get a fixed width spacer!

[sourcecode language=”python”]
from enthought.traits.api import HasTraits, Int
from enthought.traits.ui.api import View, Item, VGroup, HGroup, \
Spring, spring

class ExampleView(HasTraits):
a = Int
b = Int

def trait_view(self, parent=None):
regular_group = HGroup(
HGroup(Item(‘a’),
show_border=True),
HGroup(Item(‘b’),
show_border=True),
show_border=True,
label=’regular’)

flex_group = HGroup(
HGroup(Item(‘a’),
show_border=True),
spring,
HGroup(Item(‘b’),
show_border=True),
show_border=True,
label=’flexible’)

fixed_group = HGroup(
HGroup(Item(‘a’),
show_border=True),
Spring(width=200, springy=False),
HGroup(Item(‘b’),
show_border=True),
show_border=True,
label=’fixed’)

return View(VGroup(regular_group,
flex_group,
fixed_group),
resizable=True)

ExampleView().configure_traits()
[/sourcecode]

spring_example

Simplified creation of NumPy arrays from pre-allocated memory.

About 18 months ago, I wrote a blog-post that illustrates how to create a NumPy array that refers to another block of pre-allocated memory by creating a small low-level Python object that wraps around the memory and correctly deallocates the memory once NumPy is done with it.

The basic idea was to create a new Python object in C and point the base member of the NumPy array structure to this newly created object after using PyArray_SimpleNewFromData to create the array from a block of pre-allocated memory. NumPy will decref the object pointed to by its base member when it is destroyed. The example I provided created a new Python object in C and pointed the base member to it. That way, when the NumPy array is destroyed, the tp_dealloc function will be called on the newly created object.

The solution I provided is very flexible and also illustrates how to create a new Python object in C. However, as Lisandro Dalcin pointed out in the comments to that post, there is a simpler way to do it if you just need to call a particular deallocation function to free the underlying memory (or decrease a reference) after the NumPy array is destroyed.

The simple solution is to use a low-level Python object that holds a pointer to anything as well as a destructor. This low-level object is called a CObject in Python 2.x. The equivalent in Python 3.x (backported to Python 2.7) is the Capsule object.

Creating a CObject in C code is very simple. You just call PyCObject_FromVoidPtr with an arbitrary pointer as the first argument and a destructor function as the second argument. The signature of this destructor function should take the arbitrary pointer as its only argument and return nothing. The Python Object returned can be assigned to the base member of the ndarray directly.

Using the CObject removes the need to create your own “dummy” Python Object just to handle calling some needed code after NumPy is done with the memory. Thus, the code example can be updated to just:

[sourcecode language=”c++”]
int nd=2;
npy_intp dims[2]={10,20};
size_t size;
PyObject arr=NULL;
void *mymem;

size = PyArray_MultiplyList(dims, nd);
mymem = _aligned_malloc(size, 16);
arr = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, mymem);
if (arr == NULL) {
_aligned_free(mymem);
Py_XDECREF(arr);
}
PyArray_BASE(arr) = PyCObject_FromVoidPtr(mymem, _aligned_free);
[/sourcecode]

For completeness, the original code containing the _aligned_malloc and
_aligned_free function calls is reproduced here:

[sourcecode language=”c++”]
#include
#define uintptr_t size_t

#define _NOT_POWER_OF_TWO(n) (((n) & ((n) – 1)))
#define _UI(p) ((uintptr_t) (p))
#define _CP(p) ((char *) p)

#define _PTR_ALIGN(p0, alignment) \
((void *) (((_UI(p0) + (alignment + sizeof(void*))) \
& (~_UI(alignment – 1)))))

/* pointer must sometimes be aligned; assume sizeof(void*) is a power of two */
#define _ORIG_PTR(p) (*(((void **) (_UI(p) & (~_UI(sizeof(void*) – 1)))) – 1))

static void *_aligned_malloc(size_t size, size_t alignment) {
void *p0, *p;

if (_NOT_POWER_OF_TWO(alignment)) {
errno = EINVAL;
return ((void *) 0);
}

if (size == 0) {
return ((void *) 0);
}
if (alignment < sizeof(void *)) { alignment = sizeof(void *); } /* including the extra sizeof(void*) is overkill on a 32-bit machine, since malloc is already 8-byte aligned, as long as we enforce alignment >= 8 …but oh well */
p0 = malloc(size + (alignment + sizeof(void *)));
if (!p0) {
return ((void *) 0);
}
p = _PTR_ALIGN(p0, alignment);
_ORIG_PTR(p) = p0;
return p;
}

static void _aligned_free(void *memblock) {
if (memblock) {
free(_ORIG_PTR(memblock));
}
}
[/sourcecode]

Thanks to Lisandro for pointing out this simplified approach.

EPD 6.1: MKL on Linux, Windows, & OSX

epd-6-1-long
There were several reasons we initially decided to include MKL, an extensively threaded, highly optimized library, in the Enthought Python Distribution. For one thing, we like that MKL detects the processing capability of the machine and then runs optimal algorithm for that hardware. Secondly, we knew that MKL would offer faster linear algebra routines than the ATLAS framework, previously used for EPD Linux and Windows, and Accelerate library, previously used for OSX.

We didn’t anticipate, however, just how dramatic that speed up would be. Our benchmarking tests document the astounding increases in processing speed that MKL lends to EPD.

In EPD 6.1, NumPy and SciPy are dynamically linked against the MKL linear algebra routines. This allows EPD users to seamlessly benefit from the highly optimized BLAS and LAPACK routines in the MKL. In addition, EPD 6.1 comes bundled with all of the MKL run-time libraries so that advanced users can take advantage (with ctypes) of even more of the MKL library such as fast Fourier transforms, trust-region optimization methods, sparse solvers, and vector math.

We’re really pleased with the optimizations MKL offers to our EPD users. Try out EPD 6.1 for yourself!