<commit_before>/*
%EXPM_SRT_3D Compute a transformation matrix, given the Lie algebra vector
%
%   M = expm_srt_3d(X)
%
% Computes the transformation matrix defined by a Lie vector consisting of
% a rotation, translation and uniform scaling.
%
% The computation is done in closed form, using the formulae given in the
% paper:
% "Distances and Means of Direct Similarities"
% M-T Pham et al.
%
%IN:
%   X - DxN array, where each column specifies a different
%       transformation. X(1:3,:) are the rotation components, X(4:6,:) are
%       the translation components and X(7,:) are the scale components. D
%       can be 3 (rotation only, 6 (rotation and translation), or 7
%       (rotation, translation and scale).
%
%OUT:
%   M - 3x(3+D~=3)xN array of transformation matrices.
*/

#include <math.h>
#include <stdlib.h>

// out - pointer to a column major matrix of size 3x4, or 3x3 if t==NULL.
// r - pointer to a 3 element vector of rotation components (x, y, z).
// t - pointer to a 3 element vector of translation components (x, y, z). Zero translation assumed if NULL.
// s - a uniform scaling component.
template<class REAL> void expm_srt_3d(REAL *out, const REAL *r, const REAL *t=NULL, const REAL s=0)
{
    // First compute the rotation part
    // Angle of rotation
    REAL theta2 = r[0] * r[0] + r[1] * r[1] + r[2] * r[2];
    REAL theta = sqrt(theta2);
    REAL cos_theta = cos(theta);
    REAL sin_theta = sin(theta);
    REAL cosf = REAL(0.5);
    REAL sinc = REAL(1.0);
    if (theta2 > REAL(2.23e-16)) {
        cosf = (REAL(1.0) - cos_theta) / theta2;
        sinc = sin_theta / theta;
    }
    REAL w2[6];
    // Diagonals
    w2[0] = r[0] * r[0] - theta2;
    w2[1] = r[1] * r[1] - theta2;
    w2[2] = r[2] * r[2] - theta2;
    out[0] = w2[0] * cosf + REAL(1.0);
    out[4] = w2[1] * cosf + REAL(1.0);
    out[8] = w2[2] * cosf + REAL(1.0);
    // Off diagonals
    w2[3] = r[0] * r[1];
    out[1] = w2[3] * cosf;
    out[3] = out[1] - sinc * r[2];
    out[1] += sinc * r[2];
    w2[4] = r[0] * r[2];
    out[2] = w2[4] * cosf;
    out[6] = out[2] + sinc * r[1];
    out[2] -= sinc * r[1];
    w2[5] = r[1] * r[2];
    out[5] = w2[5] * cosf;
    out[7] = out[5] - sinc * r[0];
    out[5] += sinc * r[0];

    // Multiply by scale
    REAL exp_s = REAL(1.0);
    if (s) {
        exp_s = exp(s);
        for (int a = 0; a < 9; ++a)
            out[a] = exp_s * out[a];
    }

    // Now compute the translation part
    if (t) {
        REAL exp_sa = abs(s) < REAL(1.0e-10) ? REAL(1.0) : (exp_s - REAL(1.0)) / s;
        REAL eta_r, eta_i;
        theta2 += REAL(1e-38); // Avoid divides by 0
        if (s) {
            REAL x = exp_s * cos_theta - REAL(1.0);
            REAL y = exp_s * sin_theta;
            eta_r = exp_sa - (s * x + theta * y) / (s * s + theta2);
            eta_i = (s * y - theta * x) / (theta * (s * s + theta2) + REAL(1e-38));
        } else {
            eta_r = REAL(1.0) - sinc;
            eta_i = cosf;
        }
        eta_r /= theta2;

        out[9] = t[0] * (eta_r * w2[0] + exp_sa);
        out[9] += t[1] * (eta_r * w2[3] - eta_i * r[2]);
        out[9] += t[2] * (eta_r * w2[4] + eta_i * r[1]);
        out[10] = t[0] * (eta_r * w2[3] + eta_i * r[2]);
        out[10] += t[1] * (eta_r * w2[1] + exp_sa);
        out[10] += t[2] * (eta_r * w2[5] - eta_i * r[0]);
        out[11] = t[0] * (eta_r * w2[4] - eta_i * r[1]);
        out[11] += t[1] * (eta_r * w2[5] + eta_i * r[0]);
        out[11] += t[2] * (eta_r * w2[2] + exp_sa);
    }
}

#ifdef _MATLAB_
#include <mex.h>
#ifdef _OPENMP
#include <omp.h>
#endif

#if _MATLAB_ < 805 // R2015a
extern "C" mxArray *mxCreateUninitNumericArray(mwSize ndim, const size_t *dims, mxClassID classid, mxComplexity ComplexFlag);
#endif

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
	// Check number of arguments
	if (nrhs != 1)
		mexErrMsgTxt("Unexpected number of input arguments.");
	if (nlhs != 1)
		mexErrMsgTxt("Unexpected number of output arguments.");

	// Check argument types are valid
	if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS || mxIsComplex(prhs[0]))
		mexErrMsgTxt("Input must be a real double matrix");
    int nparams = mxGetM(prhs[0]);
    if (nparams != 3 && nparams != 6 && nparams != 7)
        mexErrMsgTxt("Input must have 3, 6 or 7 rows");

    // Construct the output array
    int ndims = mxGetNumberOfDimensions(prhs[0]);
	if (ndims > 19)
		mexErrMsgTxt("Input has an unsupported number of dimensions");
	size_t out_dims[20];
	out_dims[0] = 3;
	out_dims[1] = 3 + (nparams != 3);
	const int *dims = mxGetDimensions(prhs[0]);
	int nmatrices = 1;
	for (int a = 1; a < ndims; ++a) {
		out_dims[a+1] = dims[a];
		nmatrices *= dims[a];
	}
    plhs[0] = mxCreateUninitNumericArray(ndims+1, out_dims, mxDOUBLE_CLASS, mxREAL);
    double *out = (double *)mxGetData(plhs[0]);

    // Get the data pointers and strides
    double *r = (double *)mxGetData(prhs[0]);
    double *t = NULL;
    int t_stride = 0;
    if (nparams > 3) {
        t = r + 3;
        t_stride = nparams;
    }
    double s_ = 0;
    double *s = &s_;
    int s_stride = 0;
    if (nparams == 7) {
        s = r + 6;
        s_stride = 7;
    }
    int out_stride = 3 * (3 + (nparams != 3));

	// Call the expm function
    int a;
#pragma omp parallel for if (nmatrices > 1000) num_threads(omp_get_num_procs()) default(shared) private(a)
    for (a = 0; a < nmatrices; ++a)
        expm_srt_3d(&out[out_stride*a], &r[nparams*a], &t[t_stride*a], s[s_stride*a]);

	return;
}
#endif<commit_msg>Fix compilation<commit_after>/*
%EXPM_SRT_3D Compute a transformation matrix, given the Lie algebra vector
%
%   M = expm_srt_3d(X)
%
% Computes the transformation matrix defined by a Lie vector consisting of
% a rotation, translation and uniform scaling.
%
% The computation is done in closed form, using the formulae given in the
% paper:
% "Distances and Means of Direct Similarities"
% M-T Pham et al.
%
%IN:
%   X - DxN array, where each column specifies a different
%       transformation. X(1:3,:) are the rotation components, X(4:6,:) are
%       the translation components and X(7,:) are the scale components. D
%       can be 3 (rotation only, 6 (rotation and translation), or 7
%       (rotation, translation and scale).
%
%OUT:
%   M - 3x(3+D~=3)xN array of transformation matrices.
*/

#include <cmath>

// out - pointer to a column major matrix of size 3x4, or 3x3 if t==NULL.
// r - pointer to a 3 element vector of rotation components (x, y, z).
// t - pointer to a 3 element vector of translation components (x, y, z). Zero translation assumed if NULL.
// s - a uniform scaling component.
template<class REAL> void expm_srt_3d(REAL *out, const REAL *r, const REAL *t=NULL, const REAL s=0)
{
    // First compute the rotation part
    // Angle of rotation
    REAL theta2 = r[0] * r[0] + r[1] * r[1] + r[2] * r[2];
    REAL theta = std::sqrt(theta2);
    REAL cos_theta = std::cos(theta);
    REAL sin_theta = std::sin(theta);
    REAL cosf = REAL(0.5);
    REAL sinc = REAL(1.0);
    if (theta2 > REAL(2.23e-16)) {
        cosf = (REAL(1.0) - cos_theta) / theta2;
        sinc = sin_theta / theta;
    }
    REAL w2[6];
    // Diagonals
    w2[0] = r[0] * r[0] - theta2;
    w2[1] = r[1] * r[1] - theta2;
    w2[2] = r[2] * r[2] - theta2;
    out[0] = w2[0] * cosf + REAL(1.0);
    out[4] = w2[1] * cosf + REAL(1.0);
    out[8] = w2[2] * cosf + REAL(1.0);
    // Off diagonals
    w2[3] = r[0] * r[1];
    out[1] = w2[3] * cosf;
    out[3] = out[1] - sinc * r[2];
    out[1] += sinc * r[2];
    w2[4] = r[0] * r[2];
    out[2] = w2[4] * cosf;
    out[6] = out[2] + sinc * r[1];
    out[2] -= sinc * r[1];
    w2[5] = r[1] * r[2];
    out[5] = w2[5] * cosf;
    out[7] = out[5] - sinc * r[0];
    out[5] += sinc * r[0];

    // Multiply by scale
    REAL exp_s = REAL(1.0);
    if (s) {
        exp_s = std::exp(s);
        for (int a = 0; a < 9; ++a)
            out[a] = exp_s * out[a];
    }

    // Now compute the translation part
    if (t) {
        REAL exp_sa = std::abs(s) < REAL(1.0e-10) ? REAL(1.0) : (exp_s - REAL(1.0)) / s;
        REAL eta_r, eta_i;
        theta2 += REAL(1e-38); // Avoid divides by 0
        if (s) {
            REAL x = exp_s * cos_theta - REAL(1.0);
            REAL y = exp_s * sin_theta;
            eta_r = exp_sa - (s * x + theta * y) / (s * s + theta2);
            eta_i = (s * y - theta * x) / (theta * (s * s + theta2) + REAL(1e-38));
        } else {
            eta_r = REAL(1.0) - sinc;
            eta_i = cosf;
        }
        eta_r /= theta2;

        out[9] = t[0] * (eta_r * w2[0] + exp_sa);
        out[9] += t[1] * (eta_r * w2[3] - eta_i * r[2]);
        out[9] += t[2] * (eta_r * w2[4] + eta_i * r[1]);
        out[10] = t[0] * (eta_r * w2[3] + eta_i * r[2]);
        out[10] += t[1] * (eta_r * w2[1] + exp_sa);
        out[10] += t[2] * (eta_r * w2[5] - eta_i * r[0]);
        out[11] = t[0] * (eta_r * w2[4] - eta_i * r[1]);
        out[11] += t[1] * (eta_r * w2[5] + eta_i * r[0]);
        out[11] += t[2] * (eta_r * w2[2] + exp_sa);
    }
}

#ifdef _MATLAB_
#include <mex.h>
#ifdef _OPENMP
#include <omp.h>
#endif

#if _MATLAB_ < 805 // R2015a
extern "C" mxArray *mxCreateUninitNumericArray(mwSize ndim, const size_t *dims, mxClassID classid, mxComplexity ComplexFlag);
#endif

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
	// Check number of arguments
	if (nrhs != 1)
		mexErrMsgTxt("Unexpected number of input arguments.");
	if (nlhs != 1)
		mexErrMsgTxt("Unexpected number of output arguments.");

	// Check argument types are valid
	if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS || mxIsComplex(prhs[0]))
		mexErrMsgTxt("Input must be a real double matrix");
    int nparams = mxGetM(prhs[0]);
    if (nparams != 3 && nparams != 6 && nparams != 7)
        mexErrMsgTxt("Input must have 3, 6 or 7 rows");

    // Construct the output array
    int ndims = mxGetNumberOfDimensions(prhs[0]);
	if (ndims > 19)
		mexErrMsgTxt("Input has an unsupported number of dimensions");
	size_t out_dims[20];
	out_dims[0] = 3;
	out_dims[1] = 3 + (nparams != 3);
	const int *dims = mxGetDimensions(prhs[0]);
	int nmatrices = 1;
	for (int a = 1; a < ndims; ++a) {
		out_dims[a+1] = dims[a];
		nmatrices *= dims[a];
	}
    plhs[0] = mxCreateUninitNumericArray(ndims+1, out_dims, mxDOUBLE_CLASS, mxREAL);
    double *out = (double *)mxGetData(plhs[0]);

    // Get the data pointers and strides
    double *r = (double *)mxGetData(prhs[0]);
    double *t = NULL;
    int t_stride = 0;
    if (nparams > 3) {
        t = r + 3;
        t_stride = nparams;
    }
    double s_ = 0;
    double *s = &s_;
    int s_stride = 0;
    if (nparams == 7) {
        s = r + 6;
        s_stride = 7;
    }
    int out_stride = 3 * (3 + (nparams != 3));

	// Call the expm function
    int a;
