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 AST rewriter and JIT decorator #326

Open
wants to merge 27 commits into
base: experimental/abc-mangling
Choose a base branch
from

Conversation

hugohadfield
Copy link
Member

No description provided.

clifford/jit_func.py Outdated Show resolved Hide resolved
clifford/_layout.py Outdated Show resolved Hide resolved

np.testing.assert_allclose(test_func(e1, e2, einf).value, slow_test_func(e1, e2, einf).value)

def test_benchmark(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this an actual benchmark:

Suggested change
def test_benchmark(self):
@pytest.mark.parametrize('use_jit', [False, True])
def test_benchmark(self, use_jit, benchmark):

@lgtm-com
Copy link

lgtm-com bot commented Jun 5, 2020

This pull request introduces 1 alert when merging 8094a61 into c07db7d - view on LGTM.com

new alerts:

  • 1 for Except block handles 'BaseException'

clifford/jit_func.py Outdated Show resolved Hide resolved
Comment on lines 301 to 315
def impl(a, b):
return omt_func(a, b)
return impl
elif isinstance(a, types.Array) and isinstance(b, types.abstract.Number):
def impl(a, b):
return omt_func(a, as_ga(b))
return impl
elif isinstance(a, types.abstract.Number) and isinstance(b, types.Array):
def impl(a, b):
return omt_func(as_ga(a), b)
return impl
else:
def impl(a, b):
return a^b
return impl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tempting to make these shorter:

Suggested change
def impl(a, b):
return omt_func(a, b)
return impl
elif isinstance(a, types.Array) and isinstance(b, types.abstract.Number):
def impl(a, b):
return omt_func(a, as_ga(b))
return impl
elif isinstance(a, types.abstract.Number) and isinstance(b, types.Array):
def impl(a, b):
return omt_func(as_ga(a), b)
return impl
else:
def impl(a, b):
return a^b
return impl
return lambda a, b: omt_func(a, b)
elif isinstance(a, types.Array) and isinstance(b, types.abstract.Number):
return lambda a, b: omt_func(a, as_ga(b))
elif isinstance(a, types.abstract.Number) and isinstance(b, types.Array):
return lambda a, b: omt_func(as_ga(a), b)
else:
return lambda a, b: return a^b

clifford/jit_func.py Outdated Show resolved Hide resolved
eric-wieser
eric-wieser previously approved these changes Jun 5, 2020
Copy link
Member

@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to merge this into the experimental branch, if you think you're stabilized now

@lgtm-com
Copy link

lgtm-com bot commented Jun 6, 2020

This pull request introduces 2 alerts when merging e0263f8 into c07db7d - view on LGTM.com

new alerts:

  • 1 for Unused local variable
  • 1 for Except block handles 'BaseException'

@lgtm-com
Copy link

lgtm-com bot commented Jun 6, 2020

This pull request introduces 2 alerts when merging e878dbe into c07db7d - view on LGTM.com

new alerts:

  • 1 for Unused local variable
  • 1 for Except block handles 'BaseException'

fname = func.__name__
source = inspect.getsource(func)
# Remove the decorator first line.
source = '\n'.join(source.splitlines()[1:])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to remove this at the ast level

@lgtm-com
Copy link

lgtm-com bot commented Jun 6, 2020

This pull request introduces 2 alerts when merging 482b091 into c07db7d - view on LGTM.com

new alerts:

  • 1 for Unused local variable
  • 1 for Except block handles 'BaseException'


test_compound_func = jit_func(self.layout,
mv_constants={'test_func_1': test_func_1_jit,
'test_func_2': test_func_2_jit})(compound_func)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a way to nest functions by using(abusing?) the mv_constants argument, it is clearly not a great solution but I'm not sure how to pull it off otherwise. Perhaps the decorator has knowledge of what functions are available?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator can probably use inspect.currentframe().f_back.f_locals

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect you should be using f_globals instead of globals() too.

@eric-wieser eric-wieser dismissed their stale review June 8, 2020 09:13

Lots of changes since I lasted looked.

clifford/_ast_transformer.py Outdated Show resolved Hide resolved
clifford/_ast_transformer.py Outdated Show resolved Hide resolved
clifford/_ast_transformer.py Outdated Show resolved Hide resolved

test_compound_func = jit_func(self.layout,
mv_constants={'test_func_1': test_func_1_jit,
'test_func_2': test_func_2_jit})(compound_func)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator can probably use inspect.currentframe().f_back.f_locals

Comment on lines 847 to 877
@_cached_property
def overload_mul_func(self):
return get_overload_mul(self)

@_cached_property
def overload_xor_func(self):
return get_overload_xor(self)

@_cached_property
def overload_or_func(self):
return get_overload_or(self)

@_cached_property
def overload_add_func(self):
return get_overload_add(self)

@_cached_property
def overload_sub_func(self):
return get_overload_sub(self)

@_cached_property
def overload_reverse_func(self):
return get_overload_reverse(self)

@_cached_property
def project_to_grade_func(self):
return get_project_to_grade_func(self)

@_cached_property
def overload_call_func(self):
return get_overload_call(self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these really belong here. You've put them here because it makes them easy to cache, but I think it would be better to cache them manually in a weakref.WeakKeyDictionary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So perhaps something like

def weak_cache(f):
    _cache = weakref.WeakKeyDictionary()
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        a, *args = args
        try:
            return _cache[a]
        except KeyError:
            ret =_cache[a] = f(a, *args, **kwargs)
            return ret
    wrapped._cache = _cache
    return wrapped


@weak_cache
def _get_jit_impls(layout):
    return {
        'as_ga': get_as_ga_value_vector_func(layout),
        'ga_add': get_overload_add_func(layout),
        'ga_sub': get_overload_sub_func(layout),
        'ga_mul': get_overload_mul_func(layout),
        'ga_xor': get_overload_xor_func(layout),
        'ga_or': get_overload_or_func(layout),
        'ga_rev': get_overload_reverse_func(layout),
        'ga_call': get_overload_call_func(layout),
    }

@@ -0,0 +1,92 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps clifford/experimental/jit_func

Comment on lines 46 to 47
# Remove the decorators from the function
tree = DecoratorRemover().visit(tree)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly you want to leave behind all the non-jitfunc decorators, but that's fine as a todo.

clifford/jit_func.py Outdated Show resolved Hide resolved
clifford/jit_func.py Outdated Show resolved Hide resolved
@@ -117,7 +117,8 @@ def linear_operator_as_matrix(func, input_blades, output_blades):
ndimout = len(output_blades)
mat = np.zeros((ndimout, ndimin))
for i, b in enumerate(input_blades):
mat[:, i] = np.array([func(b)[j] for j in output_blades])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes sense to spin out a quick PR with just this fix against master.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I was thinking that

@eric-wieser
Copy link
Member

What do we want to do with this now that we have proper numba support?

@hugohadfield
Copy link
Member Author

Shall we just close it? I don't think it adds anything on top of what we have, although it was an interesting and fun idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants