using System.Collections.Generic;
using Barracuda;
namespace MLAgents.InferenceBrain
{
///
/// Mapping between Tensor names and generators.
/// A TensorGenerator implements a Dictionary of strings (node names) to an Action.
/// The Action take as argument the tensor, the current batch size and a Dictionary of
/// Agent to AgentInfo corresponding to the current batch.
/// Each Generator reshapes and fills the data of the tensor based of the data of the batch.
/// When the TensorProxy is an Input to the model, the shape of the Tensor will be modified
/// depending on the current batch size and the data of the Tensor will be filled using the
/// Dictionary of Agent to AgentInfo.
/// When the TensorProxy is an Output of the model, only the shape of the Tensor will be
/// modified using the current batch size. The data will be pre-filled with zeros.
///
public class TensorGenerator
{
public interface IGenerator
{
///
/// Modifies the data inside a Tensor according to the information contained in the
/// AgentInfos contained in the current batch.
///
/// The tensor the data and shape will be modified
/// The number of agents present in the current batch
/// List of Agents containing the
/// information that will be used to populate the tensor's data
void Generate(
TensorProxy tensorProxy, int batchSize, IEnumerable agents);
}
private readonly Dictionary m_Dict = new Dictionary();
///
/// Returns a new TensorGenerators object.
///
/// The BrainParameters used to determine what Generators will be
/// used
/// The seed the Generators will be initialized with.
/// Tensor allocator
///
public TensorGenerator(
BrainParameters bp, int seed, ITensorAllocator allocator, object barracudaModel = null)
{
// Generator for Inputs
m_Dict[TensorNames.BatchSizePlaceholder] =
new BatchSizeGenerator(allocator);
m_Dict[TensorNames.SequenceLengthPlaceholder] =
new SequenceLengthGenerator(allocator);
m_Dict[TensorNames.VectorObservationPlacholder] =
new VectorObservationGenerator(allocator);
m_Dict[TensorNames.RecurrentInPlaceholder] =
new RecurrentInputGenerator(allocator);
if (barracudaModel != null)
{
var model = (Model)barracudaModel;
for (var i = 0; i < model?.memories.Length; i++)
{
m_Dict[model.memories[i].input] =
new BarracudaRecurrentInputGenerator(i, allocator);
}
}
m_Dict[TensorNames.PreviousActionPlaceholder] =
new PreviousActionInputGenerator(allocator);
m_Dict[TensorNames.ActionMaskPlaceholder] =
new ActionMaskInputGenerator(allocator);
m_Dict[TensorNames.RandomNormalEpsilonPlaceholder] =
new RandomNormalInputGenerator(seed, allocator);
if (bp.cameraResolutions != null)
{
for (var visIndex = 0;
visIndex < bp.cameraResolutions.Length;
visIndex++)
{
var index = visIndex;
var bw = bp.cameraResolutions[visIndex].blackAndWhite;
m_Dict[TensorNames.VisualObservationPlaceholderPrefix + visIndex] =
new VisualObservationInputGenerator(index, bw, allocator);
}
}
// Generators for Outputs
m_Dict[TensorNames.ActionOutput] = new BiDimensionalOutputGenerator(allocator);
m_Dict[TensorNames.RecurrentOutput] = new BiDimensionalOutputGenerator(allocator);
m_Dict[TensorNames.ValueEstimateOutput] = new BiDimensionalOutputGenerator(allocator);
}
///
/// Populates the data of the tensor inputs given the data contained in the current batch
/// of agents.
///
/// Enumerable of tensors that will be modified.
/// The number of agents present in the current batch
///
/// List of Agents that contains the
/// data that will be used to modify the tensors
/// One of the tensor does not have an
/// associated generator.
public void GenerateTensors(
IEnumerable tensors,
int currentBatchSize,
IEnumerable agents)
{
foreach (var tensor in tensors)
{
if (!m_Dict.ContainsKey(tensor.name))
{
throw new UnityAgentsException(
$"Unknown tensorProxy expected as input : {tensor.name}");
}
m_Dict[tensor.name].Generate(tensor, currentBatchSize, agents);
}
}
}
}