class_docstr: Linear Elliptical Slice Sampler.
  
  Ideas:
  - Add batch support, broadcasting over parallel chains.
  - Optimize computations if possible, potentially with torch.compile.
  - Extend fixed features constraint to general linear equality constraints.
class_docstr_tok: ["Linear","Elliptical","Slice","Sampler",".","Ideas",":","-","Add","batch","support",",","broadcasting","over","parallel","chains",".","-","Optimize","computations","if","possible",",","potentially","with","torch.compile",".","-","Extend","fixed","features","constraint","to","general","linear","equality","constraints","."]
docstr: Determine active indices.
  
  Args:
      nu: A `d x 1`-dim tensor (the "new" direction, drawn from N(0, I)).
      theta: A sorted `M`-dim tensor of intersection angles in [0, 2pi].
  
  Returns:
      A tuple of Tensors of active constraint intersection angles `theta_active`,
      and the change in the feasibility of the points on the ellipse on the left
      and right of the active intersection angles `delta_active`. `delta_active`
      is is negative if decreasing the angle renders the sample feasible, and
      positive if increasing the angle renders the sample feasible.
docstr_tok: ["Determine","active","indices",".","Args",":","nu",":","A","`","d","x","1","`","-dim","tensor","(","the","``","new","''","direction",",","drawn","from","N","(","0",",","I",")",")",".","theta",":","A","sorted","`","M","`","-dim","tensor","of","intersection","angles","in","[","0",",","2pi","]",".","Returns",":","A","tuple","of","Tensors","of","active","constraint","intersection","angles","`","theta_active","`",",","and","the","change","in","the","feasibility","of","the","points","on","the","ellipse","on","the","left","and","right","of","the","active","intersection","angles","`","delta_active","`",".","`","delta_active","`","is","is","negative","if","decreasing","the","angle","renders","the","sample","feasible",",","and","positive","if","increasing","the","angle","renders","the","sample","feasible","."]
code: def _active_theta_and_delta(
      self, nu: Tensor, theta: Tensor
  ) -> Tensor:
      r"""Determine active indices.
  
      Args:
          nu: A `d x 1`-dim tensor (the "new" direction, drawn from N(0, I)).
          theta: A sorted `M`-dim tensor of intersection angles in [0, 2pi].
  
      Returns:
          A tuple of Tensors of active constraint intersection angles `theta_active`,
          and the change in the feasibility of the points on the ellipse on the left
          and right of the active intersection angles `delta_active`. `delta_active`
          is is negative if decreasing the angle renders the sample feasible, and
          positive if increasing the angle renders the sample feasible.
      """
      # In order to determine if an angle that gives rise to an intersection with a
      # constraint boundary leads to a change in the feasibility of the solution,
      # we evaluate the constraints on the midpoint of the intersection angles.
      # This gets rid of the `delta_theta` parameter in the original implementation,
      # which cannot be set universally since it can be both 1) too large, when
      # the distance in adjacent intersection angles is small, and 2) too small,
      # when it approaches the numerical precision limit.
      # The implementation below solves both problems and gets rid of the parameter.
      if (
          len(theta) < 2
      ):  # if we have no or only a tangential intersection
          theta_active = torch.tensor(
              [], dtype=theta.dtype, device=theta.device
          )
          delta_active = torch.tensor(
              [], dtype=int, device=theta.device
          )
          return theta_active, delta_active
      theta_mid = (
          theta[:-1] + theta[1:]
      ) / 2  # midpoints of intersection angles
      last_mid = (theta[:1] + theta[-1:] + _twopi) / 2
      last_mid = last_mid.where(last_mid < _twopi, last_mid - _twopi)
      theta_mid = torch.cat((last_mid, theta_mid, last_mid), dim=0)
      samples_mid = self._get_cart_coords(nu=nu, theta=theta_mid)
      delta_feasibility = (
          self._is_feasible(samples_mid, transformed=True)
          .to(dtype=int)
          .diff()
      )
      active_indices = delta_feasibility.nonzero()
      return theta[active_indices], delta_feasibility[active_indices]
  
imports: from __future__ import annotations
  import math
  from typing import List, Optional, Tuple, Union
  import torch
  from botorch.utils.sampling import PolytopeSampler
  from torch import Tensor
label_desc: Use image node_id 12 for calling the LinearEllipticalSliceSampler obj's underlying member method code with example usage: obj._active_theta_and_delta(nu, theta) and returns: theta,  delta_feasibility, theta_active,  delta_active