class_docstr: Tensor product spline object.
  
  The value at point ``xp = (x1, x2, ..., xN)`` is evaluated as a linear
  combination of products of one-dimensional b-splines in each of the ``N``
  dimensions::
  
     c[i1, i2, ..., iN] * B(x1; i1, t1) * B(x2; i2, t2) * ... * B(xN; iN, tN)
  
  
  Here ``B(x; i, t)`` is the ``i``-th b-spline defined by the knot vector
  ``t`` evaluated at ``x``.
  
  Parameters
  ----------
  t : tuple of 1D ndarrays
      knot vectors in directions 1, 2, ... N,
      ``len(t[i]) == n[i] + k + 1``
  c : ndarray, shape (n1, n2, ..., nN, ...)
      b-spline coefficients
  k : int or length-d tuple of integers
      spline degrees.
      A single integer is interpreted as having this degree for
      all dimensions.
  extrapolate : bool, optional
      Whether to extrapolate out-of-bounds inputs, or return `nan`.
      Default is to extrapolate.
  
  Attributes
  ----------
  t : tuple of ndarrays
      Knots vectors.
  c : ndarray
      Coefficients of the tensor-produce spline.
  k : tuple of integers
      Degrees for each dimension.
  extrapolate : bool, optional
      Whether to extrapolate or return nans for out-of-bounds inputs.
      Defaults to true.
  
  Methods
  -------
  __call__
  
  See Also
  --------
  BSpline : a one-dimensional B-spline object
  NdPPoly : an N-dimensional piecewise tensor product polynomial
code: def __init__(self, t, c, k, *, extrapolate=None):
      ndim = len(t)
  
      try:
          len(k)
      except TypeError:
          # make k a tuple
          k = (k,) * ndim
  
      if len(k) != ndim:
          raise ValueError(f"{len(t) = } != {len(k) = }.")
  
      self.k = tuple(operator.index(ki) for ki in k)
      self.t = tuple(np.ascontiguousarray(ti, dtype=float) for ti in t)
      self.c = np.asarray(c)
  
      if extrapolate is None:
          extrapolate = True
      self.extrapolate = bool(extrapolate)
  
      self.c = np.asarray(c)
  
      for d in range(ndim):
          td = self.t[d]
          kd = self.k[d]
          n = td.shape[0] - kd - 1
          if kd < 0:
              raise ValueError(
                  f"Spline degree in dimension {d} cannot be"
                  f" negative."
              )
          if td.ndim != 1:
              raise ValueError(
                  f"Knot vector in dimension {d} must be"
                  f" one-dimensional."
              )
          if n < kd + 1:
              raise ValueError(
                  f"Need at least {2*kd + 2} knots for degree"
                  f" {kd} in dimension {d}."
              )
          if (np.diff(td) < 0).any():
              raise ValueError(
                  f"Knots in dimension {d} must be in a"
                  f" non-decreasing order."
              )
          if len(np.unique(td[kd : n + 1])) < 2:
              raise ValueError(
                  f"Need at least two internal knots in"
                  f" dimension {d}."
              )
          if not np.isfinite(td).all():
              raise ValueError(
                  f"Knots in dimension {d} should not have"
                  f" nans or infs."
              )
          if self.c.ndim < ndim:
              raise ValueError(
                  f"Coefficients must be at least" f" {d}-dimensional."
              )
          if self.c.shape[d] != n:
              raise ValueError(
                  f"Knots, coefficients and degree in dimension"
                  f" {d} are inconsistent:"
                  f" got {self.c.shape[d]} coefficients for"
                  f" {len(td)} knots, need at least {n} for"
                  f" k={k}."
              )
  
      dt = _get_dtype(self.c.dtype)
      self.c = np.ascontiguousarray(self.c, dtype=dt)
  
file_path: scipy/scipy/interpolate/_ndbspline.py
imports: import operator
  import numpy
  from math import prod
  from .None import _bspl
label_desc: Use image node_id 1 to create a new NdBSpline object  with example: obj = NdBSpline(t, c, k)