浏览代码

Merge branch 'main' into ai-hw-2021

/ai-hw-2021
Ruo-Ping Dong 4 年前
当前提交
deeea398
共有 74 个文件被更改,包括 870 次插入686 次删除
  1. 100
      .yamato/com.unity.ml-agents-optional-dep-tests.yml
  2. 263
      .yamato/com.unity.ml-agents-test.yml
  3. 6
      Project/Assets/ML-Agents/Examples/Basic/Scripts/BasicSensorComponent.cs
  4. 8
      Project/Assets/ML-Agents/Examples/SharedAssets/Scripts/SensorBase.cs
  5. 4
      Project/Assets/ML-Agents/TestScenes/TestCompressedTexture/TestTextureSensor.cs
  6. 16
      Project/Assets/ML-Agents/TestScenes/TestCompressedTexture/TestTextureSensorComponent.cs
  7. 14
      com.unity.ml-agents.extensions/Runtime/Match3/Match3Sensor.cs
  8. 14
      com.unity.ml-agents.extensions/Runtime/Match3/Match3SensorComponent.cs
  9. 20
      com.unity.ml-agents.extensions/Runtime/Sensors/ArticulationBodySensorComponent.cs
  10. 11
      com.unity.ml-agents.extensions/Runtime/Sensors/GridSensor.cs
  11. 10
      com.unity.ml-agents.extensions/Runtime/Sensors/PhysicsBodySensor.cs
  12. 20
      com.unity.ml-agents.extensions/Runtime/Sensors/RigidBodySensorComponent.cs
  13. 43
      com.unity.ml-agents.extensions/Tests/Editor/Match3/Match3SensorTests.cs
  14. 19
      com.unity.ml-agents.extensions/Tests/Editor/Sensors/ChannelHotShapeTests.cs
  15. 15
      com.unity.ml-agents.extensions/Tests/Editor/Sensors/ChannelShapeTests.cs
  16. 27
      com.unity.ml-agents.extensions/Tests/Editor/Sensors/GridSensorTestUtils.cs
  17. 6
      com.unity.ml-agents.extensions/Tests/Runtime/Sensors/ArticulationBodySensorTests.cs
  18. 6
      com.unity.ml-agents.extensions/Tests/Runtime/Sensors/RigidBodySensorTests.cs
  19. 3
      com.unity.ml-agents.extensions/package.json
  20. 8
      com.unity.ml-agents/CHANGELOG.md
  21. 16
      com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs
  22. 14
      com.unity.ml-agents/Editor/Unity.ML-Agents.Editor.asmdef
  23. 14
      com.unity.ml-agents/Runtime/Agent.cs
  24. 2
      com.unity.ml-agents/Runtime/Analytics/Events.cs
  25. 18
      com.unity.ml-agents/Runtime/Analytics/InferenceAnalytics.cs
  26. 29
      com.unity.ml-agents/Runtime/Analytics/TrainingAnalytics.cs
  27. 44
      com.unity.ml-agents/Runtime/Communicator/GrpcExtensions.cs
  28. 20
      com.unity.ml-agents/Runtime/Policies/BarracudaPolicy.cs
  29. 4
      com.unity.ml-agents/Runtime/Policies/HeuristicPolicy.cs
  30. 14
      com.unity.ml-agents/Runtime/Policies/RemotePolicy.cs
  31. 6
      com.unity.ml-agents/Runtime/Sensors/BufferSensor.cs
  32. 6
      com.unity.ml-agents/Runtime/Sensors/BufferSensorComponent.cs
  33. 5
      com.unity.ml-agents/Runtime/Sensors/CameraSensor.cs
  34. 15
      com.unity.ml-agents/Runtime/Sensors/CameraSensorComponent.cs
  35. 24
      com.unity.ml-agents/Runtime/Sensors/ISensor.cs
  36. 14
      com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs
  37. 2
      com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponent2D.cs
  38. 18
      com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs
  39. 5
      com.unity.ml-agents/Runtime/Sensors/Reflection/ReflectionSensorBase.cs
  40. 5
      com.unity.ml-agents/Runtime/Sensors/RenderTextureSensor.cs
  41. 16
      com.unity.ml-agents/Runtime/Sensors/RenderTextureSensorComponent.cs
  42. 7
      com.unity.ml-agents/Runtime/Sensors/SensorComponent.cs
  43. 27
      com.unity.ml-agents/Runtime/Sensors/StackingSensor.cs
  44. 6
      com.unity.ml-agents/Runtime/Sensors/VectorSensor.cs
  45. 10
      com.unity.ml-agents/Runtime/Unity.ML-Agents.asmdef
  46. 7
      com.unity.ml-agents/Runtime/Utilities.cs
  47. 12
      com.unity.ml-agents/Tests/Editor/Analytics/TrainingAnalyticsTest.cs
  48. 137
      com.unity.ml-agents/Tests/Editor/Communicator/GrpcExtensionsTests.cs
  49. 11
      com.unity.ml-agents/Tests/Editor/Inference/ParameterLoaderTest.cs
  50. 40
      com.unity.ml-agents/Tests/Editor/MLAgentsEditModeTest.cs
  51. 2
      com.unity.ml-agents/Tests/Editor/PublicAPI/PublicApiValidation.cs
  52. 10
      com.unity.ml-agents/Tests/Editor/Unity.ML-Agents.Editor.Tests.asmdef
  53. 24
      com.unity.ml-agents/Tests/Runtime/RuntimeAPITest.cs
  54. 4
      com.unity.ml-agents/Tests/Runtime/Sensor/BufferSensorTest.cs
  55. 7
      com.unity.ml-agents/Tests/Runtime/Sensor/CameraSensorComponentTest.cs
  56. 4
      com.unity.ml-agents/Tests/Runtime/Sensor/FloatVisualSensorTests.cs
  57. 16
      com.unity.ml-agents/Tests/Runtime/Sensor/RayPerceptionSensorTests.cs
  58. 5
      com.unity.ml-agents/Tests/Runtime/Sensor/RenderTextureSensorComponentTests.cs
  59. 6
      com.unity.ml-agents/Tests/Runtime/Sensor/SensorShapeValidatorTests.cs
  60. 20
      com.unity.ml-agents/Tests/Runtime/Sensor/StackingSensorTests.cs
  61. 12
      com.unity.ml-agents/Tests/Runtime/Sensor/Unity.ML-Agents.Runtime.Sensor.Tests.asmdef
  62. 12
      com.unity.ml-agents/Tests/Runtime/Unity.ML-Agents.Runtime.Tests.asmdef
  63. 5
      com.unity.ml-agents/Tests/Runtime/Utils/TestClasses.cs
  64. 4
      com.unity.ml-agents/package.json
  65. 27
      docs/Migrating.md
  66. 4
      markdown-link-check.full.json
  67. 40
      .yamato/com.unity.ml-agents-coverage.yml
  68. 17
      .yamato/coverage_tests.metafile
  69. 109
      com.unity.ml-agents/Runtime/Sensors/CompressionSpec.cs
  70. 3
      com.unity.ml-agents/Runtime/Sensors/CompressionSpec.cs.meta
  71. 30
      com.unity.ml-agents/Tests/Runtime/Sensor/CompressionSpecTests.cs
  72. 3
      com.unity.ml-agents/Tests/Runtime/Sensor/CompressionSpecTests.cs.meta
  73. 20
      com.unity.ml-agents/Runtime/Sensors/ISparseChannelSensor.cs
  74. 11
      com.unity.ml-agents/Runtime/Sensors/ISparseChannelSensor.cs.meta

100
.yamato/com.unity.ml-agents-optional-dep-tests.yml


OptionalDependencyTestsLinux:
name : LinuxOptionalDependenciesTests
agent:
type: Unity::VM
image: package-ci/ubuntu:stable
flavor: b1.medium
commands:
- |
curl -L https://artifactory.prd.it.unity3d.com/artifactory/api/gpg/key/public | sudo apt-key add -
sudo sh -c "echo 'deb https://artifactory.prd.it.unity3d.com/artifactory/unity-apt-local bionic main' > /etc/apt/sources.list.d/unity.list"
sudo apt update
sudo apt install -y unity-config
npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
unity-config settings editor-path ./.Editor
unity-config project create opt-deps-test
unity-config project add dependency com.unity.ml-agents/
unity-config project add testable com.unity.ml-agents
unity-config project add dependency com.unity.modules.imageconversion@1.0.0
unity-config project add dependency com.unity.modules.jsonserialize@1.0.0
unity-config project add dependency com.unity.modules.physics@1.0.0
unity-config project add dependency com.unity.modules.physics2d@1.0.0
upm-ci project test -u 2019.4 --type project-tests --project-path opt-deps-test --package-filter com.unity.ml-agents
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
triggers:
cancel_old_ci: true
expression: |
(pull_request.target eq "main" OR
pull_request.target match "release.+") AND
NOT pull_request.draft AND
(pull_request.changes.any match "com.unity.ml-agents/**" OR
pull_request.changes.any match ".yamato/com.unity.ml-agents-test.yml")
optional_deps:
- name: Analytics
project: "OptionalDepedencyTests/NoAnalyticsModule"
version: 2020.2
- name: Physics
project: OptionalDepedencyTests/NoPhysicsModule
version: 2020.2
- name: Physics2D
project: OptionalDepedencyTests/NoPhysics2DModule
version: 2020.2
---
{% for optional_dep in optional_deps %}
OptionalDependencyTests_{{ optional_dep.name }}:
name : Test Optional Package Dependencies {{ optional_dep.name }}
agent:
type: Unity::VM
image: package-ci/ubuntu:stable
flavor: b1.medium
commands:
- |
curl -L https://artifactory.prd.it.unity3d.com/artifactory/api/gpg/key/public | sudo apt-key add -
sudo sh -c "echo 'deb https://artifactory.prd.it.unity3d.com/artifactory/unity-apt-local bionic main' > /etc/apt/sources.list.d/unity.list"
sudo apt update
sudo apt install -y unity-config
npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
unity-config settings editor-path ./.Editor
unity-config project create opt-deps-test
unity-config project add dependency com.unity.ml-agents/
unity-config project add testable com.unity.ml-agents
unity-config project add dependency com.unity.modules.imageconversion@1.0.0
unity-config project add dependency com.unity.modules.jsonserialize@1.0.0
{% unless optional_dep.name == "Physics" %}
unity-config project add dependency com.unity.modules.physics@1.0.0
{% endunless %}
{% unless optional_dep.name == "Physics2D" %}
unity-config project add dependency com.unity.modules.physics2d@1.0.0
{% endunless %}
{% unless optional_dep.name == "Analytics" %}
unity-config project add dependency com.unity.modules.unityanalytics@1.0.0
{% endunless %}
upm-ci project test -u {{ optional_dep.version }} --type project-tests --project-path opt-deps-test --package-filter com.unity.ml-agents
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
{% for coverage_editor in coverage_test_editors %}
{% for coverage_plathform in coverage_test_platforms %}
{% for coverage_package in coverage_test_packages %}
- .yamato/com.unity.ml-agents-coverage.yml#test_coverage_{{ coverage_package.name }}_{{ coverage_platform.name }}_{{ coverage_editor.version }}
{% endfor %}
{% endfor %}
{% endfor %}
triggers:
cancel_old_ci: true
expression: |
(pull_request.target eq "main" OR
pull_request.target match "release.+") AND
NOT pull_request.draft AND
(pull_request.changes.any match "com.unity.ml-agents/**" OR
pull_request.changes.any match ".yamato/com.unity.ml-agents-test.yml")
{% endfor %}

