[gecode-users] Question about: Float variables, Literals and/or Constants

Holger Winnemoeller holger.winnemoeller at gmail.com
Thu Dec 17 22:08:17 CET 2009


Great! Again, this was totally the solution. Thank you.

Just in case this is ever relevant to someone else, I am attaching my little
demo program that I used to test the approach.

Cheers,
Holger.

On Wed, Dec 16, 2009 at 10:53 PM, Mikael Zayenz Lagerkvist <zayenz at gmail.com
> wrote:

> Hi,
>
> The float variables are not yet truly integrated into Gecode, so I would
> not advise to use them currently.
>
> As for how to express the multiplication with a constant, I would suggest
> you use the minimodel interface:
>     post(*this, d*mutualSize == n*resultSize);
>
> Cheers,
> Mikael
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.gecode.org/pipermail/users/attachments/20091217/90328ac0/attachment.htm>
-------------- next part --------------
// 12/16/2009 By Holger Winnemoeller
// License: Do with this what you will - hope it helps...
#include <gecode/set.hh>
#include <gecode/support.hh>
#include <gecode/minimodel.hh>
#include <gecode/kernel.hh>
#include <gecode/search.hh>
#include <vector>
#include <algorithm>

#include "quotient.hpp"

using namespace std;
using namespace Gecode;

struct SubSet : public vector<int>
{
	Quotient quota;
	SubSet(){}
	SubSet(vector<int>& inventory, int takeNumItems, const Quotient& quota) : vector<int> (takeNumItems), quota(quota)
	{
		for(int i=0; i<takeNumItems; ++i) // fill data from allItems
		{
			(*this)[i] = inventory.back();
			inventory.pop_back();
			sort(begin(), end());
		}
	}
	bool contains(int val) const
	{
		for(size_t i=0; i < size(); ++i)
			if((*this)[i] == val) return true;
		return false;
	}
};

// A "Minimal" space definition for the problem
class MiniSpace: public Space
{
	SetVarArray mSetStore;
	void RequestFromSubset(SubSet& subset, IntVar& resultSetSize, int numRequestedSize)
	{
		// 1.) Create a new set domain that holds the "special" items
		IntSet specialSet(&subset[0], (int)subset.size());
		// 2.) Create a set variable that we'll associate with that set later on
		SetVar specialSelected(*this);
		// 3.) Set "specialSelected" to the intersection of whatever a possible solution is, and
		//     the "specialSet". This ensures that we can talk about items that are included
		//     in the solution, but also belong to the special set
		rel(*this, mSetStore[0], SOT_INTER, specialSet, SRT_EQ, specialSelected);
		// 4.) Now ensure that we only have "requestFromEachCategory" of these overlapping items
		IntVar intersectionSize (*this, 0, (int)subset.size());
		cardinality(*this, specialSelected, intersectionSize);

#if 1	// Using the Minimodel interface
		post(*this, subset.quota.numerator * resultSetSize == subset.quota.denominator * intersectionSize);
#else	// Using the clunky Holger way...
		const Quotient& requestFromEachCategory = subset.quota;
		IntVar lhs (*this, 0, requestFromEachCategory.denominator * (int)subset.size());				
		IntVar rhs (*this, 0, requestFromEachCategory.numerator   * numRequestedSize);	
		
		IntVar numerator(*this, requestFromEachCategory.numerator, requestFromEachCategory.numerator);
		IntVar denominator(*this, requestFromEachCategory.denominator, requestFromEachCategory.denominator);

		mult(*this, intersectionSize, denominator, lhs);				
		mult(*this, resultSetSize,    numerator,   rhs);	

		rel(*this, lhs, IRT_EQ, rhs);
#endif
		branch(*this, specialSelected, SET_VAL_MIN_INC); //  THIS IS THE SAVER!!!!!
	}

public:
	MiniSpace(int problemSize, vector<SubSet>& subsets, int numRequestedSize)
	{
		puts("Setting up problem...");
		// Create a SetVariable array (only need one element really, but this allows me to define the domain in one step)
		mSetStore = SetVarArray(*this, 1, IntSet::empty, 0, problemSize);
		SetVar resultSet = mSetStore[0]; // get the one variable that was defined in the above line

		// impose the constraint that we only want "numRequestedSize" items in the result set
		IntVar resultSetSize(*this, 0, numRequestedSize);
		cardinality(*this, resultSet, resultSetSize);
		rel(*this, resultSetSize, IRT_EQ, numRequestedSize);

		for(size_t i=0; i<subsets.size(); ++i)
			RequestFromSubset(subsets[i], resultSetSize, numRequestedSize); 

		// Given these constraints, branch
		branch(*this, mSetStore, SET_VAR_NONE, SET_VAL_MIN_INC);
	}
	MiniSpace(bool share, MiniSpace& s) : Space(share, s) {mSetStore.update(*this, share, s.mSetStore);}
	virtual Space* copy(bool share) {return new MiniSpace(share, *this);}
	void print(const vector<SubSet>& subsets) const
	{   // Print out a result set, but also a distribution of what subsets of an inventory the items belong to
		// the last number is for items that don't belong to any subset (i.e. inventory diff (union subsets))
		vector<int> scores (subsets.size()+1);
		for(size_t i=0; i<scores.size(); ++i)
			scores[i] = 0;
		int nonSubSetIdx = (int) subsets.size();
		for(SetVarGlbValues d(mSetStore[0]);d();++d)
		{
			int id = d.val();
			bool fromSet = false;
			for(size_t i=0; i<subsets.size(); ++i)
				if(subsets[i].contains(id))
				{
					scores[i]++;
					fromSet = true;
					break;
				}
			if(!fromSet)
				scores[nonSubSetIdx]++;
			std::cout << d.val() << " ";
		}
		printf(". Dist(");
		int total = 0;
		for(size_t i=0; i<scores.size(); ++i)
		{
			total += scores[i];
			printf("%s%i",i?",":"",scores[i]);
		}
		printf("), Sum = %i\n", total);

	}
};

