Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental PR #2

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8]
python-version: [3.11]
name: "lint | Python ${{ matrix.python-version }}"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -45,37 +45,37 @@ jobs:
matrix:
include:
# fastest jobs first
- python-version: 3.8
- python-version: 3.11
name: without JIT
disable_jit: 1
- python-version: 3.8
- python-version: 3.11
name: doctests
mode: doctests
# really slow job next, so it runs in parallel with the others
- python-version: 3.8
- python-version: 3.11
name: slow tests
mode: very_slow
- python-version: 3.5
name: default
- python-version: 3.8
name: default
- python-version: 3.9
- python-version: 3.11
name: default
- python-version: 3.8
- python-version: 3.12
name: default
- python-version: 3.11
name: conda
conda: true
- python-version: 3.8
- python-version: 3.11
name: benchmarks
mode: bench

name: "build | ${{ matrix.name }} | Python ${{matrix.python-version}}"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

# python / pip
- name: Set up Python ${{ matrix.python-version }}
if: "!matrix.conda"
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -89,7 +89,7 @@ jobs:
# conda
- name: Set up Python ${{ matrix.python-version }} (conda)
if: matrix.conda
uses: conda-incubator/setup-miniconda@v2
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
Expand Down Expand Up @@ -146,24 +146,24 @@ jobs:
# if: ${{ always() }}
# with:
# report_paths: 'junit/test-results.xml'
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v5

deploy:
needs: test
runs-on: ubuntu-latest
name: deploy
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: "Install"
run: |
python -m pip install --upgrade pip;
python -m pip install build
python -m build --sdist --wheel --outdir dist/
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist
Expand Down
7 changes: 6 additions & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.9"

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
Expand All @@ -15,7 +21,6 @@ formats: all

# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.8
install:
- method: pip
path: .
Expand Down
56 changes: 16 additions & 40 deletions clifford/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,7 @@

# Major library imports.
import numpy as np
import numba as _numba # to avoid clashing with clifford.numba
import sparse
try:
from numba.np import numpy_support as _numpy_support
except ImportError:
import numba.numpy_support as _numpy_support