263
.yamato/com.unity.ml-agents-test.yml


{% metadata_file .yamato/coverage_tests.metafile %}
- version: 2019.4
enableCodeCoverage: !!bool true
# We want some scene tests to run in the DevProject, but packages there only support 2020+
testProject: Project
enableNoDefaultPackages: !!bool true
- version: 2020.2
enableCodeCoverage: !!bool true
testProject: DevProject
enableNoDefaultPackages: !!bool true
- version: 2021.1
enableCodeCoverage: !!bool true
testProject: DevProject
enableNoDefaultPackages: !!bool true
- version: 2019.4
enableCodeCoverage: !!bool true
# We want some scene tests to run in the DevProject, but packages there only support 2020+
testProject: Project
enableNoDefaultPackages: !!bool true
- version: 2020.2
enableCodeCoverage: !!bool true
testProject: DevProject
enableNoDefaultPackages: !!bool true
- version: 2021.1
enableCodeCoverage: !!bool true
testProject: DevProject
enableNoDefaultPackages: !!bool true
- version: trunk
# Workaround for MLA-1596 - need to make sure we load the right results.
enableCodeCoverage: !!bool false
testProject: DevProject
- version: trunk
# Workaround for MLA-1596 - need to make sure we load the right results.
enableCodeCoverage: !!bool false
testProject: DevProject
- name: win
type: Unity::VM
image: package-ci/win10:stable
flavor: b1.large
- name: mac
type: Unity::VM::osx
image: package-ci/mac:stable
flavor: b1.small
- name: linux
type: Unity::VM
image: package-ci/ubuntu:stable
flavor: b1.medium
- name: win
type: Unity::VM
image: package-ci/win10:stable
flavor: b1.large
- name: mac
type: Unity::VM::osx
image: package-ci/mac:stable
flavor: b1.small
- name: linux
type: Unity::VM
image: package-ci/ubuntu:stable
flavor: b1.medium
- name: com.unity.ml-agents
assembly: Unity.ML-Agents
minCoveragePct: 72
- name: com.unity.ml-agents.extensions
assembly: Unity.ML-Agents.Extensions*
minCoveragePct: 75
- name: com.unity.ml-agents
assembly: Unity.ML-Agents
minCoveragePct: 72
- name: com.unity.ml-agents.extensions
assembly: Unity.ML-Agents.Extensions*
minCoveragePct: 75
name: Run All Combinations of Editors/Platforms Tests
dependencies:
{% for editor in test_editors %}
{% for platform in test_platforms %}
- .yamato/com.unity.ml-agents-test.yml#test_com.unity.ml-agents_{{ platform.name }}_{{ editor.version }}
{% endfor %}
{% endfor %}
{% for editor in trunk_editor %}
{% for platform in test_platforms %}
- .yamato/com.unity.ml-agents-test.yml#test_com.unity.ml-agents_{{ platform.name }}_{{ editor.version }}
{% endfor %}
{% endfor %}
triggers:
cancel_old_ci: true
recurring:
- branch: main
frequency: daily
name: Run All Combinations of Editors/Platforms Tests
dependencies:
{% for coverage_editor in coverage_test_editors %}
{% for coverage_plathform in coverage_test_platforms %}
{% for coverage_package in coverage_test_packages %}
- .yamato/com.unity.ml-agents-coverage.yml#test_coverage_{{ coverage_package.name }}_{{ coverage_platform.name }}_{{ coverage_editor.version }}
{% endfor %}
{% endfor %}
{% endfor %}
{% for editor in test_editors %}
{% for platform in test_platforms %}
- .yamato/com.unity.ml-agents-test.yml#test_com.unity.ml-agents_{{ platform.name }}_{{ editor.version }}
{% endfor %}
{% endfor %}
{% for editor in trunk_editor %}
{% for platform in test_platforms %}
- .yamato/com.unity.ml-agents-test.yml#test_com.unity.ml-agents_{{ platform.name }}_{{ editor.version }}
{% endfor %}
{% endfor %}
triggers:
cancel_old_ci: true
recurring:
- branch: main
frequency: daily
{% for package in packages %}
{% for package in packages %}
{% for platform in test_platforms %}
{% for platform in test_platforms %}
{% if editor.enableCodeCoverage %}
{% capture coverageOptions %} --enable-code-coverage --code-coverage-options 'generateHtmlReport;assemblyFilters:+{{ package.assembly }}'{% endcapture %}
{% else %}
{% assign coverageOptions = "" %}
{% endif %}
{% if editor.enableNoDefaultPackages %}
{% if editor.enableNoDefaultPackages %}
{% else %}
{% else %}
{% endif %}
{% endif %}
name : {{ package.name }} test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
- upm-ci project test -u {{ editor.version }} --project-path {{ editor.testProject }} --package-filter {{ package.name }} {{ coverageOptions }} {{ noDefaultPackagesOptions }} --extra-utr-arg "reruncount=2"
{% if editor.enableCodeCoverage %}
- python3 ml-agents/tests/yamato/check_coverage_percent.py upm-ci~/test-results/ {{ package.minCoveragePct }}
{% endif %}
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
triggers:
cancel_old_ci: true
{% if platform.name == "linux" %}
expression: |
(pull_request.target eq "main" OR
pull_request.target match "release.+") AND
NOT pull_request.draft AND
(pull_request.changes.any match "com.unity.ml-agents/**" OR
pull_request.changes.any match " {{ editor.testProject }}/**" OR
{% if package.name == "com.unity.ml-agents.extensions" %}
pull_request.changes.any match "com.unity.ml-agents.extensions/**" OR
name : {{ package.name }} test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
- upm-ci project test -u {{ editor.version }} --project-path {{ editor.testProject }} --package-filter {{ package.name }} {{ noDefaultPackagesOptions }} --extra-utr-arg "reruncount=2"
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
{% for coverage_editor in coverage_test_editors %}
{% for coverage_plathform in coverage_test_platforms %}
{% for coverage_package in coverage_test_packages %}
- .yamato/com.unity.ml-agents-coverage.yml#test_coverage_{{ coverage_package.name }}_{{ coverage_platform.name }}_{{ coverage_editor.version }}
{% endfor %}
{% endfor %}
{% endfor %}
triggers:
cancel_old_ci: true
{% if platform.name == "linux" %}
expression: |
(pull_request.target eq "main" OR
pull_request.target match "release.+") AND
NOT pull_request.draft AND
(pull_request.changes.any match "com.unity.ml-agents/**" OR
pull_request.changes.any match " {{ editor.testProject }}/**" OR
{% if package.name == "com.unity.ml-agents.extensions" %}
pull_request.changes.any match "com.unity.ml-agents.extensions/**" OR
{% endif %}
pull_request.changes.any match ".yamato/com.unity.ml-agents-test.yml")
pull_request.changes.any match ".yamato/com.unity.ml-agents-test.yml")
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
{% for package in packages %}
{% for package in packages %}
{% for platform in test_platforms %}
{% for platform in test_platforms %}
{% if editor.enableCodeCoverage %}
{% if editor.enableCodeCoverage %}
{% else %}
{% assign coverageOptions = "" %}
{% endif %}
{% else %}
{% assign coverageOptions = "" %}
{% endif %}
name : {{ package.name }} test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- python3 -m pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple --upgrade
- unity-downloader-cli -u trunk -c editor --wait --fast
- npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
- upm-ci project test -u {{ editor.version }} --project-path {{ editor.testProject }} --package-filter {{ package.name }} {{ coverageOptions }} --extra-create-project-arg="-upmNoDefaultPackages" --extra-utr-arg "reruncount=2"
{% if editor.enableCodeCoverage %}
- python3 ml-agents/tests/yamato/check_coverage_percent.py upm-ci~/test-results/ {{ package.minCoveragePct }}
{% endif %}
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
triggers:
cancel_old_ci: true
{% endfor %}
name : {{ package.name }} test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- python3 -m pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple --upgrade
- unity-downloader-cli -u trunk -c editor --wait --fast
- npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
- upm-ci project test -u {{ editor.version }} --project-path {{ editor.testProject }} --package-filter {{ package.name }} {{ coverageOptions }} --extra-create-project-arg="-upmNoDefaultPackages" --extra-utr-arg "reruncount=2"
{% if editor.enableCodeCoverage %}
- python3 ml-agents/tests/yamato/check_coverage_percent.py upm-ci~/test-results/ {{ package.minCoveragePct }}
{% endif %}
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
{% for coverage_editor in coverage_test_editors %}
{% for coverage_plathform in coverage_test_platforms %}
{% for coverage_package in coverage_test_packages %}
- .yamato/com.unity.ml-agents-coverage.yml#test_coverage_{{ coverage_package.name }}_{{ coverage_platform.name }}_{{ coverage_editor.version }}
{% endfor %}
{% endfor %}
{% endfor %}
triggers:
cancel_old_ci: true
{% endfor %}
{% endfor %}
{% endfor %}

6
Project/Assets/ML-Agents/Examples/Basic/Scripts/BasicSensorComponent.cs


{
return new BasicSensor(basicController);
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
return new[] { BasicController.k_Extents };
}
}
/// <summary>

8
Project/Assets/ML-Agents/Examples/SharedAssets/Scripts/SensorBase.cs


