def test_gradients_simple_oscillator(ffmt, integrator,
                                     use_richardson_extrapolation, device):
    if use_richardson_extrapolation and integrator.__implicit__:
        pytest.skip(
            "Richardson Extrapolation is too slow with implicit methods")
    D.set_float_fmt(ffmt)

    print("Testing {} float format".format(D.float_fmt()))

    import torch

    torch.set_printoptions(precision=17)

    device = torch.device(device)

    torch.autograd.set_detect_anomaly(False)  # Enable if a test fails

    def rhs(t, state, k, m, **kwargs):
        return D.array([[0.0, 1.0], [-k / m, 0.0]], device=device) @ state

    csts = dict(k=1.0, m=1.0)
    T = 2 * D.pi * D.sqrt(D.array(csts['m'] / csts['k'])).to(device)
    dt = max(0.5 * (D.epsilon()**0.5)**(1.0 / (max(2, integrator.order - 1))),
             5e-2)

    def true_solution_sho(t, initial_state, k, m):
        w2 = D.array(k / m).to(device)
        w = D.sqrt(w2)
        A = D.sqrt(initial_state[0]**2 + initial_state[1]**2 / w2)
        phi = D.atan2(-initial_state[1], w * initial_state[0])
        return D.stack([A * D.cos(w * t + phi), -w * A * D.sin(w * t + phi)]).T

    method = integrator
    if use_richardson_extrapolation:
        method = de.integrators.generate_richardson_integrator(method)

    with de.utilities.BlockTimer(section_label="Integrator Tests"):
        y_init = D.array([1., 1.], requires_grad=True).to(device)

        a = de.OdeSystem(rhs,
                         y_init,
                         t=(0, T),
                         dt=T * dt,
                         rtol=D.epsilon()**0.5,
                         atol=D.epsilon()**0.5,
                         constants=csts)
        a.set_method(method)
        print("Testing {} with dt = {:.4e}".format(a.integrator, a.dt))

        a.integrate(eta=True)

        Jy = D.jacobian(a.y[-1], a.y[0])
        true_Jy = D.jacobian(true_solution_sho(a.t[-1], a.y[0], **csts),
                             a.y[0])

        print(a.integrator.adaptive,
              D.mean(D.abs(D.stack(a.t[1:]) - D.stack(a.t[:-1]))),
              D.norm(true_Jy - Jy),
              D.epsilon()**0.5)

        if a.integrator.adaptive:
            assert (D.allclose(true_Jy,
                               Jy,
                               rtol=4 * a.rtol**0.75,
                               atol=4 * a.atol**0.75))
        print("{} method test succeeded!".format(a.integrator))
        print("")

    print("{} backend test passed successfully!".format(D.backend()))
def test_gradients_simple_decay(ffmt, integrator, use_richardson_extrapolation,
                                device):
    if use_richardson_extrapolation and integrator.__implicit__:
        pytest.skip(
            "Richardson Extrapolation is too slow with implicit methods")
    D.set_float_fmt(ffmt)
    if integrator.__symplectic__:
        pytest.skip(
            "Exponential decay system is not in the form compatible with symplectic integrators"
        )
    print("Testing {} float format".format(D.float_fmt()))

    import torch

    torch.set_printoptions(precision=17)

    device = torch.device(device)

    torch.autograd.set_detect_anomaly(False)  # Enable if a test fails

    def rhs(t, state, k, **kwargs):
        return -k * state

    def rhs_jac(t, state, k, **kwargs):
        return -k

    rhs.jac = rhs_jac

    y_init = D.array(5.0, requires_grad=True)
    csts = dict(k=D.array(1.0, device=device))
    dt = max(0.5 * (D.epsilon()**0.5)**(1.0 / (max(2, integrator.order - 1))),
             5e-2)

    def true_solution_decay(t, initial_state, k):
        return initial_state * D.exp(-k * t)

    method = integrator
    if use_richardson_extrapolation:
        method = de.integrators.generate_richardson_integrator(method)

    with de.utilities.BlockTimer(section_label="Integrator Tests"):
        y_init = D.ones((1, ), requires_grad=True).to(device)
        y_init = y_init * D.e

        a = de.OdeSystem(rhs,
                         y_init,
                         t=(0, 1.0),
                         dt=dt,
                         rtol=D.epsilon()**0.5,
                         atol=D.epsilon()**0.5,
                         constants=csts)
        a.set_method(method)
        print("Testing {} with dt = {:.4e}".format(a.integrator, a.dt))

        a.integrate(eta=True)

        Jy = D.jacobian(a.y[-1], a.y[0])
        true_Jy = D.jacobian(true_solution_decay(a.t[-1], a.y[0], **csts),
                             a.y[0])

        print(a.y[-1], true_solution_decay(a.t[-1], a.y[0], **csts),
              D.abs(a.y[-1] - true_solution_decay(a.t[-1], a.y[0], **csts)))
        print(a.integrator.adaptive,
              D.mean(D.abs(D.stack(a.t[1:]) - D.stack(a.t[:-1]))),
              D.norm(true_Jy - Jy), 32 * a.rtol)
        print(a.integrator.adaptive, true_Jy, Jy)

        if a.integrator.adaptive:
            assert (D.allclose(true_Jy, Jy, rtol=32 * a.rtol,
                               atol=32 * a.atol))
        print("{} method test succeeded!".format(a.integrator))
        print("")

    print("{} backend test passed successfully!".format(D.backend()))
