docs: def combine_weights(samples, weights, proposals):
      """Calculate the `deterministic mixture weights` according to
      [Cor+12]_ given ``samples``, standard ``weights`` and their ``proposals`` for a
      number of steps in which importance samples are computed for the same target
      density but different proposals.
  
      Return the weights as a :class:`pypmc.tools.History` such that the
      weights for each proposal are easily accessible.
  
      :param samples:
  
          Iterable of matrix-like arrays; the weighted samples whose importance
          weights shall be combined. One sample per row in each array, one array
          for each step, or different proposal.
  
      :param weights:
  
          Iterable of 1D arrays; the standard importance weights
          :math:`P(x_i^t)/q_t(x_i^t)`. Each array in the iterable contains all
          weights of the samples of step ``t``, they array's size has to match the
          ``t``-th entry in samples.
  
      :param proposals:
  
          Iterable of :py:class:`pypmc.density.base.ProbabilityDensity` instances;
          the proposal densities from which the ``samples`` have been
          drawn.
  
      """
      # shallow copy --> can safely modify (need numpy arrays --> can overwrite with np.asarray)
      samples = list(samples)
      weights = list(weights)
  
      assert len(samples) == len(weights), \
      "Got %i importance-sampling runs but %i weights" % (len(samples), len(weights))
  
      assert len(samples) == len(proposals), \
      "Got %i importance-sampling runs but %i proposal densities" % (len(samples), len(proposals))
  
      # number of samples from each proposal
      N = _np.empty(len(proposals))
      N_total = 0
  
      # basic consistency checks, conversion to numpy array and counting of the total number of samples
      for i in range(len(N)):
          samples[i] = _np.asarray(samples[i])
          assert len(samples[i].shape) == 2, '``samples[%i]`` is not matrix like.' % i
          dim = samples[0].shape[-1]
          assert samples[i].shape[-1] == dim, \
              "Dimension of samples[0] (%i) does not match the dimension of samples[%i] (%i)" \
              % (dim, i, samples[i].shape[-1])
          N[i] = len(samples[i])
          N_total += N[i]
  
          weights[i] = _np.asarray(weights[i])
          assert N[i] == len(weights[i]), \
              'Length of weights[%i] (%i) does not match length of samples[%i] (%i)' \
              % (i, N[i], i, len(weights[i]))
  
      combined_weights_history = _History(1, N_total)
  
      # if all weights positive => prefer log scale
      all_positive = True
      for w in weights:
          all_positive &= (w[:] > 0.0).all()
          if not all_positive:
              break
      if all_positive:
          combined_weights_history = _combine_weights_log(samples, weights, proposals, combined_weights_history, N_total, N)
      else:
          combined_weights_history = _combine_weights_linear(samples, weights, proposals, combined_weights_history, N_total, N)
  
      assert _np.isfinite(combined_weights_history[:][:,0]).all(), 'Encountered inf or nan mixture weights'
      return combined_weights_history