{
/// <summary>
/// Write the observations to the output buffer. This size of the buffer will be product
/// of the sizes returned by <see cref="GetObservationShape"/>.
/// of the Shape array values returned by <see cref="ObservationSpec"/>.
/// </summary>
/// <param name="output"></param>
public abstract void WriteObservation(float[] output);

/// <returns>The number of elements written.</returns>
public virtual int Write(ObservationWriter writer)
{
// TODO reuse buffer for similar agents, don't call GetObservationShape()
// TODO reuse buffer for similar agents
var numFloats = this.ObservationSize();
float[] buffer = new float[numFloats];
WriteObservation(buffer);

}
/// <inheritdoc/>
public virtual SensorCompressionType GetCompressionType()
public virtual CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
}
}

4
Project/Assets/ML-Agents/TestScenes/TestCompressedTexture/TestTextureSensor.cs


public void Reset() { }
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return m_CompressionType;
return CompressionSpec.Default();
}
}

16
Project/Assets/ML-Agents/TestScenes/TestCompressedTexture/TestTextureSensorComponent.cs


}
return m_Sensor;
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
var width = TestTexture.width;
var height = TestTexture.height;
var observationShape = new[] { height, width, 3 };
var stacks = ObservationStacks > 1 ? ObservationStacks : 1;
if (stacks > 1)
{
observationShape[2] *= stacks;
}
return observationShape;
}
}

14
com.unity.ml-agents.extensions/Runtime/Match3/Match3Sensor.cs


/// or uncompressed visual observations. Uses AbstractBoard.GetCellType()
/// and AbstractBoard.GetSpecialType() to determine the observation values.
/// </summary>
public class Match3Sensor : ISparseChannelSensor, IBuiltInSensor
public class Match3Sensor : ISensor, IBuiltInSensor
{
private Match3ObservationType m_ObservationType;
private AbstractBoard m_Board;

private int m_Columns;
private int m_NumCellTypes;
private int m_NumSpecialTypes;
private ISparseChannelSensor sparseChannelSensorImplementation;
private int SpecialTypeSize
{

{
}
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
internal SensorCompressionType GetCompressionType()
{
return m_ObservationType == Match3ObservationType.CompressedVisual ?
SensorCompressionType.PNG :

/// <inheritdoc/>
public string GetName()
public CompressionSpec GetCompressionSpec()
return m_Name;
return new CompressionSpec(GetCompressionType(), m_SparseChannelMapping);
public int[] GetCompressedChannelMapping()
public string GetName()
return m_SparseChannelMapping;
return m_Name;
}
/// <inheritdoc/>

14
com.unity.ml-agents.extensions/Runtime/Match3/Match3SensorComponent.cs


return new Match3Sensor(board, ObservationType, SensorName);
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
var board = GetComponent<AbstractBoard>();
if (board == null)
{
return System.Array.Empty<int>();
}
var specialSize = board.NumSpecialTypes == 0 ? 0 : board.NumSpecialTypes + 1;
return ObservationType == Match3ObservationType.Vector ?
new[] { board.Rows * board.Columns * (board.NumCellTypes + specialSize) } :
new[] { board.Rows, board.Columns, board.NumCellTypes + specialSize };
}
}
}

20
com.unity.ml-agents.extensions/Runtime/Sensors/ArticulationBodySensorComponent.cs


return new PhysicsBodySensor(RootBody, Settings, sensorName);
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
if (RootBody == null)
{
return new[] { 0 };
}
// TODO static method in PhysicsBodySensor?
// TODO only update PoseExtractor when body changes?
var poseExtractor = new ArticulationBodyPoseExtractor(RootBody);
var numPoseObservations = poseExtractor.GetNumPoseObservations(Settings);
var numJointObservations = 0;
foreach(var articBody in poseExtractor.GetEnabledArticulationBodies())
{
numJointObservations += ArticulationBodyJointExtractor.NumObservations(articBody, Settings);
}
return new[] { numPoseObservations + numJointObservations };
}
}
}

11
com.unity.ml-agents.extensions/Runtime/Sensors/GridSensor.cs


}
/// <inheritdoc/>
public virtual SensorCompressionType GetCompressionType()
public virtual CompressionSpec GetCompressionSpec()
return CompressionType;
return new CompressionSpec(CompressionType);
}
/// <inheritdoc/>

m_ObservationSpec = ObservationSpec.Visual(GridNumSideX, GridNumSideZ, ObservationPerCell);
}
return m_ObservationSpec;
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
var shape = m_ObservationSpec.Shape;
return new int[] { shape[0], shape[1], shape[2] };
}
/// <inheritdoc/>

10
com.unity.ml-agents.extensions/Runtime/Sensors/PhysicsBodySensor.cs


}
#if UNITY_2020_1_OR_NEWER
public PhysicsBodySensor(ArticulationBody rootBody, PhysicsSensorSettings settings, string sensorName=null)
public PhysicsBodySensor(ArticulationBody rootBody, PhysicsSensorSettings settings, string sensorName = null)
{
var poseExtractor = new ArticulationBodyPoseExtractor(rootBody);
m_PoseExtractor = poseExtractor;

var numJointExtractorObservations = 0;
m_JointExtractors = new List<IJointExtractor>(poseExtractor.NumEnabledPoses);
foreach(var articBody in poseExtractor.GetEnabledArticulationBodies())
foreach (var articBody in poseExtractor.GetEnabledArticulationBodies())
{
var jointExtractor = new ArticulationBodyJointExtractor(articBody);
numJointExtractorObservations += jointExtractor.NumObservations(settings);

var numTransformObservations = m_PoseExtractor.GetNumPoseObservations(settings);
m_ObservationSpec = ObservationSpec.Vector(numTransformObservations + numJointExtractorObservations);
}
#endif
/// <inheritdoc/>

public void Reset() { }
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
/// <inheritdoc/>

{
return BuiltInSensorType.PhysicsBodySensor;
}
}
}

20
com.unity.ml-agents.extensions/Runtime/Sensors/RigidBodySensorComponent.cs


return new PhysicsBodySensor(GetPoseExtractor(), Settings, _sensorName);
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
if (RootBody == null)
{
return new[] { 0 };
}
var poseExtractor = GetPoseExtractor();
var numPoseObservations = poseExtractor.GetNumPoseObservations(Settings);
var numJointObservations = 0;
foreach (var rb in poseExtractor.GetEnabledRigidbodies())
{
var joint = rb.GetComponent<Joint>();
numJointObservations += RigidBodyJointExtractor.NumObservations(rb, joint, Settings);
}
return new[] { numPoseObservations + numJointObservations };
}
/// <summary>
/// Get the DisplayNodes of the hierarchy.
/// </summary>

43
com.unity.ml-agents.extensions/Tests/Editor/Match3/Match3SensorTests.cs


sensorComponent.ObservationType = Match3ObservationType.Vector;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3 * 3 * 2 };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3 * 3 * 2);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
var expectedObs = new float[]
{

sensorComponent.ObservationType = Match3ObservationType.Vector;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3 * 3 * (2 + 3) };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3 * 3 * (2 + 3));
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
var expectedObs = new float[]
{

};
SensorTestHelper.CompareObservation(sensor, expectedObs);
}
[Test]
public void TestVisualObservations()

sensorComponent.ObservationType = Match3ObservationType.UncompressedVisual;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3, 3, 2 };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3, 3, 2);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(SensorCompressionType.None, sensor.GetCompressionType());
Assert.AreEqual(SensorCompressionType.None, sensor.GetCompressionSpec().SensorCompressionType);
var expectedObs = new float[]
{

sensorComponent.ObservationType = Match3ObservationType.UncompressedVisual;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3, 3, 2 + 3 };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3, 3, 2 + 3);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(SensorCompressionType.None, sensor.GetCompressionType());
Assert.AreEqual(SensorCompressionType.None, sensor.GetCompressionSpec().SensorCompressionType);
var expectedObs = new float[]
{

sensorComponent.ObservationType = Match3ObservationType.CompressedVisual;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3, 3, 2 };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3, 3, 2);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(SensorCompressionType.PNG, sensor.GetCompressionType());
Assert.AreEqual(SensorCompressionType.PNG, sensor.GetCompressionSpec().SensorCompressionType);
var pngData = sensor.GetCompressedObservation();
if (WritePNGDataToFile)

Assert.AreEqual(expectedPng, pngData);
}
[Test]
public void TestCompressedVisualObservationsSpecial()
{

sensorComponent.ObservationType = Match3ObservationType.CompressedVisual;
var sensor = sensorComponent.CreateSensor();
var expectedShape = new[] { 3, 3, 2 + 3 };
Assert.AreEqual(expectedShape, sensorComponent.GetObservationShape());
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(3, 3, 2 + 3);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(SensorCompressionType.PNG, sensor.GetCompressionType());
Assert.AreEqual(SensorCompressionType.PNG, sensor.GetCompressionSpec().SensorCompressionType);
var concatenatedPngData = sensor.GetCompressedObservation();
var pathPrefix = "match3obs_special";

}
var expectedPng = LoadPNGs(pathPrefix, 2);
Assert.AreEqual(expectedPng, concatenatedPngData);
}
/// <summary>

}
return bytesOut.ToArray();
}
}
}

19
com.unity.ml-agents.extensions/Tests/Editor/Sensors/ChannelHotShapeTests.cs


1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 1 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 1);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}

1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 2 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 2);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}
[Test]

1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 3 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 3);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}

1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 6 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 6);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}
}

15
com.unity.ml-agents.extensions/Tests/Editor/Sensors/ChannelShapeTests.cs


1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 1 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 1);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}
[Test]

1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 2 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 2);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}
[Test]

1f, 1f, 10, 10, LayerMask.GetMask("Default"), false, colors);
gridSensor.Start();
int[] expectedShape = { 10, 10, 7 };
GridObsTestUtils.AssertArraysAreEqual(expectedShape, gridSensor.GetObservationShape());
var expectedShape = new InplaceArray<int>(10, 10, 7);
Assert.AreEqual(expectedShape, gridSensor.GetObservationSpec().Shape);
}
}
}

27
com.unity.ml-agents.extensions/Tests/Editor/Sensors/GridSensorTestUtils.cs


