def test_merge_with_weird_eq(): # numpy arrays don't compare equal like other python objects # SCALAR CASE x = tt.constant(np.asarray(1), name="x") y = tt.constant(np.asarray(1), name="y") g = FunctionGraph([x, y], [x + y]) MergeOptimizer().optimize(g) assert len(g.apply_nodes) == 1 node = list(g.apply_nodes)[0] assert len(node.inputs) == 2 assert node.inputs[0] is node.inputs[1] # NONSCALAR CASE # This was created to test TensorConstantSignature x = tt.constant(np.ones(5), name="x") y = tt.constant(np.ones(5), name="y") g = FunctionGraph([x, y], [x + y]) MergeOptimizer().optimize(g) assert len(g.apply_nodes) == 1 node = list(g.apply_nodes)[0] assert len(node.inputs) == 2 assert node.inputs[0] is node.inputs[1]
def Env(inputs, outputs, validate=True): e = FunctionGraph(inputs, outputs, clone=False) e.attach_feature(destroyhandler.DestroyHandler()) e.attach_feature(ReplaceValidate()) if validate: e.validate() return e
def test_merge_outputs(self): x, y, z = inputs() e1 = op3(op2(x, y)) e2 = op3(op2(x, y)) g = FunctionGraph([x, y, z], [e1, e2]) MergeOptimizer().optimize(g) assert str(g) == "[*1 -> Op3(Op2(x, y)), *1]"
def test_multiple(self): # it should replace all occurrences of the pattern x, y, z = inputs() e = op1(op2(x, y), op2(x, y), op2(y, z)) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op2, "1", "2"), (op4, "1")).optimize(g) assert str(g) == "[Op1(Op4(x), Op4(x), Op4(y))]"
def est_both_assert_merge_2_reverse(self): # Test case "test_both_assert_merge_2" but in reverse order x1 = tt.matrix("x1") x2 = tt.matrix("x2") x3 = tt.matrix("x3") e = tt.dot(x1, tt.opt.assert_op(x2, (x2 > x3).all())) + tt.dot( tt.opt.assert_op(x1, (x1 > x3).all()), x2) g = FunctionGraph([x1, x2, x3], [e]) MergeOptimizer().optimize(g) strg = aesara.printing.debugprint(g, file="str") strref = """Elemwise{add,no_inplace} [id A] '' 7 |dot [id B] '' 6 | |Assert{msg='Aesara Assert failed!'} [id C] '' 5 | | |x1 [id D] | | |All [id E] '' 3 | | |Elemwise{gt,no_inplace} [id F] '' 1 | | |x1 [id D] | | |x3 [id G] | |Assert{msg='Aesara Assert failed!'} [id H] '' 4 | |x2 [id I] | |All [id J] '' 2 | |Elemwise{gt,no_inplace} [id K] '' 0 | |x2 [id I] | |x3 [id G] |dot [id B] '' 6 """ print(strg) assert strg == strref, (strg, strref)
def test_replace_subgraph(self): # replacing inside the graph x, y, z = inputs() e = op1(op2(x, y), z) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op2, "1", "2"), (op1, "2", "1")).optimize(g) assert str(g) == "[Op1(Op1(y, x), z)]"
def test_multi(self): x, y, z = inputs() e0 = op1(x, y) e = op3(op4(e0), e0) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op4, (op1, "x", "y")), (op3, "x", "y")).optimize(g) assert str(g) == "[Op3(Op4(*1 -> Op1(x, y)), *1)]"
def test_nested_even(self): # regardless of the order in which we optimize, this # should work x, y, z = inputs() e = op1(op1(op1(op1(x)))) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op1, (op1, "1")), "1").optimize(g) assert str(g) == "[x]"
def test_constant_unification(self): x = Constant(MyType(), 2, name="x") y = MyVariable("y") z = Constant(MyType(), 2, name="z") e = op1(op1(x, y), y) g = FunctionGraph([y], [e]) PatternOptimizer((op1, z, "1"), (op2, "1", z)).optimize(g) assert str(g) == "[Op1(Op2(y, z), y)]"
def test_nested_out_pattern(self): x, y, z = inputs() e = op1(x, y) g = FunctionGraph([x, y, z], [e]) PatternOptimizer( (op1, "1", "2"), (op4, (op1, "1"), (op2, "2"), (op3, "1", "2"))).optimize(g) assert str(g) == "[Op4(Op1(x), Op2(y), Op3(x, y))]"
def test_replace_output(self): # replacing the whole graph x, y, z = inputs() e = op1(op2(x, y), z) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op1, (op2, "1", "2"), "3"), (op4, "3", "2")).optimize(g) assert str(g) == "[Op4(z, y)]"
def test_no_recurse(self): # if the out pattern is an acceptable in pattern # and that the ignore_newtrees flag is True, # it should do the replacement and stop x, y, z = inputs() e = op1(op2(x, y), z) g = FunctionGraph([x, y, z], [e]) PatternOptimizer((op2, "1", "2"), (op2, "2", "1"), ign=True).optimize(g) assert str(g) == "[Op1(Op2(y, x), z)]"
def test_constant_merging(self): x = MyVariable("x") y = Constant(MyType(), 2, name="y") z = Constant(MyType(), 2, name="z") e = op1(op2(x, y), op2(x, y), op2(x, z)) g = FunctionGraph([x, y, z], [e]) MergeOptimizer().optimize(g) strg = str(g) assert (strg == "[Op1(*1 -> Op2(x, y), *1, *1)]" or strg == "[Op1(*1 -> Op2(x, z), *1, *1)]")
def test_ambiguous(self): # this test should always work with TopoOptimizer and the # ignore_newtrees flag set to False. Behavior with ignore_newtrees # = True or with other NavigatorOptimizers may differ. x, y, z = inputs() e = op1(op1(op1(op1(op1(x))))) g = FunctionGraph([x, y, z], [e]) TopoPatternOptimizer((op1, (op1, "1")), (op1, "1"), ign=False).optimize(g) assert str(g) == "[Op1(x)]"
def test_unification_2(self): x, y, z = inputs() e = op1(op2(x, y), z) # the arguments to op2 are different g = FunctionGraph([x, y, z], [e]) PatternOptimizer( (op1, (op2, "1", "1"), "2"), # they are the same in the pattern (op4, "2", "1"), ).optimize(g) # The replacement should NOT occur assert str(g) == "[Op1(Op2(x, y), z)]"
def test_merge_noinput(self): # Check that identical Apply nodes without inputs will be merged x = NoInputOp(param=0)() y = NoInputOp(param=0)() z = NoInputOp(param=1)() fg = FunctionGraph([], [x, y, z]) MergeOptimizer().optimize(fg) no_input_ops = [ n for n in fg.apply_nodes if isinstance(n.op, NoInputOp) ] assert len(no_input_ops) == 2, fg.apply_nodes
def test_constraints(self): x, y, z = inputs() e = op4(op1(op2(x, y)), op1(op1(x, y))) g = FunctionGraph([x, y, z], [e]) def constraint(r): # Only replacing if the input is an instance of Op2 return r.owner.op == op2 PatternOptimizer((op1, { "pattern": "1", "constraint": constraint }), (op3, "1")).optimize(g) assert str(g) == "[Op4(Op3(Op2(x, y)), Op1(Op1(x, y)))]"
def test_identical_constant_args(self): x = MyVariable("x") y = Constant(MyType(), 2, name="y") z = Constant(MyType(), 2, name="z") ctv_backup = config.compute_test_value config.compute_test_value = "off" try: e1 = op1(y, z) finally: config.compute_test_value = ctv_backup g = FunctionGraph([x, y, z], [e1]) MergeOptimizer().optimize(g) strg = str(g) assert strg == "[Op1(y, y)]" or strg == "[Op1(z, z)]"
def test_match_same_illegal(self): x, y, z = inputs() e = op2(op1(x, x), op1(x, y)) g = FunctionGraph([x, y, z], [e]) def constraint(r): # Only replacing if the input is an instance of Op2 return r.owner.inputs[0] is not r.owner.inputs[1] PatternOptimizer({ "pattern": (op1, "x", "y"), "constraint": constraint }, (op3, "x", "y")).optimize(g) assert str(g) == "[Op2(Op1(x, x), Op3(x, y))]"
def test_multiple_merges(self): x, y, z = inputs() e1 = op1(x, y) e2 = op2(op3(x), y, z) e = op1(e1, op4(e2, e1), op1(e2)) g = FunctionGraph([x, y, z], [e]) MergeOptimizer().optimize(g) strg = str(g) # note: graph.as_string can only produce the following two possibilities, but if # the implementation was to change there are 6 other acceptable answers. assert ( strg == "[Op1(*1 -> Op1(x, y), Op4(*2 -> Op2(Op3(x), y, z), *1), Op1(*2))]" or strg == "[Op1(*2 -> Op1(x, y), Op4(*1 -> Op2(Op3(x), y, z), *2), Op1(*1))]" )
def test_1(self): x, y, z = map(MyVariable, "xyz") e = op3(op4(x, y)) g = FunctionGraph([x, y, z], [e]) # print g opt = EquilibriumOptimizer( [ PatternSub((op1, "x", "y"), (op2, "x", "y")), PatternSub((op4, "x", "y"), (op1, "x", "y")), PatternSub((op3, (op2, "x", "y")), (op4, "x", "y")), ], max_use_ratio=10, ) opt.optimize(g) # print g assert str(g) == "[Op2(x, y)]"
def est_both_assert_merge_1(self): # Merge two nodes, both have assert on the same node # with different conditions. x1 = tt.matrix("x1") x2 = tt.matrix("x2") x3 = tt.matrix("x3") e = tt.dot(tt.opt.assert_op(x1, (x1 > x3).all()), x2) + tt.dot( tt.opt.assert_op(x1, (x1 > x2).all()), x2) g = FunctionGraph([x1, x2, x3], [e]) MergeOptimizer().optimize(g) strg = aesara.printing.debugprint(g, file="str") strref1 = """Elemwise{add,no_inplace} [id A] '' 6 |dot [id B] '' 5 | |Assert{msg='Aesara Assert failed!'} [id C] '' 4 | | |x1 [id D] | | |All [id E] '' 3 | | | |Elemwise{gt,no_inplace} [id F] '' 1 | | | |x1 [id D] | | | |x3 [id G] | | |All [id H] '' 2 | | |Elemwise{gt,no_inplace} [id I] '' 0 | | |x1 [id D] | | |x2 [id J] | |x2 [id J] |dot [id B] '' 5 """ strref2 = """Elemwise{add,no_inplace} [id A] '' 6 |dot [id B] '' 5 | |Assert{msg='Aesara Assert failed!'} [id C] '' 4 | | |x1 [id D] | | |All [id E] '' 3 | | | |Elemwise{gt,no_inplace} [id F] '' 1 | | | |x1 [id D] | | | |x2 [id G] | | |All [id H] '' 2 | | |Elemwise{gt,no_inplace} [id I] '' 0 | | |x1 [id D] | | |x3 [id J] | |x2 [id G] |dot [id B] '' 5 """ # print(strg) assert strg == strref1 or strg == strref2, (strg, strref1, strref2)
def est_one_assert_merge(self): # Merge two nodes, one has assert, the other not. x1 = tt.matrix("x1") x2 = tt.matrix("x2") e = tt.dot(x1, x2) + tt.dot(tt.opt.assert_op(x1, (x1 > x2).all()), x2) g = FunctionGraph([x1, x2], [e]) MergeOptimizer().optimize(g) strg = aesara.printing.debugprint(g, file="str") strref = """Elemwise{add,no_inplace} [id A] '' 4 |dot [id B] '' 3 | |Assert{msg='Aesara Assert failed!'} [id C] '' 2 | | |x1 [id D] | | |All [id E] '' 1 | | |Elemwise{gt,no_inplace} [id F] '' 0 | | |x1 [id D] | | |x2 [id G] | |x2 [id G] |dot [id B] '' 3 """ assert strg == strref, (strg, strref)
def test_both_assert_merge_identical(self): # Merge two nodes, both have assert on the same node # with the same conditions. x1 = tt.matrix("x1") x2 = tt.matrix("x2") e = tt.dot(tt.opt.assert_op(x1, (x1 > x2).all()), x2) + tt.dot( tt.opt.assert_op(x1, (x1 > x2).all()), x2) g = FunctionGraph([x1, x2], [e]) MergeOptimizer().optimize(g) strg = aesara.printing.debugprint(g, file="str") strref = """Elemwise{add,no_inplace} [id A] '' 4 |dot [id B] '' 3 | |Assert{msg='Aesara Assert failed!'} [id C] '' 2 | | |x1 [id D] | | |All [id E] '' 1 | | |Elemwise{gt,no_inplace} [id F] '' 0 | | |x1 [id D] | | |x2 [id G] | |x2 [id G] |dot [id B] '' 3 """ # print(strg) assert strg == strref, (strg, strref)
def test_low_use_ratio(self): x, y, z = map(MyVariable, "xyz") e = op3(op4(x, y)) g = FunctionGraph([x, y, z], [e]) # print 'before', g # display pesky warnings along with stdout # also silence logger for 'aesara.gof.opt' _logger = logging.getLogger("aesara.gof.opt") oldlevel = _logger.level _logger.setLevel(logging.CRITICAL) try: opt = EquilibriumOptimizer( [ PatternSub((op1, "x", "y"), (op2, "x", "y")), PatternSub((op4, "x", "y"), (op1, "x", "y")), PatternSub((op3, (op2, "x", "y")), (op4, "x", "y")), ], max_use_ratio=1.0 / len(g.apply_nodes), ) # each opt can only be applied once opt.optimize(g) finally: _logger.setLevel(oldlevel) # print 'after', g assert str(g) == "[Op1(x, y)]"
def test_straightforward_2(self): x, y, z = inputs() e = op1(op2(x), op3(y), op4(z)) g = FunctionGraph([x, y, z], [e]) OpSubOptimizer(op3, op4).optimize(g) assert str(g) == "[Op1(Op2(x), Op4(y), Op4(z))]"
def test_deep_merge(self): x, y, z = inputs() e = op1(op3(op2(x, y), z), op4(op3(op2(x, y), z))) g = FunctionGraph([x, y, z], [e]) MergeOptimizer().optimize(g) assert str(g) == "[Op1(*1 -> Op3(Op2(x, y), z), Op4(*1))]"
def test_no_merge(self): x, y, z = inputs() e = op1(op3(op2(x, y)), op3(op2(y, x))) g = FunctionGraph([x, y, z], [e]) MergeOptimizer().optimize(g) assert str(g) == "[Op1(Op3(Op2(x, y)), Op3(Op2(y, x)))]"
def test_pickle(self): v = tt.vector() func = FunctionGraph([v], [v + 1]) s = pickle.dumps(func) pickle.loads(s)
def test_straightforward(self): x, y, z = inputs() e = op1(op2(x, y), op2(x, y), op2(x, z)) g = FunctionGraph([x, y, z], [e]) MergeOptimizer().optimize(g) assert str(g) == "[Op1(*1 -> Op2(x, y), *1, Op2(x, z))]"