public static IterationSnapshot Combine(IterationSnapshot[] snapshots) { var r = new IterationSnapshot(); r.IterCount = snapshots[0].IterCount; r.IterCurrent = snapshots[0].IterCurrent; r.GenCount = snapshots[0].GenCount; r.StepCount = snapshots[0].StepCount; r.pBestCount = new int[snapshots[0].StepCount, snapshots[0].GenCount]; r.pCount = new int[snapshots[0].StepCount, snapshots[0].GenCount]; r.GBestFitness = float.PositiveInfinity; r.ParticleCount = 0; for (int iShot = 0; iShot < snapshots.Length; iShot++) { r.ParticleCount += snapshots[iShot].ParticleCount; if (r.GBestFitness > snapshots[iShot].GBestFitness) { r.GBest = snapshots[iShot].GBest; r.GBestFitness = snapshots[iShot].GBestFitness; } for (int iStep = 0; iStep < r.StepCount; iStep++) { for (int iGen = 0; iGen < r.GenCount; iGen++) { r.PBestCount[iStep, iGen] += snapshots[iShot].PBestCount[iStep, iGen]; r.PCount[iStep, iGen] += snapshots[iShot].PCount[iStep, iGen]; } } } return(r); }
private static int[] UCPExecutePSORuns( MGInput m, Options o, float[] p_target, OnNewSnapshotCallback progressCallback) { Random random = o.randomSeed.HasValue ? new Random(o.randomSeed.Value) : new Random(); int runCount = o.runCount; int iterCount = o.iterCount; int pCount = o.particleCount; int stepCount = o.stepCount; int stepSize = m.tCount / stepCount; int[] rBest = new int[stepCount]; float rBestFitness = float.MaxValue; float totalIter = runCount * iterCount; var threads = new Thread[runCount]; var results = new PSOResult[runCount]; Dictionary <StateStep, float> cDict; cDict = MGHelper.ConstructCostDictionary(m, stepCount, stepSize, 400.0f, p_target); // Precompute an array that maps generator indices to the operating // costs for that generator if it ran at maximum power for a // timestep. float[] c_thr_max_1m = MGHelper.ThermalGenerator.GetCostAtMaxPower( m.genCount, m.p_thr_max, m.thr_c_a, m.thr_c_b, m.thr_c_c, stepSize); var lockObject = new object(); var iterLatest = new int[runCount]; var progressPerRun = new IterationSnapshot[runCount, iterCount]; for (int iRun = 0; iRun < runCount; iRun++) { iterLatest[iRun] = -1; } void progressInner( int runIndex, int iterIndex, IterationSnapshot progressStruct) { lock (lockObject) { iterLatest[runIndex] = iterLatest[runIndex] < iterIndex ? iterIndex : iterLatest[runIndex]; progressPerRun[runIndex, iterIndex] = progressStruct; progressCallback?.Invoke( runIndex, iterIndex, (int[])iterLatest.Clone(), (IterationSnapshot[, ])progressPerRun.Clone()); } } #if !UNITY_WEBGL || (UNITY_WEBGL && UNITY_SSM_WEBGL_THREADING_CAPABLE) // Run the PSO multiple times in parallel. for (int iRun = 0; iRun < runCount; iRun++) { int _iRun = iRun; threads[_iRun] = new Thread(() => { var r = random.Next(); results[_iRun] = UCPExecutePSORun( _iRun, m, o, r, p_target, c_thr_max_1m, cDict, progressInner); }); threads[_iRun].IsBackground = true; threads[_iRun].Start(); } for (int iRun = 0; iRun < threads.Length; iRun++) { threads[iRun].Join(); } #else for (int iRun = 0; iRun < runCount; iRun++) { var r = random.Next(); results[iRun] = UCPExecutePSORun( iRun, m, o, r, p_target, c_thr_max_1m, cDict, progressInner); } #endif // Pick the best run. for (int iRun = 0; iRun < runCount; iRun++) { PSOResult result = results[iRun]; if (result.bestFitness < rBestFitness) { rBestFitness = result.bestFitness; Array.Copy(result.bestPos, rBest, stepCount); } } return(rBest); }
private static PSOResult UCPExecutePSORun( int runIndex, MGInput mg, Options OptsPSO, int randomSeed, float[] p_target, float[] c_thr_max_1m, Dictionary <StateStep, float> cDict, OnNewSnapshotInnerCallback progressCallback = null) { var random = new Random(randomSeed); int iterCount = OptsPSO.iterCount; int pCount = OptsPSO.particleCount; int stepCount = OptsPSO.stepCount; int stepSize = mg.tCount / stepCount; // The power demand that needs to be fulfilled at each time step. float[] p_target_step_max = new float[stepCount]; // The min. downtime of each generator normalized to our time step. float[] dtime_norm = new float[mg.genCount]; // The min. uptime of each generator normalized to our time step. float[] utime_norm = new float[mg.genCount]; // Initialize min. downtime and uptime arrays. for (int iGen = 0; iGen < mg.genCount; iGen++) { dtime_norm[iGen] = mg.thr_min_dtime[iGen] / stepSize; utime_norm[iGen] = mg.thr_min_utime[iGen] / stepSize; } // Iterate through all time steps and initialize p_target_step_max. for (int iStep = 0; iStep < stepCount; iStep++) { // Start time of the current step. int t1 = iStep * stepSize; // End time of the current step. int t2 = (iStep + 1) * stepSize - 1; // Sum up the power demand for every real time unit across // our time step. for (int t = t1; t < t2; t++) { p_target_step_max[iStep] += UnityEngine.Mathf.Max( p_target_step_max[iStep], p_target[t]); } } // Alpha - a criterion variable for how cost efficient a generator is. // Initialize an array to keep track of the alpha of every generator. float[] thr_alpha = MGHelper.ThermalGenerator.GetAlpha( mg.genCount, mg.p_thr_max, mg.thr_c_a, mg.thr_c_b, mg.thr_c_c); // Get an array of the indices of generators, sorted by their alpha. // In other words, if the 5th generator has the 2nd lowest alpha, // then thr_sorted_by_alpha[2] == 5. var thr_sorted_by_alpha = thr_alpha .Select(x => thr_alpha.OrderBy(alpha => alpha).ToList() .IndexOf(x)).ToArray(); // Initialize an array for our particles. var p = InitParticles(pCount, mg.genCount, stepCount, random); // Initialize an array for keeping track of the global best at each time step. var gBest = new int[stepCount]; // Variable for keeping track of the global best. float gBestFitness = float.MaxValue; // How many iterations have past since we've improved our global best. int iterSinceLastGBest = -1; // Go through all of our iterations, updating the particles each time. for (int iIter = 0; iIter < iterCount; iIter++) { iterSinceLastGBest++; // Percent of iterations completed. float completion = iIter / (float)iterCount; // Get the probability of inertia being applied to the // particle's velocity update. // // This probability decreases with each iteration, effectively // slowing down particles. float prob_inertia = UnityEngine.Mathf.Lerp( 1.0f, 0.35f, completion); // Get the probability of the particle's velocity being updated // to a random value. // // This probability increases with each iteration the swarm // goes through without finding a better global best. float prob_craziness = UnityEngine.Mathf.Lerp( 0.005f, 0.50f, iterSinceLastGBest / 100.0f); // Determines if the current iteration is an iteration where // it's possible to apply a heuristic adjustment to the swarm. // // This allows us to apply the adjustment only every n iterations. bool isHeuristicIter = iIter % 10 == 0; // Probability of a particle undergoing heuristic adjustment // when the iteration is a heuristic adjustment enabled // iteration. // // This allows us to control, on average, what percentage of // particles get affected by the heuristic adjustment. float prob_heuristic = 0.25f; // Step 1. // Iterate through all particles, evaluating their fitness // and establishing their personal best. for (int iP = 0; iP < pCount; iP++) { float fitness = GetFitness(p[iP].pos, stepCount, cDict); if (fitness < p[iP].fitnessBest) { p[iP].fitnessBest = fitness; Array.Copy(p[iP].pos, p[iP].pBest, stepCount); } } // Step 2. // Iterate through all particles, establishing the global best // in the swarm. for (int iP = 0; iP < pCount; iP++) { if (p[iP].fitnessBest < gBestFitness) { gBestFitness = p[iP].fitnessBest; Array.Copy(p[iP].pBest, gBest, stepCount); iterSinceLastGBest = 0; } } // Step 3. // Update the velocity and position of every particle. // // Optionally apply a heuristic adjustment aimed at speeding // the convergence of the swarm. // // Revert state switches of generators that would violate the // min/max uptime constraints of that generator. This is // necessary because the particles have no knowledge of those // constraionts. So we deal with that simply by undoing // illegal state switches. for (int iP = 0; iP < pCount; iP++) { Binary.UpdateVel( p[iP].pos, p[iP].vel, p[iP].pBest, gBest, prob_inertia, prob_craziness, mg.genCount, random); Binary.UpdatePos(p[iP].pos, p[iP].vel); /* * if (isHeuristicIter) * { * if (random.NextDouble() < prob_heuristic) * { * HeuristicAdjustment( * mg.genCount, * stepCount, * stepSize, * p[iP].pos, * thr_sorted_by_alpha, * mg.p_thr_max, * p_target_step_max); * } * } */ MGHelper.FixMinTimeConstraints( p[iP].pos, stepCount, mg.genCount, dtime_norm, utime_norm); } var snapshot = new IterationSnapshot( iIter, iterCount, stepCount, mg.genCount, gBestFitness, gBest, p); progressCallback?.Invoke(runIndex, iIter, snapshot); } return(new PSOResult { bestPos = gBest, bestFitness = gBestFitness }); }