ld error duplicate symbols while trying to build Pymesh on MacOS - c++

I've been trying to build Pymesh on MacOS 10.15 with GCC 9.3.0 and I get the following error in the make step
Scanning dependencies of target lib_IGL
[ 48%] Building CXX object tools/IGL/CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
[ 48%] Building CXX object tools/IGL/CMakeFiles/lib_IGL.dir/DiskCutter.cpp.o
[ 48%] Building CXX object tools/IGL/CMakeFiles/lib_IGL.dir/HarmonicSolver.cpp.o
[ 49%] Building CXX object tools/IGL/CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
[ 49%] Linking CXX shared library ../../../python/pymesh/lib/libPyMesh-IGL.dylib
duplicate symbol 'typeinfo name for CORE::Realbase_for' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::Realbase_for' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::Realbase_for' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::Realbase_for' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::Realbase_forCORE::BigInt' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::Realbase_forCORE::BigInt' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::ConstPolyRepCORE::BigInt' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::ConstPolyRepCORE::BigInt' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::Realbase_forCORE::BigFloat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::Realbase_forCORE::BigFloat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::ConstPolyRepCORE::BigFloat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::ConstPolyRepCORE::BigFloat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::Realbase_forCORE::BigRat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::Realbase_forCORE::BigRat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::ConstPolyRepCORE::BigRat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::ConstPolyRepCORE::BigRat' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::ConstPolyRepCORE::Expr' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::ConstPolyRepCORE::Expr' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::AddSubRepCORE::Add' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::AddSubRepCORE::Add' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo name for CORE::AddSubRepCORE::Sub' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
duplicate symbol 'typeinfo for CORE::AddSubRepCORE::Sub' in:
CMakeFiles/lib_IGL.dir/CellPartition.cpp.o
CMakeFiles/lib_IGL.dir/MinkowskiSum.cpp.o
ld: 22 duplicate symbols for architecture x86_64
collect2: error: ld returned 1 exit status
make[2]: *** [../python/pymesh/lib/libPyMesh-IGL.dylib] Error 1
make[1]: *** [tools/IGL/CMakeFiles/lib_IGL.dir/all] Error 2
make: *** [all] Error 2
So I went over to the files to see what was wrong
MinkowskiSum.cpp
#ifdef WITH_IGL_AND_CGAL
#include "MinkowskiSum.h"
#include <Math/MatrixUtils.h>
#include <igl/copyleft/cgal/minkowski_sum.h>
#include <igl/copyleft/cgal/mesh_boolean.h>
#include <igl/MeshBooleanType.h>
#include <vector>
using namespace PyMesh;
MinkowskiSum::Ptr MinkowskiSum::create(const Mesh::Ptr& mesh) {
const MatrixFr vertices = MatrixUtils::reshape<MatrixFr>(
mesh->get_vertices(), mesh->get_num_vertices(), mesh->get_dim());
const MatrixIr faces = MatrixUtils::reshape<MatrixIr>(
mesh->get_faces(), mesh->get_num_faces(),
mesh->get_vertex_per_face());
return MinkowskiSum::Ptr(new MinkowskiSum(vertices, faces));
}
MinkowskiSum::Ptr MinkowskiSum::create_raw(
const MatrixFr& vertices, const MatrixIr& faces) {
return MinkowskiSum::Ptr(new MinkowskiSum(vertices, faces));
}
void MinkowskiSum::run(const MatrixFr& path) {
const size_t num_pts = path.rows();
if (num_pts <= 1) {
m_out_vertices = m_vertices;
m_out_faces = m_faces;
return;
}
std::vector<MatrixFr> vertices;
std::vector<MatrixIr> faces;
for (size_t i=1; i<num_pts; i++) {
const Eigen::Matrix<Float, 1, 3> s = path.row(i-1);
const Eigen::Matrix<Float, 1, 3> d = path.row(i);
MatrixFr V;
MatrixIr F;
VectorI J;
igl::copyleft::cgal::minkowski_sum<
MatrixFr, MatrixIr,
Float, 3, 1,
Float, 3, 1,
MatrixFr, MatrixIr, VectorI>(m_vertices, m_faces, s, d, V, F, J);
vertices.emplace_back(V);
faces.emplace_back(F);
}
size_t v_count = 0;
for (size_t i=0; i<num_pts-1; i++) {
faces[i].array() += v_count;
v_count += vertices[i].rows();
}
MatrixFr combined_vertices = MatrixUtils::vstack(vertices);
MatrixIr combined_faces = MatrixUtils::vstack(faces);
// Self union to remove self-intersections.
MatrixFr empty_vertices;
MatrixIr empty_faces;
VectorI J;
igl::copyleft::cgal::mesh_boolean(
combined_vertices, combined_faces,
empty_vertices, empty_faces,
igl::MESH_BOOLEAN_TYPE_UNION,
m_out_vertices, m_out_faces, J);
}
#endif
Minkowski_Sum.h
#pragma once
#ifdef WITH_IGL_AND_CGAL
#include <memory>
#include <Core/EigenTypedef.h>
#include <Mesh.h>
namespace PyMesh {
class MinkowskiSum {
public:
typedef std::shared_ptr<MinkowskiSum> Ptr;
static Ptr create(const Mesh::Ptr& mesh);
static Ptr create_raw(const MatrixFr& vertices, const MatrixIr& faces);
public:
MinkowskiSum(const MatrixFr& vertices, const MatrixIr& faces):
m_vertices(vertices), m_faces(faces) { }
void run(const MatrixFr& path);
MatrixFr get_vertices() const { return m_out_vertices; }
MatrixIr get_faces() const { return m_out_faces; }
private:
MatrixFr m_vertices;
MatrixIr m_faces;
MatrixFr m_out_vertices;
MatrixIr m_out_faces;
};
}
#endif
minkowski_sum.h
#ifndef IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H
#define IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H
#include "../../igl_inline.h"
#include <Eigen/Core>
namespace igl
{
namespace copyleft
{
namespace cgal
{
// Compute the Minkowski sum of a closed triangle mesh (V,F) and a
// set of simplices in 3D.
//
// Inputs:
// VA #VA by 3 list of mesh vertices in 3D
// FA #FA by 3 list of triangle indices into VA
// VB #VB by 3 list of mesh vertices in 3D
// FB #FB by ss list of simplex indices into VB, ss<=3
// resolve_overlaps whether or not to resolve self-union. If false
// then result may contain self-intersections if input mesh is
// non-convex.
// Outputs:
// W #W by 3 list of mesh vertices in 3D
// G #G by 3 list of triangle indices into W
// J #G by 2 list of indices into
//
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedW,
typename DerivedG,
typename DerivedJ>
IGL_INLINE void minkowski_sum(
const Eigen::MatrixBase<DerivedVA> & VA,
const Eigen::MatrixBase<DerivedFA> & FA,
const Eigen::MatrixBase<DerivedVB> & VB,
const Eigen::MatrixBase<DerivedFB> & FB,
const bool resolve_overlaps,
Eigen::PlainObjectBase<DerivedW> & W,
Eigen::PlainObjectBase<DerivedG> & G,
Eigen::PlainObjectBase<DerivedJ> & J);
// Compute the Minkowski sum of a closed triangle mesh (V,F) and a
// segment [s,d] in 3D.
//
// Inputs:
// VA #VA by 3 list of mesh vertices in 3D
// FA #FA by 3 list of triangle indices into VA
// s segment source endpoint in 3D
// d segment source endpoint in 3D
// resolve_overlaps whether or not to resolve self-union. If false
// then result may contain self-intersections if input mesh is
// non-convex.
// Outputs:
// W #W by 3 list of mesh vertices in 3D
// G #G by 3 list of triangle indices into W
// J #G list of indices into [F;#V+F;[s d]] of birth parents
//
template <
typename DerivedVA,
typename DerivedFA,
typename sType, int sCols, int sOptions,
typename dType, int dCols, int dOptions,
typename DerivedW,
typename DerivedG,
typename DerivedJ>
IGL_INLINE void minkowski_sum(
const Eigen::MatrixBase<DerivedVA> & VA,
const Eigen::MatrixBase<DerivedFA> & FA,
const Eigen::Matrix<sType,1,sCols,sOptions> & s,
const Eigen::Matrix<dType,1,dCols,dOptions> & d,
const bool resolve_overlaps,
Eigen::PlainObjectBase<DerivedW> & W,
Eigen::PlainObjectBase<DerivedG> & G,
Eigen::PlainObjectBase<DerivedJ> & J);
template <
typename DerivedVA,
typename DerivedFA,
typename sType, int sCols, int sOptions,
typename dType, int dCols, int dOptions,
typename DerivedW,
typename DerivedG,
typename DerivedJ>
IGL_INLINE void minkowski_sum(
const Eigen::MatrixBase<DerivedVA> & VA,
const Eigen::MatrixBase<DerivedFA> & FA,
const Eigen::Matrix<sType,1,sCols,sOptions> & s,
const Eigen::Matrix<dType,1,dCols,dOptions> & d,
Eigen::PlainObjectBase<DerivedW> & W,
Eigen::PlainObjectBase<DerivedG> & G,
Eigen::PlainObjectBase<DerivedJ> & J);
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "minkowski_sum.cpp"
#endif
#endif
mesh_boolean.h
#ifndef IGL_COPYLEFT_CGAL_MESH_BOOLEAN_H
#define IGL_COPYLEFT_CGAL_MESH_BOOLEAN_H
#include "../../igl_inline.h"
#include "../../MeshBooleanType.h"
#include <Eigen/Core>
#include <functional>
#include <vector>
namespace igl
{
namespace copyleft
{
namespace cgal
{
// MESH_BOOLEAN Compute boolean csg operations on "solid", consistently
// oriented meshes.
//
// Inputs:
// VA #VA by 3 list of vertex positions of first mesh
// FA #FA by 3 list of triangle indices into VA
// VB #VB by 3 list of vertex positions of second mesh
// FB #FB by 3 list of triangle indices into VB
// type type of boolean operation
// Outputs:
// VC #VC by 3 list of vertex positions of boolean result mesh
// FC #FC by 3 list of triangle indices into VC
// J #FC list of indices into [FA;FA.rows()+FB] revealing "birth" facet
// Returns true if inputs induce a piecewise constant winding number
// field and type is valid
//
// See also: mesh_boolean_cork, intersect_other,
// remesh_self_intersections
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const Eigen::MatrixBase<DerivedVA > & VA,
const Eigen::MatrixBase<DerivedFA > & FA,
const Eigen::MatrixBase<DerivedVB > & VB,
const Eigen::MatrixBase<DerivedFB > & FB,
const MeshBooleanType & type,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const Eigen::MatrixBase<DerivedVA > & VA,
const Eigen::MatrixBase<DerivedFA > & FA,
const Eigen::MatrixBase<DerivedVB > & VB,
const Eigen::MatrixBase<DerivedFB > & FB,
const std::string & type_str,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
//
// Inputs:
// VA #VA by 3 list of vertex positions of first mesh
// FA #FA by 3 list of triangle indices into VA
// VB #VB by 3 list of vertex positions of second mesh
// FB #FB by 3 list of triangle indices into VB
// wind_num_op function handle for filtering winding numbers from
// tuples of integer values to [0,1] outside/inside values
// keep function handle for determining if a patch should be "kept"
// in the output based on the winding number on either side
// Outputs:
// VC #VC by 3 list of vertex positions of boolean result mesh
// FC #FC by 3 list of triangle indices into VC
// J #FC list of indices into [FA;FB] revealing "birth" facet
// Returns true iff inputs induce a piecewise constant winding number
// field
//
// See also: mesh_boolean_cork, intersect_other,
// remesh_self_intersections
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const Eigen::MatrixBase<DerivedVA> & VA,
const Eigen::MatrixBase<DerivedFA> & FA,
const Eigen::MatrixBase<DerivedVB> & VB,
const Eigen::MatrixBase<DerivedFB> & FB,
const std::function<int(const Eigen::Matrix<int,1,Eigen::Dynamic>) >& wind_num_op,
const std::function<int(const int, const int)> & keep,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
// MESH_BOOLEAN Variadic boolean operations
//
// Inputs:
// Vlist k-long list of lists of mesh vertex positions
// Flist k-long list of lists of mesh face indices, so that Flist[i] indexes
// vertices in Vlist[i]
// wind_num_op function handle for filtering winding numbers from
// n-tuples of integer values to [0,1] outside/inside values
// keep function handle for determining if a patch should be "kept"
// in the output based on the winding number on either side
// Outputs:
// VC #VC by 3 list of vertex positions of boolean result mesh
// FC #FC by 3 list of triangle indices into VC
// J #FC list of indices into [Flist[0];Flist[1];...;Flist[k]]
// revealing "birth" facet
// Returns true iff inputs induce a piecewise constant winding number
// field
//
// See also: mesh_boolean_cork, intersect_other,
// remesh_self_intersections
template <
typename DerivedV,
typename DerivedF,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const std::vector<DerivedV > & Vlist,
const std::vector<DerivedF > & Flist,
const std::function<int(const Eigen::Matrix<int,1,Eigen::Dynamic>) >& wind_num_op,
const std::function<int(const int, const int)> & keep,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
template <
typename DerivedV,
typename DerivedF,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const std::vector<DerivedV > & Vlist,
const std::vector<DerivedF > & Flist,
const MeshBooleanType & type,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
// Given a merged mesh (V,F) and list of sizes of inputs
//
// Inputs:
// V #V by 3 list of merged mesh vertex positions
// F #F by 3 list of merged mesh face indices so that first sizes(0)
// faces come from the first input, and the next sizes(1) faces come
// from the second input, and so on.
// sizes #inputs list of sizes so that sizes(i) is the #faces in the
// ith input
// wind_num_op function handle for filtering winding numbers from
// tuples of integer values to [0,1] outside/inside values
// keep function handle for determining if a patch should be "kept"
// in the output based on the winding number on either side
// Outputs:
// VC #VC by 3 list of vertex positions of boolean result mesh
// FC #FC by 3 list of triangle indices into VC
// J #FC list of birth parent indices
//
template <
typename DerivedVV,
typename DerivedFF,
typename Derivedsizes,
typename DerivedVC,
typename DerivedFC,
typename DerivedJ>
IGL_INLINE bool mesh_boolean(
const Eigen::MatrixBase<DerivedVV > & VV,
const Eigen::MatrixBase<DerivedFF > & FF,
const Eigen::MatrixBase<Derivedsizes> & sizes,
const std::function<int(const Eigen::Matrix<int,1,Eigen::Dynamic>) >& wind_num_op,
const std::function<int(const int, const int)> & keep,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC,
Eigen::PlainObjectBase<DerivedJ > & J);
// Inputs:
// VA #VA by 3 list of vertex positions of first mesh
// FA #FA by 3 list of triangle indices into VA
// VB #VB by 3 list of vertex positions of second mesh
// FB #FB by 3 list of triangle indices into VB
// type type of boolean operation
// Outputs:
// VC #VC by 3 list of vertex positions of boolean result mesh
// FC #FC by 3 list of triangle indices into VC
// Returns true ff inputs induce a piecewise constant winding number
// field and type is valid
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedVC,
typename DerivedFC>
IGL_INLINE bool mesh_boolean(
const Eigen::MatrixBase<DerivedVA > & VA,
const Eigen::MatrixBase<DerivedFA > & FA,
const Eigen::MatrixBase<DerivedVB > & VB,
const Eigen::MatrixBase<DerivedFB > & FB,
const MeshBooleanType & type,
Eigen::PlainObjectBase<DerivedVC > & VC,
Eigen::PlainObjectBase<DerivedFC > & FC);
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "mesh_boolean.cpp"
#endif
#endif
minkowski_sum.cpp
#include "minkowski_sum.h"
#include "mesh_boolean.h"
#include "../../slice.h"
#include "../../slice_mask.h"
#include "../../LinSpaced.h"
#include "../../unique_rows.h"
#include "../../get_seconds.h"
#include "../../edges.h"
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <cassert>
#include <vector>
#include <iostream>
template <
typename DerivedVA,
typename DerivedFA,
typename DerivedVB,
typename DerivedFB,
typename DerivedW,
typename DerivedG,
typename DerivedJ>
IGL_INLINE void igl::copyleft::cgal::minkowski_sum(
const Eigen::MatrixBase<DerivedVA> & VA,
const Eigen::MatrixBase<DerivedFA> & FA,
const Eigen::MatrixBase<DerivedVB> & VB,
const Eigen::MatrixBase<DerivedFB> & FB,
const bool resolve_overlaps,
Eigen::PlainObjectBase<DerivedW> & W,
Eigen::PlainObjectBase<DerivedG> & G,
Eigen::PlainObjectBase<DerivedJ> & J)
{
using namespace std;
using namespace Eigen;
assert(FA.cols() == 3 && "FA must contain a closed triangle mesh");
assert(FB.cols() <= FA.cols() &&
"FB must contain lower diemnsional simplices than FA");
const auto tictoc = []()->double
{
static double t_start;
double now = igl::get_seconds();
double interval = now-t_start;
t_start = now;
return interval;
};
tictoc();
Matrix<typename DerivedFB::Scalar,Dynamic,2> EB;
edges(FB,EB);
Matrix<typename DerivedFA::Scalar,Dynamic,2> EA(0,2);
if(FB.cols() == 3)
{
edges(FA,EA);
}
// number of copies of A along edges of B
const int n_ab = EB.rows();
// number of copies of B along edges of A
const int n_ba = EA.rows();
vector<DerivedW> vW(n_ab + n_ba);
vector<DerivedG> vG(n_ab + n_ba);
vector<DerivedJ> vJ(n_ab + n_ba);
vector<int> offsets(n_ab + n_ba + 1);
offsets[0] = 0;
// sweep A along edges of B
for(int e = 0;e<n_ab;e++)
{
Matrix<typename DerivedJ::Scalar,Dynamic,1> eJ;
minkowski_sum(
VA,
FA,
VB.row(EB(e,0)).eval(),
VB.row(EB(e,1)).eval(),
false,
vW[e],
vG[e],
eJ);
assert(vG[e].rows() == eJ.rows());
assert(eJ.cols() == 1);
vJ[e].resize(vG[e].rows(),2);
vJ[e].col(0) = eJ;
vJ[e].col(1).setConstant(e);
offsets[e+1] = offsets[e] + vW[e].rows();
}
// sweep B along edges of A
for(int e = 0;e<n_ba;e++)
{
Matrix<typename DerivedJ::Scalar,Dynamic,1> eJ;
const int ee = n_ab+e;
minkowski_sum(
VB,
FB,
VA.row(EA(e,0)).eval(),
VA.row(EA(e,1)).eval(),
false,
vW[ee],
vG[ee],
eJ);
vJ[ee].resize(vG[ee].rows(),2);
vJ[ee].col(0) = eJ.array() + (FA.rows()+1);
vJ[ee].col(1).setConstant(ee);
offsets[ee+1] = offsets[ee] + vW[ee].rows();
}
// Combine meshes
int n=0,m=0;
for_each(vW.begin(),vW.end(),[&n](const DerivedW & w){n+=w.rows();});
for_each(vG.begin(),vG.end(),[&m](const DerivedG & g){m+=g.rows();});
assert(n == offsets.back());
W.resize(n,3);
G.resize(m,3);
J.resize(m,2);
{
int m_off = 0,n_off = 0;
for(int i = 0;i<vG.size();i++)
{
W.block(n_off,0,vW[i].rows(),3) = vW[i];
G.block(m_off,0,vG[i].rows(),3) = vG[i].array()+offsets[i];
J.block(m_off,0,vJ[i].rows(),2) = vJ[i];
n_off += vW[i].rows();
m_off += vG[i].rows();
}
assert(n == n_off);
assert(m == m_off);
}
if(resolve_overlaps)
{
Eigen::Matrix<typename DerivedJ::Scalar, Eigen::Dynamic,1> SJ;
mesh_boolean(
DerivedW(W),
DerivedG(G),
Matrix<typename DerivedW::Scalar,Dynamic,Dynamic>(),
Matrix<typename DerivedG::Scalar,Dynamic,Dynamic>(),
MESH_BOOLEAN_TYPE_UNION,
W,
G,
SJ);
slice(DerivedJ(J),SJ,1,J);
}
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::copyleft::cgal::minkowski_sum<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, CGAL::Lazy_exact_nt<CGAL::Gmpq>, 3, 1, CGAL::Lazy_exact_nt<CGAL::Gmpq>, 3, 1, Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, 1, 3, 1, 1, 3> const&, Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, 1, 3, 1, 1, 3> const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
// generated by autoexplicit.sh
template void igl::copyleft::cgal::minkowski_sum<
Eigen::Matrix<float, -1, 3, 1, -1, 3>,
Eigen::Matrix<int, -1, 3, 1, -1, 3>,
double, 3, 1,
float, 3, 1,
Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1>,
Eigen::Matrix<int, -1, -1, 0, -1, -1>,
Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::Matrix<double, 1, 3, 1, 1, 3> const&, Eigen::Matrix<float, 1, 3, 1, 1, 3> const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Lazy_exact_nt<CGAL::Gmpq>, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
#endif
I read somewhere that the error can be caused because of directly using #include on .cpp files which is done in the last two header files so I tried copying the content of the .cpp files and placing it instead of the #include and I still get the same error.
Does anybody know what to do here?

Related

Extending Eigen::EigenBase with operator()

I would like to use MatrixXd class for meshes with offsets (0.5, 0) and (0, 0.5). In mathematical formulas, velocity is calculated between cells i,i+1, and this is written as vel(i+0.5,j). I would like to introduce syntax like this one:
#include <Eigen/Dense>
int main() {
Eigen::MatrixXd m = Eigen::MatrixXd::Zero(5,5);
// Want to use similar syntax:
// m(0, 1.5) = 1.0;
// and
// m(3.5, 1) = 2.0;
// Instead of:
m(0, 2) = 1.0;
m(4, 1) = 2.0;
}
Using EIGEN_MATRIXBASE_PLUGIN like this one:
inline Scalar& operator()(int r, int c) {
return Base::operator()(r, c);
}
inline Scalar& operator()(double r, int c) {
return Base::operator()(int(r + 0.5), c);
}
inline Scalar& operator()(int r, double c) {
return Base::operator()(r, int(c + 0.5));
}
However, this approach:
Works only for only X-axis or only Y-axis offset, not both at the same time.
Works only for specific offset hardcoded into plugin.
Breaks some internal Eigen convections, which can be demostrated by trying to compile BiCG example with IncompleteLUT preconditioner:
int n = 10000;
VectorXd x(n), b(n);
SparseMatrix<double> A(n,n);
/* ... fill A and b ... */
BiCGSTAB<SparseMatrix<double>,IncompleteLUT<double>> solver;
solver.compute(A);
x = solver.solve(b);
Causes following errors:
term does not evaluate to a function taking 1 arguments
'Eigen::SparseMatrix<double,1,int>::insertBackByOuterInnerUnordered': function does not take 1 arguments
Adding operator()(double offset_col, double offset_row) to adress second issue like this:
double r_offset = -0.5, c_offset = -0.5;
inline void set_r_offset(double val) { r_offset = val; }
inline void set_c_offset(double val) { c_offset = val; }
inline double get_r_offset() { return r_offset; }
inline double get_c_offset() { return c_offset; }
inline Scalar& operator()(double r, double c) {
// double r_offset = -0.5, c_offset = -0.5;
return Base::operator()(int(r - r_offset), int(c - c_offset));
}
This causes illegal free:
==6035== Invalid free() / delete / delete[] / realloc()
==6035== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6035== by 0x4E4224A: aligned_free (Memory.h:177)
==6035== by 0x4E4224A: conditional_aligned_free<true> (Memory.h:230)
==6035== by 0x4E4224A: conditional_aligned_delete_auto<double, true> (Memory.h:416)
==6035== by 0x4E4224A: resize (DenseStorage.h:406)
==6035== by 0x4E4224A: resize (PlainObjectBase.h:293)
==6035== by 0x4E4224A: resize_if_allowed<Eigen::Matrix<double, -1, -1>, Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> >, double, double> (AssignEvaluator.h:720)
==6035== by 0x4E4224A: call_dense_assignment_loop<Eigen::Matrix<double, -1, -1>, Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> >, Eigen::internal::assign_op<double, double> > (AssignEvaluator.h:734)
==6035== by 0x4E4224A: run (AssignEvaluator.h:879)
==6035== by 0x4E4224A: call_assignment_no_alias<Eigen::Matrix<double, -1, -1>, Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> >, Eigen::internal::assign_op<double, double> > (AssignEvaluator.h:836)
==6035== by 0x4E4224A: call_assignment<Eigen::Matrix<double, -1, -1>, Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> >, Eigen::internal::assign_op<double, double> > (AssignEvaluator.h:804)
==6035== by 0x4E4224A: call_assignment<Eigen::Matrix<double, -1, -1>, Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> > > (AssignEvaluator.h:782)
==6035== by 0x4E4224A: _set<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> > > (PlainObjectBase.h:710)
==6035== by 0x4E4224A: operator=<Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, -1, -1> > > (Matrix.h:225)
==6035== by 0x11044C: main (Runner.cpp:16)
==6035== Address 0x2e642f73726573 is not stack'd, malloc'd or (recently) free'd
If offsets are not introduced as class members, but are local variables in operator(), no errors are detected by valgrind.
Is it possible to implement new MatrixXd::operator()(double, double) with settable offsets?
EDIT:
Operator() is defined in a parent class DenseCoeffsBase:
EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE CoeffReturnType operator()(Index row, Index col) const
{
eigen_assert(row >= 0 && row < rows()
&& col >= 0 && col < cols());
return coeff(row, col);
}
Perhaps, I see one problem with your operator which returns reference to temporary object of Scalar:
inline Scalar& operator()(double r, double c) {
// double r_offset = -0.5, c_offset = -0.5;
return Base::operator()(int(r - r_offset), int(c - c_offset));
}
So you should return Scalar by copy.
Could you share code of Base::operator()(int(r - r_offset), int(c - c_offset));?

Write arbitrary Eigen object to row-major plain storage

I am writing a module to write data to a file which uses by convention only row-major storage. I would like my function to be able to allow both column-major and row-major Eigen objects as input.
Currently I first use Eigen to copy a column-major object to a row-major object, before I write. My code works well for most cases, but for Eigen::VectorXi compiling fails with an assertion that I don't understand. How do I solve this? Can I avoid creating many cases?
The code (writing is mimicked by outputting a std::vector):
#include <vector>
#include <iostream>
#include <Eigen/Eigen>
template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T,Rows,Cols,Options,MaxRows,MaxCols>& matrix)
{
std::vector<T> data(static_cast<size_t>(matrix.size()));
if (matrix.IsRowMajor) {
std::copy(matrix.data(), matrix.data()+matrix.size(), data.begin());
return data;
} else {
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
return write(tmp);
}
}
int main()
{
Eigen::VectorXi matrix = Eigen::VectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(matrix);
}
The compilation error:
In file included from test.cpp:3:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Eigen:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Dense:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Core:457:
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:903:7: error: static_assert failed "INVALID_MATRIX_TEMPLATE_PARAMETERS"
EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/util/StaticAssert.h:33:40: note: expanded from macro 'EIGEN_STATIC_ASSERT'
#define EIGEN_STATIC_ASSERT(X,MSG) static_assert(X,#MSG);
^ ~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:535:7: note: in instantiation of member function 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
>::_check_template_params' requested here
_check_template_params();
^
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/Matrix.h:377:9: note: in instantiation of function template specialization 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
>::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
: Base(other.derived())
^
test.cpp:14:79: note: in instantiation of function template specialization 'Eigen::Matrix<int, -1, 1, 1, -1, 1>::Matrix<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
^
test.cpp:23:31: note: in instantiation of function template specialization 'write<int, -1, 1, 0, -1, 1>' requested here
std::vector<int> output = write(matrix);
^
1 error generated.
Understanding the static assertion
Unfortunately the assertion is really not self-explanatory and the only thing you can get from it is the hint, that something is wrong with your template parameters. If we look into Eigen's source code we find the following beginning on line 903:
EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
&& EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)
&& ((RowsAtCompileTime == Dynamic) || (RowsAtCompileTime >= 0))
&& ((ColsAtCompileTime == Dynamic) || (ColsAtCompileTime >= 0))
&& ((MaxRowsAtCompileTime == Dynamic) || (MaxRowsAtCompileTime >= 0))
&& ((MaxColsAtCompileTime == Dynamic) || (MaxColsAtCompileTime >= 0))
&& (MaxRowsAtCompileTime == RowsAtCompileTime || RowsAtCompileTime==Dynamic)
&& (MaxColsAtCompileTime == ColsAtCompileTime || ColsAtCompileTime==Dynamic)
&& (Options & (DontAlign|RowMajor)) == Options),
INVALID_MATRIX_TEMPLATE_PARAMETERS)
Even though the compiler indicates that
EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
causes the error, the following line really does:
EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)
Understanding what triggers the assertion
You provide Eigen::VectorXi as an input for write. Eigen::VectorXi is really just a typedef for
Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::ColMajor, Eigen::Dynamic, 1>
Therefore the line
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
in write expands to
Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::RowMajor, Eigen::Dynamic, 1> tmp = matrix;
which triggers the assertion, since a matrix with MaxColsAtCompileTime==1 and MaxRowsAtCompileTime!=1 must not be RowMajor.
Solve your problem
The problem now is that even though you can check if your input matrix is a vector, row-major or column-major, you cannot declare
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols>
if it is no legal to do so at compile-time (and it isn't due to the static assertion).
You have the following options to make your code work:
1. if constexpr (C++17)
C++17 offers a way for detecting at compile-time if a certain conditional branch will be taken or not. The downside of this approach (beside the requirement for a C++17 compiler) is that you can only test for constant expressions.
In the concrete example this looks like this:
template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& matrix)
{
typedef Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> MatrixType;
std::vector<T> data(static_cast<size_t>(matrix.size()));
if constexpr (MatrixType::MaxRowsAtCompileTime == 1 ||
MatrixType::MaxColsAtCompileTime ==1 ||
(MatrixType::Options&Eigen::RowMajor) == Eigen::RowMajor) {
std::copy(matrix.data(), matrix.data() + matrix.size(), data.begin());
return data;
} else {
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
return write(tmp);
}
}
2. SFINAE
You can dispatch the call to write at compile-time using SFINAE by using std::enable_if. The following example uses a slightly modified version of your original code but everything should be clear from context:
// matrix is either a vector or in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
typename std::enable_if<Derived::MaxRowsAtCompileTime == 1 ||
Derived::MaxColsAtCompileTime == 1 ||
(Derived::Options & Eigen::RowMajor) == Eigen::RowMajor,
Derived>::type* = 0)
{
std::vector<typename Derived::Scalar> data(
static_cast<size_t>(matrix.size()));
std::copy(matrix.derived().data(), matrix.derived().data() + matrix.size(),
data.begin());
return data;
}
// matrix is neither a vector nor in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
typename std::enable_if<Derived::MaxRowsAtCompileTime != 1 &&
Derived::MaxColsAtCompileTime != 1 &&
(Derived::Options & Eigen::RowMajor) == 0,
Derived>::type* = 0)
{
Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime,
Derived::ColsAtCompileTime, Eigen::RowMajor,
Derived::MaxRowsAtCompileTime, Derived::MaxColsAtCompileTime> tmp = matrix;
return write(tmp);
}
This works using a C++11 compiler.
Other options would be to specialise the template but it will get even more lengthy than the SFINAE approach.
Some test cases:
Eigen::Matrix<int, 3, 3, Eigen::RowMajor> m;
m << 1, 2, 3,
1, 2, 3,
1, 2, 3;
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 1 2 3 1 2 3 1 2 3
Eigen::Matrix<int, 3, 3, Eigen::ColMajor> m;
m << 1, 2, 3,
1, 2, 3,
1, 2, 3;
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 1 2 3 1 2 3 1 2 3
Eigen::VectorXi m = Eigen::VectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 0 1 2 3 4 5 6 7 8 9
Eigen::RowVectorXi m = Eigen::RowVectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 0 1 2 3 4 5 6 7 8 9
A simpler solution is to let Eigen::Ref does all the job for you:
Ref<const Matrix<T,Rows,Cols,Cols==1?ColMajor:RowMajor,MaxRows,MaxCols>,0, InnerStride<1> > row_maj(matrix);
Then row_maj will be guaranteed to be sequentially stored in row-major order. If matrix is compatible, then no copy occurs. No branch, no SFINAE, etc.
Here matrix can be any expression, not only a Matrix<...> but also sub-matrices, Map, another Ref, etc.
To handle any expressions, just replace Rows and the likes with XprType::RowsAtCompileTime where XprType is the type of matrix.
template <class XprType>
std::vector<typename XprType::Scalar> write(const Eigen::MatrixBase<XprType>& matrix)
{...}

