Donnerstag, 7. Juli 2011

AAAARRRRRRGH!! Matlab!!

Happy Matlab licensing trouble - yay!

We have 5 Matlab licenses in our lab. Originally, we bought them as "Concurrent licenses", that is, Matlab allowed us to have 5 instances of Matlab running in parallel, regardless which computer they are running on. At some point, Mathworks somehow transformed these licenses into "Designated Computer" licenses, that is, each license must be associated with a designated computer and will only run on that machine. Although this was an obviously bad deal, we didn't care too much about that change back then, since we were busy doing more important stuff than caring about licensing issues.

Anyway, Matlab is a dying species in our lab since most of us are using Python for scientific computing, except for a few legacy scripts. But every now and then, I need to run one of those legacy scripts.

I do much development on my laptop, but for numbercrunching I use our compute server. Hence, I need my computing environment on both machines, although not necessarily at the same time. I had one of these designated computer licenses, and thanks to Mathworks' provident care, I was able to deactivate and reactivate them over the web when switching between computers. So I changed the designated computer a few times between those machines. Today I wanted to change again, but Mathworks wouldn't let me:

"No more machine transfers available for this license."

WTF?

OK, you're forcing me to port even my old scripts to python. Pity you. I spent already too much time struggling with licensing issues - time which I would much more like to spend on research. Goodbye Matlab.

Freitag, 1. Juli 2011

Using Python decorators to work around version incompatibilities

I'm using PyNN to simulate networks of spiking neurons. PyNN ist a "metasimulator" than can operate with several simulator backends, such as NEST, NEURON, or several others. The cool thing is that PyNN also has a backend for the FACETS hardware, which I'm using in a project. I can prototype the simulation in the simulator, and run it on the hardware afterwards, without changing my simulation script.

In theory.

In practice, things are a bit different. The hardware interface works with PyNN version 0.6, but PyNN has progressed towards 0.7 already. The current version of NEST works only with the current development version (0.7+, that is). This caused some headache for me and others developing for the hardware. Update: Some people wondered and asked me why I wouldn't simply use the old version of NEST that works with 0.6. Well, I could, but actually, that version has other bugs which make this solution a no-go.

Fortunately, the API changes between PyNN 0.6 and 0.7 are not so extensive, so one can work around the differences with relatively little code. Still, one wants to have an elegant way of automatically detecting the PyNN version and using the appropriate code automatically.

Python decorators are particularly well suited for that purpose. Python decorators are functions or classes that return a function. Using a decorator, you can check for the PyNN version in the decorator function and return the appropriate function which does what you want in the current PyNN version.

Confused? OK, here's an example: Assume that I want to retrieve the IDs of all cells in a population. In PyNN 0.6 I must use
def get_population_ids_06(pop):
return [id for id in pop.ids()]

while in PyNN 0.7 I can use
def get_population_ids_07(pop):
return [id for id in pop]

Now I want to have my script automatically figure out which function to use based on the PyNN version which is used. And here comes the decorator into play:
def pynn_version_workaround(pop):
if pynn_version.split(' ')[0] == "0.6.0":
return get_population_ids_06
else:
return get_population_ids_07

Now I have simply to define a dummy function which is to be mangled through the decorator:
@pynn_version_workaround
def get_population_ids(pop):
pass

So, calling get_population_ids is actually first calling pynn_version_workaround, which determines the pyNN version and returns the appropriate function, which is then called with the provided arguments.

Nice, isn't it?