class_docstr: Brownian interval with fixed entropy.
  
  Computes increments (and optionally Levy area).
  
  To use:
  >>> bm = BrownianInterval(t0=0.0, t1=1.0, size=(4, 1), device='cuda')
  >>> bm(0., 0.5)
  tensor([[ 0.0733],
          [-0.5692],
          [ 0.1872],
          [-0.3889]], device='cuda:0')
class_docstr_tok: ["Brownian","interval","with","fixed","entropy",".","Computes","increments","(","and","optionally","Levy","area",")",".","To","use",":",">",">",">","bm","=","BrownianInterval","(","t0=0.0",",","t1=1.0",",","size=","(","4",",","1",")",",","device='cuda","'",")",">",">",">","bm","(","0.",",","0.5",")","tensor","(","[","[","0.0733","]",",","[","-0.5692","]",",","[","0.1872","]",",","[","-0.3889","]","]",",","device='cuda:0","'",")"]
docstr: Initialize the Brownian interval.
  
  Args:
      t0 (float or Tensor): Initial time.
      t1 (float or Tensor): Terminal time.
      size (tuple of int): The shape of each Brownian sample.
          If zero dimensional represents a scalar Brownian motion.
          If one dimensional represents a batch of scalar Brownian motions.
          If >two dimensional the last dimension represents the size of a
          a multidimensional Brownian motion, and all previous dimensions
          represent batch dimensions.
      dtype (torch.dtype): The dtype of each Brownian sample.
          Defaults to the PyTorch default.
      device (str or torch.device): The device of each Brownian sample.
          Defaults to the CPU.
      entropy (int): Global seed, defaults to `None` for random entropy.
      levy_area_approximation (str): Whether to also approximate Levy
          area. Defaults to 'none'. Valid options are 'none',
          'space-time', 'davie' or 'foster', corresponding to different
          approximation types.
          This is needed for some higher-order SDE solvers.
      dt (float or Tensor): The expected average step size of the SDE
          solver. Set it if you know it (e.g. when using a fixed-step
          solver); else it will be estimated from the first few queries.
          This is used to set up the data structure such that it is
          efficient to query at these intervals.
      tol (float or Tensor): What tolerance to resolve the Brownian motion
          to. Must be non-negative. Defaults to zero, i.e. floating point
          resolution. Usually worth setting in conjunction with
          `halfway_tree`, below.
      pool_size (int): Size of the pooled entropy. If you care about
          statistical randomness then increasing this will help (but will
          slow things down).
      cache_size (int): How big a cache of recent calculations to use.
          (As new calculations depend on old calculations, this speeds
          things up dramatically, rather than recomputing things.)
          Set this to `None` to use an infinite cache, which will be fast
          but memory inefficient.
      halfway_tree (bool): Whether the dependency tree (the internal data
          structure) should be the dyadic tree. Defaults to `False`.
          Normally, the sample path is determined by both `entropy`,
          _and_ the locations and order of the query points. Setting this
           to `True` will make it deterministic with respect to just
           `entropy`; however this is much slower.
      W (Tensor): The increment of the Brownian motion over the interval
          [t0, t1]. Will be generated randomly if not provided.
      H (Tensor): The space-time Levy area of the Brownian motion over the
          interval [t0, t1]. Will be generated randomly if not provided.