can boost vf2 deal with muti-graph like this situation?

I want to use vf2 in this situation.
Graph gsmall,glarge;
add_vertex(vertex_prop('a'),gsmall);
add_vertex(vertex_prop('b'),gsmall);
add_edge(0, 1, edge_prop('m'), gsmall);
add_vertex(vertex_prop('a'),glarge);
add_vertex(vertex_prop('b'),glarge);
add_edge(0, 1, edge_prop('m'), glarge);
add_edge(0, 1, edge_prop('n'), glarge);
std::cout << is_subgraph_isomorphic(gsmall,glarge) << std::endl;
If the pattern's property of edge can match with graph's part of properties of edge, then return true, but now it must match all. That example returns false. I want to make it true, so how?
Edit:
I solved this question. Use vector and overload operator "=="
http://coliru.stacked-crooked.com/a/6307210b2861bc63
But I found another problem. It will give wrong results when there is self-loops in graph.
http://coliru.stacked-crooked.com/a/46d336ecfddbbab9 is true
but http://coliru.stacked-crooked.com/a/413d56146ceffd42 is false.
I think they are both ture. I can't understand how it could be like this.
Please help me again! Thanks!
Boost can deal with it. However, you're not looking for a isomorphism in the sense of the library:
An isomorphism between two graphs G1=(V1, E1) and G2=(V2, E2) is a bijective mapping M of the vertices of one graph to vertices of the other graph that preserves the edge structure of the graphs
So, for all corresponding vertices, the same edges need to be present. In other words, the subgraph may be smaller (lower order) but each vertex must have equivalent structure (this implies the same number of edges).
In your case the small graph is structurally different because the large graph has a self loop, but the small doesn't. (The self loop is significant because both vertices exist in the subgraph).
If you really think for your purpose you need to ignore self loops, you'll have to filter them out.
Here's an example that employs the filtered_graph adaptor to achieve that:
Live On Coliru
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
template <typename SortedRange1, typename SortedRange2,
typename V = std::common_type_t<typename boost::range_value<SortedRange1>::type, typename boost::range_value<SortedRange2>::type>,
typename Cmp = std::less<V> >
static inline bool has_intersection(SortedRange1 const& a, SortedRange2 const& b, Cmp cmp = {}) {
auto equivalent = [cmp](V const& a, V const& b)
{ return !cmp(a,b) && !cmp(b,a); };
auto ai = a.begin();
auto bi = b.begin();
while (ai != a.end() && (bi = b.lower_bound(*ai)) != b.end())
if (equivalent(*ai++, *bi))
return true;
return false;
}
// Define graph type
using Label = char;
struct EdgeProperties {
using Labels = boost::container::flat_set<char, std::less<>, boost::container::small_vector<char, 3> >;
EdgeProperties(std::initializer_list<Label> elabels = {}) :_elabels(elabels) {}
bool operator==(EdgeProperties const& other) const {
return has_intersection(_elabels, other._elabels);
}
Labels _elabels;
};
typedef boost::property<boost::edge_name_t, EdgeProperties> edge_prop;
typedef boost::property<boost::vertex_name_t, long/*, boost::property<boost::vertex_index_t, int>*/ > vertex_prop;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, vertex_prop, edge_prop> Graph;
int main()
{
Graph gsmall, glarge;
add_vertex(vertex_prop('a'),gsmall);
add_vertex(vertex_prop('b'),gsmall);
add_edge(0, 1, edge_prop({'m'}), gsmall);
//add_edge(0, 0, edge_prop({'n'}), gsmall);
add_vertex(vertex_prop('a'),glarge);
add_vertex(vertex_prop('b'),glarge);
add_vertex(vertex_prop('c'),glarge);
add_edge(0, 1, edge_prop({'m'}), glarge);
add_edge(0, 0, edge_prop({'n'}), glarge);
add_edge(0, 2, edge_prop({'o'}), glarge);
// Create predicate of edge
auto edge_comp = make_property_map_equivalent(
get(boost::edge_name, gsmall),
get(boost::edge_name, glarge));
// Create callback
boost::vf2_print_callback<Graph, Graph> callback(gsmall, glarge);
struct FilterSelfEdges {
Graph const* _g;
bool operator()(Graph::edge_descriptor ed) const {
return source(ed, *_g) != target(ed, *_g);
}
};
using Filtered = boost::filtered_graph<Graph, FilterSelfEdges>;
// Execute
const bool result = boost::vf2_subgraph_iso(
gsmall, Filtered(glarge, FilterSelfEdges{&glarge}), callback, boost::vertex_order_by_mult(gsmall),
boost::edges_equivalent(edge_comp));
std::cout << "subgraph isomorphic? " << std::boolalpha << result << std::endl;
}
Prints
(0, 0) (1, 1)
subgraph isomorphic? true

