From 6f13532966080a051127167c6eb2117e47d96f3a Mon Sep 17 00:00:00 2001 From: Samir Gadre Date: Mon, 19 Jul 2021 20:53:50 -0700 Subject: [PATCH 1/6] exposed unity random seed to python api surface --- .../Assets/Scripts/BaseFPSAgentController.cs | 26 ++++++++++++------- unity/Assets/Scripts/DebugInputField.cs | 8 +++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/unity/Assets/Scripts/BaseFPSAgentController.cs b/unity/Assets/Scripts/BaseFPSAgentController.cs index a06beef2f8..7e1fb24a1e 100644 --- a/unity/Assets/Scripts/BaseFPSAgentController.cs +++ b/unity/Assets/Scripts/BaseFPSAgentController.cs @@ -100,7 +100,7 @@ public bool IsVisible { // first default all Vis capsules of all modes to not enabled HideAllAgentRenderers(); - // The VisibilityCapsule will be set to either Tall or Bot + // The VisibilityCapsule will be set to either Tall or Bot // from the SetAgentMode call in BaseFPSAgentController's Initialize() foreach (Renderer r in VisibilityCapsule.GetComponentsInChildren()) { r.enabled = value; @@ -560,7 +560,7 @@ public void Initialize(ServerAction action) { navmeshAgent.height = collider.height; } - // navmeshAgent.radius = + // navmeshAgent.radius = if (action.gridSize <= 0 || action.gridSize > 5) { errorMessage = "grid size must be in the range (0,5]"; @@ -1133,7 +1133,7 @@ protected bool moveInDirection( CheckIfItemBlocksAgentMovement(direction, forceAction) && // forceAction = true allows ignoring movement restrictions caused by held objects CheckIfAgentCanMove(direction, ignoreColliders)) { - // only default hand if not manually interacting with things + // only default hand if not manually interacting with things if (!manualInteract) { DefaultAgentHand(); } @@ -1488,7 +1488,7 @@ public virtual SimpleSimObj[] allSceneObjects() { public void ResetObjectFilter() { this.simObjFilter = null; // this could technically be a FastEmit action - // but could cause confusion since the result of this + // but could cause confusion since the result of this // action should return all the objects. Resetting the filter // should cause all the objects to get returned, which FastEmit would not do. actionFinished(true); @@ -1504,9 +1504,9 @@ public void SetObjectFilter(string[] objectIds) { } simObjFilter = filter.ToArray(); // this could technically be a FastEmit action - // but could cause confusion since the result of this + // but could cause confusion since the result of this // action should return a limited set of objects. Setting the filter - // should cause only the objects in the filter to get returned, + // should cause only the objects in the filter to get returned, // which FastEmit would not do. actionFinished(true); } @@ -2407,7 +2407,7 @@ protected void teleportFull( Quaternion oldRotation = transform.rotation; float oldHorizon = m_Camera.transform.localEulerAngles.x; - // here we actually teleport + // here we actually teleport transform.position = position; transform.localEulerAngles = new Vector3(0, rotation.y, 0); m_Camera.transform.localEulerAngles = new Vector3(horizon, 0, 0); @@ -3054,6 +3054,12 @@ public void ResetAgentHandRotation() { AgentHand.transform.rotation = this.transform.rotation; } + // set random seed used by unity + public void SetRandomSeed(int seed) { + UnityEngine.Random.InitState(seed); + actionFinishedEmit(true); + } + // randomly repositions sim objects in the current scene public void InitialRandomSpawn( int randomSeed = 0, @@ -3151,7 +3157,7 @@ public void InitialRandomSpawn( actionFinished(success); } - // On demand public function for getting what sim objects are visible at that moment + // On demand public function for getting what sim objects are visible at that moment public List GetAllVisibleSimObjPhysics(float maxDistance) { var camera = this.GetComponentInChildren(); return new List(GetAllVisibleSimObjPhysics(camera, maxDistance)); @@ -3284,7 +3290,7 @@ public void GetMapViewCameraProperties() { ); } - /* + /* Get the 2D (x, z) convex hull of a GameObject. See the Get2DSemanticHulls function for more information. @@ -3345,7 +3351,7 @@ of these points (smallest convex region enclosing the object's points). If the objectIds (or objectTypes) parameter is non-null, then only objects with those ids (or types) will be returned. - + ONLY ONE OF objectIds OR objectTypes IS ALLOWED TO BE NON-NULL. Returns a dictionary mapping object ids to a list of (x,z) coordinates corresponding diff --git a/unity/Assets/Scripts/DebugInputField.cs b/unity/Assets/Scripts/DebugInputField.cs index bc8af52d86..31f8d475c5 100644 --- a/unity/Assets/Scripts/DebugInputField.cs +++ b/unity/Assets/Scripts/DebugInputField.cs @@ -1296,7 +1296,7 @@ IEnumerator executeBatch(JArray jActions) { action["putNearXY"] = bool.Parse(splitcommand[1]); } // set true to place with kinematic = true so that it doesn't fall or roll in place - making placement more consistant and not physics engine reliant - this more closely mimics legacy pivot placement behavior - // action["placeStationary"] = true; + // action["placeStationary"] = true; action["x"] = 0.5f; action["y"] = 0.5f; // set this true to ignore Placement Restrictions @@ -1565,7 +1565,7 @@ IEnumerator executeBatch(JArray jActions) { } action.action = "CreateObjectAtLocation"; - action.randomizeObjectAppearance = false;// pick randomly from available or not? + action.randomizeObjectAppearance = false;// pick randomly from available or not? action.objectVariation = 1; // if random false, which version of the object to spawn? (there are only 3 of each type atm) CurrentActiveController().ProcessControlCommand(action); @@ -2080,7 +2080,7 @@ IEnumerator executeBatch(JArray jActions) { break; } - // manual pickup object- test hand + // manual pickup object- test hand case "pum": { ServerAction action = new ServerAction(); action.action = "PickupObject"; @@ -2318,7 +2318,7 @@ IEnumerator executeBatch(JArray jActions) { } // move hand backward. relative to agent's facing - // pass in move magnitude or default is 0.25 units + // pass in move magnitude or default is 0.25 units case "mhb": { Dictionary action = new Dictionary(); action["action"] = "MoveHandBack"; From 5749852e1c45796785168dee37587e74ad5e571c Mon Sep 17 00:00:00 2001 From: mattdeitke Date: Mon, 19 Jul 2021 21:49:33 -0700 Subject: [PATCH 2/6] add tests --- ai2thor/tests/test_unity.py | 125 +++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/ai2thor/tests/test_unity.py b/ai2thor/tests/test_unity.py index 201b6c50e2..9dc29fca9e 100644 --- a/ai2thor/tests/test_unity.py +++ b/ai2thor/tests/test_unity.py @@ -87,16 +87,9 @@ def fifo_controller(): fifo_wsgi = [_fifo_controller, _wsgi_controller] fifo_wsgi_stoch = [_fifo_controller, _wsgi_controller, _stochastic_controller] -BASE_FP28_POSITION = dict( - x=-1.5, - z=-1.5, - y=0.901, -) +BASE_FP28_POSITION = dict(x=-1.5, z=-1.5, y=0.901,) BASE_FP28_LOCATION = dict( - **BASE_FP28_POSITION, - rotation={"x": 0, "y": 0, "z": 0}, - horizon=0, - standing=True, + **BASE_FP28_POSITION, rotation={"x": 0, "y": 0, "z": 0}, horizon=0, standing=True, ) @@ -126,6 +119,14 @@ def assert_near(point1, point2, error_message=""): ) +def assert_images_near(image1, image2, max_mean_pixel_diff=1): + return np.mean(np.abs(image1 - image2).flatten()) <= max_mean_pixel_diff + + +def assert_images_far(image1, image2, min_mean_pixel_diff=10): + return np.mean(np.abs(image1 - image2).flatten()) >= min_mean_pixel_diff + + def test_stochastic_controller(stochastic_controller): stochastic_controller.reset(TEST_SCENE) assert stochastic_controller.last_event.metadata["lastActionSuccess"] @@ -199,9 +200,7 @@ def test_deprecated_segmentation_params(fifo_controller): # renderClassImage has been renamed to renderSemanticSegmentation fifo_controller.reset( - TEST_SCENE, - renderObjectImage=True, - renderClassImage=True, + TEST_SCENE, renderObjectImage=True, renderClassImage=True, ) event = fifo_controller.last_event with warnings.catch_warnings(): @@ -218,9 +217,7 @@ def test_deprecated_segmentation_params2(fifo_controller): # renderClassImage has been renamed to renderSemanticSegmentation fifo_controller.reset( - TEST_SCENE, - renderSemanticSegmentation=True, - renderInstanceSegmentation=True, + TEST_SCENE, renderSemanticSegmentation=True, renderInstanceSegmentation=True, ) event = fifo_controller.last_event @@ -434,32 +431,40 @@ def test_add_third_party_camera(controller): "action: AddThirdPartyCamera has an invalid argument: orthographicSize" ) + def test_third_party_camera_depth(fifo_controller): - fifo_controller.reset( - TEST_SCENE, - width=300, - height=300, - renderDepthImage=True - ) + fifo_controller.reset(TEST_SCENE, width=300, height=300, renderDepthImage=True) - agent_position = {'x': -2.75, 'y': 0.9009982347488403, 'z': -1.75} - agent_rotation = {'x': 0.0, 'y': 90.0, 'z': 0.0} + agent_position = {"x": -2.75, "y": 0.9009982347488403, "z": -1.75} + agent_rotation = {"x": 0.0, "y": 90.0, "z": 0.0} - agent_init_position = {'x': -2.75, 'y': 0.9009982347488403, 'z': -1.25} - camera_position = {'x': -2.75, 'y': 1.5759992599487305, 'z': -1.75} - camera_rotation = {'x': 0.0, 'y': 90.0, 'z': 0.0} + agent_init_position = {"x": -2.75, "y": 0.9009982347488403, "z": -1.25} + camera_position = {"x": -2.75, "y": 1.5759992599487305, "z": -1.75} + camera_rotation = {"x": 0.0, "y": 90.0, "z": 0.0} # teleport agent into a position the third-party camera won't see - fifo_controller.step(action="Teleport", position=agent_init_position, rotation=agent_rotation, horizon=0.0, standing=True) + fifo_controller.step( + action="Teleport", + position=agent_init_position, + rotation=agent_rotation, + horizon=0.0, + standing=True, + ) camera_event = fifo_controller.step( dict( action=Actions.AddThirdPartyCamera, position=camera_position, - rotation=camera_rotation + rotation=camera_rotation, ) ) camera_depth = camera_event.third_party_depth_frames[0] - agent_event = fifo_controller.step(action="Teleport", position=agent_position, rotation=agent_rotation, horizon=0.0, standing=True) + agent_event = fifo_controller.step( + action="Teleport", + position=agent_position, + rotation=agent_rotation, + horizon=0.0, + standing=True, + ) agent_depth = agent_event.depth_frame mse = np.square((np.subtract(camera_depth, agent_depth))).mean() # if the clipping planes aren't the same between the agent and third-party camera @@ -670,9 +675,7 @@ def test_open_interactable_with_filter(controller): controller.step(dict(action="SetObjectFilter", objectIds=[])) assert controller.last_event.metadata["objects"] == [] controller.step( - action="OpenObject", - objectId=fridge["objectId"], - raise_for_failure=True, + action="OpenObject", objectId=fridge["objectId"], raise_for_failure=True, ) controller.step(dict(action="ResetObjectFilter")) @@ -704,9 +707,7 @@ def test_open_interactable(controller): assert fridge["visible"], "Object is not interactable!" assert_near(controller.last_event.metadata["agent"]["position"], position) event = controller.step( - action="OpenObject", - objectId=fridge["objectId"], - raise_for_failure=True, + action="OpenObject", objectId=fridge["objectId"], raise_for_failure=True, ) fridge = next( obj @@ -1148,8 +1149,7 @@ def test_teleport(controller): # Teleporting too high before_position = controller.last_event.metadata["agent"]["position"] controller.step( - "Teleport", - **{**BASE_FP28_LOCATION, "y": 1.0}, + "Teleport", **{**BASE_FP28_LOCATION, "y": 1.0}, ) assert not controller.last_event.metadata[ "lastActionSuccess" @@ -1160,8 +1160,7 @@ def test_teleport(controller): # Teleporting into an object controller.step( - "Teleport", - **{**BASE_FP28_LOCATION, "z": -3.5}, + "Teleport", **{**BASE_FP28_LOCATION, "z": -3.5}, ) assert not controller.last_event.metadata[ "lastActionSuccess" @@ -1169,8 +1168,7 @@ def test_teleport(controller): # Teleporting into a wall controller.step( - "Teleport", - **{**BASE_FP28_LOCATION, "z": 0}, + "Teleport", **{**BASE_FP28_LOCATION, "z": 0}, ) assert not controller.last_event.metadata[ "lastActionSuccess" @@ -1624,8 +1622,7 @@ def test_manipulathor_move(controller): event = controller.reset(scene=TEST_SCENE, agentMode="arm", gridSize=0.25) assert_near( - point1=start_position, - point2=event.metadata["agent"]["position"], + point1=start_position, point2=event.metadata["agent"]["position"], ) event = controller.step(action="MoveAgent", ahead=0.25, right=0.15) @@ -1636,8 +1633,7 @@ def test_manipulathor_move(controller): event = controller.step(action="MoveAgent", ahead=-0.25, right=-0.15) assert_near( - point1=start_position, - point2=event.metadata["agent"]["position"], + point1=start_position, point2=event.metadata["agent"]["position"], ) event = controller.step(action="MoveRight") @@ -1648,8 +1644,7 @@ def test_manipulathor_move(controller): event = controller.step(action="MoveLeft") assert_near( - point1=start_position, - point2=event.metadata["agent"]["position"], + point1=start_position, point2=event.metadata["agent"]["position"], ) event = controller.step(action="MoveAhead") @@ -1660,18 +1655,13 @@ def test_manipulathor_move(controller): event = controller.step(action="MoveBack") assert_near( - point1=start_position, - point2=event.metadata["agent"]["position"], + point1=start_position, point2=event.metadata["agent"]["position"], ) @pytest.mark.parametrize("controller", fifo_wsgi) def test_manipulathor_rotate(controller): - event = controller.reset( - scene=TEST_SCENE, - agentMode="arm", - rotateStepDegrees=90 - ) + event = controller.reset(scene=TEST_SCENE, agentMode="arm", rotateStepDegrees=90) assert_near( point1={"x": -0.0, "y": 180.0, "z": 0.0}, point2=event.metadata["agent"]["rotation"], @@ -1722,6 +1712,24 @@ def test_unsupported_manipulathor(controller): assert not event, "PickupObject(objectId) should have failed with agentMode=arm" +@pytest.mark.parametrize("controller", fifo_wsgi) +def test_set_random_seed(controller): + f1_1 = controller.reset().frame + f1_2 = controller.step(action="SetRandomSeed", seed=42).frame + f1_3 = controller.step(action="RandomizeMaterials").frame + + f2_1 = controller.reset().frame + f2_2 = controller.step(action="SetRandomSeed", seed=42).frame + f2_3 = controller.step(action="RandomizeMaterials").frame + + assert_images_near(f1_1, f2_1) + assert_images_near(f1_1, f1_2) + assert_images_near(f2_1, f2_2) + assert_images_near(f1_3, f2_3) + assert_images_far(f2_1, f2_3) + assert_images_far(f1_1, f1_3) + + @pytest.mark.parametrize("controller", fifo_wsgi) def test_randomize_materials_scenes(controller): for p in [0, 200, 300, 400]: @@ -1865,8 +1873,7 @@ def test_randomize_materials_params(controller): == 325 ) assert controller.step( - action="RandomizeMaterials", - inRoomTypes=["Kitchen", "LivingRoom"], + action="RandomizeMaterials", inRoomTypes=["Kitchen", "LivingRoom"], ) assert ( controller.last_event.metadata["actionReturn"]["totalMaterialsConsidered"] @@ -1877,8 +1884,7 @@ def test_randomize_materials_params(controller): controller.reset(scene="FloorPlan_Train5_2") assert not controller.step( - action="RandomizeMaterials", - inRoomTypes=["Kitchen", "LivingRoom"], + action="RandomizeMaterials", inRoomTypes=["Kitchen", "LivingRoom"], ) assert not controller.step(action="RandomizeMaterials", inRoomTypes=["LivingRoom"]) assert controller.step(action="RandomizeMaterials", inRoomTypes=["RoboTHOR"]) @@ -1889,8 +1895,7 @@ def test_randomize_materials_params(controller): controller.reset(scene="FloorPlan_Val3_2") assert not controller.step( - action="RandomizeMaterials", - inRoomTypes=["Kitchen", "LivingRoom"], + action="RandomizeMaterials", inRoomTypes=["Kitchen", "LivingRoom"], ) assert not controller.step(action="RandomizeMaterials", inRoomTypes=["LivingRoom"]) assert controller.step(action="RandomizeMaterials", inRoomTypes=["RoboTHOR"]) From 51acff3d3b11277ef572366af8e602cd49dcc1ba Mon Sep 17 00:00:00 2001 From: Matt Deitke Date: Wed, 28 Jul 2021 13:59:39 -0700 Subject: [PATCH 3/6] remove unused randomizer editor --- unity/Assets/Editor/RandomizerEditor.cs | 186 ------------------- unity/Assets/Editor/RandomizerEditor.cs.meta | 12 -- unity/Assets/Scripts/Randomizer.cs | 170 ----------------- unity/Assets/Scripts/Randomizer.cs.meta | 12 -- 4 files changed, 380 deletions(-) delete mode 100644 unity/Assets/Editor/RandomizerEditor.cs delete mode 100644 unity/Assets/Editor/RandomizerEditor.cs.meta delete mode 100644 unity/Assets/Scripts/Randomizer.cs delete mode 100644 unity/Assets/Scripts/Randomizer.cs.meta diff --git a/unity/Assets/Editor/RandomizerEditor.cs b/unity/Assets/Editor/RandomizerEditor.cs deleted file mode 100644 index 5f11983e5f..0000000000 --- a/unity/Assets/Editor/RandomizerEditor.cs +++ /dev/null @@ -1,186 +0,0 @@ -using UnityEngine; -using UnityEditor; -using System.Collections.Generic; -using System; - -[CustomEditor(typeof(Randomizer))] -public class RandomizerEditor : Editor { - static Color newColor = Color.white; - static bool showMatPreviews = false; - - public override void OnInspectorGUI() { - if (Application.isPlaying) { - return; - } - - showMatPreviews = EditorGUILayout.Toggle("Show mat previews", showMatPreviews); - - Randomizer r = (Randomizer)target; - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - GUI.color = Color.white; - r.Title = EditorGUILayout.TextField("Title (for reference):", r.Title); - r.Style = (RandomizerStyle)EditorGUILayout.EnumPopup("Style", r.Style); - r.UseLocalSceneNumber = EditorGUILayout.Toggle("Use local scene number", r.UseLocalSceneNumber); - int lastSceneNumber = r.SceneNumber; - if (r.UseLocalSceneNumber) { - r.SceneNumber = EditorGUILayout.IntSlider(r.SceneNumber, 0, 100); - } - if (r.SceneNumber != lastSceneNumber) { - r.Randomize(); - } - EditorGUILayout.EndVertical(); - - GUI.color = Color.grey; - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - GUI.color = Color.white; - - switch (r.Style) { - case RandomizerStyle.GameObject: - DisplayAddRemoveList(ref r.TargetGameObjects, "Target GameObjects:"); - break; - - case RandomizerStyle.MeshAndMat: - DisplayAddRemoveList(ref r.TargetMeshes, "Target Meshes"); - DisplayAddRemoveList(ref r.TargetRenderers, "Target Renderers"); - DisplayMatIndexes(ref r.TargetMats, r.TargetRenderers); - DisplayAddRemoveList(ref r.Mats, "Substitute Materials"); - break; - - case RandomizerStyle.MatColorFixed: - DisplayAddRemoveList(ref r.TargetRenderers, "Target Renderers:"); - DisplayMatIndexes(ref r.TargetMats, r.TargetRenderers); - DisplayColorList(ref r.Colors); - break; - - case RandomizerStyle.MatColorRandom: - DisplayAddRemoveList(ref r.TargetRenderers, "Target Renderers:"); - DisplayMatIndexes(ref r.TargetMats, r.TargetRenderers); - r.ColorRangeLow = EditorGUILayout.ColorField("Low Color Range", r.ColorRangeLow); - r.ColorRangeHigh = EditorGUILayout.ColorField("High Color Range", r.ColorRangeHigh); - r.ColorSaturation = EditorGUILayout.Slider("Saturation", r.ColorSaturation, 0f, 1f); - EditorGUILayout.ColorField("Random Color Preview:", Randomizer.GetRandomColor(r.SceneNumber, r.ColorRangeLow, r.ColorRangeHigh, r.ColorSaturation)); - break; - } - - EditorGUILayout.EndVertical(); - - EditorUtility.SetDirty(r); - EditorUtility.SetDirty(r.gameObject); - if (!Application.isPlaying) { - UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()); - } - } - - void DisplayColorList(ref Color[] colorArray) { - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - EditorGUILayout.LabelField("Colors:", EditorStyles.miniLabel); - if (colorArray == null || colorArray.Length == 0) { - colorArray = new Color[1]; - colorArray[0] = Color.white; - } - List itemList = new List(colorArray); - int indexToRemove = -1; - for (int i = 0; i < itemList.Count; i++) { - EditorGUILayout.BeginHorizontal(); - itemList[i] = EditorGUILayout.ColorField(itemList[i]); - if (GUILayout.Button("Remove")) { - indexToRemove = i; - } - EditorGUILayout.EndHorizontal(); - } - if (indexToRemove >= 0) { - itemList.RemoveAt(indexToRemove); - } - GUI.color = Color.white; - EditorGUILayout.BeginHorizontal(); - newColor = EditorGUILayout.ColorField("Add color:", newColor); - if (GUILayout.Button("Click to add")) { - itemList.Add(newColor); - } - EditorGUILayout.EndHorizontal(); - colorArray = itemList.ToArray(); - EditorGUILayout.EndVertical(); - } - - void DisplayMatIndexes(ref int[] matIndexArray, Renderer[] renderers) { - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - EditorGUILayout.LabelField("Target Material Indexes:", EditorStyles.miniLabel); - if (renderers == null || renderers.Length == 0) { - EditorGUILayout.LabelField("(No renderers)"); - return; - } - if (matIndexArray == null) { - matIndexArray = new int[0]; - } - if (matIndexArray.Length != renderers.Length) { - Array.Resize(ref matIndexArray, renderers.Length); - } - for (int i = 0; i < renderers.Length; i++) { - Material[] sharedMats = renderers[i].sharedMaterials; - string[] options = new string[sharedMats.Length]; - for (int j = 0; j < sharedMats.Length; j++) { - string option = "(NULL)"; - if (sharedMats[j] != null) { - option = sharedMats[j].name + "(" + j.ToString() + ")"; - } - options[j] = option; - } - EditorGUILayout.BeginHorizontal(); - int newMatIndex = EditorGUILayout.Popup(renderers[i].name, matIndexArray[i], options); - if (showMatPreviews && sharedMats[newMatIndex] != null) { - Editor matEditor = MaterialEditor.CreateEditor(sharedMats[newMatIndex]); - matEditor.OnPreviewGUI(GUILayoutUtility.GetRect(25, 25), GUIStyle.none); - GameObject.DestroyImmediate(matEditor); - } - matIndexArray[i] = newMatIndex; - EditorGUILayout.EndHorizontal(); - } - EditorGUILayout.EndVertical(); - } - - void DisplayAddRemoveList(ref T[] itemArray, string header) where T : UnityEngine.Object { - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - EditorGUILayout.LabelField(header, EditorStyles.miniLabel); - if (itemArray == null) { - itemArray = new T[0]; - } - if (itemArray.Length == 0) { - EditorGUILayout.LabelField("(No items)"); - } - List itemList = new List(itemArray); - int indexToRemove = -1; - bool showMatPreview = false; - if (typeof(T) == typeof(Material)) { - showMatPreview = true; - } - for (int i = 0; i < itemList.Count; i++) { - if (showMatPreview) { - EditorGUILayout.BeginHorizontal(); - } - if (GUILayout.Button(itemList[i].name)) { - if (EditorUtility.DisplayDialog("Confirm Remove", "Remove " + itemList[i].name + "?", "Yes", "Cancel")) { - indexToRemove = i; - } - } - if (showMatPreview) { - Material mat = itemList[i] as Material; - if (showMatPreviews && mat != null) { - Editor matEditor = MaterialEditor.CreateEditor(mat); - matEditor.OnPreviewGUI(GUILayoutUtility.GetRect(25, 25), GUIStyle.none); - GameObject.DestroyImmediate(matEditor); - } - EditorGUILayout.EndHorizontal(); - } - } - if (indexToRemove >= 0) { - itemList.RemoveAt(indexToRemove); - } - GUI.color = Color.white; - T newGameObject = (T)EditorGUILayout.ObjectField("Add new:", null, typeof(T), true); - if (newGameObject != null) { - itemList.Add(newGameObject); - } - itemArray = itemList.ToArray(); - EditorGUILayout.EndVertical(); - } -} \ No newline at end of file diff --git a/unity/Assets/Editor/RandomizerEditor.cs.meta b/unity/Assets/Editor/RandomizerEditor.cs.meta deleted file mode 100644 index 22b4947cad..0000000000 --- a/unity/Assets/Editor/RandomizerEditor.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 114ef57dfee974dd19ee33690b14dbf7 -timeCreated: 1484688737 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/unity/Assets/Scripts/Randomizer.cs b/unity/Assets/Scripts/Randomizer.cs deleted file mode 100644 index acc345aa95..0000000000 --- a/unity/Assets/Scripts/Randomizer.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright Allen Institute for Artificial Intelligence 2017 -using UnityEngine; -using System.Collections; - -public enum RandomizerStyle { - MeshAndMat, - GameObject, - MatColorFixed, - MatColorRandom, -} - -[ExecuteInEditMode] -public class Randomizer : MonoBehaviour { - public int SceneNumber = 0; - public bool UseLocalSceneNumber; - - public RandomizerStyle Style = RandomizerStyle.MeshAndMat; - - public string Title = string.Empty; - public GameObject[] TargetGameObjects; - public Renderer[] TargetRenderers; - public MeshFilter[] TargetMeshes; - public int[] TargetMats; - public Mesh[] Meshes; - public Material[] Mats; - public Color[] Colors; - public Color ColorRangeLow = Color.grey; - public Color ColorRangeHigh = Color.white; - public float ColorSaturation = 1f; - public System.Random rand = null; - private int randomSeed; - private bool seedInitialized; - - void OnEnable() { - Randomize(); - } - - public void Randomize() { - - if (!UseLocalSceneNumber) { - if (SceneManager.Current != null) { - SceneNumber = SceneManager.Current.SceneNumber; - } - } - - if (!seedInitialized) { - randomSeed = SceneNumber; - seedInitialized = true; - } - - rand = new System.Random(randomSeed); - - if (Application.isPlaying) { - StartCoroutine(StaggerRandomize()); - } else { - RandomizeNow(); - } - } - - public void Randomize(int seed) { - randomSeed = seed; - seedInitialized = true; - Randomize(); - } - - IEnumerator StaggerRandomize() { - switch (Style) { - case RandomizerStyle.GameObject: - case RandomizerStyle.MeshAndMat: - // no wait necessary - break; - - case RandomizerStyle.MatColorFixed: - case RandomizerStyle.MatColorRandom: - // wait until gameObject & meshAndMat styles are done - yield return new WaitForEndOfFrame(); - break; - } - - RandomizeNow(); - yield break; - } - - void RandomizeNow() { - if (SceneNumber < 0) { - SceneNumber = 0; - } - - switch (Style) { - case RandomizerStyle.MeshAndMat: - if (Meshes != null && Meshes.Length > 0 && TargetMeshes != null && TargetMeshes.Length > 0) { - int meshNum = SceneNumber % Meshes.Length; - for (int i = 0; i < TargetMeshes.Length; i++) { - TargetMeshes[i].sharedMesh = Meshes[meshNum]; - } - } - if (Mats != null && Mats.Length > 0 && TargetRenderers != null && TargetRenderers.Length > 0) { - int matNum = SceneNumber % Mats.Length; - for (int i = 0; i < TargetRenderers.Length; i++) { - if (TargetRenderers[i] != null) { - Material[] sharedMats = TargetRenderers[i].sharedMaterials; - sharedMats[TargetMats[i]] = Mats[matNum]; - TargetRenderers[i].sharedMaterials = sharedMats; - } - } - } - break; - - case RandomizerStyle.GameObject: - if (TargetGameObjects != null && TargetGameObjects.Length > 0) { - int goNum = rand.Next(0, TargetGameObjects.Length); - for (int i = 0; i < TargetGameObjects.Length; i++) { - TargetGameObjects[i].SetActive(i == goNum); - } - } - break; - - case RandomizerStyle.MatColorFixed: - if (Application.isPlaying) { - if (Colors != null && Colors.Length > 0 && TargetRenderers != null && TargetRenderers.Length > 0) { - int colorNum = SceneNumber % Colors.Length; - for (int i = 0; i < TargetRenderers.Length; i++) { - Material[] mats = TargetRenderers[i].materials; - mats[TargetMats[i]].color = Colors[colorNum]; - TargetRenderers[i].materials = mats; - } - } - } - break; - - case RandomizerStyle.MatColorRandom: - if (Application.isPlaying) { - if (TargetRenderers != null && TargetRenderers.Length > 0) { - Color randomColor = GetRandomColor(SceneNumber, ColorRangeLow, ColorRangeHigh, ColorSaturation); - for (int i = 0; i < TargetRenderers.Length; i++) { - Material[] mats = TargetRenderers[i].materials; - mats[TargetMats[i]].color = randomColor; - TargetRenderers[i].materials = mats; - } - } - } - break; - } - } - - public static Color GetRandomColor(int sceneNumber, Color low, Color high, float saturation) { - Color randomColor = Color.white; - System.Random rand = new System.Random(sceneNumber); - float randR = ((float)rand.Next(0, 100) / 100); - float randG = ((float)rand.Next(0, 100) / 100); - float randB = ((float)rand.Next(0, 100) / 100); - randomColor.r = Mathf.Lerp(low.r, high.r, randR); - randomColor.g = Mathf.Lerp(low.g, high.g, randG); - randomColor.b = Mathf.Lerp(low.b, high.b, randB); - Color gsColor = new Color(randomColor.grayscale, randomColor.grayscale, randomColor.grayscale); - randomColor = Color.Lerp(gsColor, randomColor, saturation); - return randomColor; - } - - //#if UNITY_EDITOR - // void Update() { - // if (Application.isPlaying) - // return; - - // if (UnityEditor.Selection.activeGameObject == gameObject) { - // Randomize (); - // } - //} - //#endif -} diff --git a/unity/Assets/Scripts/Randomizer.cs.meta b/unity/Assets/Scripts/Randomizer.cs.meta deleted file mode 100644 index 65e6150c3c..0000000000 --- a/unity/Assets/Scripts/Randomizer.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 38d79f13535a8424abe0e508813143e1 -timeCreated: 1485482829 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 100 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 6d376b4fbff2a417d333a724202144533ae00e11 Mon Sep 17 00:00:00 2001 From: Matt Deitke Date: Wed, 28 Jul 2021 14:43:01 -0700 Subject: [PATCH 4/6] support global static systemRandom --- .../Assets/Scripts/BaseFPSAgentController.cs | 5 ++ .../Assets/Scripts/DroneFPSAgentController.cs | 15 ++-- unity/Assets/Scripts/ListExtensions.cs | 1 + .../PhysicsRemoteFPSAgentController.cs | 40 +++-------- unity/Assets/Scripts/PhysicsSceneManager.cs | 2 + unity/Assets/Scripts/RandomExtensions.cs | 68 +++---------------- .../StochasticRemoteFPSAgentController.cs | 9 +-- 7 files changed, 37 insertions(+), 103 deletions(-) diff --git a/unity/Assets/Scripts/BaseFPSAgentController.cs b/unity/Assets/Scripts/BaseFPSAgentController.cs index 7e1fb24a1e..981a5a7494 100644 --- a/unity/Assets/Scripts/BaseFPSAgentController.cs +++ b/unity/Assets/Scripts/BaseFPSAgentController.cs @@ -82,6 +82,10 @@ public SimObjPhysics[] VisibleSimObjPhysics { public AgentState agentState = AgentState.Emit; + // Use this instead of constructing a new System.Random() so that its + // seed can be globally set. Starts off completely random. + protected static System.Random systemRandom = new System.Random(); + public bool clearRandomizeMaterialsOnReset = false; // these object types can have a placeable surface mesh associated ith it @@ -3057,6 +3061,7 @@ public void ResetAgentHandRotation() { // set random seed used by unity public void SetRandomSeed(int seed) { UnityEngine.Random.InitState(seed); + systemRandom = new System.Random(seed); actionFinishedEmit(true); } diff --git a/unity/Assets/Scripts/DroneFPSAgentController.cs b/unity/Assets/Scripts/DroneFPSAgentController.cs index bc1574c910..69f8d05187 100644 --- a/unity/Assets/Scripts/DroneFPSAgentController.cs +++ b/unity/Assets/Scripts/DroneFPSAgentController.cs @@ -128,10 +128,9 @@ void FixedUpdate() { if (thrust.magnitude > 0.0001 && Time.timeScale != 0) { if (dronePositionRandomNoiseSigma > 0) { - var random = new System.Random(); - var noiseX = (float)random.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); - var noiseY = (float)random.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); - var noiseZ = (float)random.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); + var noiseX = (float)systemRandom.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); + var noiseY = (float)systemRandom.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); + var noiseZ = (float)systemRandom.NextGaussian(0.0f, dronePositionRandomNoiseSigma / 3.0f); Vector3 noise = new Vector3(noiseX, noiseY, noiseZ); m_CharacterController.Move((thrust * Time.fixedDeltaTime) + noise); } else { @@ -315,7 +314,6 @@ public Vector3 GetFlyingOrientation(float moveMagnitude, int targetOrientation) // use get reachable positions, get two positions, one in front of the other public Vector3[] SeekTwoPos(Vector3[] shuffledCurrentlyReachable) { Vector3[] output = new Vector3[2]; - System.Random rnd = new System.Random(); List y_candidates = new List(new float[] { 1.0f, 1.25f, 1.5f }); foreach (Vector3 p in shuffledCurrentlyReachable) { foreach (Vector3 p2 in shuffledCurrentlyReachable) { @@ -324,7 +322,7 @@ public Vector3[] SeekTwoPos(Vector3[] shuffledCurrentlyReachable) { // if(Mathf.Abs(p.x-p2.x) < 0.5*Mathf.Abs(p.z-p2.z)){ // if(Mathf.Abs(p.x-p2.x) == 0){ if (Mathf.Abs(p.x - p2.x) <= 0.5) { - float y = y_candidates.OrderBy(x => rnd.Next()).ToArray()[0]; + float y = y_candidates.OrderBy(x => systemRandom.Next()).ToArray()[0]; output[0] = new Vector3(p.x, 1.0f, p.z); output[1] = new Vector3(p2.x, y, p2.z); return output; @@ -357,8 +355,7 @@ public void TeleportFull( } public void FlyRandomStart(float y) { - System.Random rnd = new System.Random(); - Vector3[] shuffledCurrentlyReachable = getReachablePositions().OrderBy(x => rnd.Next()).ToArray(); + Vector3[] shuffledCurrentlyReachable = getReachablePositions().OrderBy(x => systemRandom.Next()).ToArray(); Vector3[] Random_output = SeekTwoPos(shuffledCurrentlyReachable); var thrust_dt_drone = Random_output[0]; @@ -371,7 +368,7 @@ public void FlyRandomStart(float y) { } // move drone and launcher to some start position - // using the 'position' variable name is an artificat from using ServerAction.position for the thrust_dt + // using the 'position' variable name is an artifact from using ServerAction.position for the thrust_dt public void FlyAssignStart(Vector3 position, float x, float y, float z) { // drone uses action.position Vector3 thrust_dt = position; diff --git a/unity/Assets/Scripts/ListExtensions.cs b/unity/Assets/Scripts/ListExtensions.cs index db68f50ab6..3cdeee001e 100644 --- a/unity/Assets/Scripts/ListExtensions.cs +++ b/unity/Assets/Scripts/ListExtensions.cs @@ -44,6 +44,7 @@ public static IList Shuffle_(this IList list) { } public static IList Shuffle_(this IList list, int seed) { + // NOTE: this doesn't use systemRandom return list.Shuffle_(new System.Random(seed)); } diff --git a/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs b/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs index 552ed1c863..ee0e491a55 100644 --- a/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs +++ b/unity/Assets/Scripts/PhysicsRemoteFPSAgentController.cs @@ -6305,22 +6305,12 @@ public bool objectIsCurrentlyVisible(SimObjPhysics sop, float maxDistance) { return false; } - protected static void Shuffle(System.Random rng, T[] array) { - // Taken from https://stackoverflow.com/questions/108819/best-way-to-randomize-an-array-with-net - int n = array.Length; - while (n > 1) { - int k = rng.Next(n--); - T temp = array[n]; - array[n] = array[k]; - array[k] = temp; - } - } - protected int xzManhattanDistance(Vector3 p0, Vector3 p1, float gridSize) { return (Math.Abs(Convert.ToInt32((p0.x - p1.x) / gridSize)) + Math.Abs(Convert.ToInt32((p0.z - p1.z) / gridSize))); } + // hide and seek action. public void ExhaustiveSearchForItem(ServerAction action) { if (!physicsSceneManager.ObjectIdToSimObjPhysics.ContainsKey(action.objectId)) { errorMessage = "Object ID appears to be invalid."; @@ -6343,7 +6333,8 @@ public void ExhaustiveSearchForItem(ServerAction action) { ItemInHand.gameObject.SetActive(false); } - Shuffle(new System.Random(action.randomSeed), positions); + // NOTE: this doesn't use systemRandom + positions.Shuffle_(seed: action.randomSeed); SimplePriorityQueue pq = new SimplePriorityQueue(); Vector3 agentPos = transform.position; @@ -7180,10 +7171,9 @@ protected SimObjPhysics randomlyCreateAndPlaceObjectOnFloor(string objectType, i errorMessage = "Could not get bounds of object with type " + objectType; } - System.Random rnd = new System.Random(); - Vector3[] shuffledCurrentlyReachable = candidatePositions.OrderBy(x => rnd.Next()).ToArray(); + Vector3[] shuffledCurrentlyReachable = candidatePositions.OrderBy(x => systemRandom.Next()).ToArray(); float[] rotations = { 0f, 90f, 180f, 270f }; - float[] shuffledRotations = rotations.OrderBy(x => rnd.Next()).ToArray(); + float[] shuffledRotations = rotations.OrderBy(x => systemRandom.Next()).ToArray(); SimObjPhysics objectCreated = null; foreach (Vector3 position in shuffledCurrentlyReachable) { float y = b.extents.y + getFloorY(position.x, position.y, position.z) + 0.01f; @@ -7485,8 +7475,10 @@ public void StackBooks() { actionFinished(true); } + // hide and seek action public void RandomizeHideSeekObjects(int randomSeed, float removeProb) { - System.Random rnd = new System.Random(randomSeed); + // NOTE: this does not use systemRandom + var rnd = new System.Random(randomSeed); if (!physicsSceneManager.ToggleHideAndSeek(true)) { errorMessage = "Hide and Seek object reference not set, nothing to randomize."; @@ -7547,29 +7539,17 @@ public void RandomizeHideSeekObjects(int randomSeed, float removeProb) { // @pOpen is the probability of opening an openable object. // @randOpenness specifies if the openness for each opened object should be random, between 0% : 100%, or always 100%. public void RandomlyOpenCloseObjects( - int? randomSeed = null, bool simplifyPhysics = false, float pOpen = 0.5f, bool randOpenness = true ) { - System.Random rnd; - System.Random rndOpenness; - if (randomSeed == null) { - // truly random! - rnd = new System.Random(); - rndOpenness = new System.Random(); - } else { - rnd = new System.Random((int)randomSeed); - rndOpenness = new System.Random(((int)randomSeed) + 42); - } - foreach (SimObjPhysics so in GameObject.FindObjectsOfType()) { if (so.GetComponent()) { // randomly opens an object to a random openness - if (rnd.NextDouble() < pOpen) { + if (UnityEngine.Random.value < pOpen) { openObject( target: so, - openness: randOpenness ? (float)rndOpenness.NextDouble() : 1, + openness: randOpenness ? UnityEngine.Random.value : 1, forceAction: true, simplifyPhysics: simplifyPhysics, markActionFinished: false diff --git a/unity/Assets/Scripts/PhysicsSceneManager.cs b/unity/Assets/Scripts/PhysicsSceneManager.cs index d89292ff49..476c776a39 100644 --- a/unity/Assets/Scripts/PhysicsSceneManager.cs +++ b/unity/Assets/Scripts/PhysicsSceneManager.cs @@ -536,6 +536,8 @@ List excludedReceptacleTypes } } + // NOTE: for backwards compatibility with InitialRandomSpawn, this does not use BaseFPSAgentController.systemRandom. + // InitialRandomSpawn is going to be deprecated soon. System.Random rng = new System.Random(seed); gameObjsToPlaceInReceptacles.AddRange(unduplicatedSimObjects); gameObjsToPlaceInReceptacles.Shuffle_(rng); diff --git a/unity/Assets/Scripts/RandomExtensions.cs b/unity/Assets/Scripts/RandomExtensions.cs index f4f701da1d..440064cf71 100644 --- a/unity/Assets/Scripts/RandomExtensions.cs +++ b/unity/Assets/Scripts/RandomExtensions.cs @@ -1,71 +1,23 @@ +// Some extension methods for System.Random for creating a few more kinds of random stuff. using System; using System.Collections; using System.Collections.Generic; namespace RandomExtensions { - /// - /// Some extension methods for for creating a few more kinds of random stuff. - /// public static class RandomExtensions { - /// - /// Generates normally distributed numbers. Each operation makes two Gaussians for the price of one, and apparently they can be cached or something for better performance, but who cares. - /// - /// - /// Mean of the distribution - /// Standard deviation - /// + /** + * Generates normally distributed numbers. Each operation makes two Gaussians for the price of one, + * and apparently they can be cached or something for better performance, but who cares. + * + * @param mu Mean of the distribution + * @param sigma Standard deviation + */ public static double NextGaussian(this Random r, double mu = 0, double sigma = 1) { var u1 = r.NextDouble(); var u2 = r.NextDouble(); - - var rand_std_normal = Math.Sqrt(-2.0 * Math.Log(u1)) * - Math.Sin(2.0 * Math.PI * u2); - + var rand_std_normal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); var rand_normal = mu + sigma * rand_std_normal; - return rand_normal; } - - /// - /// Generates values from a triangular distribution. - /// - /// - /// See http://en.wikipedia.org/wiki/Triangular_distribution for a description of the triangular probability distribution and the algorithm for generating one. - /// - /// - /// Minimum - /// Maximum - /// Mode (most frequent value) - /// - public static double NextTriangular(this Random r, double a, double b, double c) { - var u = r.NextDouble(); - - return u < (c - a) / (b - a) - ? a + Math.Sqrt(u * (b - a) * (c - a)) - : b - Math.Sqrt((1 - u) * (b - a) * (b - c)); - } - - /// - /// Equally likely to return true or false. Uses . - /// - /// - public static bool NextBoolean(this Random r) { - return r.Next(2) > 0; - } - - /// - /// Shuffles a list in O(n) time by using the Fisher-Yates/Knuth algorithm. - /// - /// - /// - public static void Shuffle(this Random r, IList list) { - for (var i = 0; i < list.Count; i++) { - var j = r.Next(0, i + 1); - - var temp = list[j]; - list[j] = list[i]; - list[i] = temp; - } - } } -} \ No newline at end of file +} diff --git a/unity/Assets/Scripts/StochasticRemoteFPSAgentController.cs b/unity/Assets/Scripts/StochasticRemoteFPSAgentController.cs index 777b6e68cc..9fbec752a9 100644 --- a/unity/Assets/Scripts/StochasticRemoteFPSAgentController.cs +++ b/unity/Assets/Scripts/StochasticRemoteFPSAgentController.cs @@ -88,8 +88,7 @@ public void MoveRelative( if (xzMag > 1e-5f) { // rotate a small amount with every movement since robot doesn't always move perfectly straight if (this.applyActionNoise) { - var random = new System.Random(); - var rotateNoise = (float) random.NextGaussian(rotateGaussianMu, rotateGaussianSigma / 2.0f); + var rotateNoise = (float)systemRandom.NextGaussian(rotateGaussianMu, rotateGaussianSigma / 2.0f); transform.rotation = transform.rotation * Quaternion.Euler(new Vector3(0.0f, rotateNoise, 0.0f)); } @@ -306,14 +305,12 @@ public void MoveLeft( } protected float GetMoveMagnitudeWithNoise(float moveMagnitude, float noise) { - System.Random random = new System.Random(); - float internalNoise = applyActionNoise ? (float) random.NextGaussian(movementGaussianMu, movementGaussianSigma) : 0; + float internalNoise = applyActionNoise ? (float)systemRandom.NextGaussian(movementGaussianMu, movementGaussianSigma) : 0; return moveMagnitude + noise + (float) internalNoise; } protected float GetRotateMagnitudeWithNoise(Vector3 rotation, float noise) { - System.Random random = new System.Random(); - float internalNoise = applyActionNoise ? (float)random.NextGaussian(rotateGaussianMu, rotateGaussianSigma) : 0; + float internalNoise = applyActionNoise ? (float)systemRandom.NextGaussian(rotateGaussianMu, rotateGaussianSigma) : 0; return rotation.y + noise + (float)internalNoise; } } From 540e161aa9f23ed5de827c40f06b8fd63727f65b Mon Sep 17 00:00:00 2001 From: mattdeitke Date: Wed, 28 Jul 2021 15:10:04 -0700 Subject: [PATCH 5/6] add test with diff seeds --- ai2thor/tests/test_unity.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ai2thor/tests/test_unity.py b/ai2thor/tests/test_unity.py index 9dc29fca9e..50149590ad 100644 --- a/ai2thor/tests/test_unity.py +++ b/ai2thor/tests/test_unity.py @@ -1714,6 +1714,16 @@ def test_unsupported_manipulathor(controller): @pytest.mark.parametrize("controller", fifo_wsgi) def test_set_random_seed(controller): + orig_frame = controller.reset().frame + controller.step(action="SetRandomSeed", seed=41) + s41_frame = controller.step(action="RandomizeMaterials").frame + controller.step(action="SetRandomSeed", seed=42) + s42_frame = controller.step(action="RandomizeMaterials").frame + + assert_images_far(s42_frame, s41_frame) + assert_images_far(s42_frame, orig_frame) + assert_images_far(s41_frame, orig_frame) + f1_1 = controller.reset().frame f1_2 = controller.step(action="SetRandomSeed", seed=42).frame f1_3 = controller.step(action="RandomizeMaterials").frame From 80eaf8c048004376774ff5d0755256d572db8f29 Mon Sep 17 00:00:00 2001 From: Matt Deitke Date: Wed, 28 Jul 2021 15:58:19 -0700 Subject: [PATCH 6/6] add underflow check --- unity/Assets/Scripts/RandomExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/unity/Assets/Scripts/RandomExtensions.cs b/unity/Assets/Scripts/RandomExtensions.cs index 440064cf71..f840127aaf 100644 --- a/unity/Assets/Scripts/RandomExtensions.cs +++ b/unity/Assets/Scripts/RandomExtensions.cs @@ -17,6 +17,17 @@ public static double NextGaussian(this Random r, double mu = 0, double sigma = 1 var u2 = r.NextDouble(); var rand_std_normal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); var rand_normal = mu + sigma * rand_std_normal; + + // Very rarely, it is possible to have underflow or an infinity if u1/u1 = 0. + // In such a case, just return the mean. + if ( + Double.IsNaN(rand_normal) + || Double.IsInfinity(rand_normal) + || Double.IsNegativeInfinity(rand_normal) + ) { + return mu; + } + return rand_normal; } }