int main(int argc, char* argv[])
{
	// Generate an inventory
	int problemSize = 3000;
	int numItemsInCategories = 200;
	int numSubCategories = 4;
	int requestedSize = 40;

	vector<int> inventory (problemSize);
	for(int i=0; i<problemSize; ++i) inventory[i] = problemSize-1-i; // init items
	
	// just to make it a bit more realistic
	random_shuffle(inventory.begin(), inventory.end()); // shuffle items

	// Float support in Gecode still experimental, apparently. Use quotients instead
	Quotient quotas [] = {Quotient(0.5), Quotient(0.25), Quotient(0.1), Quotient(0.1)};

	vector<SubSet> subsets(numSubCategories);
	for(int i=0; i<numSubCategories; ++i)
		subsets[i] = SubSet(inventory, numItemsInCategories, quotas[i]);

	// Run the problem
	MiniSpace problem (
		problemSize,	// total number of items in inventory
		subsets,		// several sub-inventories (non-overlapping) 
		requestedSize	// requesting X items
	); // space instance

	// Gimme my solutions
	DFS<MiniSpace> solver (&problem);
	int count=0;
	puts("Looking for solutions...");
	while(true){
		MiniSpace* solution = solver.next();
		if(!solution) break;
		std::cout << "Solution Nr." << count++ << std::endl;
		solution->print(subsets);
		std::cout << std::endl;
		delete solution;
	}
	std::cout << "Done..." << std::endl;
	system("pause");
	return 0;
}

-------------- next part --------------
// Conversion from float adapted from: http://stackoverflow.com/questions/1656945/how-can-i-turn-a-floating-point-number-into-the-closest-fraction-represented-by-a 
// License: whatever it says in the above link
#pragma once

#include <math.h>

struct Quotient
{
	int numerator, denominator;
	Quotient() : numerator(0), denominator(1){} // equiv = 0
	Quotient(int numerator, int denominator) : numerator(numerator), denominator(denominator){}
	Quotient (float val)
	{
		float input = val;
		int p0 = 1;
		int q0 = 0;
		int p1 = (int) floorf(input);
		int q1 = 1;
		int p2;
		int q2;

		float r = input - p1;
		float next_cf;
		while(r != 0.0f)
		{
			r = 1.0f / r;
			next_cf = floorf(r);
			p2 = (int) (next_cf * p1 + p0);
			q2 = (int) (next_cf * q1 + q0);

			// Limit the numerator and denominator to be 256 or less
			if(p2 > 256 || q2 > 256)
				break;

			// remember the last two fractions
			p0 = p1;
			p1 = p2;
			q0 = q1;
			q1 = q2;

			r -= next_cf;
		}

		input = (float) p1 / q1;
		// hard upper and lower bounds for ratio
		if(input > 256.0f)
		{
			p1 = 256;
			q1 = 1;
		}
		else if(input < 1.0f / 256.0f)
		{
			p1 = 1;
			q1 = 256;
		}
		numerator = p1;
		denominator = q1;
		float error = (toFloat() - val) / val;
		printf("%f = %i / %i with %.2f %% error\n",val, numerator, denominator, 100.0f * error);
	}
	float toFloat() const 
	{
		return (float) numerator / (float) denominator;
	}
};


More information about the users mailing list