#pragma omp parallel for if (nmatrices > 1000) num_threads(omp_get_num_procs()) default(shared) private(a)
    for (a = 0; a < nmatrices; ++a)
        expm_srt_3d(&out[out_stride*a], &r[nparams*a], &t[t_stride*a], s[s_stride*a]);

	return;
}
#endif<|endoftext|>


<commit_before>
#pragma once

#include <experimental/optional>
#include <utility>
#include <vector>

#include "base/array.hpp"
#include "geometry/affine_map.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/point.hpp"
#include "geometry/rp2_point.hpp"
#include "geometry/sphere.hpp"

namespace principia {
namespace geometry {
namespace internal_perspective {

using base::BoundedArray;

template<typename Vector>
using Segment = std::pair<Point<Vector>, Point<Vector>>;

template<typename Vector>
using Segments = std::vector<Segment<Vector>>;

// A perspective using the pinhole camera model.  It project a point of
// |FromFrame| to an element of ℝP².  |ToFrame| is the frame of the camera.  In
// that frame the camera is located at the origin and looking at the positive
// z-axis.  The x- and y- axis of the camera correspond to those of ℝP².
template<typename FromFrame, typename ToFrame, typename Scalar,
         template<typename, typename> class LinearMap>
class Perspective final {
 public:
  Perspective(
      AffineMap<ToFrame, FromFrame, Scalar, LinearMap> const& from_camera,
      Scalar const& focal);
  Perspective(
      AffineMap<FromFrame, ToFrame, Scalar, LinearMap> const& to_camera,
      Scalar const& focal);

  Scalar const& focal() const;

  // Returns the ℝP² element resulting from the projection of |point|.  This
  // is properly defined for all points other than the camera origin.
  RP2Point<Scalar, ToFrame> operator()(
      Point<Vector<Scalar, FromFrame>> const& point) const;

  // Returns the part of |segment| that is behind the focal plane as seen from
  // the camera.  Returns nullopt if |segment| is entirely in front of the focal
  // plane.
  std::experimental::optional<Segment<Vector<Scalar, FromFrame>>>
  SegmentBehindFocalPlane(
      Segment<Vector<Scalar, FromFrame>> const& segment) const;

  // Returns true iff the |point| is hidden by the |sphere| in this perspective.
  bool IsHiddenBySphere(Point<Vector<Scalar, FromFrame>> const& point,
                        Sphere<Scalar, FromFrame> const& sphere) const;

  // Returns sin² ɑ where ɑ is the half angle under which the |sphere| is seen.
  double SphereSin²HalfAngle(Sphere<Scalar, FromFrame> const& sphere) const;

  // Returns the (sub)segments of |segment| that are visible in this perspective
  // after taking into account the hiding by |sphere|.  The returned vector has
  // 0, 1, or 2 elements.
  BoundedArray<Segment<Vector<Scalar, FromFrame>>, 2> VisibleSegments(
      Segment<Vector<Scalar, FromFrame>> const& segment,
      Sphere<Scalar, FromFrame> const& sphere) const;

  // Same as above, but for hiding with multiple spheres.  For N spheres it may
  // return N + 1 segments.
  Segments<Vector<Scalar, FromFrame>> VisibleSegments(
      Segment<Vector<Scalar, FromFrame>> const& segment,
      std::vector<Sphere<Scalar, FromFrame>> const& spheres) const;

 private:
  AffineMap<ToFrame, FromFrame, Scalar, LinearMap> const from_camera_;
  AffineMap<FromFrame, ToFrame, Scalar, LinearMap> const to_camera_;
  Point<Vector<Scalar, FromFrame>> const camera_;
  Scalar const focal_;
};

}  // namespace internal_perspective

using internal_perspective::Perspective;
using internal_perspective::Segment;

}  // namespace geometry
}  // namespace principia

#include "geometry/perspective_body.hpp"
<commit_msg>Fix compilation error.<commit_after>
#pragma once

#include <experimental/optional>
#include <utility>
#include <vector>

#include "base/array.hpp"
#include "geometry/affine_map.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/point.hpp"
#include "geometry/rp2_point.hpp"
#include "geometry/sphere.hpp"

namespace principia {
namespace geometry {
namespace internal_perspective {

using base::BoundedArray;

template<typename Vector>
using Segment = std::pair<Point<Vector>, Point<Vector>>;

template<typename Vector>
using Segments = std::vector<Segment<Vector>>;

// A perspective using the pinhole camera model.  It project a point of
// |FromFrame| to an element of ℝP².  |ToFrame| is the frame of the camera.  In
// that frame the camera is located at the origin and looking at the positive
// z-axis.  The x- and y- axis of the camera correspond to those of ℝP².
template<typename FromFrame, typename ToFrame, typename Scalar,
         template<typename, typename> class LinearMap>
class Perspective final {
 public:
  Perspective(
      AffineMap<ToFrame, FromFrame, Scalar, LinearMap> const& from_camera,
      Scalar const& focal);
  Perspective(
      AffineMap<FromFrame, ToFrame, Scalar, LinearMap> const& to_camera,
      Scalar const& focal);

  Scalar const& focal() const;

  // Returns the ℝP² element resulting from the projection of |point|.  This
  // is properly defined for all points other than the camera origin.
  RP2Point<Scalar, ToFrame> operator()(
      Point<Vector<Scalar, FromFrame>> const& point) const;

  // Returns the part of |segment| that is behind the focal plane as seen from
  // the camera.  Returns nullopt if |segment| is entirely in front of the focal
  // plane.
  std::experimental::optional<Segment<Vector<Scalar, FromFrame>>>
  SegmentBehindFocalPlane(
      Segment<Vector<Scalar, FromFrame>> const& segment) const;

  // Returns true iff the |point| is hidden by the |sphere| in this perspective.
  bool IsHiddenBySphere(Point<Vector<Scalar, FromFrame>> const& point,
                        Sphere<Scalar, FromFrame> const& sphere) const;

  // Returns sin² ɑ where ɑ is the half angle under which the |sphere| is seen.
  double SphereSin²HalfAngle(Sphere<Scalar, FromFrame> const& sphere) const;

  // Returns the (sub)segments of |segment| that are visible in this perspective
  // after taking into account the hiding by |sphere|.  The returned vector has
  // 0, 1, or 2 elements.
  BoundedArray<Segment<Vector<Scalar, FromFrame>>, 2> VisibleSegments(
      Segment<Vector<Scalar, FromFrame>> const& segment,
      Sphere<Scalar, FromFrame> const& sphere) const;

  // Same as above, but for hiding with multiple spheres.  For N spheres it may
  // return N + 1 segments.
  Segments<Vector<Scalar, FromFrame>> VisibleSegments(
      Segment<Vector<Scalar, FromFrame>> const& segment,
      std::vector<Sphere<Scalar, FromFrame>> const& spheres) const;

 private:
  AffineMap<ToFrame, FromFrame, Scalar, LinearMap> const from_camera_;
  AffineMap<FromFrame, ToFrame, Scalar, LinearMap> const to_camera_;
  Point<Vector<Scalar, FromFrame>> const camera_;
  Scalar const focal_;
};

}  // namespace internal_perspective

using internal_perspective::Perspective;
using internal_perspective::Segment;
using internal_perspective::Segments;

}  // namespace geometry
}  // namespace principia

#include "geometry/perspective_body.hpp"
<|endoftext|>


<commit_before>#include "numerics/global_optimization.hpp"

#include "geometry/frame.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/named_quantities.hpp"
#include "gtest/gtest.h"
#include "quantities/named_quantities.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/approximate_quantity.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/is_near.hpp"
#include "testing_utilities/numerics_matchers.hpp"
#include "testing_utilities/optimization_test_functions.hpp"
#include "testing_utilities/vanishes_before.hpp"

namespace principia {
namespace numerics {

using geometry::Displacement;
using geometry::Frame;
using geometry::Position;
using geometry::Vector;
using quantities::Inverse;
using quantities::Length;
using quantities::Pow;
using quantities::si::Metre;
using testing_utilities::AbsoluteErrorFrom;
using testing_utilities::AlmostEquals;
using testing_utilities::Branin;
using testing_utilities::Componentwise;
using testing_utilities::GoldsteinPrice;
using testing_utilities::IsNear;
using testing_utilities::RelativeErrorFrom;
using testing_utilities::𝛁Branin;
using testing_utilities::𝛁GoldsteinPrice;
using testing_utilities::operator""_;
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;

// The test functions in this file are from
// https://www.sfu.ca/~ssurjano/optimization.html.
class GlobalOptimizationTest : public ::testing::Test {
 protected:
  using World = Frame<enum class WorldTag>;
};

TEST_F(GlobalOptimizationTest, Branin) {
  using Optimizer = MultiLevelSingleLinkage<double, Displacement<World>>;
  int function_invocations = 0;
  int gradient_invocations = 0;

  auto branin =
      [&function_invocations](Displacement<World> const& displacement) {
    ++function_invocations;
    auto const& coordinates = displacement.coordinates();
    // The extra |x₀| term ensures that we have a unique solution in three
    // dimensions.
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    return Pow<2>(x₀) + Branin(x₁, x₂);
  };

  auto grad_branin = [&gradient_invocations](
                         Displacement<World> const& displacement) {
    ++gradient_invocations;
    auto const& coordinates = displacement.coordinates();
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    double const g₀ = 2 * x₀;
    auto const [g₁, g₂] = 𝛁Branin(x₁, x₂);
    return Vector<Inverse<Length>, World>({g₀ / Metre, g₁ / Metre, g₂ / Metre});
  };

  Optimizer::Box const box = {
      .centre = Displacement<World>({0 * Metre, 2.5 * Metre, 7.5 * Metre}),
      .vertices = {
          Displacement<World>({2 * Metre, 0 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 7.5 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 0 * Metre, 7.5 * Metre}),
      }};

  const auto tolerance = 1e-6 * Metre;
  Optimizer optimizer(box, branin, grad_branin);
  auto const minima = optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                                 /*number_of_rounds=*/10,
                                                 tolerance);

