code: def _make_nonlinear_constraints(
      f_np_wrapper: Callable,
      nlc: Callable,
      is_intrapoint: bool,
      shapeX: torch.Size,
  ) -> List[ScipyConstraintDict]:
      """Create nonlinear constraints to be used by `scipy.minimize`.
  
      Args:
          f_np_wrapper: A wrapper function that given a constraint evaluates
              the value and gradient (using autograd) of a numpy input and returns both
              the objective and the gradient.
          nlc: Callable representing a constraint of the form `callable(x) >= 0`. In case
              of an intra-point constraint, `callable()`takes in an one-dimensional tensor
              of shape `d` and returns a scalar. In case of an inter-point constraint,
              `callable()` takes a two dimensional tensor of shape `q x d` and again
              returns a scalar.
          is_intrapoint: A Boolean indicating if a constraint is an intra-point or
              inter-point constraint (see the docstring of the `inequality_constraints`
              argument to `optimize_acqf()`).
          shapeX: Shape of the three-dimensional batch X, that should be optimized.
  
      Returns:
          A list of constraint dictionaries with the following keys
  
          - "type": Indicates the type of the constraint, here always "ineq".
          - "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 = _validate_linear_constraints_shape_input(shapeX)
      b, q, _ = shapeX
      constraints = []
  
      def get_intrapoint_constraint(
          b: int, q: int, nlc: Callable
      ) -> Callable:
          return lambda x: nlc(x[b, q])
  
      def get_interpoint_constraint(b: int, nlc: Callable) -> Callable:
          return lambda x: nlc(x[b])
  
      if is_intrapoint:
          for i in range(b):
              for j in range(q):
                  (
                      f_obj,
                      f_grad,
                  ) = _make_f_and_grad_nonlinear_inequality_constraints(
                      f_np_wrapper=f_np_wrapper,
                      nlc=get_intrapoint_constraint(b=i, q=j, nlc=nlc),
                  )
                  constraints.append(
                      {"type": "ineq", "fun": f_obj, "jac": f_grad}
                  )
      else:
          for i in range(b):
              (
                  f_obj,
                  f_grad,
              ) = _make_f_and_grad_nonlinear_inequality_constraints(
                  f_np_wrapper=f_np_wrapper,
                  nlc=get_interpoint_constraint(b=i, nlc=nlc),
              )
              constraints.append(
                  {"type": "ineq", "fun": f_obj, "jac": f_grad}
              )
  
      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 9 for calling a global function with example usage: _make_nonlinear_constraints(f_np_wrapper, nlc, is_intrapoint, shapeX) and returns: constraints, function, function