FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Minor changes to Einsum decomposition by sdpython · Pull Request #259 · sdpython/mlprodict · GitHub

This repository was archived by the owner on Jan 13, 2024. It is now read-only.

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13817c9
Einsum decomposition
sdpython Apr 20, 2021
da470a1
Update test_einsum.py
sdpython Apr 20, 2021
9f760b9
fix a couple of issues
sdpython Apr 21, 2021
c2e0f47
improve dot graph
sdpython Apr 22, 2021
6758a9a
documentation
sdpython Apr 22, 2021
14eba46
remove optimize=True in some cases
sdpython Apr 22, 2021
7943226
Add diagonal
sdpython Apr 23, 2021
8d9ded8
refactoring, fix duplicates indices
sdpython Apr 23, 2021
9290046
refactoring
sdpython Apr 23, 2021
9a8d385
fix python implemented, one broadcast case is still missing
sdpython Apr 24, 2021
f298ac6
fix python version of matmul
sdpython Apr 24, 2021
60650d9
update unit tests
sdpython Apr 25, 2021
84b3618
decompose matmul into smaller pieces
sdpython Apr 26, 2021
8dd3a65
update, still fixes to do
sdpython Apr 27, 2021
32e356d
Fix matrix multiplication
sdpython Apr 28, 2021
2035bae
fix decomposition of einsum
sdpython Apr 28, 2021
1b660dd
documentation
sdpython Apr 29, 2021
f0efa47
remove useless transpose
sdpython Apr 29, 2021
222bad2
merge expand_dims node into a single one
sdpython Apr 29, 2021
801366d
simplifies output graph
sdpython Apr 29, 2021
218f031
add onnx export to einsum
sdpython Apr 30, 2021
50747a3
fix issue with sparse
sdpython Apr 30, 2021
41f6273
fix matmul computation
sdpython Apr 30, 2021
9f3885c
fix issue with latest version of scikit-learn
sdpython Apr 30, 2021
662727f
finalize conversion to onnx
sdpython Apr 30, 2021
91ec893
Update test_einsum.py
sdpython Apr 30, 2021
b28dd2d
remove duplicate transpose
sdpython May 1, 2021
93aca50
code coverage
sdpython May 1, 2021
8fe01ff
Merge branch 'master' of https://github.com/sdpython/mlprodict into e…
sdpython May 2, 2021
File filter

Filter by extension