return duplicated;
}
/// <summary>
/// Asserts that 2 int arrays are the same
/// </summary>
/// <param name="expected">The expected array</param>
/// <param name="actual">The actual array</param>
public static void AssertArraysAreEqual(int[] expected, int[] actual)
{
Assert.AreEqual(expected.Length, actual.Length, "Lengths are not the same");
for (int i = 0; i < actual.Length; i++)
{
Assert.AreEqual(expected[i], actual[i], "Got " + Array2Str(actual) + ", expected " + Array2Str(expected));
}
}
/// <summary>
/// Asserts that 2 float arrays are the same
/// </summary>
/// <param name="expected">The expected array</param>
/// <param name="actual">The actual array</param>
public static void AssertArraysAreEqual(float[] expected, float[] actual)
{
Assert.AreEqual(expected.Length, actual.Length, "Lengths are not the same");
for (int i = 0; i < actual.Length; i++)
{
Assert.AreEqual(expected[i], actual[i], "Got " + Array2Str(actual) + ", expected " + Array2Str(expected));
}
}
/// <summary>
/// Asserts that the sub-arrays of the total array are equal to specific subarrays at specific subarray indicies and equal to a default everywhere else.

6
com.unity.ml-agents.extensions/Tests/Runtime/Sensors/ArticulationBodySensorTests.cs


0f, 0f, 0f, 1f // LocalSpaceRotations
};
SensorTestHelper.CompareObservation(sensor, expected);
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
}
[Test]

#endif
};
SensorTestHelper.CompareObservation(sensor, expected);
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
// Update the settings to only process joint observations
sensorComponent.Settings = new PhysicsSensorSettings

0f, // joint2.force
};
SensorTestHelper.CompareObservation(sensor, expected);
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
}
}
}

6
com.unity.ml-agents.extensions/Tests/Runtime/Sensors/RigidBodySensorTests.cs


// The root body is ignored since it always generates identity values
// and there are no other bodies to generate observations.
var expected = new float[0];
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
SensorTestHelper.CompareObservation(sensor, expected);
}

-1f, 1f, 0f, // Attached vel
0f, -1f, 1f // Leaf vel
};
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
SensorTestHelper.CompareObservation(sensor, expected);
// Update the settings to only process joint observations

0f, 0f, 0f, // joint2.torque
};
SensorTestHelper.CompareObservation(sensor, expected);
Assert.AreEqual(expected.Length, sensorComponent.GetObservationShape()[0]);
Assert.AreEqual(expected.Length, sensorComponent.CreateSensor().GetObservationSpec().Shape[0]);
}
}

3
com.unity.ml-agents.extensions/package.json


"unity": "2019.4",
"description": "A source-only package for new features based on ML-Agents",
"dependencies": {
"com.unity.ml-agents": "2.0.0-exp.1"
"com.unity.ml-agents": "2.0.0-exp.1",
"com.unity.modules.physics": "1.0.0"
}
}

8
com.unity.ml-agents/CHANGELOG.md


