8 #ifndef INCLUDE_MOLASSEMBLER_DG_EIGEN_REFINEMENT_PROBLEM_H
9 #define INCLUDE_MOLASSEMBLER_DG_EIGEN_REFINEMENT_PROBLEM_H
11 #include <Eigen/Dense>
16 namespace Molassembler {
17 namespace DistanceGeometry {
27 template<
unsigned dimensionality,
typename FloatType,
bool SIMD>
30 dimensionality == 3 || dimensionality == 4,
31 "EigenRefinementProblem is only suitable for three or four dimensions"
38 using VectorType = Eigen::Matrix<FloatType, Eigen::Dynamic, 1>;
54 void distanceTerm(
unsigned ,
unsigned ,
double ) {}
55 void chiralTerm(
const ChiralConstraint::SiteSequence& ,
double ) {}
56 void dihedralTerm(
const DihedralConstraint::SiteSequence& ,
double ) {}
57 void fourthDimensionTerm(
unsigned ,
double ) {}
67 static std::string
name() {
68 std::string
name =
"Eigen";
70 name +=
"RefinementProblem<dimensionality=";
71 name += std::to_string(dimensionality);
74 if(std::is_same<FloatType, float>::value) {
81 name +=
", SIMD=true>";
83 name +=
", SIMD=false>";
97 return positions.template segment<dimensionality>(dimensionality * index);
108 return positions.template segment<3>(dimensionality * index);
118 const ChiralConstraint::AtomListType& atomList
120 if(atomList.size() == 1) {
126 sum += positions.template segment<dimensionality>(dimensionality * i);
129 return sum / atomList.size();
139 const ChiralConstraint::AtomListType& atomList
141 if(atomList.size() == 1) {
147 sum += positions.template segment<3>(dimensionality * i);
150 return sum / atomList.size();
180 mutable double proportionChiralConstraintsCorrectSign = 0.0;
186 const Eigen::MatrixXd& squaredBounds,
187 std::vector<ChiralConstraint> passChiralConstraints,
188 std::vector<DihedralConstraint> passDihedralConstraints
192 const unsigned N = squaredBounds.cols();
193 const unsigned strictlyUpperTriangularElements = N * (N - 1) / 2;
198 for(
unsigned linearIndex = 0, i = 0; i < N; ++i) {
199 for(
unsigned j = i + 1; j < N; ++j, ++linearIndex) {
209 for(
unsigned i = 0; i < C; ++i) {
219 for(
unsigned i = 0; i < D; ++i) {
236 Eigen::Ref<VectorType> gradient
249 Eigen::Ref<VectorType> gradient
261 Eigen::Ref<VectorType> gradient
271 Eigen::Ref<VectorType> gradient
286 assert(parameters.size() == gradient.size());
287 assert(parameters.size() % dimensionality == 0);
304 unsigned nonZeroChiralConstraints = 0;
305 unsigned incorrectNonZeroChiralConstraints = 0;
314 if(!constraint.targetVolumeIsZero()) {
315 nonZeroChiralConstraints += 1;
319 const double volume = (
330 (volume < 0 && constraint.lower > 0)
331 || (volume > 0 && constraint.lower < 0)
333 incorrectNonZeroChiralConstraints += 1;
338 if(nonZeroChiralConstraints == 0) {
339 proportionChiralConstraintsCorrectSign = 1.0;
341 proportionChiralConstraintsCorrectSign =
static_cast<double>(
342 nonZeroChiralConstraints - incorrectNonZeroChiralConstraints
343 ) / nonZeroChiralConstraints;
346 return proportionChiralConstraintsCorrectSign;
349 template<
typename Visitor>
350 auto visitTerms(
const VectorType& positions, Visitor&& visitor)
const {
351 FloatType dummyValue = 0;
353 dummyGradient.resize(positions.size());
354 dummyGradient.setZero();
380 template<
typename Visitor>
386 const unsigned N = positions.size() / dimensionality;
389 for(
unsigned i = 0; i < N; ++i) {
390 for(
unsigned j = i + 1; j < N; ++j) {
391 const double ijDistance = (
392 positions.template segment<dimensionality>(dimensionality * j)
393 - positions.template segment<dimensionality>(dimensionality * i)
397 ijDistance - bounds.
upperBound(i, j) > visitor.deviationThreshold
398 || bounds.
lowerBound(i, j) - ijDistance > visitor.deviationThreshold
400 visitor.distanceOverThreshold(i, j, ijDistance);
401 if(visitor.earlyExit) {
402 return visitor.value;
410 const double volume = (
424 volume - constraint.upper > visitor.deviationThreshold
425 || constraint.lower - volume > visitor.deviationThreshold
427 visitor.chiralOverThreshold(constraint, volume);
428 if(visitor.earlyExit) {
429 return visitor.value;
448 const double dihedral = std::atan2(
449 a.cross(b).dot(-g.normalized()),
453 const double constraintSumHalved = (constraint.lower + constraint.upper) / 2;
455 const double phi = [&]() {
456 if(dihedral < constraintSumHalved - M_PI) {
457 return dihedral + 2 * M_PI;
460 if(dihedral > constraintSumHalved + M_PI) {
461 return dihedral - 2 * M_PI;
467 const double term = std::fabs(phi - constraintSumHalved) - (constraint.upper - constraint.lower) / 2;
469 if(term > visitor.deviationThreshold) {
470 visitor.dihedralOverThreshold(constraint, dihedral, term);
471 if(visitor.earlyExit) {
472 return visitor.value;
477 return visitor.value;
487 template<
class Visitor>
491 Eigen::Ref<VectorType> gradient,
494 assert(positions.size() == gradient.size());
529 const unsigned N = positions.size() / dimensionality;
530 const unsigned differencesCount = N * (N - 1) / 2;
535 for(
unsigned i = 0; i < N - 1; ++i) {
536 auto iPosition = positions.template segment<dimensionality>(dimensionality * i);
537 for(
unsigned j = i + 1; j < N; ++j) {
538 positionDifferences.col(offset) = (
540 - positions.template segment<dimensionality>(dimensionality * j)
548 const VectorType squareDistances = positionDifferences.colwise().squaredNorm();
557 for(
unsigned i = 0; i < N - 1; ++i) {
558 for(
unsigned j = i + 1; j < N; ++j) {
560 positions.template segment<dimensionality>(dimensionality * i)
561 - positions.template segment<dimensionality>(dimensionality * j)
564 assert(positionDifferences.col(offset) == positionDifference);
566 const FloatType squareDistance = positionDifference.squaredNorm();
568 assert(squareDistance == squareDistances(offset));
573 assert(upperTerm == upperTerms(offset));
582 for(
unsigned iOffset = 0, i = 0; i < N - 1; ++i) {
583 const unsigned crossTerms = N - i - 1;
584 for(
unsigned jOffset = 0, j = i + 1; j < N; ++j, ++jOffset) {
585 const FloatType upperTerm = upperTerms(iOffset + jOffset);
587 error += upperTerm * upperTerm;
594 4 * positionDifferences.col(iOffset + jOffset) * upperTerm
599 gradient.template segment<dimensionality>(dimensionality * i) += f;
600 gradient.template segment<dimensionality>(dimensionality * j) -= f;
605 const FloatType quotient = lowerBoundSquared + squareDistances(iOffset + jOffset);
606 const FloatType lowerTerm = 2 * lowerBoundSquared / quotient - 1;
610 error += lowerTerm * lowerTerm;
614 8 * lowerBoundSquared * positionDifferences.col(iOffset + jOffset) * lowerTerm / (
620 gradient.template segment<dimensionality>(dimensionality * i) -= g;
621 gradient.template segment<dimensionality>(dimensionality * j) += g;
626 iOffset += crossTerms;
634 template<
class Visitor>
638 Eigen::Ref<VectorType> gradient,
641 assert(positions.size() == gradient.size());
642 const unsigned N = positions.size() / dimensionality;
644 for(
unsigned linearIndex = 0, i = 0; i < N - 1; ++i) {
645 for(
unsigned j = i + 1; j < N; ++j, ++linearIndex) {
648 assert(lowerBoundSquared <= upperBoundSquared);
652 positions.template segment<dimensionality>(dimensionality * i)
653 - positions.template segment<dimensionality>(dimensionality * j)
656 const FloatType squareDistance = positionDifference.squaredNorm();
659 const FloatType upperTerm = squareDistance / upperBoundSquared - 1;
662 const FloatType value = upperTerm * upperTerm;
664 visitor.distanceTerm(i, j, value);
668 gradient.template segment<dimensionality>(dimensionality * i) += f;
669 gradient.template segment<dimensionality>(dimensionality * j) -= f;
672 const FloatType quotient = lowerBoundSquared + squareDistance;
673 const FloatType lowerTerm = 2 * lowerBoundSquared / quotient - 1;
676 const FloatType value = lowerTerm * lowerTerm;
678 visitor.distanceTerm(i, j, value);
688 gradient.template segment<dimensionality>(dimensionality * i) -= g;
689 gradient.template segment<dimensionality>(dimensionality * j) += g;
691 visitor.distanceTerm(i, j, 0.0);
702 template<
typename Visitor>
706 Eigen::Ref<VectorType> gradient,
709 unsigned nonZeroChiralConstraints = 0;
710 unsigned incorrectNonZeroChiralConstraints = 0;
722 const FloatType volume = (alphaMinusDelta).dot(
723 betaMinusDelta.cross(gammaMinusDelta)
726 if(!constraint.targetVolumeIsZero()) {
727 ++nonZeroChiralConstraints;
729 ( volume < 0 && constraint.lower > 0)
730 || (volume > 0 && constraint.lower < 0)
732 ++incorrectNonZeroChiralConstraints;
737 const FloatType upperTerm =
static_cast<FloatType
>(constraint.weight) * (volume - static_cast<FloatType>(constraint.upper));
739 const FloatType value = upperTerm * upperTerm;
741 visitor.chiralTerm(constraint.sites, value);
745 const FloatType lowerTerm =
static_cast<FloatType
>(constraint.weight) * (static_cast<FloatType>(constraint.lower) - volume);
747 const FloatType value = lowerTerm * lowerTerm;
749 visitor.chiralTerm(constraint.sites, value);
752 const FloatType factor = 2 * (
753 std::max(FloatType {0}, upperTerm)
754 - std::max(FloatType {0}, lowerTerm)
766 factor * betaMinusDelta.cross(gammaMinusDelta)
767 / constraint.sites[0].size()
771 factor * gammaMinusDelta.cross(alphaMinusDelta)
772 / constraint.sites[1].size()
776 factor * alphaMinusDelta.cross(betaMinusDelta)
777 / constraint.sites[2].size()
781 factor * betaMinusGamma.cross(alphaMinusGamma)
782 / constraint.sites[3].size()
785 for(
const AtomIndex alphaI : constraint.sites[0]) {
786 gradient.template segment<3>(dimensionality * alphaI) += iContribution;
789 for(
const AtomIndex betaI : constraint.sites[1]) {
790 gradient.template segment<3>(dimensionality * betaI) += jContribution;
793 for(
const AtomIndex gammaI : constraint.sites[2]) {
794 gradient.template segment<3>(dimensionality * gammaI) += kContribution;
797 for(
const AtomIndex deltaI : constraint.sites[3]) {
798 gradient.template segment<3>(dimensionality * deltaI) += lContribution;
801 visitor.chiralTerm(constraint.sites, 0.0);
806 if(nonZeroChiralConstraints == 0) {
807 proportionChiralConstraintsCorrectSign = 1;
809 proportionChiralConstraintsCorrectSign =
static_cast<double>(
810 nonZeroChiralConstraints - incorrectNonZeroChiralConstraints
811 ) / nonZeroChiralConstraints;
818 template<
class Visitor>
822 Eigen::Ref<VectorType> gradient,
825 assert(positions.size() == gradient.size());
826 constexpr FloatType reductionFactor = 1.0 / 10;
841 const FloatType constraintSumHalved = (
static_cast<FloatType
>(constraint.lower) + static_cast<FloatType>(constraint.upper)) / 2;
842 constexpr FloatType pi {M_PI};
845 FloatType phi = std::atan2(
846 a.cross(b).dot(-g.normalized()),
850 if(phi < constraintSumHalved - pi) {
852 }
else if(phi > constraintSumHalved + pi) {
856 const FloatType w_phi = phi - constraintSumHalved;
858 FloatType h_phi = std::fabs(w_phi) - (
static_cast<FloatType
>(constraint.upper) - static_cast<FloatType>(constraint.lower)) / 2;
864 visitor.dihedralTerm(constraint.sites, 0.0);
869 const FloatType errorContribution = h_phi * h_phi * reductionFactor;
870 error += errorContribution;
871 visitor.dihedralTerm(constraint.sites, errorContribution);
874 h_phi *=
static_cast<int>(0 < w_phi) - static_cast<int>(w_phi < 0);
877 h_phi *= 2 * reductionFactor;
880 const FloatType gLength = g.norm();
882 throw std::runtime_error(
"Encountered zero-length g vector in dihedral errors");
885 const FloatType aLengthSq = a.squaredNorm();
887 throw std::runtime_error(
"Encountered zero-length a vector in dihedral gradient contributions");
890 const FloatType bLengthSq = b.squaredNorm();
892 throw std::runtime_error(
"Encountered zero-length b vector in dihedral gradient contributions");
895 const FloatType fDotG = f.dot(g);
896 const FloatType gDotH = g.dot(h);
899 -(h_phi / constraint.sites[0].size()) * gLength * a
903 (h_phi / constraint.sites[1].size()) * (
904 (gLength + fDotG / gLength) * a
905 - (gDotH / gLength) * b
910 (h_phi / constraint.sites[2].size()) * (
911 (gDotH / gLength - gLength) * b
912 - (fDotG / gLength) * a
917 (h_phi / constraint.sites[3].size()) * gLength * b
921 !iContribution.array().isFinite().all()
922 || !jContribution.array().isFinite().all()
923 || !kContribution.array().isFinite().all()
924 || !lContribution.array().isFinite().all()
926 throw std::runtime_error(
"Encountered non-finite dihedral gradient contributions");
930 for(
const AtomIndex alphaConstitutingIndex : constraint.sites[0]) {
931 gradient.template segment<3>(dimensionality * alphaConstitutingIndex) += iContribution;
934 for(
const AtomIndex betaConstitutingIndex: constraint.sites[1]) {
935 gradient.template segment<3>(dimensionality * betaConstitutingIndex) += jContribution;
938 for(
const AtomIndex gammaConstitutingIndex : constraint.sites[2]) {
939 gradient.template segment<3>(dimensionality * gammaConstitutingIndex) += kContribution;
942 for(
const AtomIndex deltaConstitutingIndex : constraint.sites[3]) {
943 gradient.template segment<3>(dimensionality * deltaConstitutingIndex) += lContribution;
952 template<
class Visitor = DefaultTermVisitor>
956 Eigen::Ref<VectorType> gradient,
957 Visitor&& visitor = {}
959 if(dimensionality != 4) {
964 const unsigned N = positions.size() / dimensionality;
966 for(
unsigned i = 0; i < N; ++i) {
967 const FloatType fourthDimensionalValue = positions(4 * i + 3);
968 const FloatType value = fourthDimensionalValue * fourthDimensionalValue;
970 visitor.fourthDimensionTerm(i, value);
971 gradient(4 * i + 3) += 2 * fourthDimensionalValue;
974 for(
unsigned i = 0; i < N; ++i) {
975 visitor.fourthDimensionTerm(i, 0.0);
986 template<
typename RefinementType>
990 template<
unsigned,
typename,
bool>
class BaseClass,
991 unsigned dimensionality,
992 typename FloatingPointType,
994 >
static auto templateArgumentHelper(BaseClass<dimensionality, FloatingPointType, SIMD> )
996 std::integral_constant<unsigned, dimensionality>,
998 std::integral_constant<bool, SIMD>
1004 using TemplateArgumentsTuple = decltype(templateArgumentHelper(std::declval<RefinementType>()));
1005 using DimensionalityConstant = std::tuple_element_t<0, TemplateArgumentsTuple>;
1006 using FloatingPointType = std::tuple_element_t<1, TemplateArgumentsTuple>;
1007 using SimdConstant = std::tuple_element_t<2, TemplateArgumentsTuple>;
double upper
Upper bound on signed volume.
Definition: DistanceGeometry.h:41
void chiralContributionsImpl(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient, Visitor &&visitor) const
Adds chiral error and gradient contributions.
Definition: EigenRefinement.h:703
Definition: DistanceBoundsMatrix.h:26
bool dihedralTerms
Whether to enable dihedral terms.
Definition: EigenRefinement.h:175
bool compressFourthDimension
Whether to compress the fourth dimension.
Definition: EigenRefinement.h:173
static ThreeDimensionalVector getAveragePosition3D(const VectorType &positions, const ChiralConstraint::AtomListType &atomList)
Calculate the average three-dimensional position of several atoms.
Definition: EigenRefinement.h:137
void dihedralContributions(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient) const
Adds dihedral error and gradient contributions.
Definition: EigenRefinement.h:258
Data struct representing a dihedral constraint.
Definition: DistanceGeometry.h:61
Eigen-based refinement error function.
Definition: EigenRefinement.h:28
VectorType chiralLowerConstraints
Chiral lower constraints, in sequence of chiralConstraints.
Definition: EigenRefinement.h:163
double calculateProportionChiralConstraintsCorrectSign(const VectorType &positions) const
Calculates the number of chiral constraints with correct sign.
Definition: EigenRefinement.h:303
void distanceContributionsImpl(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient, Visitor &&visitor) const
Adds distance error and gradient contributions (non-SIMD)
Definition: EigenRefinement.h:635
std::vector< DihedralConstraint > dihedralConstraints
List of dihedral constraints.
Definition: EigenRefinement.h:171
VectorType chiralUpperConstraints
Chiral upper constraints, in sequence of chiralConstraints.
Definition: EigenRefinement.h:161
static double & lowerBound(Eigen::Ref< Eigen::MatrixXd > matrix, const AtomIndex i, const AtomIndex j)
Uses Floyd's algorithm to smooth the matrix.
Definition: DistanceBoundsMatrix.h:33
double lower
Lower bound on signed volume.
Definition: DistanceGeometry.h:39
double lower
Lower bound on dihedral angle.
Definition: DistanceGeometry.h:68
void fourthDimensionContributions(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient) const
Adds pairwise distance error and gradient contributions.
Definition: EigenRefinement.h:268
Eigen::Matrix< FloatType, 3, 1 > ThreeDimensionalVector
Three dimensional vector.
Definition: EigenRefinement.h:41
static FullDimensionalVector getPosition(const VectorType &positions, const AtomIndex index)
Copy a full dimensional position of an atom.
Definition: EigenRefinement.h:93
VectorType lowerDistanceBoundsSquared
Lower distance bounds squared, linearized in i < j.
Definition: EigenRefinement.h:159
std::size_t AtomIndex
Unsigned integer atom index type. Used to refer to particular atoms.
Definition: Types.h:51
static double & upperBound(Eigen::Ref< Eigen::MatrixXd > matrix, const AtomIndex i, const AtomIndex j)
Uses Floyd's algorithm to smooth the matrix.
Definition: DistanceBoundsMatrix.h:41
Eigen::Matrix< FloatType, 3, Eigen::Dynamic > ThreeDimensionalMatrixType
Three-row dynamic column matrix.
Definition: EigenRefinement.h:46
auto visitUnfulfilledConstraints(const DistanceBoundsMatrix &bounds, const VectorType &positions, Visitor &&visitor) const
Visit all unfulfilled constraints.
Definition: EigenRefinement.h:381
Eigen::Matrix< FloatType, dimensionality, Eigen::Dynamic > FullDimensionalMatrixType
Three or four-row dynamic column matrix.
Definition: EigenRefinement.h:48
void distanceContributions(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient) const
Adds pairwise distance error and gradient contributions.
Definition: EigenRefinement.h:233
void chiralContributions(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient) const
Adds chiral error and gradient contributions.
Definition: EigenRefinement.h:246
Eigen::Matrix< FloatType, Eigen::Dynamic, 1 > VectorType
Vector layout of positions.
Definition: EigenRefinement.h:38
VectorType upperDistanceBoundsSquared
Upper distance bounds squared, linearized in i < j.
Definition: EigenRefinement.h:157
This is for when you have a fully qualified Refinement typename but want to get the template argument...
Definition: EigenRefinement.h:987
Visitor that can be passed to visit all terms.
Definition: EigenRefinement.h:53
VectorType dihedralConstraintDiffsHalved
Dihedral bounds upper minus lower, halved, in sequence.
Definition: EigenRefinement.h:167
void operator()(const VectorType ¶meters, FloatType &value, Eigen::Ref< VectorType > gradient) const
Calculates the error value and gradient for all contributions.
Definition: EigenRefinement.h:285
FloatType FloatingPointType
Template argument specifying floating-point type.
Definition: EigenRefinement.h:50
VectorType dihedralConstraintSumsHalved
Dihedral bounds' averages, in sequence of dihedralConstraints.
Definition: EigenRefinement.h:165
std::vector< ChiralConstraint > chiralConstraints
List of chiral constraints.
Definition: EigenRefinement.h:169
static FullDimensionalVector getAveragePosition(const VectorType &positions, const ChiralConstraint::AtomListType &atomList)
Calculate the average full-dimensional position of several atoms.
Definition: EigenRefinement.h:116
constexpr T sum(const ArrayType< T, size > &array)
Sum up all elements of an array-like class.
Definition: Containers.h:112
static std::string name()
Get a string representation of the type name.
Definition: EigenRefinement.h:67
Eigen::Matrix< FloatType, dimensionality, 1 > FullDimensionalVector
Three or four dimensional vector (depending on dimensionality)
Definition: EigenRefinement.h:43
Data struct representing a chiral constraint.
Definition: DistanceGeometry.h:32
static ThreeDimensionalVector getPosition3D(const VectorType &positions, const AtomIndex index)
Copy a three-dimensional position of an atom.
Definition: EigenRefinement.h:104
void dihedralContributionsImpl(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient, Visitor &&visitor) const
Adds dihedral error and gradient contributions.
Definition: EigenRefinement.h:819
void fourthDimensionContributionsImpl(const VectorType &positions, FloatType &error, Eigen::Ref< VectorType > gradient, Visitor &&visitor={}) const
Adds fourth dimension error and gradient contributions.
Definition: EigenRefinement.h:953
Class storing atom-pairwise distance bounds.
double upper
Upper bound on dihedral angle.
Definition: DistanceGeometry.h:70