function generateDataForTraining(trainSplit = 0.25, valSplit = 0.15) {
  tf.util.assert(
      trainSplit > 0 && valSplit > 0 && trainSplit + valSplit <= 1,
      `Invalid trainSplit (${trainSplit}) and valSplit (${valSplit})`);

  const dateTuples = [];
  const MIN_YEAR = 1950;
  const MAX_YEAR = 2050;
  for (let date = new Date(MIN_YEAR,0,1);
       date.getFullYear() < MAX_YEAR;
       date.setDate(date.getDate() + 1)) {
    dateTuples.push([date.getFullYear(), date.getMonth() + 1, date.getDate()]);
  }
  tf.util.shuffle(dateTuples);

  const numTrain = Math.floor(dateTuples.length * trainSplit);
  const numVal = Math.floor(dateTuples.length * valSplit);
  console.log(`Number of dates used for training: ${numTrain}`);
  console.log(`Number of dates used for validation: ${numVal}`);
  console.log(
      `Number of dates used for testing: ` +
      `${dateTuples.length - numTrain - numVal}`);

  function dateTuplesToTensor(dateTuples) {
    return tf.tidy(() => {
      const inputs =
          dateFormat.INPUT_FNS.map(fn => dateTuples.map(tuple => fn(tuple)));
      const inputStrings = [];
      inputs.forEach(inputs => inputStrings.push(...inputs));
      const encoderInput =
          dateFormat.encodeInputDateStrings(inputStrings);
      const trainTargetStrings = dateTuples.map(
          tuple => dateFormat.dateTupleToYYYYDashMMDashDD(tuple));
      let decoderInput =
          dateFormat.encodeOutputDateStrings(trainTargetStrings)
          .asType('float32');
      // One-step time shift: The decoder input is shifted to the left by
      // one time step with respect to the encoder input. This accounts for
      // the step-by-step decoding that happens during inference time.
      decoderInput = tf.concat([
        tf.ones([decoderInput.shape[0], 1]).mul(dateFormat.START_CODE),
        decoderInput.slice(
            [0, 0], [decoderInput.shape[0], decoderInput.shape[1] - 1])
      ], 1).tile([dateFormat.INPUT_FNS.length, 1]);
      const decoderOutput = tf.oneHot(
          dateFormat.encodeOutputDateStrings(trainTargetStrings),
          dateFormat.OUTPUT_VOCAB.length).tile(
              [dateFormat.INPUT_FNS.length, 1, 1]);
      return {encoderInput, decoderInput, decoderOutput};
    });
  }

  const {
    encoderInput: trainEncoderInput,
    decoderInput: trainDecoderInput,
    decoderOutput: trainDecoderOutput
  } = dateTuplesToTensor(dateTuples.slice(0, numTrain));
  const {
    encoderInput: valEncoderInput,
    decoderInput: valDecoderInput,
    decoderOutput: valDecoderOutput
  } = dateTuplesToTensor(dateTuples.slice(numTrain, numTrain + numVal));
  const testDateTuples =
      dateTuples.slice(numTrain + numVal, dateTuples.length);
  return {
    trainEncoderInput,
    trainDecoderInput,
    trainDecoderOutput,
    valEncoderInput,
    valDecoderInput,
    valDecoderOutput,
    testDateTuples
  };
}
Generate sets of data for training.

@param {number} trainSplit Trainining split. Must be >0 and <1.
@param {number} valSplit Validatoin split. Must be >0 and <1.
@return An `Object` consisting of
  - trainEncoderInput, as a `tf.Tensor` of shape
    `[numTrainExapmles, inputLength]`
  - trainDecoderInput, as a `tf.Tensor` of shape
    `[numTrainExapmles, outputLength]`. The first element of every
    example has been set as START_CODE (the sequence-start symbol).
  - trainDecoderOuptut, as a one-hot encoded `tf.Tensor` of shape
    `[numTrainExamples, outputLength, outputVocabSize]`.
  - valEncoderInput, same as trainEncoderInput, but for the validation set.
  - valDecoderInput, same as trainDecoderInput, but for the validation set.
  - valDecoderOutput, same as trainDecoderOuptut, but for the validation
    set.
  - testDateTuples, date tuples ([year, month, day]) for the test set.