public bool PlaceIcons(DBSpecGen.DBSpecGen ui) { _explored = 0; DateTime start = DateTime.Now; InitializeIcons(); InitializePossibleLocations(); bool cancel = false; if (_bVerbose) { ulong configs = configurations(_iconsPerRow * _numSiteRows, _icons.Count); DBSpecGen.DBSpecGen.ShowProgress("Generating datamodel diagram: " + _icons.Count + " icons, " + (_iconsPerRow * _numSiteRows) + " locations.", -1, false, ui, out cancel); DBSpecGen.DBSpecGen.ShowProgress(" possible configurations: " + ((configs == 0) ? "more than 18446744073709551615 (ulong overflow)" : configs.ToString(CultureInfo.CurrentCulture)), -1, false, ui, out cancel); if (cancel) { DBSpecGen.DBSpecGen.ShowProgress("Operation canceled", 0, true, ui, out cancel); return(false); } } InitializeLinkedIcons(_icons); _initialized = true; Randomize(_icons); // use simulated annealing here to try and minimize both the // length of the lines connecting the icons, and the number of // crossings of the line segments... if (this._numLinkedIcons > 0) { if (anneal(_icons, ui) == -1) { return(false); } } // move the diagram up as far as we can into the upper left corner. ShiftDiagram(); // put the icons with no links along the bottom. PlaceUnlinkedIcons(); // set the size of out canvas. SetCanvasBoundaries(); if (_bVerbose) { DBSpecGen.DBSpecGen.ShowProgress(" finished, time: " + (DateTime.Now - start), -1, false, ui, out cancel); DBSpecGen.DBSpecGen.ShowProgress(" explored configurations: " + _explored, -1, false, ui, out cancel); if (cancel) { DBSpecGen.DBSpecGen.ShowProgress("Operation canceled", 0, true, ui, out cancel); return(false); } } return(true); }
private double anneal(SortedList icons, DBSpecGen.DBSpecGen ui) { double lowestEnergy = energy(icons); double currentEnergy = lowestEnergy; double delta = 0; int lastCost = Cost(icons); int variationLimit = 30 * _numLinkedIcons; // maximum number of variations at a fixed temperature int successLimit = 10 * _numLinkedIcons; // maximum number of successful variations at a fixed temp // first we need to figure out what the starting temperature should be. // this is the average difference in length for a few different configurations. // let's take 10 configurations, compute the delta from one to the next, and // take that average. double avg = 0; int numConfigs = 10; Point dummy = null; double energy1 = 0; SortedList iconsCopy = icons; double energy2 = energy(iconsCopy); for (int i = 0; i < numConfigs; ++i) { energy1 = energy2; wiggle(iconsCopy, ref dummy); energy2 = energy(iconsCopy); avg += Math.Abs(energy1 - energy2); } bool cancel = false; if (_bVerbose) { DBSpecGen.DBSpecGen.ShowProgress(" successLimit, variationLimit: " + successLimit + "," + variationLimit, -1, false, ui, out cancel); if (cancel) { DBSpecGen.DBSpecGen.ShowProgress("Operation canceled", 0, true, ui, out cancel); return(-1); } } // ok, let's set the temperature at the // average difference of these configurations double temperature = avg / numConfigs; // keep track of number of zero, positive, // and negative energy steps int numDeltaZero = 0; int numDeltaPlus = 0; int numDeltaMinus = 0; double avgEnergyUp = 0; double avgEnergyDown = 0; // take no more than 100 temperature steps... for (int tempStep = 0; tempStep < 100; ++tempStep) { // to print out the diagram as it cools so we can look at intermediate states. // need to set a breakpoint here if you want to do this. should comment out for release. //string s = "<html xmlns:v='urn:schemas-microsoft-com:vml'><body>" + GenerateHtmlString() + "</body></html>"; //System.IO.StreamWriter sw = System.IO.File.CreateText("dork.htm"); //sw.Write(s); //sw.Close(); // decrease the temperature temperature *= .90; // how many successful moves we have made at this temperature int successfulMoves = 0; int failedMoves = 0; numDeltaZero = 0; numDeltaPlus = 0; numDeltaMinus = 0; avgEnergyUp = 0; avgEnergyDown = 0; // for each temperature, try variationLimit different moves for (int i = 0; i < variationLimit; ++i) { // wiggle the current configuration... Point movedFrom = null; Icon[] iconsMoved = wiggle(icons, ref movedFrom); _explored++; // ok, we moved an icon. should we keep it? currentEnergy = energy(icons); delta = currentEnergy - lowestEnergy; double probability = _random.NextDouble(); double boltzmann = Math.Exp(-delta / temperature); // here's the key. we never move back if delta is negative. // but sometimes, we even keep it if delta is positive. // the probability that we keep an uphill evolution // depends exponentially on how big delta is compared // with the temperature bool bMoveBack = (probability < boltzmann) ? false : true; if (bMoveBack) { // ah well. didn't work this time. ++failedMoves; // move the icon back where it was. if (iconsMoved[1] != null) { SwapIcons(iconsMoved[0], iconsMoved[1], icons); } else { MoveIcon(iconsMoved[0], movedFrom, icons); } continue; } else { if (delta == 0) { ++numDeltaZero; } else if (delta < 0) { ++numDeltaMinus; avgEnergyDown += delta; } else if (delta > 0) { ++numDeltaPlus; avgEnergyUp += delta; } lowestEnergy = currentEnergy; ++successfulMoves; if (successfulMoves > successLimit) { break; } } } if (_bVerbose) { DBSpecGen.DBSpecGen.ShowProgress(" (+[avg],-[avg],0,T,E) = (" + numDeltaPlus + "[" + (int)(avgEnergyUp / numDeltaPlus) + "], " + numDeltaMinus + "[" + (int)(avgEnergyDown / numDeltaMinus) + "], " + numDeltaZero + ", " + (int)temperature + ", " + (int)lowestEnergy + ")", -1, false, ui, out cancel); if (cancel) { DBSpecGen.DBSpecGen.ShowProgress("Operation canceled", 0, true, ui, out cancel); return(-1); } } // if we didn't get very many successful moves // at this temperature, let's give up. we are cold enough. // that is, when the temperature is very low, we almost never // take an uphill step, so if we are near a minimum, // we will be trapped in it if the tempertature is low. //if ((double)successfulMoves / (double)successLimit < .1) if ((double)numDeltaMinus / (double)successLimit < .1) { break; } /* * // see if most of the moves were zero delta. zero delta moves * // happen a lot once the configuration is pretty much frozen, * // where you might have one icon flipping back and forth * // between equivalent energy states. basically we are in * // a metastable state, where there is no cost for an icon * // hopping back and forth. * if (numDeltaZero > numDeltaMinus) * { * break; * } */ } return(temperature); }