using System.Collections.Generic;
using System;
using System.Linq;
using MLAgents.InferenceBrain.Utils;
namespace MLAgents.InferenceBrain
{
///
/// Reshapes a Tensor so that its first dimension becomes equal to the current batch size
/// and initializes its content to be zeros. Will only work on 2-dimensional tensors.
/// The second dimension of the Tensor will not be modified.
///
public class BiDimensionalOutputGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
var shapeSecondAxis = tensor.Shape[tensor.Shape.Length - 1];
tensor.Shape[0] = batchSize;
if (tensor.ValueType == Tensor.TensorType.FloatingPoint)
{
tensor.Data = new float[batchSize, shapeSecondAxis];
}
else
{
tensor.Data = new int[batchSize, shapeSecondAxis];
}
}
}
///
/// Generates the Tensor corresponding to the BatchSize input : Will be a one dimensional
/// integer array of size 1 containing the batch size.
///
public class BatchSizeGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Data = new int[] {batchSize};
}
}
///
/// Generates the Tensor corresponding to the SequenceLength input : Will be a one
/// dimensional integer array of size 1 containing 1.
/// Note : the sequence length is always one since recurrent networks only predict for
/// one step at the time.
///
public class SequenceLengthGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape = new long[0];
tensor.Data = new int[] {1};
}
}
///
/// Generates the Tensor corresponding to the VectorObservation input : Will be a two
/// dimensional float array of dimension [batchSize x vectorObservationSize].
/// It will use the Vector Observation data contained in the agentInfo to fill the data
/// of the tensor.
///
public class VectorObservationGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var vecObsSizeT = tensor.Shape[tensor.Shape.Length - 1];
var floatArray = new float[batchSize, vecObsSizeT];
tensor.Data = floatArray;
var agentIndex = 0;
foreach (var agent in agentInfo.Keys)
{
var vectorObs = agentInfo[agent].stackedVectorObservation;
for (var j = 0; j < vecObsSizeT; j++)
{
floatArray[agentIndex, j] = vectorObs[j];
}
agentIndex++;
}
}
}
///
/// Generates the Tensor corresponding to the Recurrent input : Will be a two
/// dimensional float array of dimension [batchSize x memorySize].
/// It will use the Memory data contained in the agentInfo to fill the data
/// of the tensor.
///
public class RecurrentInputGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var memorySize = tensor.Shape[tensor.Shape.Length - 1];
var floatArray = new float[batchSize, memorySize];
tensor.Data = floatArray;
var agentIndex = 0;
foreach (var agent in agentInfo.Keys)
{
var memory = agentInfo[agent].memories;
if (memory == null)
{
agentIndex++;
continue;
}
for (var j = 0; j < Math.Min(memorySize, memory.Count); j++)
{
if (j >= memory.Count)
{
break;
}
floatArray[agentIndex, j] = memory[j];
}
agentIndex++;
}
}
}
public class BarracudaRecurrentInputGenerator : TensorGenerator.Generator
{
private bool firstHalf = true;
public BarracudaRecurrentInputGenerator(bool firstHalf)
{
this.firstHalf = firstHalf;
}
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var memorySize = tensor.Shape[tensor.Shape.Length - 1];
tensor.Data = new float[batchSize, memorySize];
var agentIndex = 0;
foreach (var agent in agentInfo.Keys)
{
var memory = agentInfo[agent].memories;
int offset = 0;
if (!firstHalf)
{
offset = memory.Count - (int)memorySize;
}
if (memory == null)
{
agentIndex++;
continue;
}
for (var j = 0; j < memorySize; j++)
{
if (j >= memory.Count)
{
break;
}
tensor.Data.SetValue(memory[j + offset], new int[2] {agentIndex, j});
}
agentIndex++;
}
}
}
///
/// Generates the Tensor corresponding to the Previous Action input : Will be a two
/// dimensional integer array of dimension [batchSize x actionSize].
/// It will use the previous action data contained in the agentInfo to fill the data
/// of the tensor.
///
public class PreviousActionInputGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var actionSize = tensor.Shape[tensor.Shape.Length - 1];
var intArray = new int[batchSize, actionSize];
tensor.Data = intArray;
var agentIndex = 0;
foreach (var agent in agentInfo.Keys)
{
var pastAction = agentInfo[agent].storedVectorActions;
for (var j = 0; j < actionSize; j++)
{
intArray[agentIndex, j] = (int) pastAction[j];
}
agentIndex++;
}
}
}
///
/// Generates the Tensor corresponding to the Action Mask input : Will be a two
/// dimensional float array of dimension [batchSize x numActionLogits].
/// It will use the Action Mask data contained in the agentInfo to fill the data
/// of the tensor.
///
public class ActionMaskInputGenerator : TensorGenerator.Generator
{
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var maskSize = tensor.Shape[tensor.Shape.Length - 1];
var floatArray = new float[batchSize, maskSize];
tensor.Data = floatArray;
var agentIndex = 0;
foreach (var agent in agentInfo.Keys)
{
var maskList = agentInfo[agent].actionMasks;
for (var j = 0; j < maskSize; j++)
{
var isUnmasked = (maskList != null && maskList[j]) ? 0.0f : 1.0f;
floatArray[agentIndex, j] = isUnmasked;
}
agentIndex++;
}
}
}
///
/// Generates the Tensor corresponding to the Epsilon input : Will be a two
/// dimensional float array of dimension [batchSize x actionSize].
/// It will use the generate random input data from a normal Distribution.
///
public class RandomNormalInputGenerator : TensorGenerator.Generator
{
private RandomNormal _randomNormal;
public RandomNormalInputGenerator(int seed)
{
_randomNormal = new RandomNormal(seed);
}
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
tensor.Shape[0] = batchSize;
var actionSize = tensor.Shape[tensor.Shape.Length - 1];
tensor.Data = new float[batchSize, actionSize];
_randomNormal.FillTensor(tensor);
}
}
///
/// Generates the Tensor corresponding to the Visual Observation input : Will be a 4
/// dimensional float array of dimension [batchSize x width x heigth x numChannels].
/// It will use the Texture input data contained in the agentInfo to fill the data
/// of the tensor.
///
public class VisualObservationInputGenerator : TensorGenerator.Generator
{
private int _index;
private bool _grayScale;
public VisualObservationInputGenerator(int index, bool grayScale)
{
_index = index;
_grayScale = grayScale;
}
public void Generate(Tensor tensor, int batchSize, Dictionary agentInfo)
{
var textures = agentInfo.Keys.Select(
agent => agentInfo[agent].visualObservations[_index]).ToList();
tensor.Data = Utilities.TextureToFloatArray(textures, _grayScale);
}
}
}