  EXPECT_EQ(1434, function_invocations);
  EXPECT_EQ(598, gradient_invocations);

  // Note that the fourth minima is outside the |box| passed to the optimizer.
  EXPECT_THAT(
      minima,
      ElementsAre(
          Componentwise(
              AbsoluteErrorFrom(0 * Metre, IsNear(1.4e-7_(1) * Metre)),
              AbsoluteErrorFrom(9.42478 * Metre, IsNear(2.0e-6_(1) * Metre)),
              RelativeErrorFrom(2.475 * Metre, IsNear(8.0e-9_(1)))),
          Componentwise(
              AbsoluteErrorFrom(0 * Metre, IsNear(5.7e-7_(1) * Metre)),
              AbsoluteErrorFrom(π * Metre, IsNear(4.8e-9_(1) * Metre)),
              RelativeErrorFrom(2.275 * Metre, IsNear(9.1e-8_(1)))),
          Componentwise(
              AbsoluteErrorFrom(0 * Metre, IsNear(5.9e-8_(1) * Metre)),
              RelativeErrorFrom(-π * Metre, IsNear(3.5e-8_(1))),
              RelativeErrorFrom(12.275 * Metre, IsNear(6.2e-9_(1)))),
          Componentwise(
              AbsoluteErrorFrom(0 * Metre, IsNear(1.9e-8_(1) * Metre)),
              RelativeErrorFrom(5 * π * Metre, IsNear(7.3e-10_(1))),
              RelativeErrorFrom(12.875 * Metre, IsNear(1.0e-9_(1))))));
}

TEST_F(GlobalOptimizationTest, GoldsteinPrice) {
  using Optimizer = MultiLevelSingleLinkage<double, Displacement<World>>;
  int function_invocations = 0;
  int gradient_invocations = 0;

  auto goldstein_price = [&function_invocations](
                             Displacement<World> const& displacement) {
    ++function_invocations;
    auto const& coordinates = displacement.coordinates();
    // The extra |x₀| term ensures that we have a unique solution in three
    // dimensions.
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    return Pow<2>(x₀) + GoldsteinPrice(x₁, x₂);
  };

  auto grad_goldstein_price = [&gradient_invocations](
                                  Displacement<World> const& displacement) {
    ++gradient_invocations;
    auto const& coordinates = displacement.coordinates();
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    double const g₀ = 2 * x₀;
    auto const [g₁, g₂] = 𝛁GoldsteinPrice(x₁, x₂);
    return Vector<Inverse<Length>, World>({g₀ / Metre, g₁ / Metre, g₂ / Metre});
  };

  Optimizer::Box const box = {
      .centre = Displacement<World>(),
      .vertices = {
          Displacement<World>({2 * Metre, 0 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 2 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 0 * Metre, 2 * Metre}),
      }};

  const auto tolerance = 1e-6 * Metre;
  Optimizer optimizer(box, goldstein_price, grad_goldstein_price);

  {
    auto const minima = optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                                   /*number_of_rounds=*/10,
                                                   tolerance);

    EXPECT_EQ(2740, function_invocations);
    EXPECT_EQ(1813, gradient_invocations);
    EXPECT_THAT(
        minima,
        UnorderedElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(7.6e-7_(1) * Metre)),
                AbsoluteErrorFrom(0 * Metre, IsNear(5.3e-8_(1) * Metre)),
                RelativeErrorFrom(-1 * Metre, IsNear(3.8e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.6e-8_(1) * Metre)),
                RelativeErrorFrom(-0.6 * Metre, IsNear(6.8e-10_(1))),
                RelativeErrorFrom(-0.4 * Metre, IsNear(1.1e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.6e-8_(1) * Metre)),
                RelativeErrorFrom(1.8 * Metre, IsNear(1.8e-10_(1))),
                RelativeErrorFrom(0.2 * Metre, IsNear(7.0e-10_(1))))));
  }
  {
    auto const minima =
        optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                   /*number_of_rounds=*/std::nullopt,
                                   tolerance);

    EXPECT_EQ(3620, function_invocations);
    EXPECT_EQ(2474, gradient_invocations);
    EXPECT_THAT(
        minima,
        UnorderedElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.6e-7_(1) * Metre)),
                RelativeErrorFrom(-0.6 * Metre, IsNear(1.6e-8_(1))),
                RelativeErrorFrom(-0.4 * Metre, IsNear(7.0e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(4.0e-7_(1) * Metre)),
                AbsoluteErrorFrom(0 * Metre, IsNear(1.1e-7_(1) * Metre)),
                RelativeErrorFrom(-1 * Metre, IsNear(7.0e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.3e-7_(1) * Metre)),
                RelativeErrorFrom(1.2 * Metre, IsNear(6.7e-10_(1))),
                RelativeErrorFrom(0.8 * Metre, IsNear(9.6e-10_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(3.6e-7_(1) * Metre)),
                RelativeErrorFrom(1.8 * Metre, IsNear(4.4e-8_(1))),
                RelativeErrorFrom(0.2 * Metre, IsNear(5.2e-7_(1))))));
  }
}

}  // namespace numerics
}  // namespace principia
<commit_msg>Test Branin with Bayesian termination.<commit_after>#include "numerics/global_optimization.hpp"

#include "geometry/frame.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/named_quantities.hpp"
#include "gtest/gtest.h"
#include "quantities/named_quantities.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/approximate_quantity.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/is_near.hpp"
#include "testing_utilities/numerics_matchers.hpp"
#include "testing_utilities/optimization_test_functions.hpp"
#include "testing_utilities/vanishes_before.hpp"

namespace principia {
namespace numerics {

using geometry::Displacement;
using geometry::Frame;
using geometry::Position;
using geometry::Vector;
using quantities::Inverse;
using quantities::Length;
using quantities::Pow;
using quantities::si::Metre;
using testing_utilities::AbsoluteErrorFrom;
using testing_utilities::AlmostEquals;
using testing_utilities::Branin;
using testing_utilities::Componentwise;
using testing_utilities::GoldsteinPrice;
using testing_utilities::IsNear;
using testing_utilities::RelativeErrorFrom;
using testing_utilities::𝛁Branin;
using testing_utilities::𝛁GoldsteinPrice;
using testing_utilities::operator""_;
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;

// The test functions in this file are from
// https://www.sfu.ca/~ssurjano/optimization.html.
class GlobalOptimizationTest : public ::testing::Test {
 protected:
  using World = Frame<enum class WorldTag>;
};

TEST_F(GlobalOptimizationTest, Branin) {
  using Optimizer = MultiLevelSingleLinkage<double, Displacement<World>>;
  int function_invocations = 0;
  int gradient_invocations = 0;

  auto branin =
      [&function_invocations](Displacement<World> const& displacement) {
    ++function_invocations;
    auto const& coordinates = displacement.coordinates();
    // The extra |x₀| term ensures that we have a unique solution in three
    // dimensions.
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    return Pow<2>(x₀) + Branin(x₁, x₂);
  };

  auto grad_branin = [&gradient_invocations](
                         Displacement<World> const& displacement) {
    ++gradient_invocations;
    auto const& coordinates = displacement.coordinates();
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    double const g₀ = 2 * x₀;
    auto const [g₁, g₂] = 𝛁Branin(x₁, x₂);
    return Vector<Inverse<Length>, World>({g₀ / Metre, g₁ / Metre, g₂ / Metre});
  };

  Optimizer::Box const box = {
      .centre = Displacement<World>({0 * Metre, 2.5 * Metre, 7.5 * Metre}),
      .vertices = {
          Displacement<World>({2 * Metre, 0 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 7.5 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 0 * Metre, 7.5 * Metre}),
      }};

  const auto tolerance = 1e-6 * Metre;
  Optimizer optimizer(box, branin, grad_branin);
  {
    auto const minima = optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                                   /*number_of_rounds=*/10,
                                                   tolerance);

    EXPECT_EQ(1434, function_invocations);
    EXPECT_EQ(598, gradient_invocations);

    // Note that the fourth minima is outside the |box| passed to the optimizer.
    EXPECT_THAT(
        minima,
        UnorderedElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.4e-7_(1) * Metre)),
                AbsoluteErrorFrom(9.42478 * Metre, IsNear(2.0e-6_(1) * Metre)),
                RelativeErrorFrom(2.475 * Metre, IsNear(8.0e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.7e-7_(1) * Metre)),
                AbsoluteErrorFrom(π * Metre, IsNear(4.8e-9_(1) * Metre)),
                RelativeErrorFrom(2.275 * Metre, IsNear(9.1e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.9e-8_(1) * Metre)),
                RelativeErrorFrom(-π * Metre, IsNear(3.5e-8_(1))),
                RelativeErrorFrom(12.275 * Metre, IsNear(6.2e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.9e-8_(1) * Metre)),
                RelativeErrorFrom(5 * π * Metre, IsNear(7.3e-10_(1))),
                RelativeErrorFrom(12.875 * Metre, IsNear(1.0e-9_(1))))));
  }
  function_invocations = 0;
  gradient_invocations = 0;
  {
    auto const minima =
        optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                   /*number_of_rounds=*/std::nullopt,
                                   tolerance);

    EXPECT_EQ(849, function_invocations);
    EXPECT_EQ(641, gradient_invocations);

    // Note that the fourth minima is outside the |box| passed to the optimizer.
    EXPECT_THAT(
        minima,
        ElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(4.3e-8_(1) * Metre)),
                RelativeErrorFrom(-π * Metre, IsNear(8.5e-9_(1))),
                RelativeErrorFrom(12.275 * Metre, IsNear(2.1e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(3.9e-8_(1) * Metre)),
                AbsoluteErrorFrom(π * Metre, IsNear(1.6e-9_(1) * Metre)),
                RelativeErrorFrom(2.275 * Metre, IsNear(5.6e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(3.3e-8_(1) * Metre)),
                AbsoluteErrorFrom(9.42478 * Metre, IsNear(2.0e-6_(1) * Metre)),
                RelativeErrorFrom(2.475 * Metre, IsNear(3.8e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(4.7e-7_(1) * Metre)),
                RelativeErrorFrom(5 * π * Metre, IsNear(1.3e-8_(1))),
                RelativeErrorFrom(12.875 * Metre, IsNear(3.3e-8_(1))))));
  }
}

