From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- .../adaptagrams/libcola/gradient_projection.cpp | 482 +++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 src/3rdparty/adaptagrams/libcola/gradient_projection.cpp (limited to 'src/3rdparty/adaptagrams/libcola/gradient_projection.cpp') diff --git a/src/3rdparty/adaptagrams/libcola/gradient_projection.cpp b/src/3rdparty/adaptagrams/libcola/gradient_projection.cpp new file mode 100644 index 0000000..b53e876 --- /dev/null +++ b/src/3rdparty/adaptagrams/libcola/gradient_projection.cpp @@ -0,0 +1,482 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libcola - A library providing force-directed network layout using the + * stress-majorization method subject to separation constraints. + * + * Copyright (C) 2006-2008 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of 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. + * See the file LICENSE.LGPL distributed with the library. + * + * 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. + * +*/ + +/********************************************************** + * + * Solve a quadratic function f(X) = X' A X + b X + * subject to a set of separation constraints cs + * + **********************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "libcola/cluster.h" +#include "libcola/gradient_projection.h" +#include "libcola/straightener.h" +#include "libcola/commondefs.h" +//#define CHECK_CONVERGENCE_BY_COST 1 + + +using namespace std; +using namespace vpsc; +namespace cola { +GradientProjection::GradientProjection( + const Dim k, + std::valarray *denseQ, + const double tol, + const unsigned max_iterations, + CompoundConstraints const *ccs, + UnsatisfiableConstraintInfos *unsatisfiableConstraints, + NonOverlapConstraintsMode nonOverlapConstraints, + RootCluster* clusterHierarchy, + vpsc::Rectangles* rs, + const bool scaling, + SolveWithMosek solveWithMosek) + : k(k), + denseSize(static_cast((floor(sqrt(static_cast(denseQ->size())))))), + denseQ(denseQ), + rs(rs), + ccs(ccs), + unsatisfiableConstraints(unsatisfiableConstraints), + nonOverlapConstraints(nonOverlapConstraints), + clusterHierarchy(clusterHierarchy), + tolerance(tol), + max_iterations(max_iterations), + sparseQ(nullptr), + solveWithMosek(solveWithMosek), + scaling(scaling) +{ + //printf("GP Instance: scaling=%d, mosek=%d\n",scaling,solveWithMosek); + for(unsigned i=0;iscale=1./sqrt(fabs((*denseQ)[i*denseSize+i])); + // XXX: Scale can sometimes be set to infinity here when + // there are nodes not connected to any other node. + // Thus we just set the scale for such a variable to 1. + if (!std::isfinite(vars[i]->scale)) + { + vars[i]->scale = 1; + } + } + // the following computes S'QS for Q=denseQ + // and S is diagonal matrix of scale factors + for(unsigned i=0;iscale + *vars[j]->scale; + } + } + this->denseQ = &scaledDenseQ; + } + //dumpSquareMatrix(*this->denseQ); + //dumpSquareMatrix(scaledDenseQ); + + if(ccs) { + for(CompoundConstraints::const_iterator c=ccs->begin(); + c!=ccs->end();++c) { + (*c)->generateVariables(k, vars); + OrthogonalEdgeConstraint* e=dynamic_cast(*c); + if(e) { + orthogonalEdges.push_back(e); + } + } + for(CompoundConstraints::const_iterator c=ccs->begin(); + c!=ccs->end();++c) { + (*c)->generateSeparationConstraints(k, vars, gcs, *rs); + } + } + /* + if(clusterHierarchy) { + clusterHierarchy->createVars(k,*rs,vars); + } + */ + numStaticVars=vars.size(); + //solver=setupVPSC(); +} +static inline double dotProd(valarray const & a, valarray const & b) { + double p = 0; + for (unsigned i=0; i const &b, + valarray const &x) const { + // computes cost = 2 b x - x A x + double cost = 2. * dotProd(b,x); + valarray Ax(x.size()); + for (unsigned i=0; i r(x.size()); + sparseQ->rightMultiply(x,r); + Ax+=r; + } + return cost - dotProd(x,Ax); +} + +double GradientProjection::computeSteepestDescentVector( + valarray const &b, + valarray const &x, + valarray &g) const { + // find steepest descent direction + // g = 2 ( b - A x ) + // where: A = denseQ + sparseQ + // g = 2 ( b - denseQ x) - 2 sparseQ x + // + // except the 2s don't matter because we compute + // the optimal stepsize anyway + COLA_ASSERT(x.size()==b.size() && b.size()==g.size()); + g = b; + for (unsigned i=0; i r(x.size()); + sparseQ->rightMultiply(x,r); + g-=r; + } + return computeStepSize(g,g); +} +// compute optimal step size along descent vector d relative to +// a gradient related vector g +// stepsize = ( g' d ) / ( d' A d ) +double GradientProjection::computeStepSize( + valarray const & g, valarray const & d) const { + COLA_ASSERT(g.size()==d.size()); + valarray Ad; + if(sparseQ) { + Ad.resize(g.size()); + sparseQ->rightMultiply(d,Ad); + } + double const numerator = dotProd(g, d); + double denominator = 0; + for (unsigned i=0; i & result) { + bool activeConstraints = false; + switch(solveWithMosek) { + case Off: + activeConstraints = solver->satisfy(); + for (unsigned i=0;ifinalPosition; + } + break; + case Inner: +#ifdef MOSEK_AVAILABLE + float *ba=new float[vars.size()]; + float *coords=new float[vars.size()]; + for(unsigned i=0;idesiredPosition; + } + mosek_quad_solve_sep(menv,ba,coords); + for(unsigned i=0;i const &linearCoefficients, + valarray &x) { + COLA_ASSERT(linearCoefficients.size()==x.size()); + COLA_ASSERT(x.size()==denseSize); + COLA_ASSERT(numStaticVars>=denseSize); + COLA_ASSERT(sparseQ==nullptr || + (sparseQ!=nullptr && (vars.size()==sparseQ->rowSize())) ); + + if(max_iterations==0) return 0; + + bool converged=false; + + solver = setupVPSC(); +#ifdef MOSEK_AVAILABLE + if(solveWithMosek==Outer) { + float* ba=new float[vars.size()]; + float* xa=new float[vars.size()]; + for(unsigned i=0;i b(n); + result.resize(n); + + // load desired positions into vars, note that we keep desired positions + // already calculated for dummy vars + for (unsigned i=0;iscale; + result[i]/=vars[i]->scale; + } + if(!vars[i]->fixedDesiredPosition) vars[i]->desiredPosition=result[i]; + } + runSolver(result); + + valarray g(n); /* gradient */ + valarray previous(n); /* stored positions */ + valarray d(n); /* actual descent vector */ + +#ifdef CHECK_CONVERGENCE_BY_COST + double previousCost = DBL_MAX; +#endif + unsigned counter=0; + double stepSize; + for (; counterweight; + result[i]+=step; + //printf(" after unconstrained step: x[%d]=%f\n",i,result[i]); + stepSize+=step*step; + COLA_ASSERT(!std::isnan(result[i])); + COLA_ASSERT(std::isfinite(result[i])); + if(!vars[i]->fixedDesiredPosition) vars[i]->desiredPosition=result[i]; + } + + //project to constraint boundary + bool constrainedOptimum = false; + constrainedOptimum=runSolver(result); + stepSize=0; + for (unsigned i=0;i1! + if(constrainedOptimum) { + // The following step limits the step-size in the feasible + // direction + d = result - previous; + const double beta = 0.5*computeStepSize(g, d); + // beta > 1.0 takes us back outside the feasible region + // beta < 0 clearly not useful and may happen due to numerical imp. + //printf("beta=%f\n",beta); + if(beta>0&&beta<0.99999) { + stepSize=0; + for (unsigned i=0; icost); + if(fabs(previousCost - cost) < tolerance) { + converged = true; + } + previousCost = cost; + //} +#else + if(stepSizescale; + } + } + destroyVPSC(solver); + return counter; +} +// Setup an instance of the Variable Placement with Separation Constraints +// for one iteration. +// Generate transient local constraints --- such as non-overlap constraints +// --- that are only relevant to one iteration, and merge these with the +// global constraint list (including alignment constraints, +// dir-edge constraints, containment constraints, etc). +IncSolver* GradientProjection::setupVPSC() { + if(nonOverlapConstraints!=None) { + if(clusterHierarchy) { + //printf("Setup up cluster constraints, dim=%d--------------\n",k); + //clusterHierarchy->generateNonOverlapConstraints(k,nonOverlapConstraints,*rs,vars,lcs); + } else { + for(vector::iterator i=orthogonalEdges.begin();i!=orthogonalEdges.end();i++) { + OrthogonalEdgeConstraint* e=*i; + e->generateTopologyConstraints(k,*rs,vars,lcs); + } + if(k==HORIZONTAL) { + // Make rectangles a little bit wider when processing horizontally so that any overlap + // resolved horizontally is strictly non-overlapping when processing vertically + Rectangle::setXBorder(0.0001); + // use rs->size() rather than n because some of the variables may + // be dummy vars with no corresponding rectangle + generateXConstraints(*rs,vars,lcs,nonOverlapConstraints==Both?true:false); + Rectangle::setXBorder(0); + } else { + generateYConstraints(*rs,vars,lcs); + } + } + } + cs=gcs; + cs.insert(cs.end(),lcs.begin(),lcs.end()); + switch(solveWithMosek) { + case Off: + break; +#ifdef MOSEK_AVAILABLE + case Inner: + menv = mosek_init_sep_ls(vars.size(),cs); + break; + case Outer: + unsigned n = vars.size(); + float* lap = new float[n*(n+1)/2]; + unsigned k=0; + for(unsigned i=0;ibegin(); + c!=ccs->end();++c) { + (*c)->updatePosition(vpsc::XDIM); + (*c)->updatePosition(vpsc::YDIM); + } + } + if(unsatisfiableConstraints) { + unsatisfiableConstraints->clear(); + for(Constraints::iterator i=cs.begin();i!=cs.end();i++) { + Constraint* c=*i; + if(c->unsatisfiable) { + UnsatisfiableConstraintInfo *ci = new UnsatisfiableConstraintInfo(c); + unsatisfiableConstraints->push_back(ci); + } + } + } + if(clusterHierarchy) { + clusterHierarchy->computeBoundary(*rs); + } + if(sparseQ) { + for(unsigned i=numStaticVars;i::iterator i=lcs.begin();i!=lcs.end();i++) { + delete *i; + } + lcs.clear(); + delete vpsc; +#ifdef MOSEK_AVAILABLE + if(solveWithMosek!=Off) mosek_delete(menv); +#endif +} +void GradientProjection::straighten( + cola::SparseMatrix const * Q, + vector const & cs, + vector const & snodes) +{ + COLA_ASSERT(Q->rowSize()==snodes.size()); + COLA_ASSERT(vars.size()==numStaticVars); + sparseQ = Q; + for(unsigned i=numStaticVars;ipos[k],1); + COLA_ASSERT(v->desiredPosition==snodes[i]->pos[k]); + vars.push_back(v); + } + COLA_ASSERT(lcs.size()==0); + for(vector::const_iterator i=cs.begin();i!=cs.end();i++) { + (*i)->generateSeparationConstraints(k, vars, lcs, *rs); + } +} +} // namespace cola -- cgit v1.2.3