class_docstr: Geometric Brownian Motion.
  
  Represents the 1-dimensional Ito process:
  
  ```None
    dX(t) = means(t) * X(t) * dt + volatilities(t) * X(t) * dW(t),
  ```
  
  where `W(t)` is a 1D Brownian motion, `mean(t)` and `volatility(t)` are either
  constant `Tensor`s or piecewise constant functions of time.
  
  Supports batching which enables modelling multiple univariate geometric
  brownian motions (GBMs) efficiently. No guarantee is made about the
  relationships between the batched univariate GMBs. To control the correlation
  between multiple GBMs use `MultivariateGeometricBrownianMotion`.
  
  ## Example
  
  ```python
  import tensorflow as tf
  import tf_quant_finance as tff
  process = tff.models.GeometricBrownianMotion(0.05, 1.0, dtype=tf.float64)
  times = [0.1, 0.2, 1.0]
  # Use SOBOL sequence to draw trajectories
  samples_sobol = process.sample_paths(
      times=times,
      initial_state=1.5,
      random_type=tff.math.random.RandomType.SOBOL,
      num_samples=100000)
  
  # You can also supply the random normal draws directly to the sampler
  normal_draws = tf.random.stateless_normal(
      [100000, 3, 1], seed=[4, 2], dtype=tf.float64)
  samples_custom = process.sample_paths(
      times=times,
      initial_state=1.5,
      normal_draws=normal_draws)
  ```
docstr: Returns a sample of paths from the process.
docstr_tok: ["Returns","a","sample","of","paths","from","the","process","."]
code: def _sample_paths(
      self,
      times,
      num_requested_times,
      initial_state,
      num_samples,
      random_type,
      seed,
      skip,
      normal_draws,
  ):
      """Returns a sample of paths from the process."""
      if normal_draws is None:
          # Normal draws needed for sampling
          normal_draws = utils.generate_mc_normal_draws(
              num_normal_draws=1,
              num_time_steps=num_requested_times,
              num_sample_paths=num_samples,
              random_type=random_type,
              seed=seed,
              dtype=self._dtype,
              skip=skip,
          )
      else:
          # Shape [num_time_points, num_samples, dim]
          normal_draws = tf.transpose(normal_draws, [1, 0, 2])
          num_samples = tf.shape(normal_draws)[1]
          draws_dim = normal_draws.shape[2]
          if draws_dim != 1:
              raise ValueError(
                  "`dim` should be equal to `1` but is {0}".format(
                      draws_dim
                  )
              )
      # Create a set of zeros that is the right shape to add a '0' as the first
      # element for each series of times.
      zeros = tf.zeros(
          tf.concat([times.shape[:-1], [1]], 0), dtype=self._dtype
      )
      times = tf.concat([zeros, times], -1)
      mean_integral = self._integrate_parameter(
          self._mean,
          self._mean_is_constant,
          times[..., :-1],
          times[..., 1:],
      )
      # mean_integral has shape [batch_shape, k-1], where self._mean has shape
      # [batch_shape, 1] and times has shape [k].
      mean_integral = tf.expand_dims(mean_integral, -2)
      volatility_sq_integral = self._integrate_parameter(
          self._volatility_squared,
          self._volatility_is_constant,
          times[..., :-1],
          times[..., 1:],
      )
      volatility_sq_integral = tf.expand_dims(
          volatility_sq_integral, -2
      )
      # Giving mean_integral and volatility_sq_integral
      # shape = `batch_shape + [1, k-1]`,
      # where self._mean has shape `batch_shape + [1]` and times has shape `[k]`.
  
      # The logarithm of all the increments between the times.
      log_increments = (
          (mean_integral - volatility_sq_integral / 2)
          # Ensure tf.math.sqrt is differentiable at 0.0
          + _sqrt_no_nan(volatility_sq_integral)
          * tf.transpose(tf.squeeze(normal_draws, -1))
      )
      # Since the implementation of tf.math.cumsum is single-threaded we
      # use lower-triangular matrix multiplication instead
      once = tf.ones(
          [num_requested_times, num_requested_times], dtype=self._dtype
      )
      lower_triangular = tf.linalg.band_part(once, -1, 0)
      cumsum = tf.linalg.matvec(lower_triangular, log_increments)
  
      samples = tf.expand_dims(initial_state, [-1]) * tf.math.exp(
          cumsum
      )
      return tf.expand_dims(samples, -1)
  
imports: import tensorflow.compat.v2
  from tf_quant_finance import utils
  from tf_quant_finance.math import piecewise
  from tf_quant_finance.math.pde import fd_solvers
  from tf_quant_finance.models import ito_process
  from tf_quant_finance.models import utils
label_desc: Use image node_id 11 for calling the GeometricBrownianMotion obj's underlying member method code with example usage: obj._sample_paths(times, num_requested_times, initial_state, num_samples, random_type, seed, skip, normal_draws) and returns: tf