//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Jul 28 21:48:39 PDT 2002
// Last Modified: Mon Jul 29 13:54:33 PDT 2002
// Filename: ...sig/examples/all/iwrange.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/iwrange.cpp
// Syntax: C++; museinfo
//
// Description: Find the independent variation in interval weights
// which yeild as good or better results than the current value.
//
#include "humdrum.h"
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <iomanip>
#define MINIM 0.00000001
typedef Array<double> ArrayDouble;
typedef Array<int> ArrayInt;
///////////////////////////////////////////////////////////////////////////
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void getStartingWeights(Array<double>& weights,
HumdrumFile& weightfile);
void getChordInformation(Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count, HumdrumFile& datafile);
double getErrors(Array<double>& weights,
Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count);
void makeRandomWeights(Array<ArrayDouble>& stepweights, double range);
void printWeights(Array<double> weights);
double getDistance(Array<double>& a, Array<double>& b);
void findRange(Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count,
Array<double>& initialweights,
int index, double& tune, double step);
void printKern(Array<double>& initialweights,
Array<double>& min, Array<double>& max);
void printPlot(Array<double>& iw, Array<double>& min,
Array<double>& max);
void printMmaPlot(Array<double>& iw, Array<double>& min,
Array<double>& max,
Array<ArrayInt> pitches,
Array<int> root,
Array<double> count);
double getMidRange(Array<double>& min, Array<double>& max,
Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count);
void printLineInfo(int cc, int ii, Array<double>& iw,
double minval, double maxval,
Array<ArrayInt> pitches, Array<int> root,
Array<double> count);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with --debug option
int verboseQ = 0; // used with -v option
double decay = 0.5; // decay of range between each step
double limit = 10.0; // maximum distance which can be searched
double step = 1.0; // maximum distance which can be searched
int plotQ = 0; // used with -p option
int bestQ = 0; // used with the -b option
int mmaQ = 0; // used with -M optnion
double baseerror = 0;
double minerror = -1;
double miderror = 0;
Array<double> bestweights;
///////////////////////////////////////////////////////////////////////////
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<double> count; // the frequency of the chords occurence
Array<int> root; // the root of the chord
Array<ArrayInt> pitches; // the pitch class set of the chord
Array<double> initialweights; // starting weights of the search
getStartingWeights(initialweights, weightfile);
getChordInformation(pitches, root, count, datafile);
bestweights = initialweights;
int i;
Array<double> min(initialweights.getSize());
Array<double> max(initialweights.getSize());
for (i=0; i<initialweights.getSize(); i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
findRange(pitches, root, count, initialweights, i, min[i], -step);
findRange(pitches, root, count, initialweights, i, max[i], step);
}
miderror = getMidRange(min, max, pitches, root, count);
if (plotQ) {
printPlot(initialweights, min, max);
} else if (mmaQ) {
printMmaPlot(initialweights, min, max, pitches, root, count);
} else {
printKern(initialweights, min, max);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// getMidRange --
//
double getMidRange(Array<double>& min, Array<double>& max,
Array<ArrayInt>& pitches, Array<int>& root, Array<double>& count) {
Array<double> weights(40);
weights.setAll(10000);
int i;
for (i=0; i<40; i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
weights[i] = (max[i] - min[i])/2.0 + min[i];
}
return getErrors(weights, pitches, root, count);
}
//////////////////////////////
//
// printMmaPlot --
//
void printMmaPlot(Array<double>& iw, Array<double>& min,
Array<double>& max, Array<ArrayInt> pitches,
Array<int> root, Array<double> count) {
int cc = 100;
double fifthname = 0;
char buffer[100] = {0};
cout << "(* Weights: line-of-fifths interval, base-40 pitch class, ";
cout << "interval abbreviation, weight *)\n";
cout << "weights = {\n";
int i;
for (i=0; i<iw.getSize(); i++) {
fifthname = Convert::base40IntervalToLineOfFifths((i-2+40)%40);
if (fifthname > 40) {
continue;
}
Convert::base40ToIntervalAbbr2(buffer, (i-2+40)%40);
cout << "{";
cout << fifthname;
cout << ", ";
cout << i;
cout << ", \"";
cout << buffer;
cout << "\", ";
cout << iw[i];
cout << "}";
if (i < iw.getSize()-1) {
cout << ",";
}
cout << "\n";
}
cout << "};\n\n";
cout << "(* Data: Line-of-Fifths interval, interval abbreviation, ";
cout << "interval weight, minimum, maximum, {improvement, base errors, ";
cout << "total chords}, {{weight, relative error},{...}} *)\n";
cout << "data = {\n";
for (i=0; i<iw.getSize(); i++) {
fifthname = Convert::base40IntervalToLineOfFifths((i-2+40)%40);
if (fifthname > 40) {
continue;
}
Convert::base40ToIntervalAbbr2(buffer, (i-2+40)%40);
cout << "{";
cout << fifthname << ", ";
cout << "\"" << buffer << "\", ";
cout << fixed << iw[i] << ", ";
cout << fixed << min[i] << ", ";
cout << fixed << max[i] << ", ";
printLineInfo(cc, i, iw, min[i], max[i], pitches, root, count);
cout << "}";
if (i < iw.getSize()-1) {
cout << ",\n";
}
}
cout << "};\n";
}
//////////////////////////////
//
// printLineInfo --
//
void printLineInfo(int cc, int ii, Array<double>& iw, double minval,
double maxval, Array<ArrayInt> pitches, Array<int> root,
Array<double> count) {
double sum = 0.0;
int i;
for (i=0; i<count.getSize(); i++) {
sum += count[i];
}
double baseerror = getErrors(iw, pitches, root, count);
double localerror;
Array<double> tempweights = iw;
double minimumerror = baseerror;
Array<double> results;
results.setSize(cc+1);
results.allowGrowth(0);
for (i=0; i<=cc; i++) {
tempweights[ii] = minval + i * (maxval - minval) / cc;
localerror = getErrors(tempweights, pitches, root, count);
results[i] = localerror;
if (localerror < minimumerror) {
minimumerror = localerror;
}
}
// print error rates and improvements
cout << "{";
cout << int(baseerror - minimumerror + 0.5);
cout << ", ";
cout << int(baseerror + 0.5);
cout << ", ";
cout << int (sum + 0.5);
cout << "}, ";
cout << "{";
for (i=0; i<=cc; i++) {
if (i==0 || i==cc || results[i] != results[i-1] || results[i] != results[i+1]) {
cout << "{";
cout << fixed << minval + i * (maxval - minval) / cc;
cout << ", ";
if (baseerror - minimumerror > 0.0) {
cout << (results[i] - minimumerror) / (baseerror - minimumerror);
} else {
cout << 1;
}
cout << "}";
if (i<cc) {
cout << ", ";
}
}
}
cout << "}";
}
//////////////////////////////
//
// printPlot --
//
void printPlot(Array<double>& iw, Array<double>& min,
Array<double>& max) {
printKern(iw, min,max);
cout << "@START: FIGURE\n\n";
int yvals[40] = {
-14, -7, 0, 7, 14, // C
1000,
-12, -5, 2, 9, 16, // D
1000,
-10, -3, 4, 11, 18, // E
-15, -8, -1, 6, 13, // F
1000,
-13, -6, 1, 8, 15, // G
1000,
-11, -4, 3, 10, 17, // A
1000,
-9, -2, 5, 12, 19 // B
};
double radius[40] = {
-0.4, -0.4, -0.4, -0.4, -0.4,
0.0,
0.4, 0.4, 0.4, 0.4, 0.4,
0.0,
-0.3, -0.3, -0.3, -0.3, -0.3,
0.3, 0.3, 0.3, 0.3, 0.3,
0.0,
-0.2, -0.2, -0.2, -0.2, -0.2,
0.0,
0.2, 0.2, 0.2, 0.2, 0.2,
0.0,
-0.1, -0.1, -0.1, -0.1, -0.1
};
int i;
for (i=0; i<40; i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
cout << "@START: PLOTDATA\n";
cout << min[i] << "\t" << yvals[i] << "\n";
cout << max[i] << "\t" << yvals[i] << "\n";
cout << "@END: PLOTDATA\n\n";
}
double curstyle = -1;
double laststyle = -1;
double currad = -1;
cout << "@START: POINTS\n";
for (i=0; i<40; i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
if (currad != fabs(radius[i])) {
currad = fabs(radius[i]);
cout << "@RADIUS: " << currad << "\n";
}
laststyle = curstyle;
curstyle = radius[i] < 0 ? 0 : 1;
if (curstyle != laststyle) {
if (curstyle == 1) {
cout << "@STYLE: circle\n";
} else {
cout << "@STYLE: opencircle\n";
}
}
cout << iw[i] << "\t" << yvals[i] << "\n";
}
cout <<"@END: FIGURE\n\n";
}
//////////////////////////////
//
// printKern --
//
void printKern(Array<double>& initialweights, Array<double>& min,
Array<double>& max) {
char buffer[1024] = {0};
cout << "!! Base Errors:\t" << baseerror << endl;
cout << "!! Best Errors:\t" << minerror << endl;
cout << "!! MidRange Errors:\t" << miderror << endl;
cout << "**kern\t**weight\t**min\t**max\t**range\t**midrange\n";
int i;
for (i=0; i<initialweights.getSize(); i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
cout << Convert::base40ToKern(buffer, i+3*40) << "\t"
<< initialweights[i] << "\t"
<< min[i] << "\t" << max[i] << "\t"
<< max[i] - min[i] << "\t"
<< (max[i] - min[i])/2 + min[i] << "\n";
}
cout << "*-\t*-\t*-\t*-\t*-\t*-\n";
if (!bestQ) {
return;
}
if (bestweights == initialweights) {
cout << "!! no better weights found\n";
return;
}
cout << "\n";
cout << "!! best weight set found:\n";
cout << "**kern\t**weight\n";
for (i=0; i<bestweights.getSize(); i++) {
if (i==5||i==11||i==22||i==28||i==34) {
continue;
}
cout << Convert::base40ToKern(buffer, i+3*40) << "\t"
<< bestweights[i] << "\n";
}
cout << "*-\t*-\n";
}
//////////////////////////////
//
// findRange --
//
void findRange(Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count, Array<double>& initialweights,
int index, double& tune, double step) {
Array<double> weights;
weights = initialweights;
baseerror = getErrors(weights, pitches, root, count);
if (minerror < 0) {
minerror = baseerror;
}
double starttune = tune;
tune = weights[index];
double newerrors = 0.0;
while ((fabs(step) > MINIM) && (fabs(tune - starttune) < limit*2)) {
newerrors = getErrors(weights, pitches, root, count);
if (newerrors < minerror) {
minerror = newerrors;
bestweights = weights;
}
if (newerrors <= baseerror) {
tune += step;
} else {
tune -= step;
step *= decay;
if (verboseQ) {
cerr << "Step set to " << step << endl;
}
}
weights[index] = tune;
if (verboseQ) {
cerr << "weight = " << tune << endl;
}
}
char buffer[32] = {0};
if (verboseQ) {
cerr << Convert::base40ToKern(buffer, index+3*40)
<< "=====================================" << endl;
}
}
//////////////////////////////
//
// getErrors -- try all chords and count how many root errors occured.
//
double getErrors(Array<double>& weights, Array<ArrayInt>& pitches,
Array<int>& root, Array<double>& count) {
Array<double> rootscores;
rootscores.setSize(40);
rootscores.allowGrowth(0);
int i, j, m;
double errors = 0;
int min;
for (m=0; m<root.getSize(); m++) {
rootscores.setAll(0.0);
for (i=0; i<rootscores.getSize(); i++) {
for (j=0; j<pitches[m].getSize(); j++) {
rootscores[i] += weights[(pitches[m][j]-i+400)%40];
}
}
min = 0;
for (i=0; i<rootscores.getSize(); i++) {
if (rootscores[min] > rootscores[i]) {
min = i;
}
}
if (root[m] != min+2) {
if (debugQ) {
cout << "Error: root=" << root[m]
<< "\tbut measured: " << min << endl;
}
errors += count[m];
}
}
return errors;
}
//////////////////////////////
//
// getDistance -- find the Euclidian distance between two vectors.
//
double getDistance(Array& a, Array& b) {
Array<double> c(40);
int i;
double sum = 0.0;
for (i=0; i<c.getSize(); i++) {
if (i==5 || i==11 || i==22 || i==28 || i == 34) {
continue;
}
c[i] = a[i] - b[i];
sum += c[i] * c[i];
}
return sqrt(sum);
}
//////////////////////////////
//
// makeRandomWeights --
//
void makeRandomWeights(Array& stepweights, double range) {
int i, j;
double delta = 0.0;
for (i=1; i<stepweights.getSize(); i++) {
for (j=0; j<stepweights[i].getSize(); j++) {
delta = drand48() * 2 * range - range;
stepweights[i][j] = stepweights[0][j] + delta;
}
}
}
//////////////////////////////
//
// 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<weightfile.getNumLines(); i++) {
root = -1;
weight = 10000;
if (!weightfile[i].isData()) {
continue;
}
for (j=0; j<weightfile[i].getFieldCount(); j++) {
if (strcmp("**kern", weightfile[i].getExInterp(j)) == 0) {
root = Convert::kernToBase40(weightfile[i][j]) % 40;
} else if (strcmp("**weight", weightfile[i].getExInterp(j)) == 0) {
weight = strtod(weightfile[i][j], NULL);
}
}
if (root >= 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<weights.getSize(); i++) {
if (i==5 || i==11 || i==22 || i==28 || i==34) {
continue; // invalid interval
}
cout << Convert::base40ToKern(buffer, i+4*40);
cout << "\t" << weights[i] << "\n";
}
cout << "*-\t*-\n";
}
//////////////////////////////
//
// getChordInformation --
//
void getChordInformation(Array<ArrayInt>& pitches, Array<int>& root,
Array<double>& count, HumdrumFile& datafile) {
count.setSize(datafile.getNumLines());
count.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<int> tpitches;
tpitches.setSize(100);
tpitches.setSize(0);
for (i=0; i<datafile.getNumLines(); i++) {
tpitches.setSize(0);
troot = -1;
tcount = 0.0;
for (j=0; j<datafile[i].getFieldCount(); j++) {
if ((datafile[i].getType() == E_humrec_none) ||
(datafile[i].getType() == E_humrec_empty) ||
(datafile[i].getType() == E_humrec_global_comment) ||
(datafile[i].getType() == E_humrec_bibliography) ) {
continue;
}
if (strcmp("**root", datafile[i].getExInterp(j)) == 0) {
troot = Convert::kernToBase40(datafile[i][j]) % 40;
} else if (strcmp("**count", datafile[i].getExInterp(j)) == 0) {
tcount = strtod(datafile[i][j], NULL);
} else if (strcmp("**kern", datafile[i].getExInterp(j)) == 0) {
int notecount = datafile[i].getTokenCount(j);
for (k=0; k<notecount; k++) {
datafile[i].getToken(buffer, j, k);
tpitch = Convert::kernToBase40(buffer);
tpitches.append(tpitch);
}
}
}
if (troot < 0 || tcount <= 0.0 || tpitches.getSize() == 0) {
continue;
}
root.append(troot);
count.append(tcount);
pitches.append(tpitches);
}
if (debugQ) {
cout << "**count\t**root\t**kern\n";
for (i=0; i<count.getSize(); i++) {
cout << count[i] << "\t";
cout << Convert::base40ToKern(buffer, root[i]+3*40) << "\t";
for (j=0; j<pitches[i].getSize(); j++) {
cout << Convert::base40ToKern(buffer, pitches[i][j]);
if (j < pitches[i].getSize() - 1) {
cout << " ";
}
}
cout << "\n";
}
cout << "*-\t*-\t*-\n";
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("d|decay=d:0.5", "decay of range between each step");
opts.define("l|limit=d:10.0", "number of trials with same errors");
opts.define("s|step=d:1.0", "number of trials with same errors");
opts.define("v|verbose=b", "monitor status");
opts.define("p|plot=b", "output format as a plot");
opts.define("b|best=b", "output best single parameter modified weights");
opts.define("M|mma=b", "Mathematica plotting data");
opts.define("debug=b", "trace input parsing");
opts.define("author=b", "author of the program");
opts.define("version=b", "compilation information");
opts.define("example=b", "example usage");
opts.define("h|help=b", "short description");
opts.process(argc, argv);
// handle basic options:
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, July 2002" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 28 July 2002" << endl;
cout << "compiled: " << __DATE__ << endl;
cout << MUSEINFO_VERSION << endl;
exit(0);
} else if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
decay = opts.getDouble("decay");
limit = opts.getDouble("limit");
step = opts.getDouble("step");
debugQ = opts.getBoolean("debug");
verboseQ = opts.getBoolean("verbose");
plotQ = opts.getBoolean("plot");
bestQ = opts.getBoolean("best");
mmaQ = opts.getBoolean("mma");
}
//////////////////////////////
//
// example -- example usage of the maxent program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: ffafec806053581db9fe92bcf9d46cdb iwrange.cpp [20050403]