from clifford.io import write_ga_file, read_ga_file # noqa: F401
Expand Down Expand Up @@ -152,12 +147,6 @@ def get_mult_function(mt: sparse.COO, gradeList,
return _get_mult_function_runtime_sparse(mt)


def _get_mult_function_result_type(a: _numba.types.Type, b: _numba.types.Type, mt: np.dtype):
a_dt = _numpy_support.as_dtype(getattr(a, 'dtype', a))
b_dt = _numpy_support.as_dtype(getattr(b, 'dtype', b))
return np.result_type(a_dt, mt, b_dt)


def _get_mult_function(mt: sparse.COO):
"""
Get a function similar to `` lambda a, b: np.einsum('i,ijk,k->j', a, mt, b)``
Expand All @@ -172,19 +161,14 @@ def _get_mult_function(mt: sparse.COO):
k_list, l_list, m_list = mt.coords
mult_table_vals = mt.data

@_numba_utils.generated_jit(nopython=True)
@_numba_utils.njit
def mv_mult(value, other_value):
# this casting will be done at jit-time
ret_dtype = _get_mult_function_result_type(value, other_value, mult_table_vals.dtype)
mult_table_vals_t = mult_table_vals.astype(ret_dtype)

def mult_inner(value, other_value):
output = np.zeros(dims, dtype=ret_dtype)
for k, l, m, val in zip(k_list, l_list, m_list, mult_table_vals_t):
output[l] += value[k] * val * other_value[m]
return output

return mult_inner
res = value[k_list] * mult_table_vals * other_value[m_list]
output = np.zeros(dims, dtype=res.dtype)
# Can not use "np.add.at(output, l_list, res)", as ufunc.at is not supported by numba
for l, val in zip(l_list, res):
output[l] += val
return output

return mv_mult

Expand All @@ -203,24 +187,16 @@ def _get_mult_function_runtime_sparse(mt: sparse.COO):
k_list, l_list, m_list = mt.coords
mult_table_vals = mt.data

@_numba_utils.generated_jit(nopython=True)
@_numba_utils.njit
def mv_mult(value, other_value):
# this casting will be done at jit-time
ret_dtype = _get_mult_function_result_type(value, other_value, mult_table_vals.dtype)
mult_table_vals_t = mult_table_vals.astype(ret_dtype)

def mult_inner(value, other_value):
output = np.zeros(dims, dtype=ret_dtype)
for ind, k in enumerate(k_list):
v_val = value[k]
if v_val != 0.0:
m = m_list[ind]
ov_val = other_value[m]
if ov_val != 0.0:
l = l_list[ind]
output[l] += v_val * mult_table_vals_t[ind] * ov_val
return output
return mult_inner
# Use mask where both operands are non-zero, to avoid zero-multiplications
nz_mask = (value != 0.0)[k_list] & (other_value != 0.0)[m_list]
res = value[k_list[nz_mask]] * mult_table_vals[nz_mask] * other_value[m_list[nz_mask]]
output = np.zeros(dims, dtype=res.dtype)
# Can not use "np.add.at(output, l_list[nz_mask], res)", as ufunc.at is not supported by numba
for l, val in zip(l_list[nz_mask], res):
output[l] += val
return output

return mv_mult

Expand Down
2 changes: 1 addition & 1 deletion clifford/_conformal_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def up(self, x: MultiVector) -> MultiVector:
new_val = np.zeros(self.gaDims)
new_val[:len(old_val)] = old_val
x = self.MultiVector(value=new_val)
except(AttributeError):
except AttributeError:
# if x is a scalar it does not have layout but following
# will still work
pass
Expand Down
4 changes: 2 additions & 2 deletions clifford/_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def vee(aval, bval):
def __repr__(self):
return "{}({!r}, ids={!r}, order={!r}, names={!r})".format(
type(self).__name__,
list(self.sig), self._basis_vector_ids, self._basis_blade_order, self.names
self.sig.tolist(), self._basis_vector_ids, self._basis_blade_order, self.names
)

def _repr_pretty_(self, p, cycle):
Expand All @@ -489,7 +489,7 @@ def _repr_pretty_(self, p, cycle):
prefix = '{}('.format(type(self).__name__)

with p.group(len(prefix), prefix, ')'):
p.text('{},'.format(list(self.sig)))
p.text('{},'.format(self.sig.tolist()))
p.breakable()
p.text('ids=')
p.pretty(self._basis_vector_ids)
Expand Down
2 changes: 1 addition & 1 deletion clifford/_multivector.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ def _repr_pretty_(self, p, cycle):
p.pretty(self.layout)
p.text(",")
p.breakable()
p.text(repr(list(self.value)))
p.text(repr(self.value.tolist()))
if self.value.dtype != np.float64:
p.text(",")
p.breakable()
Expand Down
28 changes: 0 additions & 28 deletions clifford/_numba_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,39 +59,11 @@ def __repr__(self):
return "_pickleable_function({!r})".format(self.__func)


class _fake_generated_jit:
def __init__(self, f):
self.__cache = {}
self.__func = pickleable_function(f)
functools.update_wrapper(self, self.__func)

def __getnewargs_ex__(self):
return (self.__func,), {}

def __getstate__(self):
return {}

def __call__(self, *args):
arg_type = tuple(numba.typeof(arg) for arg in args)
try:
func = self.__cache[arg_type]
except KeyError:
func = self.__cache[arg_type] = self.__func(*arg_type)
return func(*args)


if not DISABLE_JIT:
njit = numba.njit
generated_jit = numba.generated_jit
else:
def njit(f=None, **kwargs):
if f is None:
return pickleable_function
else:
return pickleable_function(f)

def generated_jit(f=None, **kwargs):
if f is None:
return _fake_generated_jit
else:
return _fake_generated_jit(f)
2 changes: 1 addition & 1 deletion clifford/cga.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ def __init__(self, cga, *args) -> None:
arg = float(arg)

if arg < 0:
raise(ValueError('dilation should be positive'))
raise ValueError('dilation should be positive')

mv = e**((-log(arg)/2.)*(self.cga.E0))

Expand Down
6 changes: 3 additions & 3 deletions clifford/test/test_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def test_2d_mv_array(self, g3, rng): # noqa: F811
# check properties of the array are preserved (no need to check both a and b)
np.testing.assert_array_equal(mv_array_a.value, value_array_a)
assert mv_array_a.value.dtype == value_array_a.dtype
assert type(mv_array_a.value) == type(value_array_a)
assert type(mv_array_a.value) is type(value_array_a)

# Check addition
mv_array_sum = mv_array_a + mv_array_b
Expand Down Expand Up @@ -806,9 +806,9 @@ def check_inv(self, A):
for m, a in enumerate(A):
for n, b in enumerate(A.inv):
if m == n:
assert(a | b == 1)
assert (a | b == 1)
else:
assert(a | b == 0)
assert (a | b == 0)

@pytest.mark.parametrize(('p', 'q'), [
(2, 0), (3, 0), (4, 0)
Expand Down
14 changes: 5 additions & 9 deletions clifford/test/test_function_cache.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import numpy as np
from clifford._numba_utils import generated_jit
from clifford._numba_utils import njit
import pytest


@generated_jit(cache=True)
def foo(x):
from clifford.g3 import e3

def impl(x):
return (x * e3).value
return impl
@njit(cache=True)
def foo(x, y):
return (x * y).value


# Make the test fail on a failed cache warning
@pytest.mark.filterwarnings("error")
def test_function_cache():
from clifford.g3 import e3
np.testing.assert_array_equal((1.0*e3).value, foo(1.0))
np.testing.assert_array_equal((1.0*e3).value, foo(1.0, e3))
Loading
Loading