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.

80 lines
2.2KB

  1. /* Functions to compute 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 "npy_util.h"
  6. #include "bithacks.h"
  7. // Forward function declaration
  8. static PyObject *permanent(PyObject *self, PyObject *args);
  9. // Method list
  10. static PyMethodDef methods[] = {
  11. { "permanent", permanent, METH_VARARGS, "Computes the permanent of a numpy using the most appropriate method available"},
  12. { NULL, NULL, 0, NULL } // Sentinel
  13. };
  14. #if PY_MAJOR_VERSION >= 3
  15. static struct PyModuleDef cModPyDem =
  16. {
  17. PyModuleDef_HEAD_INIT,
  18. "permanent", "Computes the permanent of a numpy using the most appropriate method available",
  19. -1,
  20. methods
  21. };
  22. PyMODINIT_FUNC
  23. PyInit_permanent(void)
  24. {
  25. import_array();
  26. return PyModule_Create(&cModPyDem);
  27. }
  28. #else
  29. PyMODINIT_FUNC initpermanent(void) {
  30. (void) Py_InitModule("permanent", methods);
  31. import_array();
  32. }
  33. #endif
  34. // Ryser's algorithm
  35. static npy_complex128 ryser(PyArrayObject *submatrix) {
  36. int n = (int) PyArray_DIM(submatrix, 0);
  37. npy_complex128 rowsum, rowsumprod;
  38. npy_complex128 perm = complex_zero;
  39. int exp = 1 << n;
  40. int i, y, z;
  41. for (i=0; i<exp; ++i) {
  42. rowsumprod = complex_one;
  43. for (y=0; y<n; ++y) {
  44. rowsum = complex_zero;
  45. for (z=0; z<n; ++z) {
  46. if ((i & (1 << z)) != 0) { complex_inc(&rowsum, SM(z, y)); }
  47. }
  48. complex_multiply(&rowsumprod, rowsum);
  49. }
  50. complex_inc(&perm, complex_float_prod(rowsumprod, bitparity(i)));
  51. }
  52. if (n%2 == 1) { perm=complex_float_prod(perm, -1); }
  53. return perm;
  54. }
  55. // This is a wrapper which chooses the optimal permanent function
  56. static PyObject *permanent(PyObject *self, PyObject *args) {
  57. // Parse the input
  58. PyArrayObject *submatrix;
  59. if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &submatrix)) {return NULL;}
  60. if (!PyArray_ISCOMPLEX(submatrix)) {
  61. PyErr_SetString(PyExc_TypeError, "Array dtype must be `complex`.");
  62. return NULL;
  63. }
  64. // Compute the permanent
  65. npy_complex128 p = ryser(submatrix);
  66. return PyComplex_FromDoubles(p.real, p.imag);
  67. }