code: def _logtrick_gen(bounds):
      
      # Test which parameters we can apply the log trick too
      ispos = np.array([isinstance(b, bt.Positive) for b in bounds], dtype=bool)
      nispos = ~ispos
  
      # Functions that implement the log trick
      def logx(x):
          xwarp = np.empty_like(x)
          xwarp[ispos] = np.log(x[ispos])
          xwarp[nispos] = x[nispos]
          return xwarp
  
      def expx(xwarp):
          x = np.empty_like(xwarp)
          x[ispos] = np.exp(xwarp[ispos])
          x[nispos] = xwarp[nispos]
          return x
  
      def gradx(grad, xwarp):
          gwarp = np.empty_like(grad)
          gwarp[ispos] = grad[ispos] * np.exp(xwarp[ispos])
          gwarp[nispos] = grad[nispos]
          return gwarp
  
      # Redefine bounds as appropriate for new ranges for numerical stability
      for i, (b, pos) in enumerate(zip(bounds, ispos)):
          if pos:
              upper = EXPMAX if b.upper is None else np.log(b.upper)
              bounds[i] = bt.Bound(lower=LOGMINPOS, upper=upper)
  
      return logx, expx, gradx, bounds
docs: Generate warping functions and new bounds for the log trick.