Skip to content

Commit

Permalink
fix reproducibility issue
Browse files Browse the repository at this point in the history
  • Loading branch information
dfremont committed Jan 11, 2025
1 parent 8ecc9bc commit d633f10
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 10 deletions.
7 changes: 6 additions & 1 deletion src/scenic/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,12 @@ def findMeshInteriorPoint(mesh, num_samples=None):
num_samples = 1
else:
num_samples = math.ceil(min(1e6, max(1, math.log(0.01, 1 - p_volume))))
samples = trimesh.sample.volume_mesh(mesh, num_samples)

# Do the "random" number generation ourselves so that it's deterministic
# (this helps debugging and reproducibility)
rng = numpy.random.default_rng(49493130352093220597973654454967996892)
pts = (rng.random((num_samples, 3)) * mesh.extents) + mesh.bounds[0]
samples = pts[mesh.contains(pts)]
if samples.size > 0:
return samples[0]

Expand Down
54 changes: 45 additions & 9 deletions tests/syntax/test_distributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,18 +668,54 @@ def test_reproducibility():
assert iterations == baseIterations


def test_reproducibility_lazy_interior():
"""Regression test for a reproducibility issue involving lazy region computations.
In this test, an interior point of the objects' shape is computed on demand
during the first sample, then cached. The code for doing so used NumPy's PRNG,
meaning that a second sample with the same random seed could differ.
"""
scenario = compileScenic(
"""
import numpy
from scenic.core.distributions import distributionFunction
@distributionFunction
def gen(arg):
return numpy.random.random()
region = BoxRegion().difference(BoxRegion(dimensions=(0.1, 0.1, 0.1)))
shape = MeshShape(region.mesh) # Shape which does not contain its center
other = new Object with shape shape
ego = new Object at (Range(0.9, 1.1), 0), with shape shape
param foo = ego intersects other # trigger computation of interior point
param bar = gen(ego) # generate number using NumPy's PRNG
"""
)
seed = random.randint(0, 1000000000)
random.seed(seed)
numpy.random.seed(seed)
s1 = sampleScene(scenario, maxIterations=60)
random.seed(seed)
numpy.random.seed(seed)
s2 = sampleScene(scenario, maxIterations=60)
assert s1.params["bar"] == s2.params["bar"]
assert s1.egoObject.x == s2.egoObject.x


@pytest.mark.slow
def test_reproducibility_3d():
scenario = compileScenic(
"ego = new Object\n"
"workspace = Workspace(SpheroidRegion(dimensions=(25,15,10)))\n"
"region = BoxRegion(dimensions=(25,15,0.1))\n"
"obj_1 = new Object in workspace, facing Range(0, 360) deg, with width Range(0.5, 1), with length Range(0.5,1)\n"
"obj_2 = new Object in workspace, facing (Range(0, 360) deg, Range(0, 360) deg, Range(0, 360) deg)\n"
"obj_3 = new Object in workspace, on region\n"
"param foo = Uniform(1, 4, 9, 16, 25, 36)\n"
"x = Range(0, 1)\n"
"require x > 0.8"
"""
ego = new Object
workspace = Workspace(SpheroidRegion(dimensions=(5,5,5)))
region = BoxRegion(dimensions=(25,15,0.1))
#obj_1 = new Object in workspace, facing Range(0, 360) deg, with width Range(0.5, 1), with length Range(0.5,1)
obj_2 = new Object in workspace, facing (Range(0, 360) deg, Range(0, 360) deg, Range(0, 360) deg)
#obj_3 = new Object in workspace, on region
param foo = ego intersects obj_2
x = Range(0, 1)
require x > 0.8
"""
)
seeds = [random.randint(0, 100) for i in range(10)]
for seed in seeds:
Expand Down

0 comments on commit d633f10

Please sign in to comment.