`WriteMask(int branch, IEnumerable<int> actionIndices)` was replaced with
`SetActionEnabled(int branch, int actionIndex, bool isEnabled)`. (#5060)
- IActuator now implements IHeuristicProvider. (#5110)
- `ISensor.GetObservationShape()` was removed, and `GetObservationSpec()` was added. (#5127)
- `ISensor.GetObservationShape()` was removed, and `GetObservationSpec()` was added. The `ITypedSensor`
and `IDimensionPropertiesSensor` interfaces were removed. (#5127)
- `ISensor.GetCompressionType()` was removed, and `GetCompressionSpec()` was added. The `ISparseChannelSensor`
interface was removed. (#5164)
- The abstract method `SensorComponent.GetObservationShape()` was no longer being called, so it has been removed. (#5172)
#### ml-agents / ml-agents-envs / gym-unity (Python)

- The `.onnx` models discrete action output now contains the discrete actions values and not the logits. Models created with this version will not be usable with previous versions of the package (#5080)
- Added ML-Agents package settings. (#5027)
- Make com.unity.modules.unityanalytics an optional dependency. (#5109)
- Make com.unity.modules.physics and com.unity.modules.physics2d optional dependencies. (#5112)
#### ml-agents / ml-agents-envs / gym-unity (Python)
### Bug Fixes

16
com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs


protected void OnRayPerceptionInspectorGUI(bool is3d)
{
#if !MLA_UNITY_PHYSICS_MODULE
if (is3d)
{
EditorGUILayout.HelpBox("The Physics Module is not currently present. " +
"Please add it to your project in order to use the Ray Perception APIs in the " +
$"{nameof(RayPerceptionSensorComponent3D)}", MessageType.Warning);
}
#endif
#if !MLA_UNITY_PHYSICS2D_MODULE
if (!is3d)
{
EditorGUILayout.HelpBox("The Physics2D Module is not currently present. " +
"Please add it to your project in order to use the Ray Perception APIs in the " +
$"{nameof(RayPerceptionSensorComponent3D)}", MessageType.Warning);
}
#endif
var so = serializedObject;
so.Update();

14
com.unity.ml-agents/Editor/Unity.ML-Agents.Editor.asmdef


"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.modules.physics",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS_MODULE"
},
{
"name": "com.unity.modules.physics2d",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS2D_MODULE"
}
]
}

14
com.unity.ml-agents/Runtime/Agent.cs


internal class AgentVectorActuator : VectorActuator
{
public AgentVectorActuator(IActionReceiver actionReceiver,
IHeuristicProvider heuristicProvider,
ActionSpec actionSpec,
string name = "VectorActuator"
IHeuristicProvider heuristicProvider,
ActionSpec actionSpec,
string name = "VectorActuator"
) : base(actionReceiver, heuristicProvider, actionSpec, name)
{ }

/// <param name="reward">The new value of the reward.</param>
public void SetReward(float reward)
{
#if DEBUG
#endif
m_CumulativeReward += (reward - m_Reward);
m_Reward = reward;
}

/// <param name="increment">Incremental reward value.</param>
public void AddReward(float increment)
{
#if DEBUG
#endif
m_Reward += increment;
m_CumulativeReward += increment;
}

#if DEBUG
#endif
#if DEBUG
#endif
m_GroupReward += increment;
}

2
com.unity.ml-agents/Runtime/Analytics/Events.cs


return new EventObservationSpec
{
SensorName = sensor.GetName(),
CompressionType = sensor.GetCompressionType().ToString(),
CompressionType = sensor.GetCompressionSpec().SensorCompressionType.ToString(),
BuiltInSensorType = (int)builtInSensorType,
DimensionInfos = dimInfos,
};

18
com.unity.ml-agents/Runtime/Analytics/InferenceAnalytics.cs


const int k_MaxNumberOfElements = 1000;
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
#endif
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
#elif MLA_UNITY_ANALYTICS_MODULE
AnalyticsResult result = AnalyticsResult.UnsupportedPlatform;
if (result == AnalyticsResult.Ok)
{
s_EventRegistered = true;
}
#endif
#else // no editor, no analytics
s_EventRegistered = false;
#endif
return s_EventRegistered;
}

IList<IActuator> actuators
)
{
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
// The event shouldn't be able to report if this is disabled but if we know we're not going to report
// Lets early out and not waste time gathering all the data
if (!IsAnalyticsEnabled())

var data = GetEventForModel(nnModel, behaviorName, inferenceDevice, sensors, actionSpec, actuators);
// Note - to debug, use JsonUtility.ToJson on the event.
// Debug.Log(JsonUtility.ToJson(data, true));
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
#else
return;
#endif
}

29
com.unity.ml-agents/Runtime/Analytics/TrainingAnalytics.cs


namespace Unity.MLAgents.Analytics
{
internal class TrainingAnalytics
internal static class TrainingAnalytics
{
const string k_VendorKey = "unity.ml-agents";
const string k_TrainingEnvironmentInitializedEventName = "ml_agents_training_environment_initialized";

};
/// <summary>
/// Whether or not we've registered this particular event yet
/// </summary>
static bool s_EventsRegistered = false;
/// <summary>
/// Hourly limit for this event name
/// </summary>
const int k_MaxEventsPerHour = 1000;

const int k_MaxNumberOfElements = 1000;
private static bool s_SentEnvironmentInitialized;
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
/// <summary>
/// Whether or not we've registered this particular event yet
/// </summary>
static bool s_EventsRegistered = false;
#endif
private static Guid s_TrainingSessionGuid;

static bool EnableAnalytics()
internal static bool EnableAnalytics()
#if MLA_UNITY_ANALYTICS_MODULE
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
if (s_EventsRegistered)
{
return true;

#if UNITY_EDITOR
#else
return false;
#endif // UNITY_EDITOR
}
s_EventsRegistered = true;

IList<IActuator> actuators
)
{
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
if (!IsAnalyticsEnabled())
return;

// Debug.Log(
// $"Would send event {k_RemotePolicyInitializedEventName} with body {JsonUtility.ToJson(data, true)}"
// );
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
if (AnalyticsUtils.s_SendEditorAnalytics)
{
EditorAnalytics.SendEventWithLimit(k_RemotePolicyInitializedEventName, data);

[Conditional("MLA_UNITY_ANALYTICS_MODULE")]
public static void TrainingBehaviorInitialized(TrainingBehaviorInitializedEvent tbiEvent)
{
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
if (!IsAnalyticsEnabled())
return;

// Debug.Log(
// $"Would send event {k_TrainingBehaviorInitializedEventName} with body {JsonUtility.ToJson(tbiEvent, true)}"
// );
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
#else
return;
#endif
}

44
com.unity.ml-agents/Runtime/Communicator/GrpcExtensions.cs


var obsSpec = sensor.GetObservationSpec();
var shape = obsSpec.Shape;
ObservationProto observationProto = null;
var compressionType = sensor.GetCompressionType();
var compressionSpec = sensor.GetCompressionSpec();
var compressionType = compressionSpec.SensorCompressionType;
// Check capabilities if we need to concatenate PNGs
if (compressionType == SensorCompressionType.PNG && shape.Length == 3 && shape[2] > 3)
{

if (compressionType != SensorCompressionType.None && shape.Length == 3 && shape[2] > 3)
{
var trainerCanHandleMapping = Academy.Instance.TrainerCapabilities == null || Academy.Instance.TrainerCapabilities.CompressedChannelMapping;
var isTrivialMapping = IsTrivialMapping(sensor);
var isTrivialMapping = compressionSpec.IsTrivialMapping();
if (!trainerCanHandleMapping && !isTrivialMapping)
{
if (!s_HaveWarnedTrainerCapabilitiesMapping)

throw new UnityAgentsException(
$"GetCompressedObservation() returned null data for sensor named {sensor.GetName()}. " +
"You must return a byte[]. If you don't want to use compressed observations, " +
"return SensorCompressionType.None from GetCompressionType()."
"return CompressionSpec.Default() from GetCompressionSpec()."
CompressionType = (CompressionTypeProto)sensor.GetCompressionType(),
CompressionType = (CompressionTypeProto)sensor.GetCompressionSpec().SensorCompressionType,
var compressibleSensor = sensor as ISparseChannelSensor;
if (compressibleSensor != null)
if (compressionSpec.CompressedChannelMapping != null)
observationProto.CompressedChannelMapping.AddRange(compressibleSensor.GetCompressedChannelMapping());
observationProto.CompressedChannelMapping.AddRange(compressionSpec.CompressedChannelMapping);
}
}

};
}
internal static bool IsTrivialMapping(ISensor sensor)
{
var compressibleSensor = sensor as ISparseChannelSensor;
if (compressibleSensor is null)
{
return true;
}
var mapping = compressibleSensor.GetCompressedChannelMapping();
if (mapping == null)
{
return true;
}
// check if mapping equals zero mapping
if (mapping.Length == 3 && mapping.All(m => m == 0))
{
return true;
}
// check if mapping equals identity mapping
for (var i = 0; i < mapping.Length; i++)
{
if (mapping[i] != i)
{
return false;
}
}
return true;
}
#region Analytics
internal static TrainingEnvironmentInitializedEvent ToTrainingEnvironmentInitializedEvent(
this TrainingEnvironmentInitialized inputProto)

NumNetworkHiddenUnits = inputProto.NumNetworkHiddenUnits,
};
}
#endregion
#endregion
}
}

20
com.unity.ml-agents/Runtime/Policies/BarracudaPolicy.cs


using Unity.Barracuda;
using System.Collections.Generic;
using System.Diagnostics;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Inference;
using Unity.MLAgents.Sensors;

/// </summary>
private bool m_AnalyticsSent;
/// <inheritdoc />
/// <summary>
/// Instantiate a BarracudaPolicy with the necessary objects for it to run.
/// </summary>
/// <param name="actionSpec">The action spec of the behavior.</param>
/// <param name="actuators">The actuators used for this behavior.</param>
/// <param name="model">The Neural Network to use.</param>
/// <param name="inferenceDevice">Which device Barracuda will run on.</param>
/// <param name="behaviorName">The name of the behavior.</param>
public BarracudaPolicy(
ActionSpec actionSpec,
IList<IActuator> actuators,

/// <inheritdoc />
public void RequestDecision(AgentInfo info, List<ISensor> sensors)
{
SendAnalytics(sensors);
m_AgentId = info.episodeId;
m_ModelRunner?.PutObservations(info, sensors);
}
[Conditional("MLA_UNITY_ANALYTICS_MODULE")]
void SendAnalytics(IList<ISensor> sensors)
{
if (!m_AnalyticsSent)
{
m_AnalyticsSent = true;

m_Actuators
);
}
m_AgentId = info.episodeId;
m_ModelRunner?.PutObservations(info, sensors);
}
/// <inheritdoc />

4
com.unity.ml-agents/Runtime/Policies/HeuristicPolicy.cs


/// Trivial implementation of the IList interface that does nothing.
/// This is only used for "writing" observations that we will discard.
/// </summary>
class NullList : IList<float>
internal class NullList : IList<float>
{
public IEnumerator<float> GetEnumerator()
{

{
foreach (var sensor in sensors)
{
if (sensor.GetCompressionType() == SensorCompressionType.None)
if (sensor.GetCompressionSpec().SensorCompressionType == SensorCompressionType.None)
{
m_ObservationWriter.SetTarget(m_NullList, sensor.GetObservationSpec(), 0);
sensor.Write(m_ObservationWriter);

14
com.unity.ml-agents/Runtime/Policies/RemotePolicy.cs


using System.Diagnostics;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Analytics;

string m_FullyQualifiedBehaviorName;
ActionSpec m_ActionSpec;
ActionBuffers m_LastActionBuffer;
private bool m_AnalyticsSent = false;
bool m_AnalyticsSent;
internal ICommunicator m_Communicator;

/// <inheritdoc />
public void RequestDecision(AgentInfo info, List<ISensor> sensors)
{
SendAnalytics(sensors);
m_AgentId = info.episodeId;
m_Communicator?.PutObservations(m_FullyQualifiedBehaviorName, info, sensors);
}
[Conditional("MLA_UNITY_ANALYTICS_MODULE")]
void SendAnalytics(IList<ISensor> sensors)
{
if (!m_AnalyticsSent)
{
m_AnalyticsSent = true;

m_Actuators
);
}
m_AgentId = info.episodeId;
m_Communicator?.PutObservations(m_FullyQualifiedBehaviorName, info, sensors);
}
/// <inheritdoc />

6
com.unity.ml-agents/Runtime/Sensors/BufferSensor.cs


}
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
/// <inheritdoc/>

{
return BuiltInSensorType.BufferSensor;
}
}

6
com.unity.ml-agents/Runtime/Sensors/BufferSensorComponent.cs


return m_Sensor;
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
return new[] { MaxNumObservables, ObservableSize };
}
/// <summary>
/// Appends an observation to the buffer. If the buffer is full (maximum number
/// of observation is reached) the observation will be ignored. the length of

5
com.unity.ml-agents/Runtime/Sensors/CameraSensor.cs


public void Reset() { }
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return m_CompressionType;
return new CompressionSpec(m_CompressionType);
}
/// <summary>

{
return BuiltInSensorType.CameraSensor;
}
}
}

15
com.unity.ml-agents/Runtime/Sensors/CameraSensorComponent.cs


}
/// <summary>
/// Computes the observation shape of the sensor.
/// </summary>
/// <returns>The observation shape of the associated <see cref="CameraSensor"/> object.</returns>
public override int[] GetObservationShape()
{
var stacks = ObservationStacks > 1 ? ObservationStacks : 1;
var cameraSensorshape = CameraSensor.GenerateShape(m_Width, m_Height, Grayscale);
if (stacks > 1)
{
cameraSensorshape[cameraSensorshape.Length - 1] *= stacks;
}
return cameraSensorshape;
}
/// <summary>
/// Update fields that are safe to change on the Sensor at runtime.
/// </summary>
internal void UpdateSensor()

24
com.unity.ml-agents/Runtime/Sensors/ISensor.cs


namespace Unity.MLAgents.Sensors
{
/// <summary>
/// The compression setting for visual/camera observations.
/// </summary>
public enum SensorCompressionType
{
/// <summary>
/// No compression. Data is preserved as float arrays.
/// </summary>
None,
/// <summary>
/// PNG format. Data will be stored in binary format.
/// </summary>
PNG
}
/// <summary>
/// The Dimension property flags of the observations
/// </summary>
[System.Flags]

void Reset();
/// <summary>
/// Return the compression type being used. If no compression is used, return
/// <see cref="SensorCompressionType.None"/>.
/// Return information on the compression type being used. If no compression is used, return
/// <see cref="CompressionSpec.Default()"/>.
/// <returns>Compression type used by the sensor.</returns>
SensorCompressionType GetCompressionType();
/// <returns>CompressionSpec used by the sensor.</returns>
CompressionSpec GetCompressionSpec();
/// <summary>
/// Get the name of the sensor. This is used to ensure deterministic sorting of the sensors

14
com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs


}
/// <inheritdoc/>
public virtual SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
/// <inheritdoc/>

unscaledCastRadius;
// Do the cast and assign the hit information for each detectable tag.
bool castHit;
float hitFraction;
GameObject hitObject;
var castHit = false;
var hitFraction = 1.0f;
GameObject hitObject = null;
#if MLA_UNITY_PHYSICS_MODULE
RaycastHit rayHit;
if (scaledCastRadius > 0f)
{

// To avoid 0/0, set the fraction to 0.
hitFraction = castHit ? (scaledRayLength > 0 ? rayHit.distance / scaledRayLength : 0.0f) : 1.0f;
hitObject = castHit ? rayHit.collider.gameObject : null;
#endif
#if MLA_UNITY_PHYSICS2D_MODULE
RaycastHit2D rayHit;
if (scaledCastRadius > 0f)
{

castHit = rayHit;
hitFraction = castHit ? rayHit.fraction : 1.0f;
hitObject = castHit ? rayHit.collider.gameObject : null;
#endif
}
var rayOutput = new RayPerceptionOutput.RayOutput

2
com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponent2D.cs


public RayPerceptionSensorComponent2D()
{
// Set to the 2D defaults (just in case they ever diverge).
RayLayerMask = Physics2D.DefaultRaycastLayers;
RayLayerMask = -5;
}
/// <inheritdoc/>

18
com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs


set { m_RayLength = value; UpdateSensor(); }
}
// The value of the default layers.
const int k_PhysicsDefaultLayers = -5;
LayerMask m_RayLayerMask = Physics.DefaultRaycastLayers;
LayerMask m_RayLayerMask = k_PhysicsDefaultLayers;
/// <summary>
/// Controls which layers the rays can hit.

anglesOut[2 * i + 2] = 90 + (i + 1) * delta;
}
return anglesOut;
}
/// <summary>
/// Returns the observation shape for this raycast sensor which depends on the number
/// of tags for detected objects and the number of rays.
/// </summary>
/// <returns></returns>
public override int[] GetObservationShape()
{
var numRays = 2 * RaysPerDirection + 1;
var numTags = m_DetectableTags?.Count ?? 0;
var obsSize = (numTags + 2) * numRays;
var stacks = ObservationStacks > 1 ? ObservationStacks : 1;
return new[] { obsSize * stacks };
}
/// <summary>

5
com.unity.ml-agents/Runtime/Sensors/Reflection/ReflectionSensorBase.cs


public void Reset() { }
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
/// <inheritdoc/>

{
return BuiltInSensorType.ReflectionSensor;
}
}
}

5
com.unity.ml-agents/Runtime/Sensors/RenderTextureSensor.cs


public void Reset() { }
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return m_CompressionType;
return new CompressionSpec(m_CompressionType);
}
/// <inheritdoc/>

}
/// <summary>
/// Converts a RenderTexture to a 2D texture.

16
com.unity.ml-agents/Runtime/Sensors/RenderTextureSensorComponent.cs