Creating arrays using for loops in OpenGL

What I have to do is create a square that is made up of 8 triangles, all the same size, using arrays. The coordinates of the four corners of the square are, (-10, -10, 10), (-10, -10, -10), (10, -10, -10), (10, -10, 10). And that's starting with the upper left and going counter clockwise.
I have already created it before just entering values into the array but now I have to figure out how to do it using for loops in C++. So I know that for each array (I need to create a vertex, index and color array) I need to create a for loop and that that for loop has to have a for loop inside of it.
I like to use Eigen::Vector2f for Vec but anything with a similar interface should work:
template< typename Vec >
void glVec2d( const Vec& vec )
{
glVertex2d( vec.x(), vec.y() );
}
template< typename Vec >
void glTex2d( const Vec& vec )
{
glTexCoord2d( vec.x(), vec.y() );
}
template< typename Vec >
void glQuad2d
(
const Vec& A, // lower left coord
const Vec& B, // lower right coord
const Vec& C, // upper right coord
const Vec& D, // upper left coord
unsigned int divs = 2,
const Vec& At = Vec(0,0),
const Vec& Bt = Vec(1,0),
const Vec& Ct = Vec(1,1),
const Vec& Dt = Vec(0,1)
)
{
// base case
if( divs == 0 )
{
glTex2d( At );
glVec2d( A );
glTex2d( Bt );
glVec2d( B );
glTex2d( Ct );
glVec2d( C );
glTex2d( Dt );
glVec2d( D );
return;
}
Vec AB = (A+B) * 0.5;
Vec BC = (B+C) * 0.5;
Vec CD = (C+D) * 0.5;
Vec AD = (A+D) * 0.5;
Vec ABCD = (AB+CD) * 0.5;
Vec ABt = (At+Bt) * 0.5;
Vec BCt = (Bt+Ct) * 0.5;
Vec CDt = (Ct+Dt) * 0.5;
Vec ADt = (At+Dt) * 0.5;
Vec ABCDt = (ABt+CDt) * 0.5;
// subdivided point layout
// D CD C
//
// AD ABCD BC
//
// A AB B
// subdivide
glQuad2d( A, AB, ABCD, AD, divs - 1, At, ABt, ABCDt, ADt );
glQuad2d( AB, B, BC, ABCD, divs - 1, ABt, Bt, BCt, ABCDt );
glQuad2d( ABCD, BC, C, CD, divs - 1, ABCDt, BCt, Ct, CDt );
glQuad2d( AD, ABCD, CD, D, divs - 1, ADt, ABCDt, CDt, Dt );
}
It's currently recursive but you could always add an explicit stack for some for-loop action.

