Python code to run a C program that calculates whether points are in the mandelbrot set This is part of a project to explore the Mandelbrot set. The code to graphically navigate the complex plane and allow the user to adjust the colormap and number of iterations is all written in Python, because that sort of user interface work is easiest in a scripting language. However, Python is not very good at inner loops that run hundreds of times for each pixel, so I wrote a C program that does the actual computation. The python function runs the C executable in a separate process that passes the result back from C to python as a human-readable string. Parsing the string back to integers is slow. What is a more pythonic way of creating the interface between C and python? Also, any general tips for writing faster, better C are appreciated, I'm new to the language.

Python code:

import numpy as np
from subprocess import Popen
import subprocess

def getcounts(xmin, xmax, ymin, ymax, dim, depth):
    """render a view of the mandelbrot set on the given rectangle of the
    complex plane, by calling a C script and interpreting stdout as a
    series of integers
    output is a numpy array reshaped into a dim*dim square"""

    p = Popen(["./a.out", repr(xmin), repr(xmax), repr(ymin),
    repr(ymax), str(dim), str(depth)], stdout=subprocess.PIPE)

    counts = np.fromstring(p.stdout.readline()[:-1], dtype = np.int, sep = ' ')

    return counts.reshape([dim, dim]).transpose()


C code:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt; 
int main(int argc, char *argv[]){
  int dim = 100;        /*defaults produce a nice overview of the mandelbrot set*/
  int depth = 300;
  double minx = -2;
  double maxx = 2;
  double miny = -1.5;
  double maxy = 1.5;     

  if(argc &gt; 6){
      minx = atof(argv[1]);
      maxx = atof(argv[2]);
      miny = atof(argv[3]);
      maxy = atof(argv[4]);
      dim = atoi(argv[5]);
      depth = atoi(argv[6]);
  }

  double xstep = (maxx - minx)/dim;
  double ystep = (maxy - miny)/dim;

  int i, j;
  for(i = 0; i &lt; dim; i++){
    double c_real = minx + i * xstep;
    for(j = 0; j &lt; dim; j++){
      double c_imag = miny + j * ystep;
      int count = 0;
      double z_real = 0;
      double z_imag = 0;
      double zreal_temp;
      while((z_real * z_real + z_imag * z_imag &lt; 4) &amp;&amp; count &lt; depth){
          count ++;
          zreal_temp = z_real * z_real - z_imag * z_imag + c_real;
          z_imag = z_real * z_imag * 2 + c_imag;
          z_real = zreal_temp;
      }
      printf("%d ", count);
    }   
  }
}


Finally, here's a python script to test the above function: I've cut out all the bits for moving around and adjusting settings since this question is about the C - Python interface

import numpy as np
import matplotlib.pyplot as plt
xmin = -2
xmax = 2
ymin = -2
ymax = 2
depth = 60   
dim = 600
counts = getcounts(xmin, xmax, ymin, ymax, dim, depth)
plt.imshow(counts, extent = [xmin, xmax, ymax, ymin], interpolation = 'nearest')
plt.show()
In my opinion, Popen is simply the wrong approach to this problem. Everything else follows from that. Python provides an API to directly expose C functions to Python. Boost.Python is a fantastic library to make it easy to expose C++ classes without having to deal with the messy direct Python API. Boost.Numpy makes it easy to return numpy arrays directly.

Regardless of which one you choose, you should choose one and use it to create something to the effect of mandelbrot.so which will have one function that returns a list of lists. You can then just use that function directly. Rather than write getcounts() to do some work doing string processing, just call mandelbrot.getcounts() with all the same args. Something like:

import numpy as np
import matplotlib.pyplot as plt
import mandelbrot

xmin = -2
xmax = 2
ymin = -2
ymax = 2
depth = 60   
dim = 600
counts = mandelbrot.getcounts(xmin, xmax, ymin, ymax, dim, depth)
plt.imshow(counts, extent = [xmin, xmax, ymax, ymin], interpolation = 'nearest')
plt.show()


There are several huge advantages to doing it this way:


The ability to actually do any sort of error checking.
Not having to do any output processing converting between one program's output and another program's desired input.
Not having to start a new process and do input handling on that one will make the code perform dramatically better.
It'll read more Pythonic on one end (just call a function that returns a value) and more C/C++-ic on the other end (have a function that returns a value there too, instead of printing output). This has the added benefit of being able to actually write tests for your C/C++ code.