return m_Sensor;
}
/// <inheritdoc/>
public override int[] GetObservationShape()
{
var width = RenderTexture != null ? RenderTexture.width : 0;
var height = RenderTexture != null ? RenderTexture.height : 0;
var observationShape = new[] { height, width, Grayscale ? 1 : 3 };
var stacks = ObservationStacks > 1 ? ObservationStacks : 1;
if (stacks > 1)
{
observationShape[2] *= stacks;
}
return observationShape;
}
/// <summary>
/// Update fields that are safe to change on the Sensor at runtime.
/// </summary>

7
com.unity.ml-agents/Runtime/Sensors/SensorComponent.cs


/// </summary>
/// <returns>Created ISensor object.</returns>
public abstract ISensor CreateSensor();
/// <summary>
/// Returns the shape of the sensor observations that will be created.
/// </summary>
/// <returns>Shape of the sensor observation.</returns>
public abstract int[] GetObservationShape();
}
}

27
com.unity.ml-agents/Runtime/Sensors/StackingSensor.cs


/// Internally, a circular buffer of arrays is used. The m_CurrentIndex represents the most recent observation.
/// Currently, observations are stacked on the last dimension.
/// </summary>
public class StackingSensor : ISparseChannelSensor, IBuiltInSensor
public class StackingSensor : ISensor, IBuiltInSensor
{
/// <summary>
/// The wrapped sensor.

m_StackedObservations[i] = new float[m_UnstackedObservationSize];
}
if (m_WrappedSensor.GetCompressionType() != SensorCompressionType.None)
if (m_WrappedSensor.GetCompressionSpec().SensorCompressionType != SensorCompressionType.None)
{
m_StackedCompressedObservations = new byte[numStackedObservations][];
m_EmptyCompressedObservation = CreateEmptyPNG();

{
Array.Clear(m_StackedObservations[i], 0, m_StackedObservations[i].Length);
}
if (m_WrappedSensor.GetCompressionType() != SensorCompressionType.None)
if (m_WrappedSensor.GetCompressionSpec().SensorCompressionType != SensorCompressionType.None)
{
for (var i = 0; i < m_NumStackedObservations; i++)
{

return outputBytes;
}
/// <inheritdoc/>
public int[] GetCompressedChannelMapping()
public CompressionSpec GetCompressionSpec()
return m_CompressionMapping;
}
/// <inheritdoc/>
public SensorCompressionType GetCompressionType()
{
return m_WrappedSensor.GetCompressionType();
var wrappedSpec = m_WrappedSensor.GetCompressionSpec();
return new CompressionSpec(wrappedSpec.SensorCompressionType, m_CompressionMapping);
}
/// <summary>

}
/// <summary>
/// Constrct stacked CompressedChannelMapping.
/// Construct stacked CompressedChannelMapping.
/// </summary>
internal int[] ConstructStackedCompressedChannelMapping(ISensor wrappedSenesor)
{

int[] wrappedMapping = null;
int wrappedNumChannel = m_WrappedSpec.Shape[2];
var sparseChannelSensor = m_WrappedSensor as ISparseChannelSensor;
if (sparseChannelSensor != null)
{
wrappedMapping = sparseChannelSensor.GetCompressedChannelMapping();
}
wrappedMapping = wrappedSenesor.GetCompressionSpec().CompressedChannelMapping;
if (wrappedMapping == null)
{
if (wrappedNumChannel == 1)

6
com.unity.ml-agents/Runtime/Sensors/VectorSensor.cs


}
/// <inheritdoc/>
public virtual SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
/// <inheritdoc/>

void AddFloatObs(float obs)
{
#if DEBUG
#endif
m_Observations.Add(obs);
}

10
com.unity.ml-agents/Runtime/Unity.ML-Agents.asmdef


"name": "com.unity.modules.unityanalytics",
"expression": "1.0.0",
"define": "MLA_UNITY_ANALYTICS_MODULE"
},
{
"name": "com.unity.modules.physics",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS_MODULE"
},
{
"name": "com.unity.modules.physics2d",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS2D_MODULE"
}
]
}

7
com.unity.ml-agents/Runtime/Utilities.cs


using System;
using System.Diagnostics;
/// <summary>
/// Calculates the cumulative sum of an integer array. The result array will be one element
/// larger than the input array since it has a padded 0 at the beginning.

return result;
}
#if DEBUG
[Conditional("DEBUG")]
if (float.IsNaN(value))
{
throw new ArgumentException($"NaN {valueCategory} passed to {caller}.");

throw new ArgumentException($"Inifinity {valueCategory} passed to {caller}.");
}
}
#endif
}

12
com.unity.ml-agents/Tests/Editor/Analytics/TrainingAnalyticsTest.cs


using Unity.MLAgents.Actuators;
using Unity.MLAgents.Analytics;
using Unity.MLAgents.Policies;
using UnityEditor;
namespace Unity.MLAgents.Tests.Analytics
{

Assert.AreEqual(2, remotePolicyEvent.ActuatorInfos[0].NumContinuousActions);
Assert.AreEqual(0, remotePolicyEvent.ActuatorInfos[0].NumDiscreteActions);
}
[Test]

}
Academy.Instance.Dispose();
}
[Test]
public void TestEnableAnalytics()
{
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE
Assert.IsTrue(EditorAnalytics.enabled == TrainingAnalytics.EnableAnalytics());
#else
Assert.IsFalse(TrainingAnalytics.EnableAnalytics());
#endif
}
}
}

137
com.unity.ml-agents/Tests/Editor/Communicator/GrpcExtensionsTests.cs


using System;
using System.Text.RegularExpressions;
using Google.Protobuf;
using Google.Protobuf.Collections;
using NUnit.Framework;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Demonstrations;

using Unity.MLAgents.Analytics;
using Unity.MLAgents.CommunicatorObjects;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.MLAgents.Tests
{

[SetUp]
public void SetUp()
{
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities();
}
[Test]
public void TestDefaultBrainParametersToProto()
{

Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
brain.ToProto("foo", false);
}
[Test]

var actionSpec = new ActionSpec();
actionSpec.ToBrainParametersProto("foo", false);
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false);
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false);
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false);
}
[Test]
public void ToBrainParameters()
{
// Should be able to convert a default instance to proto.
var actionSpec = new ActionSpec();
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities();
// Continuous
actionSpec = ActionSpec.MakeContinuous(3);
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities();
// Discrete
actionSpec = ActionSpec.MakeDiscrete(1, 2, 3);
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
HybridActions = false
};
actionSpec.ToBrainParametersProto("foo", false).ToBrainParameters();
}
[Test]

var agentInfo = new AgentInfo();
agentInfo.ToInfoActionPairProto();
var pairProto = agentInfo.ToInfoActionPairProto();
pairProto.AgentInfo.Observations.Add(new ObservationProto
{
CompressedData = ByteString.Empty,
CompressionType = CompressionTypeProto.None,
FloatData = new ObservationProto.Types.FloatData(),
ObservationType = ObservationTypeProto.Default,
Name = "Sensor"
});
pairProto.AgentInfo.Observations[0].Shape.Add(0);
pairProto.GetObservationSummaries();
agentInfo.ToAgentInfoProto();
agentInfo.groupId = 1;
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
MultiAgentGroups = false
};
agentInfo.ToAgentInfoProto();
LogAssert.Expect(LogType.Warning, new Regex(".+"));
Academy.Instance.TrainerCapabilities = new UnityRLCapabilities
{
BaseRLCapabilities = true,
MultiAgentGroups = true
};
agentInfo.ToAgentInfoProto();
}

public void Reset() { }
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return CompressionType;
return new CompressionSpec(CompressionType);
}
}
class DummySparseChannelSensor : DummySensor, ISparseChannelSensor
{
public int[] Mapping;
internal DummySparseChannelSensor()
{
}
public int[] GetCompressedChannelMapping()
{
return Mapping;
}
}

Assert.AreEqual(obsProto.CompressedData.Length, 0);
}
}
[Test]
public void TestIsTrivialMapping()
{
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(new DummySensor()), true);
var sparseChannelSensor = new DummySparseChannelSensor();
sparseChannelSensor.Mapping = null;
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(sparseChannelSensor), true);
sparseChannelSensor.Mapping = new[] { 0, 0, 0 };
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(sparseChannelSensor), true);
sparseChannelSensor.Mapping = new[] { 0, 1, 2, 3, 4 };
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(sparseChannelSensor), true);
sparseChannelSensor.Mapping = new[] { 1, 2, 3, 4, -1, -1 };
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(sparseChannelSensor), false);
sparseChannelSensor.Mapping = new[] { 0, 0, 0, 1, 1, 1 };
Assert.AreEqual(GrpcExtensions.IsTrivialMapping(sparseChannelSensor), false);
}
[Test]
public void TestDefaultTrainingEvents()
{

11
com.unity.ml-agents/Tests/Editor/Inference/ParameterLoaderTest.cs


{
return Sensor;
}
}
public override int[] GetObservationShape()
{
var shape = Sensor.GetObservationSpec().Shape;
return new int[] { shape[0], shape[1], shape[2] };
}
}
public class Test3DSensor : ISensor, IBuiltInSensor
{
int m_Width;

public void Update() { }
public void Reset() { }
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
public string GetName()

40
com.unity.ml-agents/Tests/Editor/MLAgentsEditModeTest.cs


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NUnit.Framework;
using System.Reflection;

agent1.collectObservationsCallsForEpisode,
agent1.GetStoredActionBuffers().ContinuousActions[0]
);
}
[Test]
public void TestNullList()
{
var nullList = new HeuristicPolicy.NullList();
Assert.Throws<NotImplementedException>(() =>
{
_ = ((IEnumerable<float>)nullList).GetEnumerator();
});
Assert.Throws<NotImplementedException>(() =>
{
_ = ((IEnumerable)nullList).GetEnumerator();
});
Assert.Throws<NotImplementedException>(() =>
{
nullList.CopyTo(new[] { 0f }, 0);
});
nullList.Add(0);
Assert.IsTrue(nullList.Count == 0);
nullList.Clear();
Assert.IsTrue(nullList.Count == 0);
nullList.Add(0);
Assert.IsFalse(nullList.Contains(0));
Assert.IsFalse(nullList.Remove(0));
Assert.IsFalse(nullList.IsReadOnly);
Assert.IsTrue(nullList.IndexOf(0) == -1);
nullList.Insert(0, 0);
Assert.IsFalse(nullList.Count > 0);
nullList.RemoveAt(0);
Assert.IsTrue(nullList.Count == 0);
Assert.IsTrue(Mathf.Approximately(0f, nullList[0]));
Assert.IsTrue(Mathf.Approximately(0f, nullList[1]));
}
}

