Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/NSchertler/GeneralizedMotorcycleGraph
23 June 2024, 01:59:38 UTC
  • Code
  • Branches (4)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/deploy-linux
    • refs/heads/deploy-osx
    • refs/heads/deploy-windows
    • refs/heads/master
    No releases to show
  • 505fc29
  • /
  • src
  • /
  • ArclengthStrategyGurobi.cpp
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:b479ebf43f61ec4e9430a375ba95076208f85f8f
origin badgedirectory badge
swh:1:dir:6b077e51a4e0137699fb0df51458390d621719a6
origin badgerevision badge
swh:1:rev:a34738fe34a051760b4042dc9d740231e511fec1
origin badgesnapshot badge
swh:1:snp:a981ea1718c19c4d9cde9d807965fd6d38bebcd2

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: a34738fe34a051760b4042dc9d740231e511fec1 authored by Nico Schertler on 31 October 2020, 07:04:57 UTC
Updated access token
Tip revision: a34738f
ArclengthStrategyGurobi.cpp
#include "ArclengthStrategyGurobi.h"

#include <nsessentials/util/TimedBlock.h>
#ifdef WITH_GUROBI
#include <gurobi_c++.h>
#endif

#include <random>

ArclengthStrategyGurobi::ArclengthStrategyGurobi(const std::chrono::steady_clock::duration& discreteOptimizationTimeLimit)
	: discreteOptimizationTimeLimit(discreteOptimizationTimeLimit)
{ }

