# Path: torchdiffeq/_impl/event_handling.py
# def find_event(interp_fn, sign0, t0, t1, event_fn, tol):
#     with torch.no_grad():
# 
#         # Num iterations for the secant method until tolerance is within target.
#         nitrs = torch.ceil(torch.log((t1 - t0) / tol) / math.log(2.0))
# 
#         for _ in range(nitrs.long()):
#             t_mid = (t1 + t0) / 2.0
#             y_mid = interp_fn(t_mid)
#             sign_mid = torch.sign(event_fn(t_mid, y_mid))
#             same_as_sign0 = (sign0 == sign_mid)
#             t0 = torch.where(same_as_sign0, t_mid, t0)
#             t1 = torch.where(same_as_sign0, t1, t_mid)
#         event_t = (t0 + t1) / 2.0
# 
#     return event_t, interp_fn(event_t)
# 
# Path: torchdiffeq/_impl/misc.py
# def _handle_unused_kwargs(solver, unused_kwargs):
#     if len(unused_kwargs) > 0:
#         warnings.warn('{}: Unexpected arguments {}'.format(solver.__class__.__name__, unused_kwargs))


import abc
import torch
from .event_handling import find_event
from .misc import _handle_unused_kwargs
        solution = torch.empty(len(t), *self.y0.shape, dtype=self.y0.dtype, device=self.y0.device)
        solution[0] = self.y0
        t = t.to(self.dtype)
        self._before_integrate(t)
        for i in range(1, len(t)):
            solution[i] = self._advance(t[i])
        return solution


class AdaptiveStepsizeEventODESolver(AdaptiveStepsizeODESolver, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def _advance_until_event(self, event_fn):
        raise NotImplementedError

    def integrate_until_event(self, t0, event_fn):
        t0 = t0.to(self.y0.device, self.dtype)
        self._before_integrate(t0.reshape(-1))
        event_time, y1 = self._advance_until_event(event_fn)
        solution = torch.stack([self.y0, y1], dim=0)
        return event_time, solution


class FixedGridODESolver(metaclass=abc.ABCMeta):
    order: int

    def __init__(self, func, y0, step_size=None, grid_constructor=None, interp="linear", perturb=False, **unused_kwargs):
        self.atol = unused_kwargs.pop('atol')
        unused_kwargs.pop('rtol', None)
        unused_kwargs.pop('norm', None)

# Path: torchdiffeq/_impl/event_handling.py
# def find_event(interp_fn, sign0, t0, t1, event_fn, tol):
#     with torch.no_grad():
# 
#         # Num iterations for the secant method until tolerance is within target.
#         nitrs = torch.ceil(torch.log((t1 - t0) / tol) / math.log(2.0))
# 
#         for _ in range(nitrs.long()):
#             t_mid = (t1 + t0) / 2.0
#             y_mid = interp_fn(t_mid)
#             sign_mid = torch.sign(event_fn(t_mid, y_mid))
#             same_as_sign0 = (sign0 == sign_mid)
#             t0 = torch.where(same_as_sign0, t_mid, t0)
#             t1 = torch.where(same_as_sign0, t1, t_mid)
#         event_t = (t0 + t1) / 2.0
# 
#     return event_t, interp_fn(event_t)
# 
# Path: torchdiffeq/_impl/misc.py
# def _handle_unused_kwargs(solver, unused_kwargs):
#     if len(unused_kwargs) > 0:
#         warnings.warn('{}: Unexpected arguments {}'.format(solver.__class__.__name__, unused_kwargs))


# Path: torchdiffeq/_impl/solvers.py
import abc
import torch
from .event_handling import find_event
from .misc import _handle_unused_kwargs

        solution = torch.empty(len(t), *self.y0.shape, dtype=self.y0.dtype, device=self.y0.device)
        solution[0] = self.y0
        t = t.to(self.dtype)
        self._before_integrate(t)
        for i in range(1, len(t)):
            solution[i] = self._advance(t[i])
        return solution


class AdaptiveStepsizeEventODESolver(AdaptiveStepsizeODESolver, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def _advance_until_event(self, event_fn):
        raise NotImplementedError

    def integrate_until_event(self, t0, event_fn):
        t0 = t0.to(self.y0.device, self.dtype)
        self._before_integrate(t0.reshape(-1))
        event_time, y1 = self._advance_until_event(event_fn)
        solution = torch.stack([self.y0, y1], dim=0)
        return event_time, solution


class FixedGridODESolver(metaclass=abc.ABCMeta):
    order: int

    def __init__(self, func, y0, step_size=None, grid_constructor=None, interp="linear", perturb=False, **unused_kwargs):
        self.atol = unused_kwargs.pop('atol')
        unused_kwargs.pop('rtol', None)
        unused_kwargs.pop('norm', None)

        _handle_unused_kwargs(self, unused_kwargs)