using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Unity.MLAgents.Actuators { /// /// Defines the structure of the actions to be used by the Actuator system. /// [Serializable] public struct ActionSpec { [SerializeField] int m_NumContinuousActions; /// /// An array of branch sizes for discrete actions. /// /// For an IActuator that uses discrete actions, the number of /// branches is the Length of the Array and each index contains the branch size. /// The cumulative sum of the total number of discrete actions can be retrieved /// by the property. /// /// For an IActuator with a Continuous it will be null. /// public int[] BranchSizes; /// /// The number of continuous actions that an Agent can take. /// public int NumContinuousActions { get { return m_NumContinuousActions; } set { m_NumContinuousActions = value; } } /// /// The number of branches for discrete actions that an Agent can take. /// public int NumDiscreteActions { get { return BranchSizes == null ? 0 : BranchSizes.Length; } } /// /// Get the total number of Discrete Actions that can be taken by calculating the Sum /// of all of the Discrete Action branch sizes. /// public int SumOfDiscreteBranchSizes { get { return BranchSizes == null ? 0 : BranchSizes.Sum(); } } /// /// Creates a Continuous with the number of actions available. /// /// The number of continuous actions available. /// An Continuous ActionSpec initialized with the number of actions available. public static ActionSpec MakeContinuous(int numActions) { var actuatorSpace = new ActionSpec(numActions, null); return actuatorSpace; } /// /// Creates a Discrete with the array of branch sizes that /// represents the action space. /// /// The array of branch sizes for the discrete actions. Each index /// contains the number of actions available for that branch. /// An Discrete ActionSpec initialized with the array of branch sizes. public static ActionSpec MakeDiscrete(params int[] branchSizes) { var actuatorSpace = new ActionSpec(0, branchSizes); return actuatorSpace; } /// /// Create an ActionSpec initialized with the specified action sizes. /// /// The number of continuous actions available. /// The array of branch sizes for the discrete actions. Each index /// contains the number of actions available for that branch. /// An ActionSpec initialized with the specified action sizes. public ActionSpec(int numContinuousActions = 0, int[] discreteBranchSizes = null) { m_NumContinuousActions = numContinuousActions; BranchSizes = discreteBranchSizes ?? Array.Empty(); } /// /// Check that the ActionSpec uses either all continuous or all discrete actions. /// This is only used when connecting to old versions of the trainer that don't support this. /// /// internal void CheckAllContinuousOrDiscrete() { if (NumContinuousActions > 0 && NumDiscreteActions > 0) { throw new UnityAgentsException( "Action spaces with both continuous and discrete actions are not supported by the trainer. " + "ActionSpecs must be all continuous or all discrete." ); } } /// /// Combines a list of actions specs and allocates a new array of branch sizes if needed. /// /// The list of action specs to combine. /// An ActionSpec which represents the aggregate of the ActionSpecs passed in. public static ActionSpec Combine(params ActionSpec[] specs) { var numContinuous = 0; var numDiscrete = 0; for (var i = 0; i < specs.Length; i++) { var spec = specs[i]; numContinuous += spec.NumContinuousActions; numDiscrete += spec.NumDiscreteActions; } if (numDiscrete <= 0) { return MakeContinuous(numContinuous); } var branchSizes = new int[numDiscrete]; var offset = 0; for (var i = 0; i < specs.Length; i++) { var spec = specs[i]; if (spec.BranchSizes.Length == 0) { continue; } var branchSizesLength = spec.BranchSizes.Length; Array.Copy(spec.BranchSizes, 0, branchSizes, offset, branchSizesLength); offset += branchSizesLength; } return new ActionSpec(numContinuous, branchSizes); } } }