Python C extension to compute the permanent.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
2.6KB

  1. /* Computes the permanent, given a numpy array */
  2. #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
  3. #include <Python.h>
  4. #include <numpy/arrayobject.h>
  5. #include <math.h>
  6. #include <complex.h>
  7. // Globals
  8. PyArrayObject *submatrix;
  9. int size;
  10. // Boilerplate: Forward function declaration.
  11. static PyObject *permanent(PyObject *self, PyObject *args);
  12. // Boilerplate: method list.
  13. static PyMethodDef methods[] = {
  14. { "permanent", permanent, METH_VARARGS, "Compute the permanent"},
  15. { NULL, NULL, 0, NULL } /* Sentinel */
  16. };
  17. // Boilerplate: Module initialization.
  18. PyMODINIT_FUNC initpermanent(void) {
  19. (void) Py_InitModule("permanent", methods);
  20. import_array();
  21. }
  22. // Array access macros.
  23. #define SM(x0, x1) (*(npy_complex64*)((PyArray_DATA(submatrix) + \
  24. (x0) * PyArray_STRIDES(submatrix)[0] + \
  25. (x1) * PyArray_STRIDES(submatrix)[1])))
  26. #define SM_shape(x0) (int) PyArray_DIM(submatrix, x0)
  27. // Ryser's algorithm takes exponential time
  28. // The advantage over naive perm() only kicks in at around 6x6 matrices
  29. // TODO: should take matrix as arg really, get rid of consts
  30. static npy_complex64 perm_ryser(void) {
  31. npy_complex64 p;
  32. p.real=0; p.imag=0;
  33. int i, j;
  34. for (i = 0; i < size; ++i) {
  35. for (j = 0; j < size; ++j) {
  36. npy_complex64 q = SM(0,0);
  37. p.real += q.real;
  38. p.imag += q.imag;
  39. }
  40. }
  41. return p;
  42. }
  43. void TODO(void) {
  44. int n = size;
  45. int i = 0; int z = 0; int y = 0;
  46. npy_complex64 perm; perm.real=0; perm.imag=0;
  47. npy_complex64 prod;
  48. npy_complex64 sum; sum.real=0; sum.imag=0;
  49. npy_complex64 element;
  50. int exp=pow(2.0, n);
  51. // Iterate over exponentially many index strings
  52. for (i=0; i<exp; ++i) {
  53. prod.real = 1; prod.imag=0;
  54. for (y=0; y<n; ++y) { // Rows
  55. sum.real = 0; sum.imag = 0;
  56. for (z=0; z<n; ++z) { // Columns
  57. if ((i & (1 << z)) > 0) { sum.real += SM(z,y).real; sum.imag += SM(z,y).imag; }
  58. }
  59. prod = c_prod(prod, sum);
  60. }
  61. perm += parity(i) * prod;
  62. }
  63. return c_prod((pow(-1,n)), perm);
  64. }
  65. // This is basically a wrapper which chooses the optimal permanent function
  66. static PyObject *permanent(PyObject *self, PyObject *args) {
  67. // Parse input
  68. if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &submatrix)) {
  69. return NULL;
  70. }
  71. // Check for stupid mistakes
  72. if ((int) PyArray_NDIM(submatrix) != 2) {return NULL;}
  73. size = (int) PyArray_DIM(submatrix, 0);
  74. if ((int) PyArray_DIM(submatrix, 1) != size) {return NULL;}
  75. // Get the permanent, convert to a python object, and return
  76. npy_complex64 p = perm_ryser();
  77. return PyComplex_FromDoubles(p.real,p.imag);
  78. }