TEST_F(GlobalOptimizationTest, GoldsteinPrice) {
  using Optimizer = MultiLevelSingleLinkage<double, Displacement<World>>;
  int function_invocations = 0;
  int gradient_invocations = 0;

  auto goldstein_price = [&function_invocations](
                             Displacement<World> const& displacement) {
    ++function_invocations;
    auto const& coordinates = displacement.coordinates();
    // The extra |x₀| term ensures that we have a unique solution in three
    // dimensions.
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    return Pow<2>(x₀) + GoldsteinPrice(x₁, x₂);
  };

  auto grad_goldstein_price = [&gradient_invocations](
                                  Displacement<World> const& displacement) {
    ++gradient_invocations;
    auto const& coordinates = displacement.coordinates();
    double const x₀ = coordinates[0] / Metre;
    double const x₁ = coordinates[1] / Metre;
    double const x₂ = coordinates[2] / Metre;
    double const g₀ = 2 * x₀;
    auto const [g₁, g₂] = 𝛁GoldsteinPrice(x₁, x₂);
    return Vector<Inverse<Length>, World>({g₀ / Metre, g₁ / Metre, g₂ / Metre});
  };

  Optimizer::Box const box = {
      .centre = Displacement<World>(),
      .vertices = {
          Displacement<World>({2 * Metre, 0 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 2 * Metre, 0 * Metre}),
          Displacement<World>({0 * Metre, 0 * Metre, 2 * Metre}),
      }};

  const auto tolerance = 1e-6 * Metre;
  Optimizer optimizer(box, goldstein_price, grad_goldstein_price);

  {
    auto const minima = optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                                   /*number_of_rounds=*/10,
                                                   tolerance);

    EXPECT_EQ(2739, function_invocations);
    EXPECT_EQ(1812, gradient_invocations);
    EXPECT_THAT(
        minima,
        UnorderedElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(7.6e-7_(1) * Metre)),
                AbsoluteErrorFrom(0 * Metre, IsNear(5.3e-8_(1) * Metre)),
                RelativeErrorFrom(-1 * Metre, IsNear(3.8e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.6e-8_(1) * Metre)),
                RelativeErrorFrom(-0.6 * Metre, IsNear(6.8e-10_(1))),
                RelativeErrorFrom(-0.4 * Metre, IsNear(1.1e-9_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(5.6e-8_(1) * Metre)),
                RelativeErrorFrom(1.8 * Metre, IsNear(1.8e-10_(1))),
                RelativeErrorFrom(0.2 * Metre, IsNear(7.0e-10_(1))))));
  }
  function_invocations = 0;
  gradient_invocations = 0;
  {
    auto const minima =
        optimizer.FindGlobalMinima(/*points_per_round=*/10,
                                   /*number_of_rounds=*/std::nullopt,
                                   tolerance);

    EXPECT_EQ(880, function_invocations);
    EXPECT_EQ(661, gradient_invocations);
    EXPECT_THAT(
        minima,
        UnorderedElementsAre(
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.6e-7_(1) * Metre)),
                RelativeErrorFrom(-0.6 * Metre, IsNear(1.6e-8_(1))),
                RelativeErrorFrom(-0.4 * Metre, IsNear(7.0e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(4.0e-7_(1) * Metre)),
                AbsoluteErrorFrom(0 * Metre, IsNear(1.1e-7_(1) * Metre)),
                RelativeErrorFrom(-1 * Metre, IsNear(7.0e-8_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(1.3e-7_(1) * Metre)),
                RelativeErrorFrom(1.2 * Metre, IsNear(6.7e-10_(1))),
                RelativeErrorFrom(0.8 * Metre, IsNear(9.6e-10_(1)))),
            Componentwise(
                AbsoluteErrorFrom(0 * Metre, IsNear(3.6e-7_(1) * Metre)),
                RelativeErrorFrom(1.8 * Metre, IsNear(4.4e-8_(1))),
                RelativeErrorFrom(0.2 * Metre, IsNear(5.2e-7_(1))))));
  }
}

}  // namespace numerics
}  // namespace principia
<|endoftext|>


<commit_before>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) The GNU Lesser General Public License as published by the Free  *
*       Software Foundation; either version 2.1 of the License, or (at  *
*       your option) any later version. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* This library is distributed in the hope that it will be useful,       *
* but WITHOUT ANY WARRANTY; without even the implied warranty of        *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*
 * Cylinder-Plane collider by Bram Stolk
 *
 * This testing basically comes down to testing the intersection
 * of the cylinder caps (discs) with the plane.
 * As a special case, we should consider co-planar discs and plane,
 * In which case we simply test for intersection between cyl axis
 * and plane.
 */

