// // Programmer: Craig Stuart Sapp // Creation Date: Wed Jul 31 17:36:57 PDT 2002 // Last Modified: Wed Jul 31 17:37:00 PDT 2002 // Filename: ...sig/examples/all/iwsimplex.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/iwsimplex.cpp // Syntax: C++; museinfo // // Description: Interval weights optimization via the downhill simplex // method of Nelder and Mead. // #include "museinfo.h" #include #include #include #include // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void getStartingWeights (Array& weights, HumdrumFile& weightfile); void getChordInformation (Array& pitches, Array& root, Array& count, HumdrumFile& datafile); double getErrors (Array& weights, Array& pitches, Array& root, Array& count); void printWeights (Array weights); void printKern (Array& initialweights); // Downhill simplex method functions void amoeba(Matrix& simplex, double ftol, double (*testFunction)(Array&), int& evalCount, int maxtests = 5000); double amoebaTry(Matrix& simplex, Array& functionEvaluation, Array& simplexSum, double (*testFunction)(Array&), int worstPoint, double fac); double testFunction(Array& weights); double runDownhillSimplexMethod(Array& returnedWeights, Array& initialWeights); void generateSimplex(Matrix& simplex, Array& initialWeights, double delta); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug option int verboseQ = 0; // used with -v option double tolerance = 0.00000001; // used with -t option double sidedelta = 10.0; // used with -s option double initdelta = 0.01; // initial value of sidedelta int trials = 5000; // used with -r option double decay = 0.8; // used with -d option int normQ = 0; // used with -n option int norm = 25; // used with -n option int repeatcount = 5; // used with -c option // global variables for testFunction Array pitches; // pitch class set of the chords Array root; // root of the chords Array counte; // frequency of the chord occurences /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile datafile; HumdrumFile weightfile; checkOptions(options, argc, argv); // process the command-line options datafile.read(options.getArg(1)); weightfile.read(options.getArg(2)); Array initialweights; // starting weights of the search Array bestweights; Array testweights; Array currentweights; getStartingWeights(initialweights, weightfile); getChordInformation(pitches, root, counte, datafile); double starterr = getErrors(initialweights, pitches, root, counte); double besterr = starterr; double lasterr = starterr + 1; int itercount = 0; int testcount = 0; currentweights = initialweights; bestweights = currentweights; while (lasterr > besterr || testcount < repeatcount) { itercount++; if (verboseQ) { cout << "Test number " << itercount << endl; } lasterr = besterr; besterr = runDownhillSimplexMethod(testweights, currentweights); sidedelta *= decay; if (besterr < lasterr) { bestweights = testweights; testcount = 0; currentweights = bestweights; } else { testcount++; } } cout << "!! Starting Error Case:\t" << starterr << "\n"; cout << "!! Best Error Case:\t" << besterr << "\n"; cout << "!! Tolerance:\t" << tolerance << "\n"; cout << "!! Initial Delta:\t" << initdelta << "\n"; cout << "!! Current Delta:\t" << sidedelta << "\n"; cout << "!! Max Trials:\t" << trials << "\n"; cout << "!! Repeat Count:\t" << repeatcount << "\n"; printKern(bestweights); return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // printKern -- // void printKern(Array& weights) { char buffer[1024] = {0}; int i; Array w; w = weights; if (normQ) { double shift = weights[2]; double scale = weights[norm] - shift; for (i=0; i& weights, Array& pitches, Array& root, Array& counte) { Array rootscores; rootscores.setSize(40); rootscores.allowGrowth(0); int i, j, m; double errors = 0; int min; for (m=0; m rootscores[i]) { min = i; } } if (root[m] != min+2) { if (debugQ) { cout << "Error: root=" << root[m] << "\tbut measured: " << min << endl; } errors += counte[m]; } } return errors; } ////////////////////////////// // // getStartingWeights -- // void getStartingWeights(Array& weights, HumdrumFile& weightfile) { weights.setSize(40); weights.setAll(100000); weights.allowGrowth(0); int i, j; int root; double weight; for (i=0; i= 0) { weights[root] = weight; } } if (debugQ) { printWeights(weights); } } ////////////////////////////// // // printWeights -- // void printWeights(Array weights) { char buffer[128] = {0}; cout << "**kern\t**weight\n"; int i; for (i=0; i& pitches, Array& root, Array& counte, HumdrumFile& datafile) { counte.setSize(datafile.getNumLines()); counte.setSize(0); root.setSize(datafile.getNumLines()); root.setSize(0); pitches.setSize(datafile.getNumLines()); pitches.setSize(0); char buffer[1024] = {0}; int i, j, k; int troot = -1; int tpitch = -1; double tcount = 0.0; Array tpitches; tpitches.setSize(100); tpitches.setSize(0); for (i=0; i& simplex, double ftol, double (*testFunction)(Array&), int& evalCount, int maxtests) { int N = simplex.getColumnCount(); int M = N + 1; int i, j; int iworst; // simplex vertex with the highest test function evaluation int inextworst; // simplex vertex with the second highest function evaluation int ibest; // simplex vertex with the lowest test function evaluation double rtol; double swap; double ysave; double ytry; Array simplexSum(N); Array functionEvaluation(M); Array testweights; for (j=0; j functionEvaluation[1] ? (inextworst = 1, 0) : (inextworst = 0, 1); ibest = 0; for (i=0; i functionEvaluation[iworst]) { inextworst = iworst; iworst = i; } else if ((functionEvaluation[i] > functionEvaluation[inextworst]) && (i != iworst)) { inextworst = i; } } // Computer the fractional range from the highest to lowest and // return if satisfactory. rtol = 2.0*fabs(functionEvaluation[iworst] - functionEvaluation[ibest]) / (fabs(functionEvaluation[iworst]) + fabs(functionEvaluation[ibest]) + TINY); if (rtol < ftol) { SWAP(functionEvaluation[0], functionEvaluation[ibest]); for (i=0; i= maxtests) { // cout << "Process Stop : evaluation count " << evalCount // << " reached" << endl; SWAP(functionEvaluation[0], functionEvaluation[ibest]); for (i=0; i= functionEvaluation[inextworst]) { ysave = functionEvaluation[iworst]; ytry = amoebaTry(simplex, functionEvaluation, simplexSum, testFunction, iworst, 0.5); if (ytry >= ysave) { for (i=0; i& simplex, Array& functionEvaluation, Array& simplexSum, double (*testFunction)(Array&), int worstPoint, double fac) { int N = simplex.getColumnCount(); // search-space dimensions Array testPoint(N); // test point to replace worstPoint double evaluation; // function evaluation at test point double fac1 = (1.0 - fac) / N; double fac2 = fac1 - fac; int i; for (i=0; i& weights) { return getErrors(weights, pitches, root, counte); } ////////////////////////////// // // runDownhillSimplexMethod -- run the downhill simplex method // and return the best weights found by the method. // double runDownhillSimplexMethod(Array& returnedWeights, Array& initialWeights) { Matrix simplex; generateSimplex(simplex, initialWeights, sidedelta); double ftol = tolerance; int evalCount = 0; int maxtests = trials; amoeba(simplex, ftol, testFunction, evalCount, maxtests); Array testweights; simplex.getRow(testweights, 0); double best = getErrors(testweights, pitches, root, counte); double test; int ibest = 0; int i; for (i=1; i& simplex, Array& initialWeights, double delta) { int N = initialWeights.getSize(); int M = N + 1; simplex.setSize(M, N); int i; simplex.setRow(0, initialWeights); for (i=1; i