サルでも解ける最適化問題

最適化問題用のC++のクラスを作りました。

離散的な状態に、整数のスコアが対応していて、そのスコアを最小化したいという問題。

#include "wl.h"

//スコアの関数を定義
int eval(状態);

int main(int argc, char **argv)
{

    状態=ランダムな初期値
    int val = eval(状態);
    int vmin = val; // current best

    int s_min = スコアの下限、低めにざっくり見積り
    int s_max = val; //スコアの上限をランダムな状態のスコアに設定
    // クラス初期化
    WangLandau wl(s_min, s_max);

    while(1)
    {
        状態' = 状態とちょっと違う状態
        int vnew = eval(状態');

	if (wl.Accept(val, vnew))
	{
	    // update
	    val = vnew;
	    状態 = 状態';

	    // best score?
	    if (val < vmin)
	    {
		vmin = val;
		printf("#New record %d\n", val);
	    }
	}//accept
    }//loop
}

以下クラスのソース

wl.h

class WangLandau
{
    public:


    // 1-dim constructor
    WangLandau(long long emin, long long emax,double pip = 0.2,
	    int histmin=10000);

    // constructor for multi-dimensional energy optimization
    WangLandau(int dim, long long *emin, long long *emax,double pip = 0.2,
	    int histmin=10000);

    ~WangLandau();

    // 1-dim version
    bool Accept(long long enow, long long enew);
    // n-dim version
    bool Accept(long long *enow, long long *enew);

    // save/load histogram
    void DumpHist(char *fn);
    void LoadHist(char *fn);

    void SetQuench(bool q0);

    private:

    //Energy range
    long long EMin, EMax;

    // multi-dimensional histogram
    int Dim;
    #define WLMAXDIM 3
    long long EMind[WLMAXDIM], EMaxd[WLMAXDIM];
    int ELen[WLMAXDIM];

    // LogW increase per hit
    double Pip;
    // Tabulise exp(-n*Pip)
    int *ThTab;
    void InitThTab();

    // Max table size: if n exceeds dEmax, exp(-n*Pip) is approximated by 0
    int dEmax;
    // Switch to quench mode
    bool Quench;

    // Energy histogram
    unsigned int *Hist;
    unsigned int *HistOld;
    int HistSize;
    void DumpHist1D(char *fn);
    void DumpHistnD(char *fn);
    void LoadHist1D(char *fn);
    void LoadHistnD(char *fn);
    // refresh histogram when enough hits are recorded
    int HistMin;
    int HistAdr(long long *ene);
    void ClrHist();
    void RefreshHist();

    // EMin boundary breached!
    void ExtendHist1D();
};

wl.cc

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "wl.h"

// 1D constructor
WangLandau::WangLandau(long long emin, long long emax,double pip, int histmin)
{
    Dim=1;
    EMin = emin;
    EMax = emax;
    HistSize = emax-emin+1;
    Hist = (unsigned int *)malloc(HistSize*sizeof(int));
    HistOld = (unsigned int *)malloc(HistSize*sizeof(int));
    if (Hist==NULL || HistOld==NULL)
    {
	fprintf(stderr, "#WangLandau:: Can't alloc Hist, emax-emin=%d too large\n", HistSize);
	exit(1);
    }
    ClrHist();
    Pip = pip;
    HistMin = histmin;
    Quench = false;
    dEmax = (int)(5.0/pip);
    ThTab = (int *)malloc(dEmax * sizeof(int));
    if (ThTab==NULL)
    {
	fprintf(stderr, "#WangLandau:: Can't alloc ThTab, size=%d pip %f too small\n",
		dEmax, pip);
	exit(1);
    }
    InitThTab();
};

WangLandau::~WangLandau()
{
    free(Hist);
    free(HistOld);
    free(ThTab);
};

// n-Dim constructor
WangLandau::WangLandau(int dim, long long *emin, long long *emax,
	double pip , int histmin)
{
    if (dim > WLMAXDIM)
    {
	printf("#WangLandau: dim=%d exceeds max limit and not practical\n", dim);
	exit(1);
    }
    Dim=dim;

    int i;
    HistSize = 1;
    for(i=0;i<dim;i++)
    {
	EMind[i] = emin[i];
	EMaxd[i] = emax[i];
	ELen[i] = emax[i]-emin[i]+1;
	HistSize *= ELen[i];
    }
    Hist = (unsigned int *)malloc(HistSize*sizeof(int));
    HistOld = (unsigned int *)malloc(HistSize*sizeof(int));
    if (Hist==NULL || HistOld==NULL)
    {
	fprintf(stderr, "#WangLandau:: Can't alloc Hist, size=%d too large\n", HistSize);
	exit(1);
    }
    ClrHist();

    Pip = pip;
    HistMin = histmin;
    Quench = false;
    dEmax = (int)(5.0/pip);
    ThTab = (int *)malloc(dEmax * sizeof(int));
    if (ThTab==NULL)
    {
	fprintf(stderr, "#WangLandau:: Can't alloc ThTab, size=%d pip %f too small\n",
		dEmax, pip);
	exit(1);
    }
    InitThTab();
};


void  WangLandau::SetQuench(bool q0)
{
    Quench = q0;
}

void WangLandau::ClrHist()
{
    int i;
    for(i=0;i<HistSize;i++) Hist[i]=HistOld[i]=0;
}


void WangLandau::InitThTab()
{
    int i;
    ThTab[0] = RAND_MAX/2;
    for(i=1;i<dEmax;i++)
        ThTab[i] = (int)(RAND_MAX * exp(-Pip*i));
}

void WangLandau::DumpHist(char *fn)
{
    if(Dim==1)
	DumpHist1D(fn);
    else
         DumpHistnD(fn);
}
void WangLandau::DumpHist1D(char *fn)
{
    FILE *fp=fopen(fn, "w");
    int i;
    int di = 10;// energy interval to calc temperature
    for(i=0;i<HistSize;i++)
    {
        int h = Hist[i];
        if(h==0)continue;
	double beta=0.0;
	if (i+10 <HistSize) beta = (Hist[i+di] - Hist[i])/(1.0*di);
        fprintf(fp, "%lld %d %.20e %e\n", EMin+i, h, -h*Pip, beta);
    }
    fclose(fp);
}

void WangLandau::DumpHistnD(char *fn)
{
    FILE *fp=fopen(fn, "w");
    char fn2[1024];
    sprintf(fn2,"%s-c",fn);
    FILE *fp2=fopen(fn2, "w");
    sprintf(fn2,"%s-n",fn);
    FILE *fp3=fopen(fn2, "w");

    int i, d;
    int div[WLMAXDIM];
    for(d=0;d<Dim;d++) div[d] = 1+ELen[d]/256;
    int laste1 = -1;
    for(i=0;i<HistSize;i++)
    {
        long long ee[WLMAXDIM];
        int h = Hist[i];
        int ho = HistOld[i];
	int ii = i;
	bool do_dump2 = true;
	for(d=0;d<Dim;d++)
	{
	    int e2 = ii%ELen[d];
	    long long ene = EMind[d] + e2;
	    ee[d]= EMaxd[d]-e2;
	    if ((e2 % div[d]) !=0) do_dump2=false;
	    ii /= ELen[d];
            if(h!=0)fprintf(fp, "%lld ", ene);
            if(h!=0 && h!= ho)fprintf(fp3, "%lld ", ene);
	}
	if (do_dump2)
	{
	    if(ee[1]!=laste1)
	    {
                fprintf(fp2, "\n");
	        laste1=ee[1];
	    }
	    int hh=Hist[HistAdr(ee)];
	    //if (hh!=0)
	    {
	        for(d=0;d<Dim;d++)
                    fprintf(fp2, "%lld ", ee[d]);

		// temperature
		ee[0]++;
		int dh1 = 0;
		int ad1=HistAdr(ee);
		if (ad1!=-1) dh1 = Hist[ad1]-hh;
		int dh2 = 0;
		ee[0]--;
		ee[1]++;
		ad1=HistAdr(ee);
		if (ad1!=-1) dh2 = Hist[ad1]-hh;

                fprintf(fp2, "%d %e %e", hh, dh1*Pip, dh2*Pip);
	    }
	}
	if(h!=0) fprintf(fp, "%d %.20e\n",  h, -h*Pip);
        if(h!=0 && h!= ho)fprintf(fp3, " %d\n", h-ho);
	HistOld[i]=Hist[i];
    }
    fclose(fp);
    fclose(fp2);
    fclose(fp3);
}

void WangLandau::LoadHist(char *fn)
{
    if(Dim==1)
	LoadHist1D(fn);
    else
        LoadHistnD(fn);
}

void WangLandau::LoadHist1D(char *fn)
{
    ClrHist();
    FILE *fp=fopen(fn, "r");
    char buf[256];
    while(1)
    {
	fgets(buf,255,fp);
	if (feof(fp))break;
	long long i;
	int h;
	double dd;
        sscanf(buf, "%lld %d %le", &i, &h, &dd);
	if (i>=EMin && i<=EMax)
	{
	    //Hist[i-EMin]= (int)(-dd/Pip);
	    Hist[i-EMin]= h;
	}
    }

    fclose(fp);

    int i;
    for(i=1;i<EMax-EMin+1;i++)
    {
        unsigned int h = Hist[i];
	if (h<Hist[i-1]) Hist[i]=Hist[i-1];
    }
}

int WangLandau::HistAdr(long long *ene)
{
    int d;
    int adrs=0, inc=1;
    for(d=0;d<Dim;d++)
    {
	int ee = ene[d] - EMind[d];
	if (ee<0 || ee >=ELen[d]) return -1;
	adrs += inc * ee;
	inc *= ELen[d];
    }
    return adrs;
}

void WangLandau::LoadHistnD(char *fn)
{
    ClrHist();
    FILE *fp=fopen(fn, "r");
    char buf[256];
    int nh=0, nh2=0;
    while(1)
    {
	fgets(buf,255,fp);
	if (feof(fp))break;
	long long ene[WLMAXDIM];
	int h;
	double dd;
	//Hack!
	if (Dim==2)
	    sscanf(buf, "%lld %lld %d %le", &ene[0], &ene[1], &h, &dd);
	if (Dim==3)
	    sscanf(buf, "%lld %lld %lld %d %le", &ene[0], &ene[1], &ene[2], &h, &dd);

	int adrs= HistAdr(ene);
	if (adrs== -1)continue;
	if (adrs>=HistSize)
	{
	    printf("#Its a bug!\n");exit(1);
	}
	Hist[adrs]= h;
	nh++;
	nh2+=h;
    }
    printf("#WangLandau: Hist %s Loaded %d entries %d hits\n",fn, nh, nh2);

    // Fill In

    fclose(fp);
}


// 1D
bool WangLandau::Accept(long long enow, long long enew)
{
    if (Quench)
    {
	if (enew <= enow) return true;
	if (enow == enew && rand()<RAND_MAX/2) return true;
	return false;
    }

    if (enow > EMax || enew > EMax)
    {
	if (enew < enow)
	{
	    if (enew <=EMax)
		Hist[enew-EMin]++;
	    return true;
	}
	else
	{
	    if (enow <=EMax)
		Hist[enow-EMin]++;
	    return false;
	}
    }
    if (enew<EMin) ExtendHist1D();

    int en1 = enow-EMin;
    int en2 = enew-EMin;

    int de = Hist[en2] - Hist[en1];

    if (de<0)
    {
	Hist[en2]++;
	return true;
    }
    else if (de >= dEmax)
    {
	Hist[en1]++;
	return false;
    }
    int th = ThTab[de];
    if (rand() < th)
    {
	Hist[en2]++;
	return true;
    }
    Hist[en1]++;
    return false;
};

// n-D
bool WangLandau::Accept(long long *enow, long long *enew)
{
    if (Quench)
    {
	// use ene[0]
	if (enew[0] <= enow[0]) return true;
	if (enow[0] == enew[0] && rand()<RAND_MAX/2) return true;
	return false;
    }

    int adNow = HistAdr(enow);
    int adNew = HistAdr(enew);

    //if(enew[1]!=enow[1]) printf("dE=%lld %lld  %c %c\n", enew[0]-enow[0], enew[1]-enow[1], (adNow==-1)?'O':'I', (adNew==-1)?'O':'I');

    if (adNew == -1 || adNow == -1)
    {

	if (adNow != -1)
	{
	    Hist[adNow]++;
	    return false;
	}

	int es1=0, es2=0;
	int d;
	for(d=0;d<Dim;d++)
	{
	    es1+=enow[d];
	    es2+=enew[d];
	    if(enow[d]<enew[d]) return false;
	}
	return true;

	//if (es2 <= es1) return true;
	//return false;
    }

    int de = Hist[adNew] - Hist[adNow];

    if (de<0)
    {
	Hist[adNew]++;
	return true;
    }
    else if (de >= dEmax)
    {
	Hist[adNow]++;
	return false;
    }
    int th = ThTab[de];
    if (rand() < th)
    {
	Hist[adNew]++;
	return true;
    }
    Hist[adNow]++;
    return false;
};

void WangLandau::ExtendHist1D()
{
    long long EMinOld = EMin;
    int elen = (EMax - EMin)*3/2;
    EMin = EMax +1 - elen;
    printf("#WangLandau: EMin reached, lowering EMin %lld to %lld and extending buffer %f Mb\n",
	    EMinOld, EMin,elen*4.0/1e6);
    unsigned int *newHist = (unsigned int *)malloc(elen*sizeof(int));
    int i;
    for(i=0;i<elen;i++) newHist[i]=0;

    // migrate data
    for(i=EMinOld;i<=EMax;i++)
	newHist[i-EMin] = Hist[i-EMinOld];

    free(Hist);
    Hist = newHist;
}