Browse Source

Compatible with PyPI, can be installed through pip

master
Pete Shadbolt 5 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
__pycache__/
*.py[cod]


+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
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
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 multiprocessing as mp
import numpy as np
import lib
import time
from matplotlib import pyplot as plt
from permanent import permanent

def perm_ryser(a):
''' the permanent calculated using the ryser formula. much faster than the naive approach '''
@@ -20,7 +20,7 @@ def perm_ryser(a):
maxtime=1
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=[]
for dimension in dimensions:
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

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_email = "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 = [
Extension(
'permanent', ['src/permanent.c'],
'permanent.permanent', ['./src/permanent.c'],
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
#include <Python.h>
#include <numpy/arrayobject.h>
#include "npy_util.h"
#include "bithacks.h"
#include "ryser.h"

// Forward function declaration
static PyObject *permanent(PyObject *self, PyObject *args);

// Method list
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
};

@@ -21,6 +20,29 @@ PyMODINIT_FUNC initpermanent(void) {
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
static PyObject *permanent(PyObject *self, PyObject *args) {
// Parse the input


Loading…
Cancel
Save