code: def _make_linear_constraints(
      indices: Tensor,
      coefficients: Tensor,
      rhs: float,
      shapeX: torch.Size,
      eq: bool = False,
  ) -> List[ScipyConstraintDict]:
      r"""Create linear constraints to be used by `scipy.minimize`.
  
      Encodes constraints of the form
      `\sum_i (coefficients[i] * X[..., indices[i]]) ? rhs`
      where `?` can be designated either as `>=` by setting `eq=False`, or as
      `=` by setting `eq=True`.
  
      If indices is one-dimensional, the constraints are broadcasted across
      all elements of the q-batch. If indices is two-dimensional, then
      constraints are applied across elements of a q-batch. In either case,
      constraints are created for all t-batches.
  
      Args:
          indices: A tensor of shape `c` or `c x 2`, where c is the number of terms
              in the constraint. If single-dimensional, contains the indices of
              the dimensions of the feature space that occur in the linear
              constraint. If two-dimensional, contains pairs of indices of the
              q-batch (0) and the feature space (1) that occur in the linear
              constraint.
          coefficients: A single-dimensional tensor of coefficients with the same
              number of elements as `indices`.
          rhs: The right hand side of the constraint.
          shapeX: The shape of the torch tensor to construct the constraints for
              (i.e. `(b) x q x d`). Must have two or three dimensions.
          eq: If True, return an equality constraint, o/w return an inequality
              constraint (indicated by "eq" / "ineq" value of the `type` key).
  
      Returns:
          A list of constraint dictionaries with the following keys
  
          - "type": Indicates the type of the constraint ("eq" if `eq=True`, "ineq" o/w)
          - "fun": A callable evaluating the constraint value on `x`, a flattened
              version of the input tensor `X`, returning a scalar.
          - "jac": A callable evaluating the constraint's Jacobian on `x`, a flattened
              version of the input tensor `X`, returning a numpy array.
  
      >>> shapeX = torch.Size([3, 5, 4])
      >>> constraints = _make_linear_constraints(
      ...     indices=torch.tensor([1., 2.]),
      ...     coefficients=torch.tensor([-0.5, 1.3]),
      ...     rhs=0.49,
      ...     shapeX=shapeX,
      ...     eq=True
      ... )
      >>> len(constraints)
      15
      >>> constraints[0].keys()
      dict_keys(['type', 'fun', 'jac'])
      >>> x = np.arange(60).reshape(shapeX)
      >>> constraints[0]["fun"](x)
      1.61  # 1 * -0.5 + 2 * 1.3 - 0.49
      >>> constraints[0]["jac"](x)
      [0., -0.5, 1.3, 0., 0., ...]
      >>> constraints[1]["fun"](x)  #
      4.81
      """
  
      shapeX = _validate_linear_constraints_shape_input(shapeX)
  
      b, q, d = shapeX
      _validate_linear_constraints_indices_input(indices, q, d)
      n = shapeX.numel()
      constraints: List[ScipyConstraintDict] = []
      coeffs = _arrayify(coefficients)
      ctype = "eq" if eq else "ineq"
  
      offsets = [q * d, d]
      if indices.dim() == 2:
          # indices has two dimensions (potential constraints across q-batch elements)
          # rule is [i, j, k] is at
          # i * offsets[0] + j * offsets[1] + k
          for i in range(b):
              list_ind = (idx.tolist() for idx in indices)
              idxr = [
                  i * offsets[0] + idx[0] * offsets[1] + idx[1]
                  for idx in list_ind
              ]
              fun = partial(
                  eval_lin_constraint,
                  flat_idxr=idxr,
                  coeffs=coeffs,
                  rhs=float(rhs),
              )
              jac = partial(
                  lin_constraint_jac, flat_idxr=idxr, coeffs=coeffs, n=n
              )
              constraints.append(
                  {"type": ctype, "fun": fun, "jac": jac}
              )
      elif indices.dim() == 1:
          # indices is one-dim - broadcast constraints across q-batches and t-batches
          for i in range(b):
              for j in range(q):
                  idxr = (
                      i * offsets[0] + j * offsets[1] + indices
                  ).tolist()
                  fun = partial(
                      eval_lin_constraint,
                      flat_idxr=idxr,
                      coeffs=coeffs,
                      rhs=float(rhs),
                  )
                  jac = partial(
                      lin_constraint_jac,
                      flat_idxr=idxr,
                      coeffs=coeffs,
                      n=n,
                  )
                  constraints.append(
                      {"type": ctype, "fun": fun, "jac": jac}
                  )
      return constraints
  
imports: from __future__ import annotations
  from functools import partial
  from typing import Callable, Dict, List, Optional, Tuple, Union
  import numpy
  import torch
  from botorch.exceptions.errors import CandidateGenerationError, UnsupportedError
  from scipy.optimize import Bounds
  from torch import Tensor
label_desc: Use image node_id 8 for calling a global function with example usage: _make_linear_constraints(indices, coefficients, rhs, shapeX, eq) and returns: constraints