Filter by extension .ipynb  (1) .py  (13) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
1,058 changes: 980 additions & 78 deletions _doc/notebooks/einsum_decomposition.ipynb

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions _unittests/ut_onnx_conv/test_conv_helpers.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
@brief test log(time=4s)
"""
import unittest
from pyquickhelper.pycode import ExtTestCase
from skl2onnx.common.data_types import FloatTensorType
from mlprodict.onnx_conv.convert import guess_schema_from_model


class TestConvHelpers(ExtTestCase):

def test_guess_schema_from_model(self):
class A:
def __init__(self, sh):
pass
r = guess_schema_from_model(A, A, [('X', FloatTensorType())])
self.assertEqual(r[0][0], 'X')


if __name__ == "__main__":
unittest.main()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from mlprodict.onnxrt import OnnxInference
from mlprodict.onnx_conv import register_converters, to_onnx
from mlprodict.tools.asv_options_helper import get_ir_version_from_onnx
from mlprodict.onnx_conv.parsers.parse_lightgbm import WrappedLightGbmBooster


class TestOnnxrtRuntimeLightGbm(ExtTestCase):
Expand All @@ -24,6 +25,14 @@ def setUp(self):
logger.disabled = True
register_converters()

def test_missing(self):
r = WrappedLightGbmBooster._generate_classes( # pylint: disable=W0212
dict(num_class=1))
self.assertEqual(r.tolist(), [0, 1])
r = WrappedLightGbmBooster._generate_classes( # pylint: disable=W0212
dict(num_class=3))
self.assertEqual(r.tolist(), [0, 1, 2])

@skipif_circleci('stuck')
@ignore_warnings((RuntimeWarning, UserWarning))
def test_onnxrt_python_lightgbm_categorical(self):
Expand Down
57 changes: 47 additions & 10 deletions _unittests/ut_testing/test_einsum.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,6 @@ def test_decompose_einsum_equation_exc(self):
lambda: decompose_einsum_equation("abc,ch->ah", (2, 2, 2), (2, 2),
strategy="donotexist"),
ValueError)
self.assertRaise(
lambda: decompose_einsum_equation("abc,ch->ah"), ValueError)
self.assertRaise(
lambda: decompose_einsum_equation("abc,ch->ah", (2, 2, 2), (2, 2),
"donotexist"),
Expand Down Expand Up @@ -179,6 +177,26 @@ def fct():
self.assertIn("numpy_extended_dot", out)
self.assertEqualArray(exp, res)

def test_decompose_einsum_equation_py_noshape(self):
m1 = numpy.arange(0, 24).astype(numpy.float32).reshape((2, 3, 4))
m2 = numpy.arange(0, 20).astype(numpy.float32).reshape((4, 5))
verbose = False
for strat, opname in [('numpy', 'batch_dot'),
('simple', 'matmul')]:
with self.subTest(strategy=strat):
seq = decompose_einsum_equation(
"bac,ch->ah", strategy=strat, verbose=verbose)
self.assertIn(opname, seq.to_dot())
res1 = apply_einsum_sequence(seq, m1, m2, verbose=verbose)
res2 = apply_einsum_sequence(
seq, m1, m2, matmul_impl='py', verbose=verbose)
if strat == 'simple':
self.assertRaise(
lambda: apply_einsum_sequence(
seq, m1, m2, matmul_impl='py2'), # pylint: disable=W0640
ValueError)
self.assertEqualArray(res1, res2)

def test_decompose_einsum_equation_py(self):
m1 = numpy.arange(0, 24).astype(numpy.float32).reshape((2, 3, 4))
m2 = numpy.arange(0, 20).astype(numpy.float32).reshape((4, 5))
Expand Down Expand Up @@ -346,16 +364,21 @@ def test_many_2(self):

for i, (eq, exp) in enumerate(res):
with self.subTest(equation=eq, index=i, total=len(res)):
verbose = 12 if eq == ',abc,cd->abd' else 0
verbose = 12 if eq == ',abc,dc->acd' else 0
if verbose:
print('\n########################################clean=False')
print("#########0", eq)
seq = decompose_einsum_equation(
eq, m1.shape, m2.shape)
res = apply_einsum_sequence(seq, m1, m2)
eq, m1.shape, m2.shape, verbose=verbose)
res = apply_einsum_sequence(seq, m1, m2, verbose=verbose)
self.assertEqualArray(exp, res)

seq = decompose_einsum_equation(
eq, m1.shape, m2.shape, strategy='numpy', clean=True)
if verbose:
print("#########", eq)
print('\n########################################clean=True')
print("#########1", eq)
seq = decompose_einsum_equation(
eq, m1.shape, m2.shape, strategy='numpy',
clean=True, verbose=verbose)
res = apply_einsum_sequence(seq, m1, m2, verbose=verbose)
self.assertEqualArray(exp, res)
onx = seq.to_onnx('Y', 'X1', 'X2', dtype=numpy.float32)
Expand Down Expand Up @@ -462,7 +485,7 @@ def optimize_compare(self, equation, operands=None, verbose=False):
inps = {n: v.astype(numpy.float32)
for n, v in zip(inps, inputs)}
got = oinf.run(inps, verbose=vv, fLOG=print)['Y']
self.assertEqualArray(exp, got, decimal=6)
self.assertEqualArray(exp, got, decimal=5)

with self.subTest(strategy='simple'):
seq = decompose_einsum_equation(
Expand Down Expand Up @@ -591,7 +614,21 @@ def test_np_test_edge_cases_duplicate_indices(self):
self.optimize_compare('baa,dcf,af,cde->be')
self.optimize_compare('fff,fae,bef,def->abd')

def test_exc(self):
self.assertRaise(
lambda: EinsumSubOp(2, 'transpose', 0, perm=(1, 1)),
RuntimeError)
self.assertRaise(
lambda: EinsumSubOp(2, 'transpose', 0, perm=(0, 1)),
ValueError)
self.assertRaise(
lambda: EinsumSubOp(2, 'matmul', 0, 1,
axes=(0, 1), left=(0, 1), right=(0, 1)),
RuntimeError)
r = repr(EinsumSubOp(2, 'transpose', 0, perm=(1, 0)))
self.assertIn("EinsumSubOp('transpose', 0, perm=(1, 0))", r)


if __name__ == "__main__":
# TestEinsum().test_many_3()
# TestEinsum().test_many_2()
unittest.main()
42 changes: 41 additions & 1 deletion _unittests/ut_testing/test_einsum_generic_dot.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
from pyquickhelper.pycode import ExtTestCase
from mlprodict.testing.einsum_impl_ext import (
numpy_extended_dot, numpy_extended_dot_python,
numpy_extended_dot_matrix)
numpy_extended_dot_matrix, numpy_diagonal,
_numpy_extended_dot_equation,
_common_check_numpy_extended_dot)


confs = [
Expand Down Expand Up @@ -1502,6 +1504,44 @@ def common_test(self, sh1, sh2, axes, left, right, fct, verbose=False):
exp.ravel(), dot.ravel(), f.getvalue()))
return True

def test_exc(self):
self.assertRaise(
lambda: numpy_diagonal(None, 6, (8, 9)),
RuntimeError)
self.assertRaise(
lambda: _numpy_extended_dot_equation(4, 5, None, None, None),
RuntimeError)
self.assertRaise(
lambda: _numpy_extended_dot_equation(1, 1, (4, 5), (6, 7), (8, 9)),
ValueError)
self.assertRaise(
lambda: _numpy_extended_dot_equation(
10, 10, (-4, 5), (6, 7), (8, 9)),
ValueError)
self.assertRaise(
lambda: _common_check_numpy_extended_dot(
numpy.array([5], dtype=numpy.float32),
numpy.array([5], dtype=numpy.int64),
None, None, None),
TypeError)

def test_verbose(self):
conf = dict(shape1=(1, 5, 4, 1), shape2=(1, 1, 4, 6),
axes=(2,), left=(0, 1), right=(3,))
sh1 = conf['shape1']
sh2 = conf['shape2']
axes = conf['axes']
left = conf['left']
right = conf['right']
m1 = numpy.empty(sh1).ravel()
m1 = numpy.arange(len(m1)).reshape(sh1).astype(numpy.float64) + 10
m2 = numpy.empty(sh2).ravel()
m2 = numpy.arange(len(m2)).reshape(sh2).astype(numpy.float64) + 1000
self.capture(
lambda: numpy_extended_dot_python(m1, m2, axes, left, right))
self.capture(
lambda: numpy_extended_dot_matrix(m1, m2, axes, left, right))


if __name__ == "__main__":
unittest.main()
69 changes: 40 additions & 29 deletions _unittests/ut_testing/test_experimental.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def ort_path_pad(self, x, pads):
sess = InferenceSession(model.SerializeToString())
return numpy.squeeze(sess.run(['Y'], {'X': x, 'P': npads}))

def fct_test(self, custom_fct, fct, *inputs):
got = custom_fct(*inputs, debug=True)
def fct_test(self, custom_fct, fct, *inputs, verbose=True):
got = custom_fct(*inputs, verbose=verbose)
exp = fct(*inputs)
try:
self.assertEqualArray(exp, got)
Expand All @@ -48,33 +48,42 @@ def fct_test(self, custom_fct, fct, *inputs):
"MISMATCH {}\n{}".format(inputs, "\n".join(rows))) from e

def test_experimental_pad_positive(self):
arr = numpy.arange(6) + 10
paddings = numpy.array([1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6) + 10
paddings = numpy.array([1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 2, 2]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6).reshape((1, 2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1, 1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr, paddings)

arr = numpy.arange(6).reshape((1, 2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1, 1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr, paddings)
for verbose in [True, False]:
with self.subTest(verbose=verbose):
arr = numpy.arange(6) + 10
paddings = numpy.array([1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6) + 10
paddings = numpy.array([1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 2, 2]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6).reshape((2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6).reshape((1, 2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1, 1, 1]).reshape((-1, 2))
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

arr = numpy.arange(6).reshape((1, 2, -1)) + 10
paddings = numpy.array([1, 1, 1, 1, 1, 1]).reshape((-1, 2)) * 2
self.fct_test(custom_pad, numpy.pad, arr,
paddings, verbose=verbose)

def test_experimental_pad_552(self):
arr = numpy.random.rand(2, 2, 2).astype(numpy.float32)
Expand Down Expand Up @@ -138,6 +147,8 @@ def test_experimental_einsum(self):
self.assertEqual(ein.shape, ein2.shape)
self.assertEqualArray(ein, ein2)

self.capture(lambda: custom_einsum(eq, x, y, verbose=True))

x = numpy.random.rand(1, 8, 3, 5)
y = numpy.random.rand(1, 8, 3, 5)
bady1 = numpy.random.rand(2, 8, 3, 5)
Expand Down
95 changes: 95 additions & 0 deletions _unittests/ut_testing/test_verify_code.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ def fct(a, b):
return a * b
'''

source3 = '''
def fct(a, b):
return a {} b
'''

source4 = '''
def fct(a, b):
return [0, 1, 2, 3][a: b]
'''

source5 = '''
def fct(a, b):
return lambda x: x * 2
'''

source6 = '''
def fct(a, b):
return [x for x in [1, 2]]
'''


class TestVerifyCode(ExtTestCase):

Expand All @@ -72,6 +92,81 @@ def test_verify_code2(self):
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_ops(self):
for op in ['**', 'and', '*', '/', '-', '+', 'or']:
with self.subTest(op=op):
_, res = verify_code(source3.format(op))
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
if 'BinOp' not in tree and 'BoolOp' not in tree:
raise AssertionError(
"Unable to find %r in\n%r" % (op, str(tree)))
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_cmp(self):
for op in ['<', '>', '==', '!=', '>=', '<=']:
with self.subTest(op=op):
_, res = verify_code(source3.format(op))
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
self.assertIn('Compare', tree)
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_slice(self):
_, res = verify_code(source4)
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
self.assertIn('Slice', tree)
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_ops_in(self):
for op in ['in', 'not in']:
with self.subTest(op=op):
_, res = verify_code(source3.format(op))
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
self.assertIn('Compare', tree)
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_lambda(self):
_, res = verify_code(source5)
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
self.assertIn('Lambda', tree)
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)

def test_verify_code_gen(self):
_, res = verify_code(source6, exc=False)
self.assertIn('CodeNodeVisitor', str(res))
tree = res.print_tree()
self.assertIn('comprehension', tree)
self.assertIn('\n', tree)
rows = res.Rows
node = rows[0]['node']
text = res.print_node(node)
self.assertIn('body=', text)


if __name__ == "__main__":
TestVerifyCode().test_verify_code_gen()
unittest.main()
Loading

Back | FazBrowse Home | New Git URL