Python C extension to compute the permanent.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

89 lignes
3.1KB

  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. int countbits(unsigned int n)
  11. {
  12. int q=n;
  13. q = (q & 0x5555555555555555) + ((q & 0xAAAAAAAAAAAAAAAA) >> 1);
  14. q = (q & 0x3333333333333333) + ((q & 0xCCCCCCCCCCCCCCCC) >> 2);
  15. q = (q & 0x0F0F0F0F0F0F0F0F) + ((q & 0xF0F0F0F0F0F0F0F0) >> 4);
  16. q = (q & 0x00FF00FF00FF00FF) + ((q & 0xFF00FF00FF00FF00) >> 8);
  17. q = (q & 0x0000FFFF0000FFFF) + ((q & 0xFFFF0000FFFF0000) >> 16);
  18. q = (q & 0x00000000FFFFFFFF) + ((q & 0xFFFFFFFF00000000) >> 32); // This last & isq't strictly qecessary.
  19. return q;
  20. }
  21. int bitparity (unsigned int n) { return 1 - (countbits(n) & 1)*2; }
  22. // Complex numbers
  23. static const npy_complex128 complex_one = {.real=1, .imag=0};
  24. static const npy_complex128 complex_zero = {.real=0, .imag=0};
  25. static npy_complex128 complex_add(npy_complex128 a, npy_complex128 b) {
  26. npy_complex128 x;
  27. x.real = a.real+b.real;
  28. x.imag = a.imag+b.imag;
  29. return x;
  30. }
  31. static npy_complex128 complex_prod(npy_complex128 a, npy_complex128 b) {
  32. npy_complex128 x;
  33. x.real = a.real*b.real - a.imag*b.imag;
  34. x.imag = a.imag*b.real + a.real*b.imag;
  35. return x;
  36. }
  37. // Boilerplate
  38. static PyObject *permanent(PyObject *self, PyObject *args); // Forward function declaration
  39. static PyMethodDef methods[] = { // Method list
  40. { "permanent", permanent, METH_VARARGS, "Compute the permanent"},
  41. { NULL, NULL, 0, NULL } /* Sentinel */
  42. };
  43. PyMODINIT_FUNC initpermanent(void) { // Module initialization
  44. (void) Py_InitModule("permanent", methods);
  45. import_array();
  46. }
  47. // Ryser's algorithm
  48. static npy_complex128 perm_ryser(PyArrayObject *submatrix) {
  49. int n = (int) PyArray_DIM(submatrix, 0);
  50. npy_complex128 rowsum, rowsumprod;
  51. npy_complex128 perm = complex_zero;
  52. int exp = 1 << n;
  53. int i, y, z;
  54. // Iterate over exponentially many index strings
  55. for (i=0; i<exp; ++i) {
  56. rowsumprod = complex_one;
  57. for (y=0; y<n; ++y) { // Rows
  58. rowsum = complex_zero;
  59. for (z=0; z<n; ++z) { // Columns
  60. if ((i & (1 << z)) != 0) { rowsum = complex_add(rowsum, SM(z,y)); }
  61. }
  62. rowsumprod = complex_prod(rowsumprod, rowsum);
  63. }
  64. int sign = bitparity(i);
  65. perm.real+=sign*rowsumprod.real; perm.imag+=sign*rowsumprod.imag;
  66. }
  67. if (n%2 == 1) {perm.real*=-1; perm.imag*=-1;}
  68. return perm;
  69. }
  70. // This is a wrapper which chooses the optimal permanent function
  71. static PyObject *permanent(PyObject *self, PyObject *args) {
  72. // Parse the input
  73. PyArrayObject *submatrix;
  74. if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &submatrix)) {return NULL;}
  75. // Compute the permanent
  76. npy_complex128 p = perm_ryser(submatrix);
  77. return PyComplex_FromDoubles(p.real, p.imag);
  78. }