Skip to content

Commit

Permalink
Allow user to seed random number generator when shuffling points (#2737)
Browse files Browse the repository at this point in the history
* Allow user to seed random number generator when shuffling points

This change also defaults to using random_device to see the mt19937 random
number generator.

* Update seeded tests to make sure the result is the same between two runs, rather than hardcode

- Unseeded test makes sure that at least one index has changed
  • Loading branch information
chambbj authored and abellgithub committed Sep 24, 2019
1 parent d0d5a0e commit bfe99cb
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 83 deletions.
21 changes: 13 additions & 8 deletions filters/RandomizeFilter.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/******************************************************************************
* Copyright (c) 2015, Hobu Inc. (hobu@hobu.co)
* Copyright (c) 2019, Bradley J Chambers (brad.chambers@gmail.com)
*
* All rights reserved.
*
Expand Down Expand Up @@ -36,16 +37,20 @@
namespace pdal
{

static StaticPluginInfo const s_info
{
"filters.randomize",
"Randomize points in a view.",
"http://pdal.io/stages/filters.randomize.html"
};
static StaticPluginInfo const s_info{
"filters.randomize", "Randomize points in a view.",
"http://pdal.io/stages/filters.randomize.html"};

CREATE_STATIC_STAGE(RandomizeFilter, s_info)

std::string RandomizeFilter::getName() const { return s_info.name; }
std::string RandomizeFilter::getName() const
{
return s_info.name;
}

} // namespace pdal
void RandomizeFilter::addArgs(ProgramArgs& args)
{
m_seedArg = &args.add("seed", "Random number generator seed", m_seed, 0u);
}

} // namespace pdal
86 changes: 51 additions & 35 deletions filters/RandomizeFilter.hpp
Original file line number Diff line number Diff line change
@@ -1,59 +1,75 @@
/******************************************************************************
* Copyright (c) 2015, Hobu Inc. (hobu@hobu.co)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/
* Copyright (c) 2015, Hobu Inc. (hobu@hobu.co)
* Copyright (c) 2019, Bradley J Chambers (brad.chambers@gmail.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#pragma once

#include <pdal/Filter.hpp>
#include <pdal/PointViewIter.hpp>

#include <random>

namespace pdal
{

class PDAL_DLL RandomizeFilter : public Filter
{
public:
RandomizeFilter()
{}
{
}

std::string getName() const;

private:
Arg* m_seedArg;
unsigned m_seed;

virtual void addArgs(ProgramArgs& args);
virtual void filter(PointView& view)
{ std::random_shuffle(view.begin(), view.end()); }
{
if (!m_seedArg->set())
{
std::random_device rng;
m_seed = rng();
}
std::mt19937 mt(m_seed);
std::shuffle(view.begin(), view.end(), mt);
}

RandomizeFilter& operator=(const RandomizeFilter&); // not implemented
RandomizeFilter(const RandomizeFilter&); // not implemented
RandomizeFilter(const RandomizeFilter&); // not implemented
};

} // namespace pdal
214 changes: 174 additions & 40 deletions test/unit/filters/RandomizeFilterTest.cpp
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
/******************************************************************************
* Copyright (c) 2015, Hobu Inc. (hobu@hobu.co)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/
* Copyright (c) 2015, Hobu Inc. (hobu@hobu.co)
* Copyright (c) 2019, Bradley J Chambers (brad.chambers@gmail.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#include <pdal/pdal_test_main.hpp>

#include <io/FauxReader.hpp>
#include <filters/RandomizeFilter.hpp>
#include <io/FauxReader.hpp>

using namespace pdal;

Expand All @@ -46,8 +47,8 @@ TEST(RandomizeFilterTest, simple)
point_count_t count = 1000;

Options readerOps;
readerOps.add("bounds", BOX3D(1, 1, 1,
(double)count, (double)count, (double)count));
readerOps.add("bounds",
BOX3D(1, 1, 1, (double)count, (double)count, (double)count));
readerOps.add("mode", "ramp");
readerOps.add("count", count);

Expand All @@ -65,10 +66,143 @@ TEST(RandomizeFilterTest, simple)
PointViewPtr v = *s.begin();
EXPECT_EQ(v->size(), (size_t)count);

/**
for (PointId i = 0; i < count; i++)
std::cerr << "X[" << i << "] = " <<
v->getFieldAs<double>(Dimension::Id::X, i) << "!\n";
**/
/**
for (PointId i = 0; i < count; i++)
std::cerr << "X[" << i << "] = " <<
v->getFieldAs<double>(Dimension::Id::X, i) << "!\n";
**/
}