2
com.unity.ml-agents/Tests/Editor/PublicAPI/PublicApiValidation.cs


Assert.IsTrue(sensorComponent.Grayscale);
}
#if MLA_UNITY_PHYSICS_MODULE
[Test]
public void CheckSetupRayPerceptionSensorComponent()
{

sensorComponent.CreateSensor();
}
#endif
}
}

10
com.unity.ml-agents/Tests/Editor/Unity.ML-Agents.Editor.Tests.asmdef


"name": "com.unity.modules.unityanalytics",
"expression": "1.0.0",
"define": "MLA_UNITY_ANALYTICS_MODULE"
},
{
"name": "com.unity.modules.physics",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS_MODULE"
},
{
"name": "com.unity.modules.physics2d",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS2D_MODULE"
}
]
}

24
com.unity.ml-agents/Tests/Runtime/RuntimeAPITest.cs


#if UNITY_INCLUDE_TESTS
using System.Collections;
using System.Collections.Generic;
using Unity.MLAgents;

{
var wrappedSensor = wrappedComponent.CreateSensor();
return new StackingSensor(wrappedSensor, numStacks);
}
public override int[] GetObservationShape()
{
int[] shape = (int[])wrappedComponent.GetObservationShape().Clone();
for (var i = 0; i < shape.Length; i++)
{
shape[i] *= numStacks;
}
return shape;
}
}

behaviorParams.BrainParameters.VectorObservationSize = 3;
behaviorParams.BrainParameters.NumStackedVectorObservations = 2;
behaviorParams.BrainParameters.VectorActionDescriptions = new[] { "Continuous1", "TestActionA", "TestActionB" };
behaviorParams.BrainParameters.ActionSpec = new ActionSpec(1, new []{2, 2});
behaviorParams.BrainParameters.ActionSpec = new ActionSpec(1, new[] { 2, 2 });
behaviorParams.BehaviorName = "TestBehavior";
behaviorParams.TeamId = 42;
behaviorParams.UseChildSensors = true;

// Can't actually create an Agent with InferenceOnly and no model, so change back
behaviorParams.BehaviorType = BehaviorType.Default;
#if MLA_UNITY_PHSYICS_MODULE
var sensorComponent = gameObject.AddComponent<RayPerceptionSensorComponent3D>();
sensorComponent.SensorName = "ray3d";
sensorComponent.DetectableTags = new List<string> { "Player", "Respawn" };

// ISensor isn't set up yet.
Assert.IsNull(sensorComponent.RaySensor);
#endif
// Make sure we can set the behavior type correctly after the agent is initialized

decisionRequester.DecisionPeriod = 2;
decisionRequester.TakeActionsBetweenDecisions = true;
#if MLA_UNITY_PHSYICS_MODULE
#endif
// Let's change the inference device
var otherDevice = behaviorParams.InferenceDevice == InferenceDevice.CPU ? InferenceDevice.GPU : InferenceDevice.CPU;
agent.SetModel(behaviorParams.BehaviorName, behaviorParams.Model, otherDevice);

var actions = agent.GetStoredActionBuffers().DiscreteActions;
// default Heuristic implementation should return zero actions.
Assert.AreEqual(new ActionSegment<int>(new[] {0, 0}), actions);
Assert.AreEqual(new ActionSegment<int>(new[] { 0, 0 }), actions);
Assert.AreEqual(1, agent.numHeuristicCalls);
Academy.Instance.EnvironmentStep();

}
}
}
#endif

4
com.unity.ml-agents/Tests/Runtime/Sensor/BufferSensorTest.cs


bufferComponent.SensorName = "TestName";
var sensor = bufferComponent.CreateSensor();
var shape = bufferComponent.GetObservationShape();
var shape = sensor.GetObservationSpec().Shape;
Assert.AreEqual(shape[0], 20);
Assert.AreEqual(shape[1], 4);

var obsWriter = new ObservationWriter();
var obs = sensor.GetObservationProto(obsWriter);
Assert.AreEqual(shape, obs.Shape);
Assert.AreEqual(shape, InplaceArray<int>.FromList(obs.Shape));
Assert.AreEqual(obs.DimensionProperties.Count, 2);
Assert.AreEqual(sensor.GetName(), "TestName");

7
com.unity.ml-agents/Tests/Runtime/Sensor/CameraSensorComponentTest.cs


cameraComponent.Grayscale = grayscale;
cameraComponent.CompressionType = compression;
var expectedShape = new[] { height, width, grayscale ? 1 : 3 };
Assert.AreEqual(expectedShape, cameraComponent.GetObservationShape());
var expectedShapeInplace = new InplaceArray<int>(height, width, grayscale ? 1 : 3);
Assert.AreEqual(expectedShapeInplace, sensor.GetObservationSpec().Shape);
var expectedShape = new InplaceArray<int>(height, width, grayscale ? 1 : 3);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(typeof(CameraSensor), sensor.GetType());
}
}

4
com.unity.ml-agents/Tests/Runtime/Sensor/FloatVisualSensorTests.cs


public void Update() { }
public void Reset() { }
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
}
}

16
com.unity.ml-agents/Tests/Runtime/Sensor/RayPerceptionSensorTests.cs


using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.TestTools;
namespace Unity.MLAgents.Tests
{

public class RayPerception3DTests
{
[Test]
public void TestDefaultLayersAreNegativeFive()
{
#if MLA_UNITY_PHYSICS_MODULE
Assert.IsTrue(Physics.DefaultRaycastLayers == -5);
#endif
#if MLA_UNITY_PHYSICS2D_MODULE
Assert.IsTrue(Physics2D.DefaultRaycastLayers == -5);
#endif
}
#if MLA_UNITY_PHYSICS_MODULE
// Use built-in tags
const string k_CubeTag = "Player";
const string k_SphereTag = "Respawn";

sphere3.transform.position = new Vector3(0, 0, -10);
sphere3.tag = k_SphereTag;
sphere3.name = "sphere3";
Physics.SyncTransforms();
}

Assert.AreEqual(-1, castOutput.RayOutputs[0].HitTagIndex);
}
}
#endif
}
}

5
com.unity.ml-agents/Tests/Runtime/Sensor/RenderTextureSensorComponentTests.cs


renderTexComponent.Grayscale = grayscale;
renderTexComponent.CompressionType = compression;
var expectedShape = new[] { height, width, grayscale ? 1 : 3 };
Assert.AreEqual(expectedShape, renderTexComponent.GetObservationShape());
var expectedShape = new InplaceArray<int>(height, width, grayscale ? 1 : 3);
Assert.AreEqual(InplaceArray<int>.FromList(expectedShape), sensor.GetObservationSpec().Shape);
Assert.AreEqual(expectedShape, sensor.GetObservationSpec().Shape);
Assert.AreEqual(typeof(RenderTextureSensor), sensor.GetType());
}
}

6
com.unity.ml-agents/Tests/Runtime/Sensor/SensorShapeValidatorTests.cs


public void Update() { }
public void Reset() { }
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return SensorCompressionType.None;
return CompressionSpec.Default();
[Test]
public void TestShapesAgree()
{

LogAssert.Expect(LogType.Assert, "Number of Sensors must match. 2 != 3");
validator.ValidateSensors(sensorList1);
}
[Test]
public void TestDimensionMismatch()
{

20
com.unity.ml-agents/Tests/Runtime/Sensor/StackingSensorTests.cs


SensorTestHelper.CompareObservation(sensor, new[] { 0f, 0f, 0f, 0f, 5f, 6f });
}
class Dummy3DSensor : ISparseChannelSensor
class Dummy3DSensor : ISensor
{
public SensorCompressionType CompressionType = SensorCompressionType.PNG;
public int[] Mapping;

public void Reset() { }
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return CompressionType;
return new CompressionSpec(CompressionType, Mapping);
}
public string GetName()

public int[] GetCompressedChannelMapping()
{
return Mapping;
}
}
[Test]

var cameraSensor = new CameraSensor(new Camera(), 64, 64,
true, "grayscaleCamera", SensorCompressionType.PNG);
var stackedCameraSensor = new StackingSensor(cameraSensor, 2);
Assert.AreEqual(stackedCameraSensor.GetCompressedChannelMapping(), new[] { 0, 0, 0, 1, 1, 1 });
Assert.AreEqual(stackedCameraSensor.GetCompressionSpec().CompressedChannelMapping, new[] { 0, 0, 0, 1, 1, 1 });
Assert.AreEqual(stackedRenderTextureSensor.GetCompressedChannelMapping(), new[] { 0, 1, 2, 3, 4, 5 });
Assert.AreEqual(stackedRenderTextureSensor.GetCompressionSpec().CompressedChannelMapping, new[] { 0, 1, 2, 3, 4, 5 });
// Test mapping with number of layers not being multiple of 3
var dummySensor = new Dummy3DSensor();

Assert.AreEqual(stackedDummySensor.GetCompressedChannelMapping(), new[] { 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1 });
Assert.AreEqual(stackedDummySensor.GetCompressionSpec().CompressedChannelMapping, new[] { 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1 });
// Test mapping with dummy layers that should be dropped
var paddedDummySensor = new Dummy3DSensor();

Assert.AreEqual(stackedPaddedDummySensor.GetCompressedChannelMapping(), new[] { 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1 });
Assert.AreEqual(stackedPaddedDummySensor.GetCompressionSpec().CompressedChannelMapping, new[] { 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1 });
}
[Test]

12
com.unity.ml-agents/Tests/Runtime/Sensor/Unity.ML-Agents.Runtime.Sensor.Tests.asmdef


"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [
{
"name": "com.unity.modules.physics",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS_MODULE"
},
{
"name": "com.unity.modules.physics2d",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS2D_MODULE"
}
]
}

12
com.unity.ml-agents/Tests/Runtime/Unity.ML-Agents.Runtime.Tests.asmdef


"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [
{
"name": "com.unity.modules.physics",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS_MODULE"
},
{
"name": "com.unity.modules.physics2d",
"expression": "1.0.0",
"define": "MLA_UNITY_PHYSICS2D_MODULE"
}
]
}

