@@ -1,3 +1,6 @@ | |||||
.pypirc | |||||
# Byte-compiled / optimized / DLL files | # Byte-compiled / optimized / DLL files | ||||
__pycache__/ | __pycache__/ | ||||
*.py[cod] | *.py[cod] | ||||
@@ -1,6 +1,6 @@ | |||||
The MIT License (MIT) | The MIT License (MIT) | ||||
Copyright (c) 2014 J. Pete Shadbolt | |||||
Copyright (c) 2014 Pete Shadbolt | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||
@@ -0,0 +1,8 @@ | |||||
# file GENERATED by distutils, do NOT edit | |||||
README | |||||
setup.py | |||||
./src/permanent.c | |||||
permanent/__init__.py | |||||
src/bithacks.h | |||||
src/npy_util.h | |||||
src/permanent.c |
@@ -0,0 +1 @@ | |||||
include src/* |
@@ -0,0 +1,4 @@ | |||||
# Fast functions for LOQC | |||||
- Permanent (hardcoded up to 4x4 and Ryser) | |||||
- Parallel permanent | |||||
- No Cython dependency |
@@ -1,21 +0,0 @@ | |||||
srcdir = src | |||||
objdir = ./ | |||||
target = permanent.so | |||||
setup = setup.py | |||||
default : $(target) test | |||||
# Compile | |||||
$(target): $(srcdir)/* | |||||
python $(setup) build_ext --inplace | |||||
test : | |||||
python ./run-tests.py | |||||
clean : | |||||
@ rm permanent.so | |||||
@ rm -rf build | |||||
tar : | |||||
@ tar czf permanent.tar.gz $(srcdir) $(utils) |
@@ -2,9 +2,9 @@ import os, sys | |||||
import time | import time | ||||
import multiprocessing as mp | import multiprocessing as mp | ||||
import numpy as np | import numpy as np | ||||
import lib | |||||
import time | import time | ||||
from matplotlib import pyplot as plt | from matplotlib import pyplot as plt | ||||
from permanent import permanent | |||||
def perm_ryser(a): | def perm_ryser(a): | ||||
''' the permanent calculated using the ryser formula. much faster than the naive approach ''' | ''' the permanent calculated using the ryser formula. much faster than the naive approach ''' | ||||
@@ -20,7 +20,7 @@ def perm_ryser(a): | |||||
maxtime=1 | maxtime=1 | ||||
dimensions=range(1,11) | dimensions=range(1,11) | ||||
for (function, label) in zip((lib.permanent, perm_ryser), ("C", "Python")): | |||||
for (function, label) in zip((permanent, perm_ryser), ("C", "Python")): | |||||
counts=[] | counts=[] | ||||
for dimension in dimensions: | for dimension in dimensions: | ||||
print dimension | print dimension | ||||
@@ -0,0 +1,5 @@ | |||||
#!/bin/bash | |||||
rm permanent/*.so | |||||
python ./setup.py build_ext --inplace && | |||||
python ./run-tests.py |
@@ -2,16 +2,17 @@ | |||||
from distutils.core import setup, Extension | from distutils.core import setup, Extension | ||||
setup(name = "loqcmath", | |||||
version = "1.0", | |||||
description = "Fast maths for LOQC", | |||||
setup(name = "permanent", | |||||
version = "0.1.2", | |||||
description = "Calculates the permanent of a Numpy matrix", | |||||
author = "Pete Shadbolt", | author = "Pete Shadbolt", | ||||
author_email = "pete.shadbolt@gmail.com", | author_email = "pete.shadbolt@gmail.com", | ||||
maintainer = "pete.shadbolt@gmail.com", | maintainer = "pete.shadbolt@gmail.com", | ||||
url = "https://www.peteshadbolt.co.uk", | |||||
url = "https://github.com/peteshadbolt/permanent", | |||||
packages = ["permanent"], | |||||
ext_modules = [ | ext_modules = [ | ||||
Extension( | Extension( | ||||
'permanent', ['src/permanent.c'], | |||||
'permanent.permanent', ['./src/permanent.c'], | |||||
extra_compile_args=["-Ofast", "-march=native"]), | extra_compile_args=["-Ofast", "-march=native"]), | ||||
], | ], | ||||
@@ -1,17 +1,16 @@ | |||||
/* Computes the permanent, given a numpy array */ | |||||
/* Functions to compute the permanent, given a numpy array */ | |||||
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION | ||||
#include <Python.h> | #include <Python.h> | ||||
#include <numpy/arrayobject.h> | #include <numpy/arrayobject.h> | ||||
#include "npy_util.h" | #include "npy_util.h" | ||||
#include "bithacks.h" | #include "bithacks.h" | ||||
#include "ryser.h" | |||||
// Forward function declaration | // Forward function declaration | ||||
static PyObject *permanent(PyObject *self, PyObject *args); | static PyObject *permanent(PyObject *self, PyObject *args); | ||||
// Method list | // Method list | ||||
static PyMethodDef methods[] = { | static PyMethodDef methods[] = { | ||||
{ "permanent", permanent, METH_VARARGS, "Compute the permanent"}, | |||||
{ "permanent", permanent, METH_VARARGS, "Computes the permanent of a numpy using the most appropriate method available"}, | |||||
{ NULL, NULL, 0, NULL } // Sentinel | { NULL, NULL, 0, NULL } // Sentinel | ||||
}; | }; | ||||
@@ -21,6 +20,29 @@ PyMODINIT_FUNC initpermanent(void) { | |||||
import_array(); | import_array(); | ||||
} | } | ||||
// Ryser's algorithm | |||||
static npy_complex128 ryser(PyArrayObject *submatrix) { | |||||
int n = (int) PyArray_DIM(submatrix, 0); | |||||
npy_complex128 rowsum, rowsumprod; | |||||
npy_complex128 perm = complex_zero; | |||||
int exp = 1 << n; | |||||
int i, y, z; | |||||
for (i=0; i<exp; ++i) { | |||||
rowsumprod = complex_one; | |||||
for (y=0; y<n; ++y) { | |||||
rowsum = complex_zero; | |||||
for (z=0; z<n; ++z) { | |||||
if ((i & (1 << z)) != 0) { complex_inc(&rowsum, SM(z, y)); } | |||||
} | |||||
complex_multiply(&rowsumprod, rowsum); | |||||
} | |||||
complex_inc(&perm, complex_float_prod(rowsumprod, bitparity(i))); | |||||
} | |||||
if (n%2 == 1) { perm=complex_float_prod(perm, -1); } | |||||
return perm; | |||||
} | |||||
// This is a wrapper which chooses the optimal permanent function | // This is a wrapper which chooses the optimal permanent function | ||||
static PyObject *permanent(PyObject *self, PyObject *args) { | static PyObject *permanent(PyObject *self, PyObject *args) { | ||||
// Parse the input | // Parse the input | ||||