def pinv(a, driver:"svd", rcond:nil)
    a = NArray.asarray(a)
    if a.ndim < 2
      raise NArray::ShapeError, "2-d array is required"
    end
    case driver
    when /^(ge)?s[dv]d$/
      s, u, vh = svd(a, driver:driver, job:'S')
      if rcond.nil? || rcond < 0
        rcond = ((SFloat===s) ? 1e3 : 1e6) * s.class::EPSILON
      elsif ! Numeric === rcond
        raise ArgumentError, "rcond must be Numeric"
      end
      cond = (s > rcond * s.max(axis:-1, keepdims:true))
      if cond.all?
        r = s.reciprocal
      else
        r = s.new_zeros
        r[cond] = s[cond].reciprocal
      end
      u *= r[false,:new,true]
      dot(u,vh).conj.swapaxes(-2,-1)
    when /^(ge)?ls[dsy]$/
      b = a.class.eye(a.shape[-2])
      x, = lstsq(a, b, driver:driver, rcond:rcond)
      x
    else
      raise ArgumentError, "#{driver.inspect} is not one of drivers: "+
        "svd, sdd, lsd, lss, lsy"
    end
  end
def pinv(a, driver:"svd", rcond:nil)
    a = NArray.asarray(a)
    if a.ndim < 2
      raise NArray::ShapeError, "2-d array is required"
    end
    case driver
    when /^(ge)?s[dv]d$/
      s, u, vh = svd(a, driver:driver, job:'S')
      if rcond.nil? || rcond < 0
        rcond = ((SFloat===s) ? 1e3 : 1e6) * s.class::EPSILON
      elsif ! Numeric === rcond
        raise ArgumentError, "rcond must be Numeric"
      end
      cond = (s > rcond * s.max(axis:-1, keepdims:true))
      if cond.all?
        r = s.reciprocal
      else
        r = s.new_zeros
        r[cond] = s[cond].reciprocal
      end
      u *= r[false,:new,true]
      dot(u,vh).conj.swapaxes(-2,-1)
    when /^(ge)?ls[dsy]$/
      b = a.class.eye(a.shape[-2])
      x, = lstsq(a, b, driver:driver, rcond:rcond)
      x
    else
      raise ArgumentError, "#{driver.inspect} is not one of drivers: "+
        "svd, sdd, lsd, lss, lsy"
    end
  end
Compute the (Moore-Penrose) pseudo-inverse of a matrix
 using svd or lstsq.

 @param a [Numo::NArray] m-by-n matrix A (>= 2-dimensinal NArray)
 @param driver [String or Symbol] choose LAPACK driver from
   SVD ('svd', 'sdd') or Least square ('lsd','lss','lsy')
   (optional, default='svd')
 @param rcond [Float] (optional, default=-1)
   RCOND is used to determine the effective rank of A.
   Singular values `S(i) <= RCOND*S(1)` are treated as zero.
   If RCOND < 0, machine precision is used instead.
 @return [Numo::NArray]
 @example
   a = Numo::DFloat.new(5,3).rand_norm
   => Numo::DFloat#shape=[5,3]
   [[-0.581255, -0.168354, 0.586895],
    [-0.595142, -0.802802, -0.326106],
    [0.282922, 1.68427, 0.918499],
    [-0.0485384, -0.464453, -0.992194],
    [0.413794, -0.60717, -0.699695]]
   b = Numo::Linalg.pinv(a,driver:"svd")
   => Numo::DFloat(view)#shape=[3,5]
   [[-0.360863, -0.813125, -0.353367, -0.891963, 0.877253],
    [-0.227645, 0.162939, 0.696655, 0.787685, -0.469346],
    [0.408671, -0.308323, -0.337807, -1.13833, 0.228051]]
   (a-a.dot(b.dot(a))).abs.max
   => 5.551115123125783e-16