#define PRTVEC3(A) \
  fprintf(stderr, #A "%6.3f %6.3f %6.3f\n", A[0],A[1],A[2]);

#include <ode/collision.h>
#include <ode/matrix.h>
#include <ode/rotation.h>
#include <ode/odemath.h>
#include <ode/objects.h>

#include "collision_util.h"	// for dCollideSpheres
#include "collision_kernel.h"	// for dxGeom

// Find the line of intersection between two planes.
// Returns false if planes are parallel
static bool isect_plane_plane
(
  const dVector3 &pos0,
  const dVector3 &nrm0,
  const dVector3 &pos1,
  const dVector3 &nrm1,
  dVector3 &org,
  dVector3 &dir
)
{
  dReal norms_dot = dDOT(nrm0,nrm1);
  if (norms_dot >= (1.0-dEpsilon))
    return false; // planes are parallel
  dReal inv_det = dReal(1.0) / (dReal(1.0) - norms_dot*norms_dot);

  dReal planeconstant0 = dDOT(nrm0, pos0);
  dReal planeconstant1 = dDOT(nrm1, pos1);
  dReal c0 = (planeconstant0 - norms_dot*planeconstant1) * inv_det;
  dReal c1 = (planeconstant1 - norms_dot*planeconstant0) * inv_det;

  org[0] =  c0 * nrm0[0] + c1 * nrm1[0];
  org[1] =  c0 * nrm0[1] + c1 * nrm1[1];
  org[2] =  c0 * nrm0[2] + c1 * nrm1[2];

  dCROSS(dir, =, nrm0, nrm1);
  dNormalize3(dir);
//  PRTVEC3(org);
//  PRTVEC3(dir);
  return true;
}

// Finds a point on the line of intersection between a plane and a disc.
// Direction of intersection (doi) points from disc center to this point.
// Intersection depth is the distance of the point to the edge of the disc.
// We could re-use this func when writing a cyl-cyl collider, probably.
static bool isect_disc_plane
(
  const dVector3 &discpos,
  const dVector3 &discnorm,
  const dVector3 &planepos,
  const dVector3 &planenorm,
  dReal rad,
  dVector3 &poi,		// point of intersection
  dVector3 &doi,		// direction of intersection
  dReal &intersectiondepth
)
{
  dVector3 org, dir;
  bool planes_do_intersect = isect_plane_plane(discpos,discnorm,planepos,planenorm,org,dir);
  if (!planes_do_intersect)
    return false;

  dVector3 tmp;

  // Determine the closest point on the intersection line to the position of disc
  dVector3Subtract(discpos, org, tmp);
  dReal t0 = dDOT(dir, tmp) / dDOT(dir,dir);

  poi[0] = org[0] + t0 * dir[0];
  poi[1] = org[1] + t0 * dir[1];
  poi[2] = org[2] + t0 * dir[2];

  dVector3Subtract(poi, discpos, doi);
  dReal dist = dVector3Length(doi);

  if (dist >= rad)
    return false;

  intersectiondepth = rad - dist;
  dNormalize3(doi);

//  fprintf(stderr,"disc intersects plane with depth %f\n", intersectiondepth);
  return true;
}

// Test code
static bool test_disc_isect()
{
  dVector3 discpos={1,0,3};
  dVector3 discnorm={0,1,0};
  dVector3 planepos={5,10,0};
  dVector3 planenorm={0,0,1};
  dReal    radius=4.0;

  dVector3 poi, doi;
  dReal depth;

  isect_disc_plane(discpos, discnorm, planepos, planenorm, radius, poi, doi, depth);
}
//static bool dummy=test_disc_isect();

// Finds out which global axis, x y or z, is the most perpendicular one to
// a given vector.
static int minor_axis(const dVector3 &v, dVector3 &axis)
{
  dVector3 x={1,0,0};
  dVector3 y={0,1,0};
  dVector3 z={0,0,1};
  float dotx = (float)dFabs(dDOT(v,x));
  float doty = (float)dFabs(dDOT(v,y));
  float dotz = (float)dFabs(dDOT(v,z));
  if (dotx <= doty && dotx <= dotz)
  {
    dVector3Copy(x, axis);
    return 0;
  }
  if (doty <= dotx && doty <= dotz)
  {
    dVector3Copy(y, axis);
    return 1;
  }
  dVector3Copy(z, axis);
  return 2;
}

int dCollideCylinderPlane(dxGeom *cylgeom, dxGeom *planegeom, int flags, dContactGeom *contact, int skip)
{
  dIASSERT (skip >= (int)sizeof(dContactGeom));
  dIASSERT ((flags & 0xffff) >= 1);

  int nContacts=0;

  // Get the properties of the cylinder (len+radius)
  dReal radius, length;
  dGeomCylinderGetParams(cylgeom, &radius, &length);
  dVector3 &cylpos = cylgeom->final_posr->pos;
  // Get the properties of the plane
  dVector4 planevec;
  dGeomPlaneGetParams(planegeom, planevec);
  dVector3 planenorm = {planevec[0],planevec[1],planevec[2]};
  dVector3 planepos = {planevec[0],planevec[1],planevec[2]};
  dVector3Scale(planepos, planevec[3]);

  dVector3 axis =
  {
    cylgeom->final_posr->R[2],
    cylgeom->final_posr->R[6],
    cylgeom->final_posr->R[10]
  };
  dReal axislen = dVector3Length(axis);
  dIASSERT(axislen>=0.9999);
  dIASSERT(axislen<=1.0001);

  dReal hl = length/dReal(2.0);
  dVector3 disc_top_pos = { cylpos[0]+axis[0]*hl, cylpos[1]+axis[1]*hl, cylpos[2]+axis[2]*hl };
  dVector3 disc_bot_pos = { cylpos[0]-axis[0]*hl, cylpos[1]-axis[1]*hl, cylpos[2]-axis[2]*hl };
  dVector3 disc_top_nrm = {  axis[0],  axis[1],  axis[2] };
  dVector3 disc_bot_nrm = { -axis[0], -axis[1], -axis[2] };

  // Discs and plane are parallel?
  // If so, the cyl may be resting on the plane, with one of its discs
  dReal norms_dot = dFabs(dDOT(planenorm,axis));
  bool coplanar = (norms_dot >= 0.999);
  if (coplanar)
  {
    dReal dist_top = dPointPlaneDistance(disc_top_pos, planevec);
    if (dist_top <= 0)
    {
      dVector3 x;
      minor_axis(axis, x);
      dVector3 y;
      dVector3Cross(axis, x, y);
      dVector3 p;
      dVector3Scale(x, radius); dVector3Scale(y, radius);
      dContactGeom *Contact;

      // Contact 1
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Add(p, x, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 2
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Add(p, y, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 3
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Subtract(p, x, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 4
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Subtract(p, y, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;
      return nContacts;
    }
    dReal dist_bot = dPointPlaneDistance(disc_bot_pos, planevec);
    if (dist_bot <= 0)
    {
      dVector3 x;
      minor_axis(axis, x);
      dVector3 y;
      dVector3Cross(axis, x, y);
      dVector3 p;
      dVector3Scale(x, radius); dVector3Scale(y, radius);
      dContactGeom *Contact;

      // Contact 1
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Add(p, x, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 2
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Add(p, y, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 3
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Subtract(p, x, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 4
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Subtract(p, y, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;
      return nContacts;
    }
    return 0;
  }

  // Test the discs!

  dVector3 poi_top, doi_top;
  dVector3 poi_bot, doi_bot;
  dReal depth_top, depth_bot;

  bool top_disc_does_intersect = isect_disc_plane(disc_top_pos, disc_top_nrm, planepos, planenorm, radius,  poi_top, doi_top, depth_top);
  bool bot_disc_does_intersect = isect_disc_plane(disc_bot_pos, disc_bot_nrm, planepos, planenorm, radius,  poi_bot, doi_bot, depth_bot);

  if (top_disc_does_intersect)
  {
    dContactGeom *Contact = SAFECONTACT(flags, contact, nContacts, skip);
    dVector3 tmp;
    dVector3Copy(doi_top, tmp);
    dVector3Scale(tmp, radius);
    dVector3Add(disc_top_pos, tmp, Contact->pos);
    // Note: ODE convention is: normal points *into* g1
    Contact->g1 = cylgeom;
    Contact->g2 = planegeom;
    dVector3Copy(planenorm, Contact->normal);
    dReal d = depth_top * dDOT(planenorm, doi_top);
    Contact->depth = dFabs(d);
    nContacts++;
  }

  if (bot_disc_does_intersect)
  {
    dContactGeom *Contact = SAFECONTACT(flags, contact, nContacts, skip);
    dVector3 tmp;
    dVector3Copy(doi_bot, tmp);
    dVector3Scale(tmp, radius);
    dVector3Add(disc_bot_pos, tmp, Contact->pos);
    // Note: ODE convention is: normal points *into* g1
    Contact->g1 = cylgeom;
    Contact->g2 = planegeom;
    dVector3Copy(planenorm, Contact->normal);
    dReal d = depth_bot * dDOT(planenorm, doi_bot);
    Contact->depth = dFabs(d);
    nContacts++;
  }

  return nContacts;
}

<commit_msg>Added missing return<commit_after>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) The GNU Lesser General Public License as published by the Free  *
*       Software Foundation; either version 2.1 of the License, or (at  *
*       your option) any later version. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* This library is distributed in the hope that it will be useful,       *
* but WITHOUT ANY WARRANTY; without even the implied warranty of        *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*
 * Cylinder-Plane collider by Bram Stolk
 *
 * This testing basically comes down to testing the intersection
 * of the cylinder caps (discs) with the plane.
 * As a special case, we should consider co-planar discs and plane,
 * In which case we simply test for intersection between cyl axis
 * and plane.
 */

#define PRTVEC3(A) \
  fprintf(stderr, #A "%6.3f %6.3f %6.3f\n", A[0],A[1],A[2]);

#include <ode/collision.h>
#include <ode/matrix.h>
#include <ode/rotation.h>
#include <ode/odemath.h>
#include <ode/objects.h>

#include "collision_util.h"	// for dCollideSpheres
#include "collision_kernel.h"	// for dxGeom

// Find the line of intersection between two planes.
// Returns false if planes are parallel
static bool isect_plane_plane
(
  const dVector3 &pos0,
  const dVector3 &nrm0,
  const dVector3 &pos1,
  const dVector3 &nrm1,
  dVector3 &org,
  dVector3 &dir
)
{
  dReal norms_dot = dDOT(nrm0,nrm1);
  if (norms_dot >= (1.0-dEpsilon))
    return false; // planes are parallel
  dReal inv_det = dReal(1.0) / (dReal(1.0) - norms_dot*norms_dot);

  dReal planeconstant0 = dDOT(nrm0, pos0);
  dReal planeconstant1 = dDOT(nrm1, pos1);
  dReal c0 = (planeconstant0 - norms_dot*planeconstant1) * inv_det;
  dReal c1 = (planeconstant1 - norms_dot*planeconstant0) * inv_det;

  org[0] =  c0 * nrm0[0] + c1 * nrm1[0];
  org[1] =  c0 * nrm0[1] + c1 * nrm1[1];
  org[2] =  c0 * nrm0[2] + c1 * nrm1[2];

  dCROSS(dir, =, nrm0, nrm1);
  dNormalize3(dir);
//  PRTVEC3(org);
//  PRTVEC3(dir);
  return true;
}

// Finds a point on the line of intersection between a plane and a disc.
// Direction of intersection (doi) points from disc center to this point.
// Intersection depth is the distance of the point to the edge of the disc.
// We could re-use this func when writing a cyl-cyl collider, probably.
static bool isect_disc_plane
(
  const dVector3 &discpos,
  const dVector3 &discnorm,
  const dVector3 &planepos,
  const dVector3 &planenorm,
  dReal rad,
  dVector3 &poi,		// point of intersection
  dVector3 &doi,		// direction of intersection
  dReal &intersectiondepth
)
{
  dVector3 org, dir;
  bool planes_do_intersect = isect_plane_plane(discpos,discnorm,planepos,planenorm,org,dir);
  if (!planes_do_intersect)
    return false;

  dVector3 tmp;

  // Determine the closest point on the intersection line to the position of disc
  dVector3Subtract(discpos, org, tmp);
  dReal t0 = dDOT(dir, tmp) / dDOT(dir,dir);

  poi[0] = org[0] + t0 * dir[0];
  poi[1] = org[1] + t0 * dir[1];
  poi[2] = org[2] + t0 * dir[2];

  dVector3Subtract(poi, discpos, doi);
  dReal dist = dVector3Length(doi);

  if (dist >= rad)
    return false;

  intersectiondepth = rad - dist;
  dNormalize3(doi);

//  fprintf(stderr,"disc intersects plane with depth %f\n", intersectiondepth);
  return true;
}

// Test code
static bool test_disc_isect()
{
  dVector3 discpos={1,0,3};
  dVector3 discnorm={0,1,0};
  dVector3 planepos={5,10,0};
  dVector3 planenorm={0,0,1};
  dReal    radius=4.0;

  dVector3 poi, doi;
  dReal depth;

  return isect_disc_plane(discpos, discnorm, planepos, planenorm, radius, poi, doi, depth);
}
//static bool dummy=test_disc_isect();

// Finds out which global axis, x y or z, is the most perpendicular one to
// a given vector.
static int minor_axis(const dVector3 &v, dVector3 &axis)
{
  dVector3 x={1,0,0};
  dVector3 y={0,1,0};
  dVector3 z={0,0,1};
  float dotx = (float)dFabs(dDOT(v,x));
  float doty = (float)dFabs(dDOT(v,y));
  float dotz = (float)dFabs(dDOT(v,z));
  if (dotx <= doty && dotx <= dotz)
  {
    dVector3Copy(x, axis);
    return 0;
  }
  if (doty <= dotx && doty <= dotz)
  {
    dVector3Copy(y, axis);
    return 1;
  }
  dVector3Copy(z, axis);
  return 2;
}

int dCollideCylinderPlane(dxGeom *cylgeom, dxGeom *planegeom, int flags, dContactGeom *contact, int skip)
{
  dIASSERT (skip >= (int)sizeof(dContactGeom));
  dIASSERT ((flags & 0xffff) >= 1);

  int nContacts=0;

  // Get the properties of the cylinder (len+radius)
  dReal radius, length;
  dGeomCylinderGetParams(cylgeom, &radius, &length);
  dVector3 &cylpos = cylgeom->final_posr->pos;
  // Get the properties of the plane
  dVector4 planevec;
  dGeomPlaneGetParams(planegeom, planevec);
  dVector3 planenorm = {planevec[0],planevec[1],planevec[2]};
  dVector3 planepos = {planevec[0],planevec[1],planevec[2]};
  dVector3Scale(planepos, planevec[3]);

  dVector3 axis =
  {
    cylgeom->final_posr->R[2],
    cylgeom->final_posr->R[6],
    cylgeom->final_posr->R[10]
  };
  dReal axislen = dVector3Length(axis);
  dIASSERT(axislen>=0.9999);
  dIASSERT(axislen<=1.0001);

  dReal hl = length/dReal(2.0);
  dVector3 disc_top_pos = { cylpos[0]+axis[0]*hl, cylpos[1]+axis[1]*hl, cylpos[2]+axis[2]*hl };
  dVector3 disc_bot_pos = { cylpos[0]-axis[0]*hl, cylpos[1]-axis[1]*hl, cylpos[2]-axis[2]*hl };
  dVector3 disc_top_nrm = {  axis[0],  axis[1],  axis[2] };
  dVector3 disc_bot_nrm = { -axis[0], -axis[1], -axis[2] };

  // Discs and plane are parallel?
  // If so, the cyl may be resting on the plane, with one of its discs
  dReal norms_dot = dFabs(dDOT(planenorm,axis));
  bool coplanar = (norms_dot >= 0.999);
  if (coplanar)
  {
    dReal dist_top = dPointPlaneDistance(disc_top_pos, planevec);
    if (dist_top <= 0)
    {
      dVector3 x;
      minor_axis(axis, x);
      dVector3 y;
      dVector3Cross(axis, x, y);
      dVector3 p;
      dVector3Scale(x, radius); dVector3Scale(y, radius);
      dContactGeom *Contact;

      // Contact 1
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Add(p, x, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 2
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Add(p, y, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 3
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Subtract(p, x, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 4
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_top_pos, p);
      dVector3Subtract(p, y, Contact->pos);
      Contact->g1 = planegeom;
      Contact->g2 = cylgeom;
      Contact->depth = dFabs(dist_top);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;
      return nContacts;
    }
    dReal dist_bot = dPointPlaneDistance(disc_bot_pos, planevec);
    if (dist_bot <= 0)
    {
      dVector3 x;
      minor_axis(axis, x);
      dVector3 y;
      dVector3Cross(axis, x, y);
      dVector3 p;
      dVector3Scale(x, radius); dVector3Scale(y, radius);
      dContactGeom *Contact;

      // Contact 1
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Add(p, x, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 2
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Add(p, y, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 3
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Subtract(p, x, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;

      // Contact 4
      Contact = SAFECONTACT(flags, contact, nContacts, skip);
      dVector3Copy(disc_bot_pos, p);
      dVector3Subtract(p, y, Contact->pos);
      Contact->g1 = cylgeom;
      Contact->g2 = planegeom;
      Contact->depth = dFabs(dist_bot);
      dVector3Copy(planenorm, Contact->normal);
      nContacts++;
      return nContacts;
    }
    return 0;
  }

  // Test the discs!

  dVector3 poi_top, doi_top;
  dVector3 poi_bot, doi_bot;
  dReal depth_top, depth_bot;

  bool top_disc_does_intersect = isect_disc_plane(disc_top_pos, disc_top_nrm, planepos, planenorm, radius,  poi_top, doi_top, depth_top);
  bool bot_disc_does_intersect = isect_disc_plane(disc_bot_pos, disc_bot_nrm, planepos, planenorm, radius,  poi_bot, doi_bot, depth_bot);

  if (top_disc_does_intersect)
  {
    dContactGeom *Contact = SAFECONTACT(flags, contact, nContacts, skip);
    dVector3 tmp;
    dVector3Copy(doi_top, tmp);
    dVector3Scale(tmp, radius);
    dVector3Add(disc_top_pos, tmp, Contact->pos);
    // Note: ODE convention is: normal points *into* g1
    Contact->g1 = cylgeom;
    Contact->g2 = planegeom;
    dVector3Copy(planenorm, Contact->normal);
    dReal d = depth_top * dDOT(planenorm, doi_top);
    Contact->depth = dFabs(d);
    nContacts++;
  }

  if (bot_disc_does_intersect)
  {
    dContactGeom *Contact = SAFECONTACT(flags, contact, nContacts, skip);
    dVector3 tmp;
    dVector3Copy(doi_bot, tmp);
    dVector3Scale(tmp, radius);
    dVector3Add(disc_bot_pos, tmp, Contact->pos);
    // Note: ODE convention is: normal points *into* g1
    Contact->g1 = cylgeom;
    Contact->g2 = planegeom;
    dVector3Copy(planenorm, Contact->normal);
    dReal d = depth_bot * dDOT(planenorm, doi_bot);
    Contact->depth = dFabs(d);
    nContacts++;
  }

  return nContacts;
}

<|endoftext|>


<commit_before>/**
@file traits.hpp
@brief Traits.

@author Timothy Howard
@copyright 2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef CACOPHONY_TRAITS_HPP_
#define CACOPHONY_TRAITS_HPP_

#include <Cacophony/config.hpp>
#include <Cacophony/types.hpp>

#include <duct/traits.hpp>

#include <type_traits>
#include <utility>

/** @cond INTERNAL */

namespace Cacophony {
namespace detail {
struct unimplemented {};
}
}

// captures
template<class... P>
Cacophony::detail::unimplemented
serialize(P&&...);

template<class... P>
Cacophony::detail::unimplemented
read(P&&...);

template<class... P>
Cacophony::detail::unimplemented
write(P&&...);

/** @endcond */ // INTERNAL

namespace Cacophony {

// Forward declarations
class accessor;
template<class>
class OutputSerializer; // external
template<class>
class InputSerializer; // external

/**
	@addtogroup traits
	@{
*/

/**
	Remove reference from type.
*/
template<class T>
using refless_type = typename std::remove_reference<T>::type;

/**
	Remove reference and const qualifier from type.
*/
template<class T>
using bare_type = typename std::remove_const<refless_type<T>>::type;

/**
	Check if a type is a size type.

	@note The default definition only accepts unsigned integrals.
	Specialize this for user-defined types.
*/
template<class T>
struct is_size_type
	: public std::integral_constant<
		bool,
		std::is_integral<bare_type<T>>::value &&
		std::is_unsigned<bare_type<T>>::value
	>
{};

/**
	Check if class is an input serializer.
*/
template<class Ser>
struct is_input_serializer
	: public std::integral_constant<
		bool,
		std::is_base_of<InputSerializer<bare_type<Ser>>, bare_type<Ser>>::value
	>
{};

/**
	Check if class is an output serializer.
*/
template<class Ser>
struct is_output_serializer
	: public std::integral_constant<
		bool,
		std::is_base_of<OutputSerializer<bare_type<Ser>>, bare_type<Ser>>::value
	>
{};

/**
	Check if class is a serializer.
*/
template<class Ser>
struct is_serializer
	: public std::integral_constant<
		bool,
		is_input_serializer <Ser>::value ||
		is_output_serializer<Ser>::value
	>
{};

/** @cond INTERNAL */

template<class Ser, class T>
struct has_non_member_serialize_impl {
	using ser_type = bare_type<Ser>;
	using value_type = bare_type<T>;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(serialize(
			std::declval<ser_type&>(),
			std::declval<value_type&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};

template<class Ser, class T>
struct has_non_member_read_impl {
	using ser_type = bare_type<Ser>;
	using value_type = T;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(read(
			std::declval<ser_type&>(),
			std::declval<value_type&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};

template<class Ser, class T>
struct has_non_member_write_impl {
	using ser_type = bare_type<Ser>;
	using value_type = T;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(write(
			std::declval<ser_type&>(),
			std::declval<value_type const&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};
/** @endcond */ // INTERNAL

/**
	Class access injector.

	Friend this class within a class if its member serialization
	functions are private or protected.
*/
class accessor
	: public duct::traits::restrict_all
{
public:
/** @cond INTERNAL */
	template<class Ser, class T>
	struct has_member_serialize_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&)) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template serialize<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::serialize))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};

	template<class Ser, class T>
	struct has_member_read_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&)) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template read<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::read))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};

	template<class Ser, class T>
	struct has_member_write_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&) const) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template write<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::write))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};
/** @endcond */ // INTERNAL

	/**
		Read a value from an input serializer.

		@note This function is enabled by the availability of the
		@c serialize() and @c read() serialization functions. A call
		will be ambiguous if both are available.
		@{
	*/
	template<class Ser, class T>
	static inline enable_ser<has_member_serialize_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		value.serialize(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_serialize_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		serialize(ser, value);
	}

	template<class Ser, class T>
	static inline enable_ser<has_member_read_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		value.read(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_read_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		read(ser, value);
	}
	/** @} */

	/**
		Write a value to an output serializer.

		@note This function is enabled by the availability of the
		@c serialize() and @c write() serialization functions. A call
		will be ambiguous if both are available.
		@{
	*/
	template<class Ser, class T>
	static inline enable_ser<has_member_serialize_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		const_cast<bare_type<T>&>(value).serialize(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_serialize_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		serialize(ser, const_cast<bare_type<T>&>(value));
	}

	template<class Ser, class T>
	static inline enable_ser<has_member_write_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		value.write(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_write_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		write(ser, value);
	}
	/** @} */
};

/**
	Whether there is a serialize() member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_member_serialize
	: public accessor::has_member_serialize_impl<Ser, T>::type
{};

/**
	Whether there is a serialize() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_serialize
	: public has_non_member_serialize_impl<Ser, T>::type
{};

/**
	Whether there is a read() member function for a serializer-value
	pair.
*/
template<class Ser, class T>
struct has_member_read
	: public accessor::has_member_read_impl<Ser, T>::type
{};

/**
	Whether there is a read() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_read
	: public has_non_member_read_impl<Ser, T>::type
{};

/**
	Whether there is a write() member function for a serializer-value
	pair.
*/
template<class Ser, class T>
struct has_member_write
	: public accessor::has_member_write_impl<Ser, T>::type
{};

/**
	Whether there is a write() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_write
	: public has_non_member_write_impl<Ser, T>::type
{};

// serializable by type

/**
	Whether a type is serializable with a specific input serializer.
*/
template<class Ser, class T>
struct is_input_serializable
	: public std::integral_constant<
		bool,
		is_input_serializer<Ser>::value && (
		// serialize()
		has_member_serialize		<Ser, T>::value ||
		has_non_member_serialize	<Ser, T>::value ||
		// write()
		has_member_read				<Ser, T>::value ||
		has_non_member_read			<Ser, T>::value
	)>
{
	//static_assert();
};

/**
	Whether a type is serializable with a specific output serializer.
*/
template<class Ser, class T>
struct is_output_serializable
	: public std::integral_constant<
		bool,
		is_output_serializer<Ser>::value && (
		// serialize()
		has_member_serialize		<Ser, T>::value ||
		has_non_member_serialize	<Ser, T>::value ||
		// read()
		has_member_write			<Ser, T>::value ||
		has_non_member_write		<Ser, T>::value
	)>
{
	//static_assert();
};

template<class T>
struct is_binary_serializable
	: public std::integral_constant<
		bool,
		!std::is_same<bool, bare_type<T>>::value &&
		std::is_arithmetic<bare_type<T>>::value
	>
{};

/**
	@c serialize() function return tag.

	@note This always constrains by is_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_serialize = enable_ser<
	is_serializer<Ser>::value &&
	constraints
>;

/**
	@c read() function return tag.

	@note This always constrains by is_input_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_read = enable_ser<
	is_input_serializer<Ser>::value &&
	constraints
>;

/**
	@c write() function return tag.

	@note This always constrains by is_output_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_write = enable_ser<
	is_output_serializer<Ser>::value &&
	constraints
>;

/** @} */ // end of doc-group traits

} // namespace Cacophony

#endif // CACOPHONY_TRAITS_HPP_
<commit_msg>traits: added loose tags.<commit_after>/**
@file traits.hpp
@brief Traits.

@author Timothy Howard
@copyright 2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#ifndef CACOPHONY_TRAITS_HPP_
#define CACOPHONY_TRAITS_HPP_

#include <Cacophony/config.hpp>
#include <Cacophony/types.hpp>

#include <duct/traits.hpp>

#include <type_traits>
#include <utility>

/** @cond INTERNAL */

namespace Cacophony {
namespace detail {
struct unimplemented {};
}
}

// captures
template<class... P>
Cacophony::detail::unimplemented
serialize(P&&...);

template<class... P>
Cacophony::detail::unimplemented
read(P&&...);

template<class... P>
Cacophony::detail::unimplemented
write(P&&...);

/** @endcond */ // INTERNAL

namespace Cacophony {

// Forward declarations
class accessor;
template<class>
class OutputSerializer; // external
template<class>
class InputSerializer; // external

/**
	@addtogroup traits
	@{
*/

/**
	Remove reference from type.
*/
template<class T>
using refless_type = typename std::remove_reference<T>::type;

/**
	Remove reference and const qualifier from type.
*/
template<class T>
using bare_type = typename std::remove_const<refless_type<T>>::type;

/**
	Check if a type is a size type.

	@note The default definition only accepts unsigned integrals.
	Specialize this for user-defined types.
*/
template<class T>
struct is_size_type
	: public std::integral_constant<
		bool,
		std::is_integral<bare_type<T>>::value &&
		std::is_unsigned<bare_type<T>>::value
	>
{};

/**
	Check if class is an input serializer.
*/
template<class Ser>
struct is_input_serializer
	: public std::integral_constant<
		bool,
		std::is_base_of<InputSerializer<bare_type<Ser>>, bare_type<Ser>>::value
	>
{};

/**
	Check if class is an output serializer.
*/
template<class Ser>
struct is_output_serializer
	: public std::integral_constant<
		bool,
		std::is_base_of<OutputSerializer<bare_type<Ser>>, bare_type<Ser>>::value
	>
{};

/**
	Check if class is a serializer.
*/
template<class Ser>
struct is_serializer
	: public std::integral_constant<
		bool,
		is_input_serializer <Ser>::value ||
		is_output_serializer<Ser>::value
	>
{};

/** @cond INTERNAL */

template<class Ser, class T>
struct has_non_member_serialize_impl {
	using ser_type = bare_type<Ser>;
	using value_type = bare_type<T>;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(serialize(
			std::declval<ser_type&>(),
			std::declval<value_type&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};

template<class Ser, class T>
struct has_non_member_read_impl {
	using ser_type = bare_type<Ser>;
	using value_type = T;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(read(
			std::declval<ser_type&>(),
			std::declval<value_type&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};

template<class Ser, class T>
struct has_non_member_write_impl {
	using ser_type = bare_type<Ser>;
	using value_type = T;
	using reversed_type = std::is_same<
		detail::unimplemented,
		decltype(write(
			std::declval<ser_type&>(),
			std::declval<value_type const&>()
		))
	>;
	using type = std::integral_constant<bool, !reversed_type::value>;
	static constexpr bool const value = type::value;
};
/** @endcond */ // INTERNAL

/**
	Class access injector.

	Friend this class within a class if its member serialization
	functions are private or protected.
*/
class accessor
	: public duct::traits::restrict_all
{
public:
/** @cond INTERNAL */
	template<class Ser, class T>
	struct has_member_serialize_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&)) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template serialize<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::serialize))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};

	template<class Ser, class T>
	struct has_member_read_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&)) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template read<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::read))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};

	template<class Ser, class T>
	struct has_member_write_impl {
		using ser_type = bare_type<Ser>;
		using value_type = bare_type<T>;

		template<class U>
		static auto match(void(U::*)(ser_type&) const) -> U;

		template<class U>
		using is_t = typename std::is_same<value_type, U>::type;

		template<class U>
		static auto test(int) -> is_t<decltype(match(
			&U::template write<ser_type>
		))>;
		template<class U>
		static auto test(int) -> is_t<decltype(match(&U::write))>;
		template<class U>
		static auto test(...) -> std::false_type;

		using type = decltype(test<value_type>(0));
		static constexpr bool const value = type::value;
	};
/** @endcond */ // INTERNAL

	/**
		Read a value from an input serializer.

		@note This function is enabled by the availability of the
		@c serialize() and @c read() serialization functions. A call
		will be ambiguous if both are available.
		@{
	*/
	template<class Ser, class T>
	static inline enable_ser<has_member_serialize_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		value.serialize(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_serialize_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		serialize(ser, value);
	}

	template<class Ser, class T>
	static inline enable_ser<has_member_read_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		value.read(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_read_impl<Ser, bare_type<T>>::value>
	in(Ser& ser, T&& value) {
		read(ser, value);
	}
	/** @} */

	/**
		Write a value to an output serializer.

		@note This function is enabled by the availability of the
		@c serialize() and @c write() serialization functions. A call
		will be ambiguous if both are available.
		@{
	*/
	template<class Ser, class T>
	static inline enable_ser<has_member_serialize_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		const_cast<bare_type<T>&>(value).serialize(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_serialize_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		serialize(ser, const_cast<bare_type<T>&>(value));
	}

	template<class Ser, class T>
	static inline enable_ser<has_member_write_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		value.write(ser);
	}

	template<class Ser, class T>
	static inline enable_ser<has_non_member_write_impl<Ser, T>::value>
	out(Ser& ser, T const& value) {
		write(ser, value);
	}
	/** @} */
};

