code: def _doubly_stochastic(P, tol=1e-3):
      # Adapted from @btaba implementation
      # https://github.com/btaba/sinkhorn_knopp
      # of Sinkhorn-Knopp algorithm
      # https://projecteuclid.org/euclid.pjm/1102992505
  
      max_iter = 1000
      c = 1 / P.sum(axis=0)
      r = 1 / (P @ c)
      P_eps = P
  
      for it in range(max_iter):
          if (np.abs(P_eps.sum(axis=1) - 1) < tol).all() and (
              np.abs(P_eps.sum(axis=0) - 1) < tol
          ).all():
              # All column/row sums ~= 1 within threshold
              break
  
          c = 1 / (r @ P)
          r = 1 / (P @ c)
          P_eps = r[:, None] * P * c
  
      return P_eps
  
file_path: catboost/contrib/python/scipy/py3/scipy/optimize/_qap.py
imports: import numpy
  import operator
  from .None import linear_sum_assignment, OptimizeResult
  from .optimize import _check_unknown_options
  from scipy._lib._util import check_random_state
  import itertools
label_desc: Use image node_id 6 for calling a global function with example usage: _doubly_stochastic(P, tol) and returns: P_eps