Browse Source

Compatible with PyPI, can be installed through pip

master
Pete Shadbolt 9 years ago
parent
commit
5ededa9998
11 changed files with 55 additions and 32 deletions
  1. +3
    -0
      .gitignore
  2. +1
    -1
      LICENSE
  3. +8
    -0
      MANIFEST
  4. +1
    -0
      MANIFEST.in
  5. +4
    -0
      README
  6. +0
    -21
      makefile
  7. +0
    -0
      permanent/__init__.py
  8. +2
    -2
      run-tests.py
  9. +5
    -0
      run-tests.sh
  10. +6
    -5
      setup.py
  11. +25
    -3
      src/permanent.c

+ 3
- 0
.gitignore View File

@@ -1,3 +1,6 @@
.pypirc
*.pdf

# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]


+ 1
- 1
LICENSE View File

@@ -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


+ 8
- 0
MANIFEST View File

@@ -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

+ 1
- 0
MANIFEST.in View File

@@ -0,0 +1 @@
include src/*

+ 4
- 0
README View File

@@ -0,0 +1,4 @@
# Fast functions for LOQC
- Permanent (hardcoded up to 4x4 and Ryser)
- Parallel permanent
- No Cython dependency

+ 0
- 21
makefile View File

@@ -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)

lib/__init__.py → permanent/__init__.py View File


+ 2
- 2
run-tests.py View File

@@ -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


+ 5
- 0
run-tests.sh View File

@@ -0,0 +1,5 @@
#!/bin/bash

rm permanent/*.so
python ./setup.py build_ext --inplace &&
python ./run-tests.py

+ 6
- 5
setup.py View File

@@ -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"]),
], ],


+ 25
- 3
src/permanent.c View File

@@ -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


Loading…
Cancel
Save