using System; using System.Collections.Generic; using Unity.Barracuda; using Unity.MLAgents.Inference; namespace Unity.MLAgents.Sensors { /// /// Allows sensors to write to both TensorProxy and float arrays/lists. /// public class ObservationWriter { IList m_Data; int m_Offset; TensorProxy m_Proxy; int m_Batch; TensorShape m_TensorShape; internal ObservationWriter() { } /// /// Set the writer to write to an IList at the given channelOffset. /// /// Float array or list that will be written to. /// Shape of the observations to be written. /// Offset from the start of the float data to write to. internal void SetTarget(IList data, int[] shape, int offset) { m_Data = data; m_Offset = offset; m_Proxy = null; m_Batch = 0; if (shape.Length == 1) { m_TensorShape = new TensorShape(m_Batch, shape[0]); } else { m_TensorShape = new TensorShape(m_Batch, shape[0], shape[1], shape[2]); } } /// /// Set the writer to write to a TensorProxy at the given batch and channel offset. /// /// Tensor proxy that will be written to. /// Batch index in the tensor proxy (i.e. the index of the Agent). /// Offset from the start of the channel to write to. internal void SetTarget(TensorProxy tensorProxy, int batchIndex, int channelOffset) { m_Proxy = tensorProxy; m_Batch = batchIndex; m_Offset = channelOffset; m_Data = null; m_TensorShape = m_Proxy.data.shape; } /// /// 1D write access at a specified index. Use AddRange if possible instead. /// /// Index to write to. public float this[int index] { set { if (m_Data != null) { m_Data[index + m_Offset] = value; } else { m_Proxy.data[m_Batch, index + m_Offset] = value; } } } /// /// 3D write access at the specified height, width, and channel. Only usable with a TensorProxy target. /// /// /// /// public float this[int h, int w, int ch] { set { if (m_Data != null) { if (h < 0 || h >= m_TensorShape.height) { throw new IndexOutOfRangeException($"height value {h} must be in range [0, {m_TensorShape.height - 1}]"); } if (w < 0 || w >= m_TensorShape.width) { throw new IndexOutOfRangeException($"width value {w} must be in range [0, {m_TensorShape.width - 1}]"); } if (ch < 0 || ch >= m_TensorShape.channels) { throw new IndexOutOfRangeException($"channel value {ch} must be in range [0, {m_TensorShape.channels - 1}]"); } var index = m_TensorShape.Index(m_Batch, h, w, ch + m_Offset); m_Data[index] = value; } else { m_Proxy.data[m_Batch, h, w, ch + m_Offset] = value; } } } /// /// Write the range of floats /// /// /// Optional write offset. public void AddRange(IEnumerable data, int writeOffset = 0) { if (m_Data != null) { int index = 0; foreach (var val in data) { m_Data[index + m_Offset + writeOffset] = val; index++; } } else { int index = 0; foreach (var val in data) { m_Proxy.data[m_Batch, index + m_Offset + writeOffset] = val; index++; } } } } }