# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. """Unit tests for relax optimizer APIs.""" import pytest import tvm import tvm.testing from tvm import relax from tvm.ir.base import assert_structural_equal from tvm.relax.training.optimizer import SGD, Adam, MomentumSGD from tvm.script.parser import relax as R def test_optimizer_error(): x1 = relax.Var("x1", R.Tensor((3, 3), "float32")) x2 = relax.Var("x2", R.Tensor((3, 3), "float64")) x3 = relax.Var("x3", R.Tuple([R.Tensor((3, 3), "float32")])) x4 = relax.Var("x4", R.Tensor((3, 3), "int64")) x5 = relax.Tuple([x1]) # fine cases SGD(0.01).init(x1) SGD(0.01).init([x1]) assert SGD(0.01).init([x2]).dtype == "float64" with pytest.raises(ValueError): SGD(0.01).init([x1, x1]) with pytest.raises(ValueError): SGD(0.01).init([x1, x2]) with pytest.raises(ValueError): SGD(0.01).init(x3) with pytest.raises(ValueError): SGD(0.01).init(x4) with pytest.raises(ValueError): SGD(0.01).init(x5) with pytest.raises( RuntimeError, match="Please call init\\(\\) for the optimizer before calling get_function\\(\\)", ): SGD(0.01).get_function() def test_sgd_simple(): x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) sgd = SGD(0.01).init([x, y]).get_function() @R.function def sgd_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple(R.Tensor((), "int64")), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple(R.Tensor((), "int64")), ): R.func_attr({"global_symbol": "SGD"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] lv: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), x_grad) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] lv1: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), y_grad) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv1) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple(R.Tensor((), "int64")) = (num_steps_new,) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(sgd, sgd_expected) def test_sgd_complex(): x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) sgd = SGD(0.01, 0.02).init([x, y]).get_function() @R.function def sgd_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple(R.Tensor((), "int64")), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple(R.Tensor((), "int64")), ): R.func_attr({"global_symbol": "SGD"}) with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] lv: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.02, "float32"), x) x_grad_new: R.Tensor((3, 3), "float32") = R.add(lv, x_grad) lv1: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), x_grad_new) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv1) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] lv2: R.Tensor((3,), "float32") = R.multiply(R.const(0.02, "float32"), y) y_grad_new: R.Tensor((3,), "float32") = R.add(lv2, y_grad) lv3: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), y_grad_new) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv3) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple(R.Tensor((), "int64")) = (num_steps_new,) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(sgd, sgd_expected) def test_momentum_sgd_simple(): x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) msgd = MomentumSGD(0.01, 0.9).init([x, y]).get_function() @R.function def msgd_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple(R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), ): R.func_attr({"global_symbol": "MomentumSGD"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] x_v: R.Tensor((3, 3), "float32") = optim_states[1] lv: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.9, "float32"), x_v) x_v_new: R.Tensor((3, 3), "float32") = R.add(lv, x_grad) lv1: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), x_v_new) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv1) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] y_v: R.Tensor((3,), "float32") = optim_states[2] lv2: R.Tensor((3,), "float32") = R.multiply(R.const(0.9, "float32"), y_v) y_v_new: R.Tensor((3,), "float32") = R.add(lv2, y_grad) lv3: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), y_v_new) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv3) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ) = (num_steps_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(msgd, msgd_expected) def test_momentum_sgd_complex(): lr, mom, damp, wd, nest = 0.01, 0.9, 0.85, 0.02, False x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) msgd = MomentumSGD(lr, mom, damp, wd, nest).init([x, y]).get_function() @R.function def msgd_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple(R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), ): R.func_attr({"global_symbol": "MomentumSGD"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] x_v: R.Tensor((3, 3), "float32") = optim_states[1] lv: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.02, "float32"), x) x_grad_new: R.Tensor((3, 3), "float32") = R.add(lv, x_grad) lv1: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.9, "float32"), x_v) lv2: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.15, "float32"), x_grad_new) x_v_new: R.Tensor((3, 3), "float32") = R.add(lv1, lv2) lv3: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), x_v_new) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv3) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] y_v: R.Tensor((3,), "float32") = optim_states[2] lv4: R.Tensor((3,), "float32") = R.multiply(R.const(0.02, "float32"), y) y_grad_new: R.Tensor((3,), "float32") = R.add(lv4, y_grad) lv5: R.Tensor((3,), "float32") = R.multiply(R.const(0.9, "float32"), y_v) lv6: R.Tensor((3,), "float32") = R.multiply(R.const(0.15, "float32"), y_grad_new) y_v_new: R.Tensor((3,), "float32") = R.add(lv5, lv6) lv7: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), y_v_new) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv7) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ) = (num_steps_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(msgd, msgd_expected) def test_momentum_sgd_nesterov(): lr, mom, damp, wd, nest = 0.01, 0.9, 0.85, 0.02, True x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) msgd = MomentumSGD(lr, mom, damp, wd, nest).init([x, y]).get_function() @R.function def msgd_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple(R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), ): R.func_attr({"global_symbol": "MomentumSGD"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] x_v: R.Tensor((3, 3), "float32") = optim_states[1] lv: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.02, "float32"), x) x_grad_new: R.Tensor((3, 3), "float32") = R.add(lv, x_grad) lv1: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.9, "float32"), x_v) lv2: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.15, "float32"), x_grad_new) x_v_new: R.Tensor((3, 3), "float32") = R.add(lv1, lv2) lv3: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.9, "float32"), x_v_new) x_g_nest: R.Tensor((3, 3), "float32") = R.add(x_grad_new, lv3) lv4: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), x_g_nest) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv4) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] y_v: R.Tensor((3,), "float32") = optim_states[2] lv5: R.Tensor((3,), "float32") = R.multiply(R.const(0.02, "float32"), y) y_grad_new: R.Tensor((3,), "float32") = R.add(lv5, y_grad) lv6: R.Tensor((3,), "float32") = R.multiply(R.const(0.9, "float32"), y_v) lv7: R.Tensor((3,), "float32") = R.multiply(R.const(0.15, "float32"), y_grad_new) y_v_new: R.Tensor((3,), "float32") = R.add(lv6, lv7) lv8: R.Tensor((3,), "float32") = R.multiply(R.const(0.9, "float32"), y_v_new) y_g_nest: R.Tensor((3,), "float32") = R.add(y_grad_new, lv8) lv9: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), y_g_nest) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv9) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32") ) = (num_steps_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(msgd, msgd_expected) def test_adam_simple(): x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) adam = Adam(0.01).init([x, y]).get_function() @R.function def adam_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ), ): R.func_attr({"global_symbol": "Adam"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) lv: R.Tensor((), "float32") = optim_states[1] beta1_prod: R.Tensor((), "float32") = R.multiply(lv, R.const(0.9, "float32")) lv1: R.Tensor((), "float32") = optim_states[2] beta2_prod: R.Tensor((), "float32") = R.multiply(lv1, R.const(0.999, "float32")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] x_m: R.Tensor((3, 3), "float32") = optim_states[3] x_v: R.Tensor((3, 3), "float32") = optim_states[5] lv2: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.9, "float32"), x_m) lv3: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.1, "float32"), x_grad) x_m_new: R.Tensor((3, 3), "float32") = R.add(lv2, lv3) lv4: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.999, "float32"), x_v) lv5: R.Tensor((3, 3), "float32") = R.multiply(x_grad, x_grad) lv6: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.001, "float32"), lv5) x_v_new: R.Tensor((3, 3), "float32") = R.add(lv4, lv6) lv7: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta1_prod) x_m_hat: R.Tensor((3, 3), "float32") = R.divide(x_m_new, lv7) lv8: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta2_prod) x_v_hat: R.Tensor((3, 3), "float32") = R.divide(x_v_new, lv8) lv9: R.Tensor((3, 3), "float32") = R.sqrt(x_v_hat) lv10: R.Tensor((3, 3), "float32") = R.add(lv9, R.const(1e-08, "float32")) lv11: R.Tensor((3, 3), "float32") = R.divide(x_m_hat, lv10) lv12: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), lv11) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv12) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] y_m: R.Tensor((3,), "float32") = optim_states[4] y_v: R.Tensor((3,), "float32") = optim_states[6] lv13: R.Tensor((3,), "float32") = R.multiply(R.const(0.9, "float32"), y_m) lv14: R.Tensor((3,), "float32") = R.multiply(R.const(0.1, "float32"), y_grad) y_m_new: R.Tensor((3,), "float32") = R.add(lv13, lv14) lv15: R.Tensor((3,), "float32") = R.multiply(R.const(0.999, "float32"), y_v) lv16: R.Tensor((3,), "float32") = R.multiply(y_grad, y_grad) lv17: R.Tensor((3,), "float32") = R.multiply(R.const(0.001, "float32"), lv16) y_v_new: R.Tensor((3,), "float32") = R.add(lv15, lv17) lv18: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta1_prod) y_m_hat: R.Tensor((3,), "float32") = R.divide(y_m_new, lv18) lv19: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta2_prod) y_v_hat: R.Tensor((3,), "float32") = R.divide(y_v_new, lv19) lv20: R.Tensor((3,), "float32") = R.sqrt(y_v_hat) lv21: R.Tensor((3,), "float32") = R.add(lv20, R.const(1e-08, "float32")) lv22: R.Tensor((3,), "float32") = R.divide(y_m_hat, lv21) lv23: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), lv22) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv23) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ) = (num_steps_new, beta1_prod, beta2_prod, x_m_new, y_m_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(adam, adam_expected) def test_adam_complex(): x = relax.Var("x", R.Tensor((3, 3), "float32")) y = relax.Var("y", R.Tensor((3,), "float32")) adam = Adam(0.01, (0.8, 0.85), 1e-7, 0.1).init([x, y]).get_function() @R.function def adam_expected( params: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), gradients: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")), R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ), ): R.func_attr({"global_symbol": "Adam"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) lv: R.Tensor((), "float32") = optim_states[1] beta1_prod: R.Tensor((), "float32") = R.multiply(lv, R.const(0.8, "float32")) lv1: R.Tensor((), "float32") = optim_states[2] beta2_prod: R.Tensor((), "float32") = R.multiply(lv1, R.const(0.85, "float32")) x: R.Tensor((3, 3), "float32") = params[0] x_grad: R.Tensor((3, 3), "float32") = gradients[0] x_m: R.Tensor((3, 3), "float32") = optim_states[3] x_v: R.Tensor((3, 3), "float32") = optim_states[5] lv2: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.1, "float32"), x) x_grad_new: R.Tensor((3, 3), "float32") = R.add(lv2, x_grad) lv3: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.8, "float32"), x_m) lv4: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.2, "float32"), x_grad_new) x_m_new: R.Tensor((3, 3), "float32") = R.add(lv3, lv4) lv5: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.85, "float32"), x_v) lv6: R.Tensor((3, 3), "float32") = R.multiply(x_grad_new, x_grad_new) lv7: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.15, "float32"), lv6) x_v_new: R.Tensor((3, 3), "float32") = R.add(lv5, lv7) lv8: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta1_prod) x_m_hat: R.Tensor((3, 3), "float32") = R.divide(x_m_new, lv8) lv9: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta2_prod) x_v_hat: R.Tensor((3, 3), "float32") = R.divide(x_v_new, lv9) lv10: R.Tensor((3, 3), "float32") = R.sqrt(x_v_hat) lv11: R.Tensor((3, 3), "float32") = R.add(lv10, R.const(1e-07, "float32")) lv12: R.Tensor((3, 3), "float32") = R.divide(x_m_hat, lv11) lv13: R.Tensor((3, 3), "float32") = R.multiply(R.const(0.01, "float32"), lv12) x_new: R.Tensor((3, 3), "float32") = R.subtract(x, lv13) y: R.Tensor((3,), "float32") = params[1] y_grad: R.Tensor((3,), "float32") = gradients[1] y_m: R.Tensor((3,), "float32") = optim_states[4] y_v: R.Tensor((3,), "float32") = optim_states[6] lv14: R.Tensor((3,), "float32") = R.multiply(R.const(0.1, "float32"), y) y_grad_new: R.Tensor((3,), "float32") = R.add(lv14, y_grad) lv15: R.Tensor((3,), "float32") = R.multiply(R.const(0.8, "float32"), y_m) lv16: R.Tensor((3,), "float32") = R.multiply(R.const(0.2, "float32"), y_grad_new) y_m_new: R.Tensor((3,), "float32") = R.add(lv15, lv16) lv17: R.Tensor((3,), "float32") = R.multiply(R.const(0.85, "float32"), y_v) lv18: R.Tensor((3,), "float32") = R.multiply(y_grad_new, y_grad_new) lv19: R.Tensor((3,), "float32") = R.multiply(R.const(0.15, "float32"), lv18) y_v_new: R.Tensor((3,), "float32") = R.add(lv17, lv19) lv20: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta1_prod) y_m_hat: R.Tensor((3,), "float32") = R.divide(y_m_new, lv20) lv21: R.Tensor((), "float32") = R.subtract(R.const(1, "float32"), beta2_prod) y_v_hat: R.Tensor((3,), "float32") = R.divide(y_v_new, lv21) lv22: R.Tensor((3,), "float32") = R.sqrt(y_v_hat) lv23: R.Tensor((3,), "float32") = R.add(lv22, R.const(1e-07, "float32")) lv24: R.Tensor((3,), "float32") = R.divide(y_m_hat, lv23) lv25: R.Tensor((3,), "float32") = R.multiply(R.const(0.01, "float32"), lv24) y_new: R.Tensor((3,), "float32") = R.subtract(y, lv25) params_new: R.Tuple(R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float32"), R.Tensor((), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), R.Tensor((3, 3), "float32"), R.Tensor((3,), "float32"), ) = (num_steps_new, beta1_prod, beta2_prod, x_m_new, y_m_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(adam, adam_expected) def test_adam_float64(): x = relax.Var("x", R.Tensor((3, 3), "float64")) y = relax.Var("y", R.Tensor((3,), "float64")) adam = Adam(0.01, (0.8, 0.85), 1e-7, 0.1).init([x, y]).get_function() @R.function def adam_expected( params: R.Tuple(R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64")), gradients: R.Tuple(R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64")), optim_states: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float64"), R.Tensor((), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), ), ) -> R.Tuple( R.Tuple(R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64")), R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float64"), R.Tensor((), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), ), ): R.func_attr({"global_symbol": "Adam"}) # block 0 with R.dataflow(): num_steps: R.Tensor((), "int64") = optim_states[0] num_steps_new: R.Tensor((), "int64") = R.add(num_steps, R.const(1, "int64")) lv: R.Tensor((), "float64") = optim_states[1] beta1_prod: R.Tensor((), "float64") = R.multiply(lv, R.const(0.8, "float64")) lv1: R.Tensor((), "float64") = optim_states[2] beta2_prod: R.Tensor((), "float64") = R.multiply(lv1, R.const(0.85, "float64")) x: R.Tensor((3, 3), "float64") = params[0] x_grad: R.Tensor((3, 3), "float64") = gradients[0] x_m: R.Tensor((3, 3), "float64") = optim_states[3] x_v: R.Tensor((3, 3), "float64") = optim_states[5] lv2: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.1, "float64"), x) x_grad_new: R.Tensor((3, 3), "float64") = R.add(lv2, x_grad) lv3: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.8, "float64"), x_m) lv4: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.2, "float64"), x_grad_new) x_m_new: R.Tensor((3, 3), "float64") = R.add(lv3, lv4) lv5: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.85, "float64"), x_v) lv6: R.Tensor((3, 3), "float64") = R.multiply(x_grad_new, x_grad_new) lv7: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.15, "float64"), lv6) x_v_new: R.Tensor((3, 3), "float64") = R.add(lv5, lv7) lv8: R.Tensor((), "float64") = R.subtract(R.const(1, "float64"), beta1_prod) x_m_hat: R.Tensor((3, 3), "float64") = R.divide(x_m_new, lv8) lv9: R.Tensor((), "float64") = R.subtract(R.const(1, "float64"), beta2_prod) x_v_hat: R.Tensor((3, 3), "float64") = R.divide(x_v_new, lv9) lv10: R.Tensor((3, 3), "float64") = R.sqrt(x_v_hat) lv11: R.Tensor((3, 3), "float64") = R.add(lv10, R.const(1e-07, "float64")) lv12: R.Tensor((3, 3), "float64") = R.divide(x_m_hat, lv11) lv13: R.Tensor((3, 3), "float64") = R.multiply(R.const(0.01, "float64"), lv12) x_new: R.Tensor((3, 3), "float64") = R.subtract(x, lv13) y: R.Tensor((3,), "float64") = params[1] y_grad: R.Tensor((3,), "float64") = gradients[1] y_m: R.Tensor((3,), "float64") = optim_states[4] y_v: R.Tensor((3,), "float64") = optim_states[6] lv14: R.Tensor((3,), "float64") = R.multiply(R.const(0.1, "float64"), y) y_grad_new: R.Tensor((3,), "float64") = R.add(lv14, y_grad) lv15: R.Tensor((3,), "float64") = R.multiply(R.const(0.8, "float64"), y_m) lv16: R.Tensor((3,), "float64") = R.multiply(R.const(0.2, "float64"), y_grad_new) y_m_new: R.Tensor((3,), "float64") = R.add(lv15, lv16) lv17: R.Tensor((3,), "float64") = R.multiply(R.const(0.85, "float64"), y_v) lv18: R.Tensor((3,), "float64") = R.multiply(y_grad_new, y_grad_new) lv19: R.Tensor((3,), "float64") = R.multiply(R.const(0.15, "float64"), lv18) y_v_new: R.Tensor((3,), "float64") = R.add(lv17, lv19) lv20: R.Tensor((), "float64") = R.subtract(R.const(1, "float64"), beta1_prod) y_m_hat: R.Tensor((3,), "float64") = R.divide(y_m_new, lv20) lv21: R.Tensor((), "float64") = R.subtract(R.const(1, "float64"), beta2_prod) y_v_hat: R.Tensor((3,), "float64") = R.divide(y_v_new, lv21) lv22: R.Tensor((3,), "float64") = R.sqrt(y_v_hat) lv23: R.Tensor((3,), "float64") = R.add(lv22, R.const(1e-07, "float64")) lv24: R.Tensor((3,), "float64") = R.divide(y_m_hat, lv23) lv25: R.Tensor((3,), "float64") = R.multiply(R.const(0.01, "float64"), lv24) y_new: R.Tensor((3,), "float64") = R.subtract(y, lv25) params_new: R.Tuple(R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64")) = ( x_new, y_new, ) optim_states_new: R.Tuple( R.Tensor((), "int64"), R.Tensor((), "float64"), R.Tensor((), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), R.Tensor((3, 3), "float64"), R.Tensor((3,), "float64"), ) = (num_steps_new, beta1_prod, beta2_prod, x_m_new, y_m_new, x_v_new, y_v_new) R.output(params_new, optim_states_new) return (params_new, optim_states_new) assert_structural_equal(adam, adam_expected) if __name__ == "__main__": tvm.testing.main()