/**
	Whether there is a serialize() member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_member_serialize
	: public accessor::has_member_serialize_impl<Ser, T>::type
{};

/**
	Whether there is a serialize() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_serialize
	: public has_non_member_serialize_impl<Ser, T>::type
{};

/**
	Whether there is a read() member function for a serializer-value
	pair.
*/
template<class Ser, class T>
struct has_member_read
	: public accessor::has_member_read_impl<Ser, T>::type
{};

/**
	Whether there is a read() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_read
	: public has_non_member_read_impl<Ser, T>::type
{};

/**
	Whether there is a write() member function for a serializer-value
	pair.
*/
template<class Ser, class T>
struct has_member_write
	: public accessor::has_member_write_impl<Ser, T>::type
{};

/**
	Whether there is a write() non-member function for a
	serializer-value pair.
*/
template<class Ser, class T>
struct has_non_member_write
	: public has_non_member_write_impl<Ser, T>::type
{};

// serializable by type

/**
	Whether a type is serializable with a specific input serializer.
*/
template<class Ser, class T>
struct is_input_serializable
	: public std::integral_constant<
		bool,
		is_input_serializer<Ser>::value && (
		// serialize()
		has_member_serialize		<Ser, T>::value ||
		has_non_member_serialize	<Ser, T>::value ||
		// write()
		has_member_read				<Ser, T>::value ||
		has_non_member_read			<Ser, T>::value
	)>
{
	//static_assert();
};