def test_gradients_complex(ffmt, integrator, use_richardson_extrapolation,
                           device):
    if use_richardson_extrapolation and integrator.__implicit__:
        pytest.skip(
            "Richardson Extrapolation is too slow with implicit methods")
    D.set_float_fmt(ffmt)

    print("Testing {} float format".format(D.float_fmt()))

    import torch

    torch.set_printoptions(precision=17)

    device = torch.device(device)

    torch.autograd.set_detect_anomaly(False)  # Enable if a test fails

    class NNController(torch.nn.Module):
        def __init__(self,
                     in_dim=2,
                     out_dim=2,
                     inter_dim=50,
                     append_time=False):
            super().__init__()

            self.append_time = append_time

            self.net = torch.nn.Sequential(
                torch.nn.Linear(in_dim + (1 if append_time else 0), inter_dim),
                torch.nn.Softplus(), torch.nn.Linear(inter_dim, out_dim),
                torch.nn.Sigmoid())

            for idx, m in enumerate(self.net.modules()):
                if isinstance(m, torch.nn.Linear):
                    torch.nn.init.xavier_normal_(m.weight, gain=1.0)
                    torch.nn.init.constant_(m.bias, 0.0)

        def forward(self, t, y, dy):
            if self.append_time:
                return self.net(
                    torch.cat([y.view(-1), dy.view(-1),
                               t.view(-1)]))
            else:
                return self.net(torch.cat([y, dy]))

    class SimpleODE(torch.nn.Module):
        def __init__(self, inter_dim=10, k=1.0):
            super().__init__()
            self.nn_controller = NNController(in_dim=4,
                                              out_dim=1,
                                              inter_dim=inter_dim)
            self.A = torch.nn.Parameter(
                torch.tensor([[0.0, 1.0], [-k, -1.0]], requires_grad=False))

        def forward(self, t, y, params=None):
            if not isinstance(t, torch.Tensor):
                torch_t = torch.tensor(t)
            else:
                torch_t = t
            if not isinstance(y, torch.Tensor):
                torch_y = torch.tensor(y)
            else:
                torch_y = y
            if params is not None:
                if not isinstance(params, torch.Tensor):
                    torch_params = torch.tensor(params)
                else:
                    torch_params = params

            dy = torch.matmul(self.A, torch_y)

            controller_effect = self.nn_controller(
                torch_t, torch_y, dy) if params is None else params

            return dy + torch.cat(
                [torch.tensor([0.0]).to(dy), (controller_effect * 2.0 - 1.0)])

    method = integrator
    if use_richardson_extrapolation:
        method = de.integrators.generate_richardson_integrator(method)

    with de.utilities.BlockTimer(section_label="Integrator Tests"):
        yi1 = D.array([1.0, 0.0], requires_grad=True).to(device)
        df = SimpleODE(k=1.0)

        a = de.OdeSystem(df,
                         yi1,
                         t=(0, 0.1),
                         dt=1e-3,
                         rtol=D.epsilon()**0.5,
                         atol=D.epsilon()**0.5)
        a.set_method(method)
        print("Testing {} with dt = {:.4e}".format(a.integrator, a.dt))

        a.integrate(eta=True)

        dyfdyi = D.jacobian(a.y[-1], a.y[0])
        dyi = D.array([0.0, 1.0]).to(device) * D.epsilon()**0.5
        dyf = D.einsum("nk,k->n", dyfdyi, dyi)
        yi2 = yi1 + dyi

        print(a.y[-1].device)

        b = de.OdeSystem(df,
                         yi2,
                         t=(0, a.t[-1]),
                         dt=a.dt,
                         rtol=a.rtol,
                         atol=a.atol)
        b.set_method(method)
        b.integrate(eta=True)

        true_diff = b.y[-1] - a.y[-1]

        print(D.norm(true_diff - dyf), D.epsilon()**0.5)

        assert (D.allclose(true_diff, dyf, rtol=4 * a.rtol, atol=4 * a.atol))
        print("{} method test succeeded!".format(a.integrator))
        print("")

    print("{} backend test passed successfully!".format(D.backend()))
Exemple #4
0
def test_gradients_unbatched():
    a = D.array([1.0, 1.0], requires_grad=True)
    assert (D.all(
        D.jacobian(a, a, batch_mode=False) == D.array([[1.0, 0.0], [0.0, 1.0]
                                                       ])))
Exemple #5
0
def test_gradients_higher_nu():
    a = D.array([1.0], requires_grad=True)
    b = a * a
    assert (D.all(D.jacobian(b, a, nu=2, batch_mode=False) == D.array([2.0])))