TEST(RandomizeFilterTest, seedZero)
{
point_count_t count = 10;

Options readerOps;
readerOps.add("bounds",
BOX3D(1, 1, 1, (double)count, (double)count, double(count)));
readerOps.add("mode", "ramp");
readerOps.add("count", count);

FauxReader r;
r.setOptions(readerOps);

Options filterOps;
filterOps.add("seed", 0);

RandomizeFilter f;
f.setInput(r);
f.setOptions(filterOps);

PointTable t;
f.prepare(t);
PointViewSet s = f.execute(t);

EXPECT_EQ(1u, s.size());
PointViewPtr v = *s.begin();
EXPECT_EQ((size_t)count, v->size());

std::vector<double> vals(count, 0.0);
for (PointId i = 0; i < count; ++i)
vals[i] = v->getFieldAs<double>(Dimension::Id::X, i);

PointViewSet s2 = f.execute(t);

EXPECT_EQ(1u, s2.size());
PointViewPtr v2 = *s2.begin();
EXPECT_EQ((size_t)count, v2->size());

for (PointId i = 0; i < count; ++i)
{
double val = v2->getFieldAs<double>(Dimension::Id::X, i);
EXPECT_EQ(vals[i], val);
}
}

TEST(RandomizeFilterTest, seedOne)
{
point_count_t count = 10;

Options readerOps;
readerOps.add("bounds",
BOX3D(1, 1, 1, (double)count, (double)count, double(count)));
readerOps.add("mode", "ramp");
readerOps.add("count", count);

FauxReader r;
r.setOptions(readerOps);

Options filterOps;
filterOps.add("seed", 1);

RandomizeFilter f;
f.setInput(r);
f.setOptions(filterOps);

PointTable t;
f.prepare(t);
PointViewSet s = f.execute(t);

EXPECT_EQ(1u, s.size());
PointViewPtr v = *s.begin();
EXPECT_EQ((size_t)count, v->size());

std::vector<double> vals(count, 0.0);
for (PointId i = 0; i < count; ++i)
vals[i] = v->getFieldAs<double>(Dimension::Id::X, i);

PointViewSet s2 = f.execute(t);

EXPECT_EQ(1u, s2.size());
PointViewPtr v2 = *s2.begin();
EXPECT_EQ((size_t)count, v2->size());

for (PointId i = 0; i < count; ++i)
{
double val = v2->getFieldAs<double>(Dimension::Id::X, i);
EXPECT_EQ(vals[i], val);
}
}

TEST(RandomizeFilterTest, unseeded)
{
point_count_t count = 10;

Options readerOps;
readerOps.add("bounds",
BOX3D(1, 1, 1, (double)count, (double)count, double(count)));
readerOps.add("mode", "ramp");
readerOps.add("count", count);

FauxReader r;
r.setOptions(readerOps);

RandomizeFilter f;
f.setInput(r);

PointTable t;
f.prepare(t);
PointViewSet s = f.execute(t);

EXPECT_EQ(1u, s.size());
PointViewPtr v = *s.begin();
EXPECT_EQ((size_t)count, v->size());

std::vector<double> vals(count, 0.0);
for (PointId i = 0; i < count; ++i)
vals[i] = v->getFieldAs<double>(Dimension::Id::X, i);

PointViewSet s2 = f.execute(t);

EXPECT_EQ(1u, s2.size());
PointViewPtr v2 = *s2.begin();
EXPECT_EQ((size_t)count, v2->size());

size_t numMatches(0);
for (PointId i = 0; i < count; ++i)
{
double val = v2->getFieldAs<double>(Dimension::Id::X, i);
if (vals[i] == val)
numMatches++;
}
EXPECT_LT(numMatches, count);
}

0 comments on commit bfe99cb

Please sign in to comment.