/**
	Whether a type is serializable with a specific output serializer.
*/
template<class Ser, class T>
struct is_output_serializable
	: public std::integral_constant<
		bool,
		is_output_serializer<Ser>::value && (
		// serialize()
		has_member_serialize		<Ser, T>::value ||
		has_non_member_serialize	<Ser, T>::value ||
		// read()
		has_member_write			<Ser, T>::value ||
		has_non_member_write		<Ser, T>::value
	)>
{
	//static_assert();
};

template<class T>
struct is_binary_serializable
	: public std::integral_constant<
		bool,
		!std::is_same<bool, bare_type<T>>::value &&
		std::is_arithmetic<bare_type<T>>::value
	>
{};

/**
	@c serialize() function return tag.

	@note This always constrains by is_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_serialize = enable_ser<
	is_serializer<Ser>::value &&
	constraints
>;

/**
	Loose @c serialize() function return tag.

	@note This tag assumes the serializer type has already been
	validated.

	@tparam constraints Constraints.
*/
template<bool const constraints = true>
using tag_serialize_loose = enable_ser<
	constraints
>;

/**
	@c read() function return tag.

	@note This always constrains by is_input_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_read = enable_ser<
	is_input_serializer<Ser>::value &&
	constraints
>;

/**
	Loose @c read() function return tag.

	@note This tag assumes the serializer type has already been
	validated.

	@tparam constraints Constraints.
*/
template<bool const constraints = true>
using tag_read_loose = enable_ser<
	constraints
>;

/**
	@c write() function return tag.

	@note This always constrains by is_output_serializer.

	@tparam Ser %Serializer type.
	@tparam constraints Additional constraints.
*/
template<class Ser, bool const constraints = true>
using tag_write = enable_ser<
	is_output_serializer<Ser>::value &&
	constraints
>;

/**
	Loose @c write() function return tag.

	@note This tag assumes the serializer type has already been
	validated.

	@tparam constraints Constraints.
*/
template<bool const constraints = true>
using tag_write_loose = enable_ser<
	constraints
>;

/** @} */ // end of doc-group traits

} // namespace Cacophony

#endif // CACOPHONY_TRAITS_HPP_
<|endoftext|>


<commit_before>#include <clip_tokenizer.h> // @manual
#include <gpt2_bpe_tokenizer.h> // @manual
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <regex.h>
#include <regex_tokenizer.h> // @manual
#include <sentencepiece.h> // @manual
#include <torch/csrc/jit/python/pybind_utils.h> // @manual
#include <torch/csrc/utils/pybind.h> // @manual
#include <torch/script.h>
#include <vectors.h> // @manual
#include <vocab.h> // @manual
#include <vocab_factory.h> // @manual

#include <iostream>