Copying from grid_graph to adjacency_list with boost::copy_graph

I'm using the boost graph library and trying to initalise a MutableGraph to start life as a grid.
Edges will be added and removed later in life, so I think adjacency_list<vecS,listS,undirectedS> is the right choice.
My reading about BGL indicated that the sensible way to initalise it with these edges would be to take advantage of boost::grid_graph by using
boost::copy_graph to copy from a boost::grid_graph that can make all the initial edges for me for free.
I thought that made sense - copy_graph copies from a model of VertexListGraph to a model of a MutableGraph, which is exactly what I have.
I initially tried using the 2-argument version of copy_graph, with the vague hope that something sensible would happen with the defaults for the rest. That turned out not to be the case, grid_graph (for reasons I couldn't quite figure out) doesn't seem to have a facility for using PropertyMaps with either edges or vertices, so the default vertex_copy and edge_copy failed (with a compiler error) copying the properties.
Since the 2-argument version clearly didn't seem appropriate I move on and tried to implement my own binary operator for copying vertices and edges. Even with a 'no-op' copy this doesn't work as well as I'd hope (i.e. it doesn't compile).
I've put together a minimum working example that illustrates the problem:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/graph/copy.hpp>
struct Position {
int x, y;
};
struct VertexProperties {
Position pos;
};
typedef boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS,
VertexProperties> Graph;
struct MyCopy {
template <typename S, typename D>
void operator()(const S& /*src*/, D& /*dest*/) {
// Nothing for now, deduced types to try and just make it compile
// TODO: set values for pos to reflect position on grid.
}
};
int main() {
boost::array<std::size_t, 2> lengths = { { 3, 3 } };
boost::grid_graph<2> grid(lengths);
Graph graph;
MyCopy copier;
// Using 3-Arg version of copy_graph so we can specify a custom way of copying to create the properties
boost::copy_graph(grid,graph,boost::bgl_named_params<MyCopy,boost::vertex_copy_t,
boost::bgl_named_params<MyCopy,boost::edge_copy_t> >(copier));
}
This example doesn't compile:
g++ -Wextra -Wall -O2 -g -o copytest.o -c copytest.cc
In file included from /usr/include/boost/graph/grid_graph.hpp:24:0,
from copytest.cc:2:
/usr/include/boost/iterator/transform_iterator.hpp: In constructor ‘boost::transform_iterator<UnaryFunction, Iterator, Reference, Value>::transform_iterator() [with UnaryFunc = boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >, Iterator = boost::counting_iterator<unsigned int, boost::use_default, boost::use_default>, Reference = boost::use_default, Value = boost::use_default]’:
/usr/include/boost/graph/copy.hpp:115:55: instantiated from ‘static void boost::detail::copy_graph_impl<0>::apply(const Graph&, MutableGraph&, CopyVertex, CopyEdge, Orig2CopyVertexIndexMap, IndexMap) [with Graph = boost::grid_graph<2u>, MutableGraph = boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties>, CopyVertex = MyCopy, CopyEdge = MyCopy, IndexMap = boost::grid_graph_index_map<boost::grid_graph<2u>, boost::array<unsigned int, 2u>, unsigned int>, Orig2CopyVertexIndexMap = boost::iterator_property_map<__gnu_cxx::__normal_iterator<void**, std::vector<void*, std::allocator<void*> > >, boost::grid_graph_index_map<boost::grid_graph<2u>, boost::array<unsigned int, 2u>, unsigned int>, void*, void*&>]’
/usr/include/boost/graph/copy.hpp:327:5: instantiated from ‘void boost::copy_graph(const VertexListGraph&, MutableGraph&, const boost::bgl_named_params<P, T, R>&) [with VertexListGraph = boost::grid_graph<2u>, MutableGraph = boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties>, P = MyCopy, T = boost::vertex_copy_t, R = boost::bgl_named_params<MyCopy, boost::edge_copy_t>]’
/mnt/home/ajw/code/hpcwales/copytest.cc:31:66: instantiated from here
/usr/include/boost/iterator/transform_iterator.hpp:100:26: error: no matching function for call to ‘boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >::grid_graph_vertex_at()’
/usr/include/boost/graph/grid_graph.hpp:104:7: note: candidates are: boost::detail::grid_graph_vertex_at<Graph>::grid_graph_vertex_at(const Graph*) [with Graph = boost::grid_graph<2u>]
/usr/include/boost/graph/grid_graph.hpp:100:33: note: boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >::grid_graph_vertex_at(const boost::detail::grid_graph_vertex_at<boost::grid_graph<2u> >&)
My analysis of that error is that it seems to be trying to default construct part of the internals of grid_graph, which can't be default constructed, for some reason that isn't terribly clear to me.
(clang doesn't really tell me anything I can't see from g++ here).
Questions:
Is this the right way to go about initalising a mutable graph to start as a regular grid? I initially thought it was much easier than writing a function to do so myself, but now I'm not so sure!
Why is the default value of orig_to_copy and/or vertex_index not appropriate here? I'm assuming that those two are the cause of the error. (Which, if any, of those is actually causing the problem? I can't decipher what the root cause of the current error is).
What is the "correct" way of fixing this?
You are on the right track, but there are two things that need to be changed in your code. The first is that there is a special method of defining custom vertex properties. The second is that there is a different syntax (more preferred and probably the only one that is correct) for BGL named parameters.
On the first item, please refer to the section of the documentation titled Custom Vertex Properties. Essentially, in order to define a custom vertex property, you need to first define a "tag type" (a struct with a name ending in _t):
struct vertex_position_t {
typedef boost::vertex_property_tag kind;
};
Then you include the tag type somewhere in the boost::property template that defines internally-stored vertex properties:
typedef boost::property<boost::vertex_index_t, std::size_t,
boost::property<vertex_position_t, Position> > VertexProperties;
The above typedef defines two internally-stored properties: index and the custom "position".
On the second item, the preferred way to use named parameters is a "method chaining-like" syntax. For example, if a function accepts two named parameters, named_param1 and named_param2, there are two functions in the boost namespace named named_param1 and named_param2, respectfully. The boost::named_param1 function accepts the value for the named_param1 parameter and returns an object having a named_param2 method (similarly, the boost::named_param2 function accepts the value for the named_param2 parameter and returns an object having a named_param1 method). You call the method to set the value for that named parameter (which in turn returns another object having methods for other supported named parameters).
In order to pass values val1 and val2 for named parameters named_param1 and named_param2, you can either use:
boost::named_parameter1(val1).named_param2(val2)
or:
boost::named_parameter2(val2).named_param1(val1)
For reference, here is a complete program that copies a grid to an object of the Graph type:
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/property_map/property_map.hpp>
struct vertex_position_t {
typedef boost::vertex_property_tag kind;
};
struct Position {
std::size_t x, y;
Position()
: x(0), y(0)
{
}
};
typedef boost::property<boost::vertex_index_t, std::size_t, boost::property<vertex_position_t, Position> > VertexProperties;
typedef boost::adjacency_list<boost::vecS, boost::listS, boost::undirectedS, VertexProperties> Graph;
typedef boost::graph_traits<Graph> GraphTraits;
namespace detail {
typedef boost::grid_graph<2> Grid;
typedef boost::graph_traits<Grid> GridTraits;
struct grid_to_graph_vertex_copier {
typedef boost::property_map< Grid, boost::vertex_index_t>::type grid_vertex_index_map;
typedef boost::property_map< ::Graph, boost::vertex_index_t>::type graph_vertex_index_map;
typedef boost::property_map< ::Graph, ::vertex_position_t>::type graph_vertex_position_map;
const Grid& grid;
grid_vertex_index_map grid_vertex_index;
graph_vertex_index_map graph_vertex_index;
graph_vertex_position_map graph_vertex_position;
grid_to_graph_vertex_copier(const Grid& grid_, Graph& graph)
: grid(grid_), grid_vertex_index(get(boost::vertex_index_t(), grid_)),
graph_vertex_index(get(boost::vertex_index_t(), graph)),
graph_vertex_position(get(::vertex_position_t(), graph))
{
}
private:
Position grid_vertex_index_to_position(std::size_t idx) const {
unsigned num_dims = grid.dimensions();
assert(grid.dimensions() == 2);
idx %= grid.length(0) * grid.length(1);
Position ret;
ret.x = idx % grid.length(0);
ret.y = idx / grid.length(0);
return ret;
}
public:
void operator()(GridTraits::vertex_descriptor grid_vertex, ::GraphTraits::vertex_descriptor graph_vertex) const {
std::size_t idx = get(grid_vertex_index, grid_vertex);
put(graph_vertex_index, graph_vertex, idx);
Position pos = grid_vertex_index_to_position(idx);
std::cout << "grid_vertex = " << idx << ", pos.x = " << pos.x << ", pos.y = " << pos.y << std::endl;
put(graph_vertex_position, graph_vertex, pos);
}
};
struct grid_to_graph_edge_copier {
void operator()(GridTraits::edge_descriptor grid_edge, ::GraphTraits::edge_descriptor graph_edge) const {
}
};
}
int main()
{
boost::array<std::size_t, 2> lengths = { { 3, 5 } };
detail::Grid grid(lengths);
Graph graph;
boost::copy_graph(grid, graph, boost::vertex_copy(detail::grid_to_graph_vertex_copier(grid, graph))
.edge_copy(detail::grid_to_graph_edge_copier()));
std::cout << std::endl;
boost::write_graphviz(std::cout, graph);
return EXIT_SUCCESS;
}
When I ran this, I received the following output:
grid_vertex = 0, pos.x = 0, pos.y = 0
grid_vertex = 1, pos.x = 1, pos.y = 0
grid_vertex = 2, pos.x = 2, pos.y = 0
grid_vertex = 3, pos.x = 0, pos.y = 1
grid_vertex = 4, pos.x = 1, pos.y = 1
grid_vertex = 5, pos.x = 2, pos.y = 1
grid_vertex = 6, pos.x = 0, pos.y = 2
grid_vertex = 7, pos.x = 1, pos.y = 2
grid_vertex = 8, pos.x = 2, pos.y = 2
grid_vertex = 9, pos.x = 0, pos.y = 3
grid_vertex = 10, pos.x = 1, pos.y = 3
grid_vertex = 11, pos.x = 2, pos.y = 3
grid_vertex = 12, pos.x = 0, pos.y = 4
grid_vertex = 13, pos.x = 1, pos.y = 4
grid_vertex = 14, pos.x = 2, pos.y = 4
graph G {
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;
12;
13;
14;
0--1 ;
1--2 ;
3--4 ;
4--5 ;
6--7 ;
7--8 ;
9--10 ;
10--11 ;
12--13 ;
13--14 ;
1--0 ;
2--1 ;
4--3 ;
5--4 ;
7--6 ;
8--7 ;
10--9 ;
11--10 ;
13--12 ;
14--13 ;
0--3 ;
1--4 ;
2--5 ;
3--6 ;
4--7 ;
5--8 ;
6--9 ;
7--10 ;
8--11 ;
9--12 ;
10--13 ;
11--14 ;
3--0 ;
4--1 ;
5--2 ;
6--3 ;
7--4 ;
8--5 ;
9--6 ;
10--7 ;
11--8 ;
12--9 ;
13--10 ;
14--11 ;
}