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.

76 lines
2.8KB

  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. // Array access macros.
  6. #define SM(x0, x1) (*(npy_complex128*)((PyArray_DATA(submatrix) + \
  7. (x0) * PyArray_STRIDES(submatrix)[0] + \
  8. (x1) * PyArray_STRIDES(submatrix)[1])))
  9. #define SM_shape(x0) (int) PyArray_DIM(submatrix, x0)
  10. // Complex numbers
  11. static const npy_complex128 complex_one = {.real=1, .imag=0};
  12. static const npy_complex128 complex_zero = {.real=0, .imag=0};
  13. static void complex_inc(npy_complex128 a, npy_complex128 b) { a.real+=b.real; a.imag+=b.imag;}
  14. static npy_complex128 complex_prod(npy_complex128 a, npy_complex128 b) {
  15. npy_complex128 x;
  16. x.real = a.real*b.real+a.imag*b.imag;
  17. x.imag = a.real*b.imag+a.imag*b.real;
  18. return x;
  19. }
  20. // Boilerplate
  21. static PyObject *permanent(PyObject *self, PyObject *args); // Forward function declaration
  22. static PyMethodDef methods[] = { // Method list
  23. { "permanent", permanent, METH_VARARGS, "Compute the permanent"},
  24. { NULL, NULL, 0, NULL } /* Sentinel */
  25. };
  26. PyMODINIT_FUNC initpermanent(void) { // Module initialization
  27. (void) Py_InitModule("permanent", methods);
  28. import_array();
  29. }
  30. // Ryser's algorithm
  31. static npy_complex128 perm_ryser(PyArrayObject *submatrix) {
  32. int n = (int) PyArray_DIM(submatrix, 0);
  33. int i = 0; int z = 0; int y = 0;
  34. npy_complex128 prod;
  35. npy_complex128 perm = complex_zero;
  36. npy_complex128 sum = complex_zero;
  37. int exp = 1 << n;
  38. printf("2^%d = %d\n", n, exp);
  39. // Iterate over exponentially many index strings
  40. for (i=0; i<exp; ++i) {
  41. prod = complex_one;
  42. printf("prod %.3f %.3f \n", prod.real, prod.imag);
  43. for (y=0; y<n; ++y) { // Rows
  44. sum = complex_zero;
  45. for (z=0; z<n; ++z) { // Columns
  46. printf("element %.3f %.3f \n", SM(z,y).real, SM(z,y).imag);
  47. // Below is a suspicious line
  48. if ((i & (1 << z)) > 0) { complex_inc(sum, SM(z,y)); }
  49. printf("sum %.3f %.3f \n", sum.real, sum.imag);
  50. }
  51. prod = complex_prod(prod, sum);
  52. }
  53. printf("prod %.3f %.3f \n", prod.real, prod.imag);
  54. if (i%2 == 0) {prod.real*=-1; prod.imag*=-1;}
  55. complex_inc(perm, prod);
  56. }
  57. if (i%2 == 0) {perm.real*=-1; perm.imag*=-1;}
  58. return perm;
  59. }
  60. // This is a wrapper which chooses the optimal permanent function
  61. static PyObject *permanent(PyObject *self, PyObject *args) {
  62. // Parse the input
  63. PyArrayObject *submatrix;
  64. if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &submatrix)) {return NULL;}
  65. // Compute the permanent
  66. npy_complex128 p = perm_ryser(submatrix);
  67. return PyComplex_FromDoubles(p.real, p.imag);
  68. }