namespace torchtext {

namespace py = pybind11;

namespace {
Vocab build_vocab_from_text_file(
    const std::string& file_path,
    const int64_t min_freq,
    const int64_t num_cpus,
    py::object fn) {
  torch::jit::script::Module module(*torch::jit::as_module(fn));
  return _build_vocab_from_text_file(file_path, min_freq, num_cpus, module);
}
} // namespace

// Registers our custom classes with pybind11.
PYBIND11_MODULE(_torchtext, m) {
  // Classes
  py::class_<Regex, c10::intrusive_ptr<Regex>>(m, "Regex")
      .def(py::init<std::string>())
      .def("Sub", &Regex::Sub)
      .def("FindAndConsume", &Regex::FindAndConsume)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Regex>& self) -> std::string {
            return _serialize_regex(self);
          },
          // __setstate__
          [](std::string state) -> c10::intrusive_ptr<Regex> {
            return _deserialize_regex(std::move(state));
          }));

  py::class_<RegexTokenizer, c10::intrusive_ptr<RegexTokenizer>>(
      m, "RegexTokenizer")
      .def_readonly("patterns_", &RegexTokenizer::patterns_)
      .def_readonly("replacements_", &RegexTokenizer::replacements_)
      .def_readonly("to_lower_", &RegexTokenizer::to_lower_)
      .def(py::init<std::vector<std::string>, std::vector<std::string>, bool>())
      .def("forward", &RegexTokenizer::forward)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<RegexTokenizer>& self)
              -> RegexTokenizerStates {
            return _serialize_regex_tokenizer(self);
          },
          // __setstate__
          [](RegexTokenizerStates states)
              -> c10::intrusive_ptr<RegexTokenizer> {
            return _deserialize_regex_tokenizer(std::move(states));
          }));

  py::class_<SentencePiece, c10::intrusive_ptr<SentencePiece>>(
      m, "SentencePiece")
      .def(py::init<std::string>())
      .def(
          "_return_content",
          [](const SentencePiece& self) { return py::bytes(self.content_); })
      .def("Encode", &SentencePiece::Encode)
      .def("EncodeAsIds", &SentencePiece::EncodeAsIds)
      .def("DecodeIds", &SentencePiece::DecodeIds)
      .def("EncodeAsPieces", &SentencePiece::EncodeAsPieces)
      .def("DecodePieces", &SentencePiece::DecodePieces)
      .def("GetPieceSize", &SentencePiece::GetPieceSize)
      .def("unk_id", &SentencePiece::unk_id)
      .def("PieceToId", &SentencePiece::PieceToId)
      .def("IdToPiece", &SentencePiece::IdToPiece)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<SentencePiece>& self) -> py::bytes {
            return py::bytes(self->content_);
          },
          // __setstate__
          [](py::bytes state) -> c10::intrusive_ptr<SentencePiece> {
            return c10::make_intrusive<SentencePiece>(std::string(state));
          }));

  py::class_<Vectors, c10::intrusive_ptr<Vectors>>(m, "Vectors")
      .def(py::init<
           std::vector<std::string>,
           std::vector<int64_t>,
           torch::Tensor,
           torch::Tensor>())
      .def_readonly("vectors_", &Vectors::vectors_)
      .def_readonly("unk_tensor_", &Vectors::unk_tensor_)
      .def("get_stoi", &Vectors::get_stoi)
      .def("__getitem__", &Vectors::__getitem__)
      .def("lookup_vectors", &Vectors::lookup_vectors)
      .def("__setitem__", &Vectors::__setitem__)
      .def("__len__", &Vectors::__len__)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Vectors>& self) -> VectorsStates {
            return _serialize_vectors(self);
          },
          // __setstate__
          [](VectorsStates states) -> c10::intrusive_ptr<Vectors> {
            return _deserialize_vectors(states);
          }));

  py::class_<Vocab, c10::intrusive_ptr<Vocab>>(m, "Vocab")
      .def(py::init<StringList, c10::optional<int64_t>>())
      .def_readonly("itos_", &Vocab::itos_)
      .def_readonly("default_index_", &Vocab::default_index_)
      .def(
          "__contains__",
          [](c10::intrusive_ptr<Vocab>& self, const py::str& item) -> bool {
            ssize_t length;
            const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
            return self->__contains__(c10::string_view{buffer, (size_t)length});
          })
      .def(
          "__getitem__",
          [](c10::intrusive_ptr<Vocab>& self, const py::str& item) -> int64_t {
            ssize_t length;
            const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
            return self->__getitem__(c10::string_view{buffer, (size_t)length});
          })
      .def("insert_token", &Vocab::insert_token)
      .def("set_default_index", &Vocab::set_default_index)
      .def("get_default_index", &Vocab::get_default_index)
      .def("__len__", &Vocab::__len__)
      .def("append_token", &Vocab::append_token)
      .def("lookup_token", &Vocab::lookup_token)
      .def("lookup_tokens", &Vocab::lookup_tokens)
      .def(
          "lookup_indices",
          [](const c10::intrusive_ptr<Vocab>& self, const py::list& items) {
            std::vector<int64_t> indices(items.size());
            int64_t counter = 0;
            for (const auto& item : items) {
              ssize_t length;
              const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
              indices[counter++] =
                  self->__getitem__(c10::string_view{buffer, (size_t)length});
            }
            return indices;
          })
      .def("get_stoi", &Vocab::get_stoi)
      .def("get_itos", &Vocab::get_itos)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Vocab>& self) -> VocabStates {
            return _serialize_vocab(self);
          },
          // __setstate__
          [](VocabStates states) -> c10::intrusive_ptr<Vocab> {
            return _deserialize_vocab(states);
          }));

  py::class_<GPT2BPEEncoder, c10::intrusive_ptr<GPT2BPEEncoder>>(
      m, "GPT2BPEEncoder")
      .def(py::init<
           std::unordered_map<std::string, int64_t>,
           std::unordered_map<std::string, int64_t>,
           std::string,
           std::unordered_map<int64_t, std::string>,
           bool>())
      .def_property_readonly("bpe_encoder_", &GPT2BPEEncoder::GetBPEEncoder)
      .def_property_readonly(
          "bpe_merge_ranks_", &GPT2BPEEncoder::GetBPEMergeRanks)
      .def_readonly("seperator_", &GPT2BPEEncoder::seperator_)
      .def_property_readonly("byte_encoder_", &GPT2BPEEncoder::GetByteEncoder)
      .def("encode", &GPT2BPEEncoder::Encode)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<GPT2BPEEncoder>& self)
              -> GPT2BPEEncoderStatesPybind {
            return _serialize_gpt2_bpe_encoder_pybind(self);
          },
          // __setstate__
          [](GPT2BPEEncoderStatesPybind states)
              -> c10::intrusive_ptr<GPT2BPEEncoder> {
            return _deserialize_gpt2_bpe_encoder_pybind(states);
          }));

  py::class_<CLIPEncoder, c10::intrusive_ptr<CLIPEncoder>>(m, "CLIPEncoder")
      .def(py::init<
           std::unordered_map<std::string, int64_t>,
           std::unordered_map<std::string, int64_t>,
           std::string,
           std::unordered_map<int64_t, std::string>,
           bool>())
      .def_property_readonly("bpe_encoder_", &CLIPEncoder::GetBPEEncoder)
      .def_property_readonly("bpe_merge_ranks_", &CLIPEncoder::GetBPEMergeRanks)
      .def_readonly("seperator_", &CLIPEncoder::seperator_)
      .def_property_readonly("byte_encoder_", &CLIPEncoder::GetByteEncoder)
      .def("encode", &CLIPEncoder::Encode)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<CLIPEncoder>& self)
              -> CLIPEncoderStatesPybind {
            return _serialize_clip_encoder_pybind(self);
          },
          // __setstate__
          [](CLIPEncoderStatesPybind states)
              -> c10::intrusive_ptr<CLIPEncoder> {
            return _deserialize_clip_encoder_pybind(states);
          }));

  // Functions
  m.def(
      "_load_token_and_vectors_from_file", &_load_token_and_vectors_from_file);
  m.def("_load_vocab_from_file", &_load_vocab_from_file);
  m.def("_build_vocab_from_text_file", &build_vocab_from_text_file);
  m.def(
      "_build_vocab_from_text_file_using_python_tokenizer",
      &_build_vocab_from_text_file_using_python_tokenizer);
}

} // namespace torchtext
<commit_msg>Fix for windows builds with python 3.10 , getting rid of ssize_t (#1627)<commit_after>#include <clip_tokenizer.h> // @manual
#include <gpt2_bpe_tokenizer.h> // @manual
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <regex.h>
#include <regex_tokenizer.h> // @manual
#include <sentencepiece.h> // @manual
#include <torch/csrc/jit/python/pybind_utils.h> // @manual
#include <torch/csrc/utils/pybind.h> // @manual
#include <torch/script.h>
#include <vectors.h> // @manual
#include <vocab.h> // @manual
#include <vocab_factory.h> // @manual

#include <iostream>

namespace torchtext {

namespace py = pybind11;

namespace {
Vocab build_vocab_from_text_file(
    const std::string& file_path,
    const int64_t min_freq,
    const int64_t num_cpus,
    py::object fn) {
  torch::jit::script::Module module(*torch::jit::as_module(fn));
  return _build_vocab_from_text_file(file_path, min_freq, num_cpus, module);
}
} // namespace

// Registers our custom classes with pybind11.
PYBIND11_MODULE(_torchtext, m) {
  // Classes
  py::class_<Regex, c10::intrusive_ptr<Regex>>(m, "Regex")
      .def(py::init<std::string>())
      .def("Sub", &Regex::Sub)
      .def("FindAndConsume", &Regex::FindAndConsume)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Regex>& self) -> std::string {
            return _serialize_regex(self);
          },
          // __setstate__
          [](std::string state) -> c10::intrusive_ptr<Regex> {
            return _deserialize_regex(std::move(state));
          }));

  py::class_<RegexTokenizer, c10::intrusive_ptr<RegexTokenizer>>(
      m, "RegexTokenizer")
      .def_readonly("patterns_", &RegexTokenizer::patterns_)
      .def_readonly("replacements_", &RegexTokenizer::replacements_)
      .def_readonly("to_lower_", &RegexTokenizer::to_lower_)
      .def(py::init<std::vector<std::string>, std::vector<std::string>, bool>())
      .def("forward", &RegexTokenizer::forward)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<RegexTokenizer>& self)
              -> RegexTokenizerStates {
            return _serialize_regex_tokenizer(self);
          },
          // __setstate__
          [](RegexTokenizerStates states)
              -> c10::intrusive_ptr<RegexTokenizer> {
            return _deserialize_regex_tokenizer(std::move(states));
          }));

  py::class_<SentencePiece, c10::intrusive_ptr<SentencePiece>>(
      m, "SentencePiece")
      .def(py::init<std::string>())
      .def(
          "_return_content",
          [](const SentencePiece& self) { return py::bytes(self.content_); })
      .def("Encode", &SentencePiece::Encode)
      .def("EncodeAsIds", &SentencePiece::EncodeAsIds)
      .def("DecodeIds", &SentencePiece::DecodeIds)
      .def("EncodeAsPieces", &SentencePiece::EncodeAsPieces)
      .def("DecodePieces", &SentencePiece::DecodePieces)
      .def("GetPieceSize", &SentencePiece::GetPieceSize)
      .def("unk_id", &SentencePiece::unk_id)
      .def("PieceToId", &SentencePiece::PieceToId)
      .def("IdToPiece", &SentencePiece::IdToPiece)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<SentencePiece>& self) -> py::bytes {
            return py::bytes(self->content_);
          },
          // __setstate__
          [](py::bytes state) -> c10::intrusive_ptr<SentencePiece> {
            return c10::make_intrusive<SentencePiece>(std::string(state));
          }));

  py::class_<Vectors, c10::intrusive_ptr<Vectors>>(m, "Vectors")
      .def(py::init<
           std::vector<std::string>,
           std::vector<int64_t>,
           torch::Tensor,
           torch::Tensor>())
      .def_readonly("vectors_", &Vectors::vectors_)
      .def_readonly("unk_tensor_", &Vectors::unk_tensor_)
      .def("get_stoi", &Vectors::get_stoi)
      .def("__getitem__", &Vectors::__getitem__)
      .def("lookup_vectors", &Vectors::lookup_vectors)
      .def("__setitem__", &Vectors::__setitem__)
      .def("__len__", &Vectors::__len__)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Vectors>& self) -> VectorsStates {
            return _serialize_vectors(self);
          },
          // __setstate__
          [](VectorsStates states) -> c10::intrusive_ptr<Vectors> {
            return _deserialize_vectors(states);
          }));

  py::class_<Vocab, c10::intrusive_ptr<Vocab>>(m, "Vocab")
      .def(py::init<StringList, c10::optional<int64_t>>())
      .def_readonly("itos_", &Vocab::itos_)
      .def_readonly("default_index_", &Vocab::default_index_)
      .def(
          "__contains__",
          [](c10::intrusive_ptr<Vocab>& self, const py::str& item) -> bool {
            Py_ssize_t length;
            const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
            return self->__contains__(c10::string_view{buffer, (size_t)length});
          })
      .def(
          "__getitem__",
          [](c10::intrusive_ptr<Vocab>& self, const py::str& item) -> int64_t {
            Py_ssize_t length;
            const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
            return self->__getitem__(c10::string_view{buffer, (size_t)length});
          })
      .def("insert_token", &Vocab::insert_token)
      .def("set_default_index", &Vocab::set_default_index)
      .def("get_default_index", &Vocab::get_default_index)
      .def("__len__", &Vocab::__len__)
      .def("append_token", &Vocab::append_token)
      .def("lookup_token", &Vocab::lookup_token)
      .def("lookup_tokens", &Vocab::lookup_tokens)
      .def(
          "lookup_indices",
          [](const c10::intrusive_ptr<Vocab>& self, const py::list& items) {
            std::vector<int64_t> indices(items.size());
            int64_t counter = 0;
            for (const auto& item : items) {
              Py_ssize_t length;
              const char* buffer = PyUnicode_AsUTF8AndSize(item.ptr(), &length);
              indices[counter++] =
                  self->__getitem__(c10::string_view{buffer, (size_t)length});
            }
            return indices;
          })
      .def("get_stoi", &Vocab::get_stoi)
      .def("get_itos", &Vocab::get_itos)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<Vocab>& self) -> VocabStates {
            return _serialize_vocab(self);
          },
          // __setstate__
          [](VocabStates states) -> c10::intrusive_ptr<Vocab> {
            return _deserialize_vocab(states);
          }));

  py::class_<GPT2BPEEncoder, c10::intrusive_ptr<GPT2BPEEncoder>>(
      m, "GPT2BPEEncoder")
      .def(py::init<
           std::unordered_map<std::string, int64_t>,
           std::unordered_map<std::string, int64_t>,
           std::string,
           std::unordered_map<int64_t, std::string>,
           bool>())
      .def_property_readonly("bpe_encoder_", &GPT2BPEEncoder::GetBPEEncoder)
      .def_property_readonly(
          "bpe_merge_ranks_", &GPT2BPEEncoder::GetBPEMergeRanks)
      .def_readonly("seperator_", &GPT2BPEEncoder::seperator_)
      .def_property_readonly("byte_encoder_", &GPT2BPEEncoder::GetByteEncoder)
      .def("encode", &GPT2BPEEncoder::Encode)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<GPT2BPEEncoder>& self)
              -> GPT2BPEEncoderStatesPybind {
            return _serialize_gpt2_bpe_encoder_pybind(self);
          },
          // __setstate__
          [](GPT2BPEEncoderStatesPybind states)
              -> c10::intrusive_ptr<GPT2BPEEncoder> {
            return _deserialize_gpt2_bpe_encoder_pybind(states);
          }));

  py::class_<CLIPEncoder, c10::intrusive_ptr<CLIPEncoder>>(m, "CLIPEncoder")
      .def(py::init<
           std::unordered_map<std::string, int64_t>,
           std::unordered_map<std::string, int64_t>,
           std::string,
           std::unordered_map<int64_t, std::string>,
           bool>())
      .def_property_readonly("bpe_encoder_", &CLIPEncoder::GetBPEEncoder)
      .def_property_readonly("bpe_merge_ranks_", &CLIPEncoder::GetBPEMergeRanks)
      .def_readonly("seperator_", &CLIPEncoder::seperator_)
      .def_property_readonly("byte_encoder_", &CLIPEncoder::GetByteEncoder)
      .def("encode", &CLIPEncoder::Encode)
      .def(py::pickle(
          // __getstate__
          [](const c10::intrusive_ptr<CLIPEncoder>& self)
              -> CLIPEncoderStatesPybind {
            return _serialize_clip_encoder_pybind(self);
          },
          // __setstate__
          [](CLIPEncoderStatesPybind states)
              -> c10::intrusive_ptr<CLIPEncoder> {
            return _deserialize_clip_encoder_pybind(states);
          }));

  // Functions
  m.def(
      "_load_token_and_vectors_from_file", &_load_token_and_vectors_from_file);
  m.def("_load_vocab_from_file", &_load_vocab_from_file);
  m.def("_build_vocab_from_text_file", &build_vocab_from_text_file);
  m.def(
      "_build_vocab_from_text_file_using_python_tokenizer",
      &_build_vocab_from_text_file_using_python_tokenizer);
}

} // namespace torchtext
<|endoftext|>