5
com.unity.ml-agents/Tests/Runtime/Utils/TestClasses.cs


return new byte[] { 0 };
}
public SensorCompressionType GetCompressionType()
public CompressionSpec GetCompressionSpec()
return compressionType;
return new CompressionSpec(compressionType);
}
public string GetName()

public class TestClasses
{
}
}

4
com.unity.ml-agents/package.json


"dependencies": {
"com.unity.barracuda": "1.3.2-preview",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.physics2d": "1.0.0"
"com.unity.modules.jsonserialize": "1.0.0"
}
}

27
docs/Migrating.md


actionMask.SetActionEnabled(branch, 3, false);
}
```
### IActuator changes
- The `ISensor.GetObservationShape()` method was removed, and `GetObservationSpec()` was added. You can use
### ISensor and SensorComponent changes
- The `ISensor.GetObservationShape()` method and `ITypedSensor`
and `IDimensionPropertiesSensor` interfaces were removed, and `GetObservationSpec()` was added. You can use
`ObservationSpec.Vector()` or `ObservationSpec.Visual()` to generate `ObservationSpec`s that are equivalent to
the previous shape. For example, if your old ISensor looked like:

return ObservationSpec.Visual(m_Height, m_Width, m_NumChannels);
}
```
- The `ISensor.GetCompressionType()` method and `ISparseChannelSensor` interface was removed,
and `GetCompressionSpec()` was added. You can use `CompressionSpec.Default()` or
`CompressionSpec.Compressed()` to generate `CompressionSpec`s that are equivalent to
the previous values. For example, if your old ISensor looked like:
```csharp
public virtual SensorCompressionType GetCompressionType()
{
return SensorCompressionType.None;
}
```
the equivalent code would now be
```csharp
public CompressionSpec GetCompressionSpec()
{
return CompressionSpec.Default();
}
```
- The abstract method `SensorComponent.GetObservationShape()` was removed.
## Migrating to Release 13
### Implementing IHeuristic in your IActuator implementations

4
markdown-link-check.full.json


"comment": "Allow future release tags"
},
{
"pattern": "https://forum.unity.com/forums/ml-agents.453/",
"comment": "Not reachable from github action"
"pattern": "https://forum.unity.com/",
"comment": "Returns 307 (temporary redirect) which causes link check to fail."
},
{
"pattern": "mailto:",

40
.yamato/com.unity.ml-agents-coverage.yml


{% metadata_file .yamato/coverage_tests.metafile %}
---
{% for package in coverage_test_packages %}
{% for editor in coverage_test_editors %}
{% for platform in coverage_test_platforms %}
{% capture coverageOptions %} --enable-code-coverage --code-coverage-options 'generateHtmlReport;assemblyFilters:+{{ package.assembly }}'{% endcapture %}
test_coverage_{{ package.name }}_{{ platform.name }}_{{ editor.version }}:
name : Coverage {{ package.name }} test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- npm install upm-ci-utils@stable -g --registry https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm
- upm-ci project test -u {{ editor.version }} --type project-tests --project-path {{ editor.testProject }} --package-filter {{ package.name }} {{ coverageOptions }} --extra-utr-arg "reruncount=2"
- python3 ml-agents/tests/yamato/check_coverage_percent.py upm-ci~/test-results/ {{ package.minCoveragePct }}
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/com.unity.ml-agents-pack.yml#pack
triggers:
cancel_old_ci: true
{% if platform.name == "linux" %}
expression: |
(pull_request.target eq "main" OR
pull_request.target match "release.+") AND
NOT pull_request.draft AND
(pull_request.changes.any match "com.unity.ml-agents/**" OR
pull_request.changes.any match " {{ editor.testProject }}/**" OR
{% if package.name == "com.unity.ml-agents.extensions" %}
pull_request.changes.any match "com.unity.ml-agents.extensions/**" OR
{% endif %}
pull_request.changes.any match ".yamato/com.unity.ml-agents-test.yml")
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}

17
.yamato/coverage_tests.metafile


coverage_test_editors:
- version: 2020.2
testProject: DevProject
coverage_test_platforms:
- name: linux
type: Unity::VM
image: package-ci/ubuntu:stable
flavor: b1.medium
coverage_test_packages:
- name: com.unity.ml-agents
assembly: Unity.ML-Agents
minCoveragePct: 72
- name: com.unity.ml-agents.extensions
assembly: Unity.ML-Agents.Extensions*
minCoveragePct: 75

109
com.unity.ml-agents/Runtime/Sensors/CompressionSpec.cs


using System.Linq;
namespace Unity.MLAgents.Sensors
{
/// <summary>
/// The compression setting for visual/camera observations.
/// </summary>
public enum SensorCompressionType
{
/// <summary>
/// No compression. Data is preserved as float arrays.
/// </summary>
None,
/// <summary>
/// PNG format. Data will be stored in binary format.
/// </summary>
PNG
}
/// <summary>
/// A description of the compression used for observations.
/// </summary>
/// <remarks>
/// Most ISensor implementations can't take advantage of compression,
/// and should return CompressionSpec.Default() from their ISensor.GetCompressionSpec() methods.
/// Visual observations, or mulitdimensional categorical observations (for example, image segmentation
/// or the piece types in a match-3 game board) can use PNG compression reduce the amount of
/// data transferred between Unity and the trainer.
/// </remarks>
public struct CompressionSpec
{
internal SensorCompressionType m_SensorCompressionType;
/// <summary>
/// The compression type that the sensor will use for its observations.
/// </summary>
public SensorCompressionType SensorCompressionType
{
get => m_SensorCompressionType;
}
internal int[] m_CompressedChannelMapping;
/// The mapping of the channels in compressed data to the actual channel after decompression.
/// The mapping is a list of integer index with the same length as
/// the number of output observation layers (channels), including padding if there's any.
/// Each index indicates the actual channel the layer will go into.
/// Layers with the same index will be averaged, and layers with negative index will be dropped.
/// For example, mapping for CameraSensor using grayscale and stacking of two: [0, 0, 0, 1, 1, 1]
/// Mapping for GridSensor of 4 channels and stacking of two: [0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1]
public int[] CompressedChannelMapping
{
get => m_CompressedChannelMapping;
}
/// <summary>
/// Return a CompressionSpec indicating possible compression.
/// </summary>
/// <param name="sensorCompressionType">The compression type to use.</param>
/// <param name="compressedChannelMapping">Optional mapping mapping of the channels in compressed data to the
/// actual channel after decompression.</param>
public CompressionSpec(SensorCompressionType sensorCompressionType, int[] compressedChannelMapping = null)
{
m_SensorCompressionType = sensorCompressionType;
m_CompressedChannelMapping = compressedChannelMapping;
}
/// <summary>
/// Return a CompressionSpec indicating no compression. This is recommended for most sensors.
/// </summary>
/// <returns></returns>
public static CompressionSpec Default()
{
return new CompressionSpec
{
m_SensorCompressionType = SensorCompressionType.None,
m_CompressedChannelMapping = null
};
}
/// <summary>
/// Return whether the compressed channel mapping is "trivial"; if so it doesn't need to be sent to the
/// trainer.
/// </summary>
/// <returns></returns>
internal bool IsTrivialMapping()
{
var mapping = CompressedChannelMapping;
if (mapping == null)
{
return true;
}
// check if mapping equals zero mapping
if (mapping.Length == 3 && mapping.All(m => m == 0))
{
return true;
}
// check if mapping equals identity mapping
for (var i = 0; i < mapping.Length; i++)
{
if (mapping[i] != i)
{
return false;
}
}
return true;
}
}
}

3
com.unity.ml-agents/Runtime/Sensors/CompressionSpec.cs.meta


fileFormatVersion: 2
guid: 0ddff1d1b7ad4170acb1a10272d4a8c2
timeCreated: 1616006929

30
com.unity.ml-agents/Tests/Runtime/Sensor/CompressionSpecTests.cs


using NUnit.Framework;
using Unity.MLAgents.Sensors;
namespace Unity.MLAgents.Tests
{
[TestFixture]
public class CompressionSpecTests
{
[Test]
public void TestIsTrivialMapping()
{
Assert.IsTrue(CompressionSpec.Default().IsTrivialMapping());
var spec = new CompressionSpec(SensorCompressionType.PNG, null);
Assert.AreEqual(spec.IsTrivialMapping(), true);
spec = new CompressionSpec(SensorCompressionType.PNG, new[] { 0, 0, 0 });
Assert.AreEqual(spec.IsTrivialMapping(), true);
spec = new CompressionSpec(SensorCompressionType.PNG, new[] { 0, 1, 2, 3, 4 });
Assert.AreEqual(spec.IsTrivialMapping(), true);
spec = new CompressionSpec(SensorCompressionType.PNG, new[] { 1, 2, 3, 4, -1, -1 });
Assert.AreEqual(spec.IsTrivialMapping(), false);
spec = new CompressionSpec(SensorCompressionType.PNG, new[] { 0, 0, 0, 1, 1, 1 });
Assert.AreEqual(spec.IsTrivialMapping(), false);
}
}
}

3
com.unity.ml-agents/Tests/Runtime/Sensor/CompressionSpecTests.cs.meta


fileFormatVersion: 2
guid: cd0990de0eb646b0b0531b91c840c9da
timeCreated: 1616030728

20
com.unity.ml-agents/Runtime/Sensors/ISparseChannelSensor.cs


namespace Unity.MLAgents.Sensors
{
/// <summary>
/// Sensor interface for sparse channel sensor which requires a compressed channel mapping.
/// </summary>
public interface ISparseChannelSensor : ISensor
{
/// <summary>
/// Returns the mapping of the channels in compressed data to the actual channel after decompression.
/// The mapping is a list of interger index with the same length as
/// the number of output observation layers (channels), including padding if there's any.
/// Each index indicates the actual channel the layer will go into.
/// Layers with the same index will be averaged, and layers with negative index will be dropped.
/// For example, mapping for CameraSensor using grayscale and stacking of two: [0, 0, 0, 1, 1, 1]
/// Mapping for GridSensor of 4 channels and stacking of two: [0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1]
/// </summary>
/// <returns>Mapping of the compressed data</returns>
int[] GetCompressedChannelMapping();
}
}

11
com.unity.ml-agents/Runtime/Sensors/ISparseChannelSensor.cs.meta


fileFormatVersion: 2
guid: 63bb76c1e31c24fa5b4a384ea0edbfb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存