Exemple #6
0
def test_gradients_batched():
    a = D.array([[1.0, 1.0], [1.0, 1.0]], requires_grad=True)
    b = a * D.array([[1.0, 1.0], [2.0, 2.0]])
    assert (D.all(
        D.jacobian(b, a, batch_mode=True) == D.array(
            [[[1.0, 0.0], [2.0, 0.0]], [[0.0, 1.0], [0.0, 2.0]]])))
Exemple #7
0
def test_gradients_no_grad():
    a = D.array([1.0, 1.0], requires_grad=False)
    assert (D.all(D.jacobian(a, a) == D.array([[0.0, 0.0], [0.0, 0.0]])))
Exemple #8
0
def test_gradients_nu_zero():
    a = D.array([1.0, 1.0], requires_grad=True)
    assert (D.all(a == D.jacobian(a, a, nu=0)))
Exemple #9
0
def test_gradients_wrong_nu():
    with pytest.raises(ValueError):
        a = D.array([1.0, 1.0], requires_grad=True)
        D.jacobian(a, a, nu=-1)
def test_gradients():
    for ffmt in D.available_float_fmt():
        D.set_float_fmt(ffmt)

        print("Testing {} float format".format(D.float_fmt()))

        import torch

        torch.set_printoptions(precision=17)
        torch.set_num_threads(1)

        torch.autograd.set_detect_anomaly(True)

        class NNController(torch.nn.Module):
            def __init__(self,
                         in_dim=2,
                         out_dim=2,
                         inter_dim=50,
                         append_time=False):
                super().__init__()

                self.append_time = append_time

                self.net = torch.nn.Sequential(
                    torch.nn.Linear(in_dim + (1 if append_time else 0),
                                    inter_dim), torch.nn.Softplus(),
                    torch.nn.Linear(inter_dim, out_dim), torch.nn.Sigmoid())

                for idx, m in enumerate(self.net.modules()):
                    if isinstance(m, torch.nn.Linear):
                        torch.nn.init.xavier_normal_(m.weight, gain=1.0)
                        torch.nn.init.constant_(m.bias, 0.0)

            def forward(self, t, y, dy):
                if self.append_time:
                    return self.net(
                        torch.cat([y.view(-1),
                                   dy.view(-1),
                                   t.view(-1)]))
                else:
                    return self.net(torch.cat([y, dy]))

        class SimpleODE(torch.nn.Module):
            def __init__(self, inter_dim=10, k=1.0):
                super().__init__()
                self.nn_controller = NNController(in_dim=4,
                                                  out_dim=1,
                                                  inter_dim=inter_dim)
                self.A = torch.tensor([[0.0, 1.0], [-k, -1.0]],
                                      requires_grad=True)

            def forward(self, t, y, params=None):
                if not isinstance(t, torch.Tensor):
                    torch_t = torch.tensor(t)
                else:
                    torch_t = t
                if not isinstance(y, torch.Tensor):
                    torch_y = torch.tensor(y)
                else:
                    torch_y = y
                if params is not None:
                    if not isinstance(params, torch.Tensor):
                        torch_params = torch.tensor(params)
                    else:
                        torch_params = params

                dy = torch.matmul(self.A, torch_y)

                controller_effect = self.nn_controller(
                    torch_t, torch_y, dy) if params is None else params

                return dy + torch.cat(
                    [torch.tensor([0.0]), (controller_effect * 2.0 - 1.0)])

        with de.utilities.BlockTimer(section_label="Integrator Tests"):
            for i in sorted(set(de.available_methods(False).values()),
                            key=lambda x: x.__name__):
                try:
                    yi1 = D.array([1.0, 0.0], requires_grad=True)
                    df = SimpleODE(k=1.0)

                    a = de.OdeSystem(df,
                                     yi1,
                                     t=(0, 1.),
                                     dt=0.0675,
                                     rtol=D.epsilon()**0.5,
                                     atol=D.epsilon()**0.5)
                    a.set_method(i)
                    a.integrate(eta=True)

                    dyfdyi = D.jacobian(a.y[-1], a.y[0])
                    dyi = D.array([0.0, 1.0]) * D.epsilon()**0.5
                    dyf = D.einsum("nk,k->n", dyfdyi, dyi)
                    yi2 = yi1 + dyi

                    b = de.OdeSystem(df,
                                     yi2,
                                     t=(0, 1.),
                                     dt=0.0675,
                                     rtol=D.epsilon()**0.5,
                                     atol=D.epsilon()**0.5)
                    b.set_method(i)
                    b.integrate(eta=True)

                    true_diff = b.y[-1] - a.y[-1]

                    print(D.norm(true_diff - dyf), D.epsilon()**0.5)

                    assert (D.allclose(true_diff,
                                       dyf,
                                       rtol=4 * D.epsilon()**0.5,
                                       atol=4 * D.epsilon()**0.5))
                    print("{} method test succeeded!".format(a.integrator))
                except:
                    raise RuntimeError(
                        "Test failed for integration method: {}".format(
                            a.integrator))
            print("")

        print("{} backend test passed successfully!".format(D.backend()))