// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "LearningArray.h" #include "UObject/NameTypes.h" namespace UE::NNE::RuntimeBasic { class FModelBuilderElement; class FModelBuilder; } namespace UE::Learning::Action { /** * Action Type * * The core type of an action object for which different loss functions and behaviors are defined. */ enum class EType : uint8 { // Empty Action Null = 0, // Vector of continuous float actions Continuous = 1, // Set of exclusive discrete actions DiscreteExclusive = 2, // Set of inclusive discrete actions DiscreteInclusive = 3, // Set of named exclusive discrete actions NamedDiscreteExclusive = 4, // Set of named inclusive discrete actions NamedDiscreteInclusive = 5, // Combination of multiple actions And = 6, // Exclusive choice from a set of actions OrExclusive = 7, // Inclusive choice from a set of actions OrInclusive = 8, // Fixed sized array of actions Array = 9, // Encoding of another action Encoding = 10, }; /** * Action Schema Element * * A single element in the action schema representing a part of an action. Internally this consists of a index used by the schema to look up * the associated action data and a generation id which can be used to check when this index is no longer valid. */ struct FSchemaElement { int32 Index = INDEX_NONE; uint32 Generation = INDEX_NONE; }; struct FSchemaContinuousParameters { // Number of values in the continuous action int32 Num = 0; // Scale factor for the continuous action float Scale = 1.0f; }; struct FSchemaDiscreteExclusiveParameters { // Number of choices in the discrete action int32 Num = 0; // Prior Probabilities of the discrete actions. TArrayView PriorProbabilities; }; struct FSchemaDiscreteInclusiveParameters { // Number of choices in the discrete action int32 Num = 0; // Prior Probabilities of the discrete actions. TArrayView PriorProbabilities; }; struct FSchemaNamedDiscreteExclusiveParameters { // Names of the discrete actions. TArrayView ElementNames; // Prior Probabilities of the discrete actions. TArrayView PriorProbabilities; }; struct FSchemaNamedDiscreteInclusiveParameters { // Names of the discrete actions. TArrayView ElementNames; // Prior Probabilities of the discrete actions. TArrayView PriorProbabilities; }; struct FSchemaAndParameters { // Names of the sub-actions. TArrayView ElementNames; // The associated sub-elements. TArrayView Elements; }; struct FSchemaOrExclusiveParameters { // Names of the sub-actions. TArrayView ElementNames; // The associated sub-elements. TArrayView Elements; // Prior Probabilities of the sub-actions. TArrayView PriorProbabilities; }; struct FSchemaOrInclusiveParameters { // Names of the sub-actions. TArrayView ElementNames; // The associated sub-elements. TArrayView Elements; // Prior Probabilities of the sub-actions. TArrayView PriorProbabilities; }; struct FSchemaArrayParameters { // The array sub-element. FSchemaElement Element; // The number of elements in the array. int32 Num = 0; }; /** Activation Function to use for encoding */ enum class EEncodingActivationFunction : uint8 { ELU = 0, ReLU = 1, TanH = 2, GELU = 3, }; struct FSchemaEncodingParameters { // The sub-element. FSchemaElement Element; // The size at which the sub-element should be encoded. int32 EncodingSize = 32; // The number of layers in the encoding int32 LayerNum = 1; // The activation function to use for encoding EEncodingActivationFunction ActivationFunction = EEncodingActivationFunction::ELU; }; /** * Action Schema * * This object allows you to construct a description of the kind of actions you might want to be generated by a policy. Internally this object * contains a pool of individual elements. This allows them to be constructed performantly and in a cache efficient way. This object is therefore * required to access any data about the individual action elements that are created. */ struct LEARNING_API FSchema { FSchemaElement CreateNull(const FName Tag = NAME_None); FSchemaElement CreateContinuous(const FSchemaContinuousParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateDiscreteExclusive(const FSchemaDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateDiscreteInclusive(const FSchemaDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateNamedDiscreteExclusive(const FSchemaNamedDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateNamedDiscreteInclusive(const FSchemaNamedDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateAnd(const FSchemaAndParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateOrExclusive(const FSchemaOrExclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateOrInclusive(const FSchemaOrInclusiveParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateArray(const FSchemaArrayParameters Parameters, const FName Tag = NAME_None); FSchemaElement CreateEncoding(const FSchemaEncodingParameters Parameters, const FName Tag = NAME_None); FSchemaContinuousParameters GetContinuous(const FSchemaElement Element) const; FSchemaDiscreteExclusiveParameters GetDiscreteExclusive(const FSchemaElement Element) const; FSchemaDiscreteInclusiveParameters GetDiscreteInclusive(const FSchemaElement Element) const; FSchemaNamedDiscreteExclusiveParameters GetNamedDiscreteExclusive(const FSchemaElement Element) const; FSchemaNamedDiscreteInclusiveParameters GetNamedDiscreteInclusive(const FSchemaElement Element) const; FSchemaAndParameters GetAnd(const FSchemaElement Element) const; FSchemaOrExclusiveParameters GetOrExclusive(const FSchemaElement Element) const; FSchemaOrInclusiveParameters GetOrInclusive(const FSchemaElement Element) const; FSchemaArrayParameters GetArray(const FSchemaElement Element) const; FSchemaEncodingParameters GetEncoding(const FSchemaElement Element) const; // Checks if the given element is valid bool IsValid(const FSchemaElement Element) const; // Gets the type of the given element EType GetType(const FSchemaElement Element) const; // Gets the tag of the given element FName GetTag(const FSchemaElement Element) const; // Get the encoded vector size of the given element int32 GetEncodedVectorSize(const FSchemaElement Element) const; // Get the action vector size of the given element int32 GetActionVectorSize(const FSchemaElement Element) const; // Get the action distribution vector size of the given element int32 GetActionDistributionVectorSize(const FSchemaElement Element) const; // Get the action modifier vector size of the given element int32 GetActionModifierVectorSize(const FSchemaElement Element) const; // Get the current generation uint32 GetGeneration() const; // Checks if the given schema is empty of elements. bool IsEmpty() const; // Empty all internal buffers of elements. This invalidates all existing elements. void Empty(); // Reset all internal buffers (without freeing memory). This invalidates all existing elements. void Reset(); private: struct FContinuousData { int32 Num = INDEX_NONE; float Scale = 0.0f; }; struct FDiscreteExclusiveData { int32 Num = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; }; struct FDiscreteInclusiveData { int32 Num = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; }; struct FNamedDiscreteExclusiveData { int32 Num = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; int32 ElementsOffset = INDEX_NONE; }; struct FNamedDiscreteInclusiveData { int32 Num = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; int32 ElementsOffset = INDEX_NONE; }; struct FAndData { int32 Num = INDEX_NONE; int32 ElementsOffset = INDEX_NONE; }; struct FOrExclusiveData { int32 Num = INDEX_NONE; int32 ElementsOffset = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; }; struct FOrInclusiveData { int32 Num = INDEX_NONE; int32 ElementsOffset = INDEX_NONE; int32 PriorProbabilitiesOffset = INDEX_NONE; }; struct FArrayData { int32 Num = INDEX_NONE; int32 ElementIndex = INDEX_NONE; }; struct FEncodingData { int32 EncodingSize = INDEX_NONE; int32 ElementIndex = INDEX_NONE; int32 LayerNum = INDEX_NONE; EEncodingActivationFunction ActivationFunction = EEncodingActivationFunction::ELU; }; uint32 Generation = 0; /** These have entries for each Schema Element */ TArray Types; TArray Tags; TArray EncodedVectorSizes; TArray ActionVectorSizes; TArray ActionDistributionVectorSizes; TArray ActionModifierVectorSizes; TArray TypeDataIndices; /** These are indexed based on the type and the index found in TypeDataIndices */ TArray ContinuousData; TArray DiscreteExclusiveData; TArray DiscreteInclusiveData; TArray NamedDiscreteExclusiveData; TArray NamedDiscreteInclusiveData; TArray AndData; TArray OrExclusiveData; TArray OrInclusiveData; TArray ArrayData; TArray EncodingData; /** This is an array of all the SubElements and their names, referenced by other elements */ TArray SubElementNames; TArray SubElementObjects; /** This is an array of all the prior probabilities, referenced by other elements. */ TArray PriorProbabilities; }; /** * Action Object Element * * A single element in the action object representing part of an action. Internally this consists of a index used by the object to look up * the associated action data and a generation id which can be used to check when this index is no longer valid. */ struct FObjectElement { int32 Index = INDEX_NONE; uint32 Generation = INDEX_NONE; }; struct FObjectContinuousParameters { // Continuous action values TArrayView Values; }; struct FObjectDiscreteExclusiveParameters { // Exclusive discrete action index int32 DiscreteIndex = INDEX_NONE; }; struct FObjectDiscreteInclusiveParameters { // Inclusive discrete action indices TArrayView DiscreteIndices; }; struct FObjectNamedDiscreteExclusiveParameters { // Named Exclusive discrete action name FName ElementName = NAME_None; }; struct FObjectNamedDiscreteInclusiveParameters { // Named Inclusive discrete action name TArrayView ElementNames; }; struct FObjectAndParameters { // Names of the sub-actions. TArrayView ElementNames; // The associated sub-elements. TArrayView Elements; }; struct FObjectOrExclusiveParameters { // Name of the chosen sub-action. FName ElementName; // The associated chosen sub-element. FObjectElement Element; }; struct FObjectOrInclusiveParameters { // Names of the chosen sub-actions. TArrayView ElementNames; // The associated chosen sub-elements. TArrayView Elements; }; struct FObjectArrayParameters { // Array of sub-elements. TArrayView Elements; }; struct FObjectEncodingParameters { // Encoded sub-element. FObjectElement Element; }; /** * Action Object * * This object allows you to construct or get data from an instance of an action you might have generated from a policy. Internally this object * contains a pool of individual elements. This allows them to be constructed performantly and in a cache efficient way. This object is therefore * required to access any data about the individual action elements that are created. */ struct LEARNING_API FObject { FObjectElement CreateNull(const FName Tag = NAME_None); FObjectElement CreateContinuous(const FObjectContinuousParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateDiscreteExclusive(const FObjectDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateDiscreteInclusive(const FObjectDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateNamedDiscreteExclusive(const FObjectNamedDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateNamedDiscreteInclusive(const FObjectNamedDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateAnd(const FObjectAndParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateOrExclusive(const FObjectOrExclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateOrInclusive(const FObjectOrInclusiveParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateArray(const FObjectArrayParameters Parameters, const FName Tag = NAME_None); FObjectElement CreateEncoding(const FObjectEncodingParameters Parameters, const FName Tag = NAME_None); FObjectContinuousParameters GetContinuous(const FObjectElement Element) const; FObjectDiscreteExclusiveParameters GetDiscreteExclusive(const FObjectElement Element) const; FObjectDiscreteInclusiveParameters GetDiscreteInclusive(const FObjectElement Element) const; FObjectNamedDiscreteExclusiveParameters GetNamedDiscreteExclusive(const FObjectElement Element) const; FObjectNamedDiscreteInclusiveParameters GetNamedDiscreteInclusive(const FObjectElement Element) const; FObjectAndParameters GetAnd(const FObjectElement Element) const; FObjectOrExclusiveParameters GetOrExclusive(const FObjectElement Element) const; FObjectOrInclusiveParameters GetOrInclusive(const FObjectElement Element) const; FObjectArrayParameters GetArray(const FObjectElement Element) const; FObjectEncodingParameters GetEncoding(const FObjectElement Element) const; // Checks if the given element is valid bool IsValid(const FObjectElement Element) const; // Gets the type of the given element EType GetType(const FObjectElement Element) const; // Gets the tag of the given element FName GetTag(const FObjectElement Element) const; // Get the current generation uint32 GetGeneration() const; // Checks if the given object is empty of elements. bool IsEmpty() const; // Empty all internal buffers of elements. This invalidates all existing elements. void Empty(); // Reset all internal buffers (without freeing memory). This invalidates all existing elements. void Reset(); private: uint32 Generation = 0; TArray Types; TArray Tags; TArray ContinuousDataOffsets; TArray ContinuousDataNums; TArray DiscreteDataOffsets; TArray DiscreteDataNums; TArray ElementDataOffsets; TArray ElementDataNums; TArray ContinuousValues; TArray DiscreteValues; TArray SubElementNames; TArray SubElementObjects; }; /** * Action Modifier Element * * A single element representing a modification to an action performed during sampling (such as masking). Internally this consists of a index * used by the object to look up the associated action modification data and a generation id which can be used to check when this index is no * longer valid. */ struct FModifierElement { int32 Index = INDEX_NONE; uint32 Generation = INDEX_NONE; }; struct FModifierContinuousParameters { // Indicates which dimensions are masked TArrayView Masked; // Values used to replace masked dimensions TArrayView MaskedValues; }; struct FModifierDiscreteExclusiveParameters { // Disallowed indices TArrayView MaskedIndices; }; struct FModifierDiscreteInclusiveParameters { // Disallowed indices TArrayView MaskedIndices; }; struct FModifierNamedDiscreteExclusiveParameters { // Disallowed actions TArrayView MaskedElementNames; }; struct FModifierNamedDiscreteInclusiveParameters { // Disallowed actions TArrayView MaskedElementNames; }; struct FModifierAndParameters { // Names of the modified sub-actions. TArrayView ElementNames; // The modifiers of the associated sub-elements. TArrayView Elements; }; struct FModifierOrExclusiveParameters { // Names of the modified sub-action. TArrayView ElementNames; // The modifiers of the associated sub-element. TArrayView Elements; // Disallowed sub-elements TArrayView MaskedElements; }; struct FModifierOrInclusiveParameters { // Names of the modified sub-actions. TArrayView ElementNames; // The modifiers of the associated sub-elements. TArrayView Elements; // Disallowed sub-elements TArrayView MaskedElements; }; struct FModifierArrayParameters { // Array of sub-element modifiers. TArrayView Elements; }; struct FModifierEncodingParameters { // Encoded sub-element modifier. FModifierElement Element; }; /** * Action Modifier * * This object allows you to describe modifications you want to apply to actions during the sampling stage. Internally this object contains a * pool of individual elements. This allows them to be constructed performantly and in a cache efficient way. This object is therefore * required to access any data about the individual action modifier elements that are created. */ struct LEARNING_API FModifier { FModifierElement CreateNull(const FName Tag = NAME_None); FModifierElement CreateContinuous(const FModifierContinuousParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateDiscreteExclusive(const FModifierDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateDiscreteInclusive(const FModifierDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateNamedDiscreteExclusive(const FModifierNamedDiscreteExclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateNamedDiscreteInclusive(const FModifierNamedDiscreteInclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateAnd(const FModifierAndParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateOrExclusive(const FModifierOrExclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateOrInclusive(const FModifierOrInclusiveParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateArray(const FModifierArrayParameters Parameters, const FName Tag = NAME_None); FModifierElement CreateEncoding(const FModifierEncodingParameters Parameters, const FName Tag = NAME_None); FModifierContinuousParameters GetContinuous(const FModifierElement Element) const; FModifierDiscreteExclusiveParameters GetDiscreteExclusive(const FModifierElement Element) const; FModifierDiscreteInclusiveParameters GetDiscreteInclusive(const FModifierElement Element) const; FModifierNamedDiscreteExclusiveParameters GetNamedDiscreteExclusive(const FModifierElement Element) const; FModifierNamedDiscreteInclusiveParameters GetNamedDiscreteInclusive(const FModifierElement Element) const; FModifierAndParameters GetAnd(const FModifierElement Element) const; FModifierOrExclusiveParameters GetOrExclusive(const FModifierElement Element) const; FModifierOrInclusiveParameters GetOrInclusive(const FModifierElement Element) const; FModifierArrayParameters GetArray(const FModifierElement Element) const; FModifierEncodingParameters GetEncoding(const FModifierElement Element) const; // Checks if the given element is valid bool IsValid(const FModifierElement Element) const; // Gets the type of the given element EType GetType(const FModifierElement Element) const; // Gets the tag of the given element FName GetTag(const FModifierElement Element) const; // Get the current generation uint32 GetGeneration() const; // Checks if the given object is empty of elements. bool IsEmpty() const; // Empty all internal buffers of elements. This invalidates all existing elements. void Empty(); // Reset all internal buffers (without freeing memory). This invalidates all existing elements. void Reset(); private: uint32 Generation = 0; TArray Types; TArray Tags; TArray ContinuousDataOffsets; TArray ContinuousDataNums; TArray DiscreteDataOffsets; TArray DiscreteDataNums; TArray ElementDataOffsets; TArray ElementDataNums; TArray MaskedDataOffsets; TArray MaskedDataNums; TArray ContinuousMaskeds; TArray ContinuousMaskedValues; TArray DiscreteValues; TArray SubElementNames; TArray SubElementModifiers; TArray MaskedElementNames; }; /** * Gets a hash value representing object compatibility between schemas i.e. if objects from one schema can be used by objects expecting another * schema. This is not a cryptographic hash, and so `AreSchemaObjectsCompatible` should still be used as the ultimate source of truth. * This function returns an int32 so that it can be used in blueprints. * * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param Salt Hash salt */ LEARNING_API int32 GetSchemaObjectsCompatibilityHash( const FSchema& Schema, const FSchemaElement SchemaElement, const int32 Salt = 0x49710e77); /** * Test if two schemas are compatible i.e. if objects from one schema can be used by objects expecting another schema. * * @param SchemaA First Schema * @param SchemaElementA First Schema Element * @param SchemaB Second Schema * @param SchemaElementB Second Schema Element * @returns true when the schemas are compatible */ LEARNING_API bool AreSchemaObjectsCompatible( const FSchema& SchemaA, const FSchemaElement SchemaElementA, const FSchema& SchemaB, const FSchemaElement SchemaElementB); /** Network weight initialization type */ enum class EWeightInitialization : uint8 { KaimingGaussian = 0, KaimingUniform = 1, }; /** * Settings for building a network from a schema. */ struct LEARNING_API FNetworkSettings { // If to use compressed linear layers. This reduces the memory usage by half at the cost of some small impact on quality and evaluation speed. bool bUseCompressedLinearLayers = false; // Which weight initialization to use. EWeightInitialization WeightInitialization = EWeightInitialization::KaimingGaussian; }; /** * Make a NNE::RuntimeBasic::FModelBuilderElement for the given Schema. This can be used if you want to plug the Decoder generated by this * schema as a part of a larger model you are making with a NNE::RuntimeBasic::FModelBuilder. * * @param OutElement Output Builder Element * @param Builder Model Builder * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param NetworkSettings Network Generation Settings */ LEARNING_API void MakeDecoderNetworkModelBuilderElementFromSchema( NNE::RuntimeBasic::FModelBuilderElement& OutElement, NNE::RuntimeBasic::FModelBuilder& Builder, const FSchema& Schema, const FSchemaElement SchemaElement, const FNetworkSettings& NetworkSettings = FNetworkSettings()); /** * Generate FileData for a Neural Network that can act as a Decoder for the given schema. This network will take as input a vector of size * EncodedActionSize and produce a vector of size ActionDistributionVectorSize. * * @param OutFileData Output File Data to write to * @param OutInputSize Size of the vector this network takes as input * @param OutOutputSize Size of the vector this network takes as output * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param NetworkSettings Network Generation Settings * @param Seed The random seed used in initializing the network weights */ LEARNING_API void GenerateDecoderNetworkFileDataFromSchema( TArray& OutFileData, uint32& OutInputSize, uint32& OutOutputSize, const FSchema& Schema, const FSchemaElement SchemaElement, const FNetworkSettings& NetworkSettings = FNetworkSettings(), const uint32 Seed = 0x05dbd1f5); /** * Sample an action vector from a vector representing the action distribution. * * @param InOutRandomState Random State/Seed to use when sampling the action vector * @param OutActionVector Output action vector * @param ActionDistributionVector Input Action Distribution vector * @param ActionModifierVector Input Action Modifier vector * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param ActionNoiseScale Scale factor for the noise. Set this to zero to always choose the mean (expected) action. */ LEARNING_API void SampleVectorFromDistributionVector( uint32& InOutRandomState, TLearningArrayView<1, float> OutActionVector, const TLearningArrayView<1, const float> ActionDistributionVector, const TLearningArrayView<1, const float> ActionModifierVector, const FSchema& Schema, const FSchemaElement SchemaElement, const float ActionNoiseScale = 1.0f); /** * Convert an action object into an action vector. * * @param OutActionVector Output action vector * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param Object Action Object * @param ObjectElement Action Object Element */ LEARNING_API void SetVectorFromObject( TLearningArrayView<1, float> OutActionVector, const FSchema& Schema, const FSchemaElement SchemaElement, const FObject& Object, const FObjectElement ObjectElement); /** * Convert an action vector into an action object * * @param OutObject Output Action Object * @param OutObjectElement Output Action Object Element * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param ActionVector Input Action vector */ LEARNING_API void GetObjectFromVector( FObject& OutObject, FObjectElement& OutObjectElement, const FSchema& Schema, const FSchemaElement SchemaElement, const TLearningArrayView<1, const float> ActionVector); /** * Convert an action modifier into an action modifier vector. * * @param OutActionModifierVector Output action modifier vector * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param Modifier Action Modifier * @param ModifierElement Action Modifier Element */ LEARNING_API void SetVectorFromModifier( TLearningArrayView<1, float> OutActionModifierVector, const FSchema& Schema, const FSchemaElement SchemaElement, const FModifier& Modifier, const FModifierElement ModifierElement); /** * Convert an action modifier vector into an action modifier * * @param OutModifier Output Action Modifier * @param OutModifierElement Output Action Modifier Element * @param Schema Action Schema * @param SchemaElement Action Schema Element * @param ActionModifierVector Input Action Modifier vector */ LEARNING_API void GetModifierFromVector( FModifier& OutModifier, FModifierElement& OutModifierElement, const FSchema& Schema, const FSchemaElement SchemaElement, const TLearningArrayView<1, const float> ActionModifierVector); }