function scaleAndAverageGradients(allGradients, normalizedRewards) {
  return tf.tidy(() => {
    const gradients = {};
    for (const varName in allGradients) {
      gradients[varName] = tf.tidy(() => {
        // Stack gradients together.
        const varGradients = allGradients[varName].map(
            varGameGradients => tf.stack(varGameGradients));
        // Expand dimensions of reward tensors to prepare for multiplication
        // with broadcasting.
        const expandedDims = [];
        for (let i = 0; i < varGradients[0].rank - 1; ++i) {
          expandedDims.push(1);
        }
        const reshapedNormalizedRewards = normalizedRewards.map(
            rs => rs.reshape(rs.shape.concat(expandedDims)));
        for (let g = 0; g < varGradients.length; ++g) {
          // This mul() call uses broadcasting.
          varGradients[g] = varGradients[g].mul(reshapedNormalizedRewards[g]);
        }
        // Concatenate the scaled gradients together, then average them across
        // all the steps of all the games.
        return tf.mean(tf.concat(varGradients, 0), 0);
      });
    }
    return gradients;
  });
}
Scale the gradient values using normalized reward values and compute average.

The gradient values are scaled by the normalized reward values. Then they
are averaged across all games and all steps.

@param {{[varName: string]: tf.Tensor[][]}} allGradients A map from variable
  name to all the gradient values for the variable across all games and all
  steps.
@param {tf.Tensor[]} normalizedRewards An Array of normalized reward values
  for all the games. Each element of the Array is a 1D tf.Tensor of which
  the length equals the number of steps in the game.
@returns {{[varName: string]: tf.Tensor}} Scaled and averaged gradients
  for the variables.