void ArclengthStrategyGurobi::CalculateParametricLengths(ParametrizationData& optData)
{
#ifdef WITH_GUROBI
	//Ratio threshold between actual halfarc length and target length. If the threshold is
	//exceeded, the arc is made a visible seam. An allowableLengthRatioError of t means that
	//the actual ratio is allowed in the range [1/t, t].
	const float allowableLengthRatioError = 2.0f;

	nse::util::TimedBlock b("Finding parametric sizes of patches using Gurobi ..");

	bool verbose = false;

	//We have one variable per halfarc, variable indices correspond to halfarc indices.
	int nVariables = optData.halfarcs.size();

	std::vector<GRBVar> variables(nVariables);
	try
	{
		//Set up the Gurobi environment
		GRBEnv env = GRBEnv();
		GRBModel model = GRBModel(env);		

		model.set(GRB_IntParam_OutputFlag, verbose ? 1 : 0);
		model.set(GRB_DoubleParam_MIPGap, 0.0025);

		auto finishBy = std::chrono::steady_clock::now() + discreteOptimizationTimeLimit;

		//Construct the objective function
		GRBQuadExpr objective = 0.0;
		
		for (int i = 0; i < optData.halfarcs.size(); ++i)
		{
			//variable for the arc length
			variables[i] = model.addVar(1.0, 5000.0, 0.0, GRB_INTEGER);
			//number of faces included in the patch
			size_t patchSize = 0;
			if (optData.halfarcs[i].face != (size_t)-1)
				patchSize = optData.patches[optData.halfarcs[i].face].Faces().size();

			//add a quadratic fitting term to the energy
			objective = objective + 
				patchSize * optData.geometricArcFitWeight[i] * (variables[i] - optData.parametricHalfarcTargetLengths[i]) * (variables[i] - optData.parametricHalfarcTargetLengths[i]);
		}		
		model.setObjective(objective, GRB_MINIMIZE);

		//constraints that keep both sides of an arc equal
		std::vector<GRBConstr> gurobiArcConstraints(optData.arcConstraints.size());	

		//Determines if an arc constraint is active
		std::vector<bool> useConstraint(optData.arcConstraints.size());

		//pentalties for breaking an arc constraint
		std::vector<double> arcConstraintPenalties(optData.arcConstraints.size());
				
		//Set up arc constraints
		for (int i = 0; i < optData.arcConstraints.size(); ++i)
		{
			auto& c = optData.arcConstraints[i];
			if (c.broken)
				continue;			
			gurobiArcConstraints[i] = model.addConstr(c.multiplier * variables[c.arc] - variables[c.arc + 1] == 0, "Arc" + std::to_string(i));
			arcConstraintPenalties[i] = optData.halfarcs[2 * i].Length();
			useConstraint[i] = true;
		}
		//Set up face constraints that keep both sides of a face equal
		std::vector<GRBConstr> gurobiFaceConstraints(optData.faceConstraints.size());
		for (int i = 0; i < optData.faceConstraints.size(); ++i)
		{
			auto& c = optData.faceConstraints[i];
			GRBLinExpr expr;
			for (int k = 0; k < 2; ++k)
			{
				double sign = (k == 0 ? +1 : -1);
				for (auto arc : c.arcs[k])
					expr = expr + sign * variables[arc];
			}
			//if both sides can grow, the face does not impose any constraints
			if (c.canGrow[0] && c.canGrow[1])
				continue;
			char sense;
			if (!c.canGrow[0] && !c.canGrow[1])
				sense = GRB_EQUAL;
			else if (c.canGrow[0])
				sense = GRB_LESS_EQUAL;
			else
				sense = GRB_GREATER_EQUAL;
			gurobiFaceConstraints[i] = model.addConstr(expr, sense, 0.0);
		}

		bool ratiosAreGood = false;
		int iterations = 0;
		while (!ratiosAreGood && iterations++ < 20)
		{
			//solve the current model
			model.set(GRB_DoubleParam_TimeLimit, std::chrono::duration_cast<std::chrono::milliseconds>(finishBy - std::chrono::steady_clock::now()).count() / 1000.0);
			model.optimize();
			auto status = model.get(GRB_IntAttr_Status);
			if (status == GRB_TIME_LIMIT)
				break;
			if (status == GRB_INFEASIBLE)
			{
				//if the model is infeasible, solve a feasibility relaxation
				std::cout << "Feasibility relaxation" << std::endl;
				GRBModel modelCopy(model);
				std::vector<GRBConstr> breakableConstraints; breakableConstraints.reserve(gurobiArcConstraints.size());
				std::vector<double> breakableConstraintPenalties; breakableConstraintPenalties.reserve(gurobiArcConstraints.size());
				for (int i = 0; i < gurobiArcConstraints.size(); ++i)
				{
					if (!useConstraint[i])
						continue;
					breakableConstraints.push_back(gurobiArcConstraints[i]);
					breakableConstraintPenalties.push_back(arcConstraintPenalties[i]);
				}
				modelCopy.feasRelax(2, false, 0, nullptr, nullptr, nullptr, breakableConstraints.size(), breakableConstraints.data(), breakableConstraintPenalties.data());
				modelCopy.optimize();
				if (modelCopy.get(GRB_IntAttr_Status) == GRB_TIME_LIMIT)
					break;

				//determine what arc constraints have been broken by the feasibility relaxation and
				//disable them
				auto feasibilityTolerance = model.get(GRB_DoubleParam_FeasibilityTol);
				for (int i = 0; i < breakableConstraints.size(); ++i)
				{
					auto& cname = breakableConstraints[i].get(GRB_StringAttr_ConstrName);
					auto ArtP = modelCopy.getVarByName("ArtP_" + cname).get(GRB_DoubleAttr_X);
					auto ArtN = modelCopy.getVarByName("ArtN_" + cname).get(GRB_DoubleAttr_X);
					if (ArtP > feasibilityTolerance || ArtN > feasibilityTolerance)
					{
						std::cout << "Broken arc " << i << std::endl;
						auto arcConstraintId = std::stoi(cname.substr(3));
						model.remove(breakableConstraints[i]);
						useConstraint[arcConstraintId] = false;
						optData.arcConstraints[arcConstraintId].broken = true;
					}
				}

				//solve again with the disabled arc constraints
				model.set(GRB_DoubleParam_TimeLimit, std::chrono::duration_cast<std::chrono::seconds>(finishBy - std::chrono::steady_clock::now()).count());
				model.optimize();
				if (model.get(GRB_IntAttr_Status) == GRB_TIME_LIMIT)
					break;
			}

			//check if actual / target length ratios are in the desired range
			float minRatio = 1000000;
			float maxRatio = 0;
			std::vector<std::pair<float, size_t>> failedMinRatioArcs;
			for (int i = 0; i < optData.halfarcs.size(); ++i)
			{
				auto ratio = (variables[i].get(GRB_DoubleAttr_X) / optData.parametricHalfarcTargetLengths[i]);
				if (ratio < minRatio)
				{
					minRatio = ratio;
					if (ratio < 1.0f / allowableLengthRatioError)
						failedMinRatioArcs.emplace_back(ratio, i);
				}
				if (ratio > maxRatio)
					maxRatio = ratio;
				//if (verbose)
				//	std::cout << "Length: " << variables[i].get(GRB_DoubleAttr_X) << " (optimal length: " << optData.halfarcs[i].parametricTargetLength << ", ratio: " << ratio << ", vertex: " << optData.mesh->from_vertex_handle(optData.graph->MotorcycleHalfedge(*optData.halfarcs[i].begin())) << ")" << std::endl;
			}
			std::cout << "Min ratio: " << minRatio << std::endl;
			std::cout << "Max ratio: " << maxRatio << std::endl;

			if (minRatio > 1.0f / allowableLengthRatioError)
			{
				ratiosAreGood = true;
				break;
			}

			//try to disable arc constraints in order to achieve better ratios
			std::sort(failedMinRatioArcs.begin(), failedMinRatioArcs.end());
			bool removedArc = false;
			for (auto& failed : failedMinRatioArcs)
			{
				if (removedArc)
					break;
				auto arcIdx = failed.second;
				auto fIdx = optData.halfarcs[arcIdx].face;
				if (fIdx == (size_t)-1)
					continue;
				auto& f = optData.patches[fIdx];
				int sideOfPatch = 0;
				for (int side = 0; side < 4; ++side)
				{
					for (auto arc : f.PatchSides()[side])
						if (arc == arcIdx)
							sideOfPatch = side;
				}
				for (auto arc : f.PatchSides()[sideOfPatch])
				{
					auto cIdx = arc / 2;
					if (!useConstraint[cIdx])
						std::cout << "Arc not constrained." << std::endl;
					else
					{
						std::cout << "Removing constraint " << cIdx << " for arc " << arc << std::endl;
						optData.arcConstraints[arc / 2].broken = true;
						model.remove(gurobiArcConstraints[cIdx]);
						removedArc = true;
						useConstraint[cIdx] = false;
					}
				}
			}
			if (!removedArc)
			{
				std::cout << "None of the arcs with bad length ratio are constrained." << std::endl;
				break;
			}
		}		

		//store the solution
		for (int i = 0; i < optData.halfarcs.size(); ++i)		
			optData.parametricHalfarcLengths[i] = variables[i].get(GRB_DoubleAttr_X);

		if (verbose)
		{
			int violatedArcConstraints = 0, violatedFaceConstraints = 0;
			for (int i = 0; i < gurobiArcConstraints.size(); ++i)
			{			
				if (!useConstraint[i])
					++violatedArcConstraints;
			}
		
			std::cout << "Violated " << violatedArcConstraints << " arc constraints." << std::endl;
			std::cout << "Obj: " << model.get(GRB_DoubleAttr_ObjVal) << std::endl;
		}
	}
	catch (GRBException e) {
		std::cout << "Error code = " << e.getErrorCode() << std::endl;
		std::cout << e.getMessage() << std::endl;
	}
	catch (std::exception& e) {
		std::cout << e.what() << std::endl;
	}
	catch (...) {
		std::cout << "Exception during optimization" << std::endl;
	}
#else
	throw std::runtime_error("Gurobi is not available.");
#endif
}

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API