Skip to content

Commit

Permalink
Add deduplicating transform inserter (celeritas-project#1112)
Browse files Browse the repository at this point in the history
* Add equality operators to transforms
* Add to-do
* Add missing include
* Maked signed permutation accessible
* Add transofrm hasher
* Add transform inserter
* Fix missing symbols in release mode
* Fix documentation
* Apply review feedback
  • Loading branch information
sethrj committed Feb 19, 2024
1 parent 796c854 commit a8e1ac4
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 7 deletions.
6 changes: 3 additions & 3 deletions src/corecel/math/HashUtils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file corecel/math/HashUtils.hh
// TODO: rename to Hasher.hh
// TODO for v1.0: rename to Hasher.hh
//---------------------------------------------------------------------------//
#pragma once

Expand Down Expand Up @@ -33,8 +33,8 @@ using Hasher = detail::FnvHasher<std::size_t>;
* is \c true, because e.g. structs have padding so this may result in reading
* uninitialized data or giving two equal structs different hashes.
*/
template<class T>
std::size_t hash_as_bytes(Span<T const> s)
template<class T, std::size_t N>
std::size_t hash_as_bytes(Span<T const, N> s)
{
std::size_t result{};
Hasher hash{&result};
Expand Down
2 changes: 2 additions & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ list(APPEND SOURCES
orangeinp/detail/CsgUnitBuilder.cc
orangeinp/detail/LocalSurfaceInserter.cc
orangeinp/detail/NodeSimplifier.cc
orangeinp/detail/TransformInserter.cc
surf/ConeAligned.cc
surf/CylAligned.cc
surf/FaceNamer.cc
Expand All @@ -48,6 +49,7 @@ list(APPEND SOURCES
surf/detail/SurfaceTranslator.cc
surf/detail/SurfaceTransformer.cc
transform/SignedPermutation.cc
transform/TransformHasher.cc
transform/TransformIO.cc
transform/Transformation.cc
transform/VariantTransform.cc
Expand Down
1 change: 1 addition & 0 deletions src/orange/orangeinp/detail/CsgUnit.hh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "geocel/BoundingBox.hh"
#include "orange/OrangeTypes.hh"
#include "orange/surf/VariantSurface.hh"
#include "orange/transform/VariantTransform.hh"

#include "../CsgTree.hh"
#include "../CsgTypes.hh"
Expand Down
74 changes: 74 additions & 0 deletions src/orange/orangeinp/detail/TransformInserter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/detail/TransformInserter.cc
//---------------------------------------------------------------------------//
#include "TransformInserter.hh"

#include "orange/transform/TransformHasher.hh"

namespace celeritas
{
namespace orangeinp
{
namespace detail
{
//---------------------------------------------------------------------------//
/*!
* Construct with a pointer to the transform vector.
*/
TransformInserter::TransformInserter(VecTransform* transforms)
: transform_{transforms}
, cache_{0, HashTransform{transforms}, EqualTransform{transforms}}
{
CELER_EXPECT(transform_);
CELER_EXPECT(transform_->empty());
}

//---------------------------------------------------------------------------//
/*!
* Construct a transform with deduplication.
*/
TransformId TransformInserter::operator()(VariantTransform&& vt)
{
CELER_ASSUME(!vt.valueless_by_exception());
TransformId result = this->size_id();
transform_->push_back(std::move(vt));
auto [iter, inserted] = cache_.insert(result);
if (!inserted)
{
// Roll back the change by erasing the last element
transform_->pop_back();
// Return the existing ID
return *iter;
}
return result;
}

//---------------------------------------------------------------------------//
/*!
* Calculate the hash of a transform.
*/
std::size_t TransformInserter::HashTransform::operator()(TransformId id) const
{
CELER_EXPECT(storage && id < storage->size());
return visit(TransformHasher{}, (*storage)[id.unchecked_get()]);
}

//---------------------------------------------------------------------------//
/*!
* Compare two transform IDs for equality in a common container.
*/
bool TransformInserter::EqualTransform::operator()(TransformId a,
TransformId b) const
{
CELER_EXPECT(storage && a < storage->size() && b < storage->size());
return (*storage)[a.unchecked_get()] == (*storage)[b.unchecked_get()];
}

//---------------------------------------------------------------------------//
} // namespace detail
} // namespace orangeinp
} // namespace celeritas
72 changes: 72 additions & 0 deletions src/orange/orangeinp/detail/TransformInserter.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/detail/TransformInserter.hh
//---------------------------------------------------------------------------//
#pragma once

#include <unordered_set>
#include <vector>

#include "orange/transform/VariantTransform.hh"

namespace celeritas
{
namespace orangeinp
{
namespace detail
{
//---------------------------------------------------------------------------//
/*!
* Deduplicate transforms as they're being built.
*
* This currently only works for *exact* transforms rather than *almost exact*
* transforms. We may eventually want to add a "transform simplifier" and
* "transform soft equal".
*/
class TransformInserter
{
public:
//!@{
//! \name Type aliases
using VecTransform = std::vector<VariantTransform>;
//!@}

public:
// Construct with a pointer to the transform vector
explicit TransformInserter(VecTransform* transforms);

// Construct a transform with deduplication
TransformId operator()(VariantTransform&& vt);

private:
//// TYPES ////

struct HashTransform
{
VecTransform* storage{nullptr};
std::size_t operator()(TransformId) const;
};
struct EqualTransform
{
VecTransform* storage{nullptr};
bool operator()(TransformId, TransformId) const;
};

//// DATA ////

VecTransform* transform_;
std::unordered_set<TransformId, HashTransform, EqualTransform> cache_;

//// HELPER FUNCTIONS ////

//! Get the ID of the next transform to be inserted
TransformId size_id() const { return TransformId(transform_->size()); }
};

//---------------------------------------------------------------------------//
} // namespace detail
} // namespace orangeinp
} // namespace celeritas
18 changes: 18 additions & 0 deletions src/orange/transform/NoTransformation.hh
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,23 @@ class NoTransformation
CELER_FUNCTION Real3 const& rotate_down(Real3 const& d) const { return d; }
};

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//
//!@{
//! Host-only comparators
inline constexpr bool
operator==(NoTransformation const&, NoTransformation const&)
{
return true;
}

inline constexpr bool
operator!=(NoTransformation const&, NoTransformation const&)
{
return false;
}
//!@}

//---------------------------------------------------------------------------//
} // namespace celeritas
23 changes: 20 additions & 3 deletions src/orange/transform/SignedPermutation.hh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ namespace celeritas
* Construction of this class takes a length 3 array of \c SignedAxis values.
* The sign is a '+' or '-' character and the axis is the position of the
* nonzero component in that row.
*
* Note that the unsigned integer type (UIntT) is at least 16 bits, which is
* sufficient accurately round-trip through a floating point value.
*/
class SignedPermutation
{
Expand All @@ -60,6 +63,7 @@ class SignedPermutation
using SignedAxes = EnumArray<Axis, SignedAxis>;
using StorageSpan = Span<real_type const, 1>;
using DataArray = Array<real_type, 1>;
using UIntT = short unsigned int;
//!@}

public:
Expand All @@ -74,6 +78,9 @@ class SignedPermutation

//// ACCESSORS ////

//! Opaque type for hashing and comparison
UIntT value() const { return compressed_; }

// Reconstruct the permutation
SignedAxes permutation() const;

Expand All @@ -98,9 +105,6 @@ class SignedPermutation
rotate_down(Real3 const& parent_dir) const;

private:
// At least 16 bits: more than enough to round trip through a float
using UIntT = short unsigned int;

//// DATA ////

UIntT compressed_;
Expand All @@ -118,6 +122,19 @@ class SignedPermutation
// Make a permutation by rotating about the given axis
SignedPermutation make_permutation(Axis ax, QuarterTurn qtr);

//!@{
//! Host-only comparators
inline bool operator==(SignedPermutation const& a, SignedPermutation const& b)
{
return a.value() == b.value();
}

inline bool operator!=(SignedPermutation const& a, SignedPermutation const& b)
{
return !(a == b);
}
//!@}

//---------------------------------------------------------------------------//
// INLINE DEFINITIONS
//---------------------------------------------------------------------------//
Expand Down
70 changes: 70 additions & 0 deletions src/orange/transform/TransformHasher.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/transform/TransformHasher.cc
//---------------------------------------------------------------------------//
#include "TransformHasher.hh"

#include <functional>

#include "corecel/Assert.hh"
#include "corecel/math/HashUtils.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* By default, calculate a hash based on the stored data.
*/
template<class T>
auto TransformHasher::operator()(T const& t) const -> result_type
{
return hash_as_bytes(t.data());
}

//---------------------------------------------------------------------------//
/*!
* Special hash for "no transformation".
*/
auto TransformHasher::operator()(NoTransformation const&) const -> result_type
{
return std::hash<result_type>{}(0);
}

//---------------------------------------------------------------------------//
/*!
* Special hash for "signed permutation".
*/
auto TransformHasher::operator()(SignedPermutation const& t) const
-> result_type
{
auto v = t.value();
return std::hash<decltype(v)>{}(v);
}

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//
/*!
* Calculate a hash for a variant transform.
*/
TransformHasher::result_type
visit(TransformHasher const& th, VariantTransform const& transform)
{
CELER_ASSUME(!transform.valueless_by_exception());
return std::visit(th, transform);
}

//---------------------------------------------------------------------------//
// EXPLICIT INSTANTIATION
//---------------------------------------------------------------------------//
//! \cond

template std::size_t TransformHasher::operator()(Translation const&) const;
template std::size_t TransformHasher::operator()(Transformation const&) const;

//! \endcond
//---------------------------------------------------------------------------//
} // namespace celeritas
48 changes: 48 additions & 0 deletions src/orange/transform/TransformHasher.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/transform/TransformHasher.hh
//---------------------------------------------------------------------------//
#pragma once

#include <cstdlib>

#include "VariantTransform.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Calculate a hash value of a transform for deduplication.
*/
class TransformHasher
{
public:
//!@{
//! \name Type aliases
using result_type = std::size_t;
//!@}

public:
// By default, calculate a hash based on the stored data
template<class T>
result_type operator()(T const&) const;

// Special hash for "no transformation"
result_type operator()(NoTransformation const&) const;

// Special hash for "signed permutation"
result_type operator()(SignedPermutation const&) const;
};

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//
// Calculate a hash for a variant transform
TransformHasher::result_type
visit(TransformHasher const&, VariantTransform const& transform);

//---------------------------------------------------------------------------//
} // namespace celeritas

0 comments on commit a8e1ac4

Please sign in to comment.