code: def __init__(
      self,
      t0: Optional[Scalar] = 0.0,
      t1: Optional[Scalar] = 1.0,
      size: Optional[Tuple[int, ...]] = None,
      dtype: Optional[torch.dtype] = None,
      device: Optional[Union[str, torch.device]] = None,
      entropy: Optional[int] = None,
      dt: Optional[Scalar] = None,
      tol: Scalar = 0.0,
      pool_size: int = 8,
      cache_size: Optional[int] = 45,
      halfway_tree: bool = False,
      levy_area_approximation: str = LEVY_AREA_APPROXIMATIONS.none,
      W: Optional[Tensor] = None,
      H: Optional[Tensor] = None,
  ):
      """Initialize the Brownian interval.
  
      Args:
          t0 (float or Tensor): Initial time.
          t1 (float or Tensor): Terminal time.
          size (tuple of int): The shape of each Brownian sample.
              If zero dimensional represents a scalar Brownian motion.
              If one dimensional represents a batch of scalar Brownian motions.
              If >two dimensional the last dimension represents the size of a
              a multidimensional Brownian motion, and all previous dimensions
              represent batch dimensions.
          dtype (torch.dtype): The dtype of each Brownian sample.
              Defaults to the PyTorch default.
          device (str or torch.device): The device of each Brownian sample.
              Defaults to the CPU.
          entropy (int): Global seed, defaults to `None` for random entropy.
          levy_area_approximation (str): Whether to also approximate Levy
              area. Defaults to 'none'. Valid options are 'none',
              'space-time', 'davie' or 'foster', corresponding to different
              approximation types.
              This is needed for some higher-order SDE solvers.
          dt (float or Tensor): The expected average step size of the SDE
              solver. Set it if you know it (e.g. when using a fixed-step
              solver); else it will be estimated from the first few queries.
              This is used to set up the data structure such that it is
              efficient to query at these intervals.
          tol (float or Tensor): What tolerance to resolve the Brownian motion
              to. Must be non-negative. Defaults to zero, i.e. floating point
              resolution. Usually worth setting in conjunction with
              `halfway_tree`, below.
          pool_size (int): Size of the pooled entropy. If you care about
              statistical randomness then increasing this will help (but will
              slow things down).
          cache_size (int): How big a cache of recent calculations to use.
              (As new calculations depend on old calculations, this speeds
              things up dramatically, rather than recomputing things.)
              Set this to `None` to use an infinite cache, which will be fast
              but memory inefficient.
          halfway_tree (bool): Whether the dependency tree (the internal data
              structure) should be the dyadic tree. Defaults to `False`.
              Normally, the sample path is determined by both `entropy`,
              _and_ the locations and order of the query points. Setting this
               to `True` will make it deterministic with respect to just
               `entropy`; however this is much slower.
          W (Tensor): The increment of the Brownian motion over the interval
              [t0, t1]. Will be generated randomly if not provided.
          H (Tensor): The space-time Levy area of the Brownian motion over the
              interval [t0, t1]. Will be generated randomly if not provided.
      """
  
      #####################################
      #    Check and normalise inputs     #
      #####################################
  
      if not _is_scalar(t0):
          raise ValueError(
              "Initial time t0 should be a float or 0-d torch.Tensor."
          )
      if not _is_scalar(t1):
          raise ValueError(
              "Terminal time t1 should be a float or 0-d torch.Tensor."
          )
      if dt is not None and not _is_scalar(dt):
          raise ValueError(
              "Expected average time step dt should be a float or 0-d torch.Tensor."
          )
  
      if t0 > t1:
          raise ValueError(
              f"Initial time {t0} should be less than terminal time {t1}."
          )
      t0 = float(t0)
      t1 = float(t1)
      if dt is not None:
          dt = float(dt)
  
      if halfway_tree:
          if tol <= 0.0:
              raise ValueError("`tol` should be positive.")
          if dt is not None:
              raise ValueError(
                  "`dt` is not used and should be set to `None` if `halfway_tree` is True."
              )
      else:
          if tol < 0.0:
              raise ValueError("`tol` should be non-negative.")
  
      size, dtype, device = _check_tensor_info(
          W, H, size=size, dtype=dtype, device=device
      )
  
      # Let numpy dictate randomness, so we have fewer seeds to set for reproducibility.
      if entropy is None:
          entropy = np.random.randint(0, 2**31 - 1)
  
      if levy_area_approximation not in LEVY_AREA_APPROXIMATIONS:
          raise ValueError(
              f"`levy_area_approximation` must be one of {LEVY_AREA_APPROXIMATIONS}, but got "
              f"'{levy_area_approximation}'."
          )
  
      #####################################
      #          Record inputs            #
      #####################################
  
      self._size = size
      self._dtype = dtype
      self._device = device
      self._entropy = entropy
      self._levy_area_approximation = levy_area_approximation
      self._dt = dt
      self._tol = tol
      self._pool_size = pool_size
      self._cache_size = cache_size
      self._halfway_tree = halfway_tree
  
      #####################################
      #   A miscellany of other things    #
      #####################################
  
      # We keep a cache of recent queries, and their results. This is very important for speed, so that we don't
      # recurse all the way up to the top every time we have a query.
      if cache_size is None:
          self._increment_and_space_time_levy_area_cache = {}
      elif cache_size == 0:
          self._increment_and_space_time_levy_area_cache = _EmptyDict()
      else:
          self._increment_and_space_time_levy_area_cache = _LRUDict(
              max_size=cache_size
          )
  
      # We keep track of the most recently queried interval, and start searching for the next interval from that
      # element of the binary tree. This is because subsequent queries are likely to be near the most recent query.
      self._last_interval = self
  
      # Precompute these as we don't want to spend lots of time checking strings in hot loops.
      self._have_H = self._levy_area_approximation in (
          LEVY_AREA_APPROXIMATIONS.space_time,
          LEVY_AREA_APPROXIMATIONS.davie,
          LEVY_AREA_APPROXIMATIONS.foster,
      )
      self._have_A = self._levy_area_approximation in (
          LEVY_AREA_APPROXIMATIONS.davie,
          LEVY_AREA_APPROXIMATIONS.foster,
      )
  
      # If we like we can quantise what level we want to compute the Brownian motion to.
      if tol == 0.0:
          self._round = lambda x: x
      else:
          ndigits = -int(math.log10(tol))
          self._round = lambda x: round(x, ndigits)
  
      # Initalise as _Interval.
      # (Must come after _round but before _w_h)
      super(BrownianInterval, self).__init__(
          start=t0, end=t1, parent=None, is_left=None, top=self
      )
  
      # Set the global increment and space-time Levy area
      generator = np.random.SeedSequence(
          entropy=entropy, pool_size=pool_size
      )
      (
          initial_W_seed,
          initial_H_seed,
          top_a_seed,
      ) = generator.generate_state(3)
      if W is None:
          W = self._randn(initial_W_seed) * math.sqrt(t1 - t0)
      else:
          _assert_floating_tensor("W", W)
      if H is None:
          H = self._randn(initial_H_seed) * math.sqrt((t1 - t0) / 12)
      else:
          _assert_floating_tensor("H", H)
      self._w_h = (W, H)
      self._top_a_seed = top_a_seed
  
      if not self._halfway_tree:
          # We create a binary tree dependency between the points. If we don't do this then the forward pass is still
          # efficient at O(N), but we end up with a dependency chain stretching along the interval [t0, t1], making
          # the backward pass O(N^2). By setting up a dependency tree of depth relative to `dt` and `cache_size` we
          # can instead make both directions O(N log N).
          self._average_dt = 0
          self._tree_dt = t1 - t0
          self._num_evaluations = (
              -100
          )  # start off with a warmup period to get a decent estimate of the average
          if dt is not None:
              # Create the dependency tree based on the supplied hint `dt`.
              self._create_dependency_tree(dt)
  
imports: import math
  import trampoline
  import warnings
  import numpy
  import torch
  from .None import brownian_base
  from ..settings import LEVY_AREA_APPROXIMATIONS
  from ..types import Scalar, Optional, Tuple, Union, Tensor
label_desc: Use image node_id 1 to create a new BrownianInterval object from inherited base classes: brownian_base, _Interval with example: obj = BrownianInterval(t0, t1, size, dtype, device, entropy, dt, tol, pool_size, cache_size, halfway_tree, levy_area_approximation, W, H)