Beispiel #1
0
        protected override void ProcessRecord()
        {
            if (this.Runs !.Count < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.Runs));
            }

            using StreamWriter writer = this.GetWriter();

            StringBuilder line = new StringBuilder();

            if (this.ShouldWriteHeader())
            {
                HeuristicParameters?highestParameters = this.Runs[0].HighestHeuristicParameters;
                if (highestParameters == null)
                {
                    throw new NotSupportedException("Cannot generate header because first run is missing highest solution parameters.");
                }

                line.Append("stand,heuristic," + highestParameters.GetCsvHeader() + ",thin age,rotation,solution,objective,runtime");
                writer.WriteLine(line);
            }

            for (int runIndex = 0; runIndex < this.Runs.Count; ++runIndex)
            {
                HeuristicSolutionDistribution distribution = this.Runs[runIndex];
                Heuristic?highestHeuristic = distribution.HighestSolution;
                if ((highestHeuristic == null) || (distribution.HighestHeuristicParameters == null))
                {
                    throw new NotSupportedException("Run " + runIndex + " is missing a highest solution or highest solution parameters.");
                }
                OrganonStandTrajectory highestTrajectory = highestHeuristic.BestTrajectory;
                string linePrefix = highestTrajectory.Name + "," + highestHeuristic.GetName() + "," + distribution.HighestHeuristicParameters.GetCsvValues() + "," + highestTrajectory.GetFirstHarvestAge() + "," + highestTrajectory.GetRotationLength();

                List <float> bestSolutions = distribution.BestObjectiveFunctionBySolution;
                for (int solutionIndex = 0; solutionIndex < bestSolutions.Count; ++solutionIndex)
                {
                    line.Clear();

                    string objectiveFunction = bestSolutions[solutionIndex].ToString(CultureInfo.InvariantCulture);
                    string runtime           = distribution.RuntimeBySolution[solutionIndex].TotalSeconds.ToString("0.000", CultureInfo.InvariantCulture);
                    line.Append(linePrefix + "," + solutionIndex + "," + objectiveFunction + "," + runtime);
                    writer.WriteLine(line);
                }
            }
        }
Beispiel #2
0
 private static void Verify(OrganonStandTrajectory thinnedTrajectory, float[] minimumThinnedVolumeScribner, int thinPeriod)
 {
     for (int periodIndex = 0; periodIndex < thinnedTrajectory.PlanningPeriods; ++periodIndex)
     {
         if (periodIndex == thinPeriod)
         {
             Assert.IsTrue(thinnedTrajectory.BasalAreaRemoved[periodIndex] > 0.0F);
             Assert.IsTrue(thinnedTrajectory.BasalAreaRemoved[periodIndex] < thinnedTrajectory.DensityByPeriod[periodIndex - 1].BasalAreaPerAcre); // assume <50% thin by volume
             Assert.IsTrue(thinnedTrajectory.ThinningVolume.ScribnerTotal[periodIndex] > 0.0F);
             Assert.IsTrue(thinnedTrajectory.ThinningVolume.ScribnerTotal[periodIndex] < minimumThinnedVolumeScribner[periodIndex]);
         }
         else
         {
             Assert.IsTrue(thinnedTrajectory.BasalAreaRemoved[periodIndex] == 0.0F);
             Assert.IsTrue(thinnedTrajectory.ThinningVolume.ScribnerTotal[periodIndex] == 0.0F);
         }
     }
 }
Beispiel #3
0
        public void Plot14ImmediateThin()
        {
            int  thinPeriod      = 1;
            int  lastPeriod      = 4;
            bool useScaledVolume = false;

            PlotsWithHeight      plot14        = PublicApi.GetPlot14();
            OrganonConfiguration configuration = OrganonTest.CreateOrganonConfiguration(new OrganonVariantNwo());
            OrganonStand         stand         = plot14.ToOrganonStand(configuration, 30, 130.0F);

            stand.PlantingDensityInTreesPerHectare = TestConstant.Plot14ReplantingDensityInTreesPerHectare;

            configuration.Treatments.Harvests.Add(new ThinByPrescription(thinPeriod)
            {
                FromAbovePercentage    = 0.0F,
                ProportionalPercentage = 30.0F,
                FromBelowPercentage    = 0.0F
            });
            OrganonStandTrajectory thinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume);

            AssertNullable.IsNotNull(thinnedTrajectory.StandByPeriod[0]);
            Assert.IsTrue(thinnedTrajectory.StandByPeriod[0] !.GetTreeRecordCount() == 222);
            thinnedTrajectory.Simulate();

            // verify thinned trajectory
            //                                        0      1       2       3       4
            float[] minimumThinnedQmd = new float[] { 9.16F, 10.47F, 11.64F, 12.64F, 13.52F };       // in
            //                                              0      1       2       3       4
            float[] minimumThinnedTopHeight = new float[] { 92.9F, 101.4F, 110.4F, 118.9F, 126.7F }; // ft
            float[] minimumThinnedVolume;
            if (thinnedTrajectory.UseScaledVolume)
            {
                minimumThinnedVolume = new float[] { 51.59F, 51.75F, 66.71F, 81.88F, 97.72F }; // Poudel 2018 + Scribner long log net MBF/ha
            }
            else
            {
                //                                   0       1       2       3       4
                minimumThinnedVolume = new float[] { 43.74F, 49.00F, 68.86F, 87.95F, 105.8F }; // Browning 1977 (FIA) MBF/ha
            }

            PublicApi.Verify(thinnedTrajectory, minimumThinnedQmd, minimumThinnedTopHeight, minimumThinnedVolume, thinPeriod, lastPeriod, 65, 70, configuration.Variant.TimeStepInYears);
            PublicApi.Verify(thinnedTrajectory, minimumThinnedVolume, thinPeriod);
            Assert.IsTrue(thinnedTrajectory.GetFirstHarvestAge() == 30);
        }
Beispiel #4
0
        private static void Verify(OrganonStandTrajectory trajectory, float[] minimumQmd, float[] minimumTopHeight, float[] minimumStandingVolumeScribner, int thinPeriod, int lastPeriod, int minTrees, int maxTrees, int timeStepInYears)
        {
            Assert.IsTrue(trajectory.BasalAreaRemoved.Length == lastPeriod + 1);
            Assert.IsTrue(trajectory.BasalAreaRemoved[0] == 0.0F);
            Assert.IsTrue(trajectory.HarvestPeriods == thinPeriod + 1); // BUGBUG: clean off by one semantic
            Assert.IsTrue(trajectory.ThinningVolume.ScribnerTotal[0] == 0.0F);
            Assert.IsTrue(trajectory.ThinningVolume.ScribnerTotal.Length == lastPeriod + 1);
            PublicApi.Verify(trajectory.IndividualTreeSelectionBySpecies, thinPeriod, minTrees, maxTrees);
            Assert.IsTrue(String.IsNullOrEmpty(trajectory.Name) == false);
            Assert.IsTrue(trajectory.PeriodLengthInYears == timeStepInYears);
            Assert.IsTrue(trajectory.PlanningPeriods == lastPeriod + 1); // BUGBUG: clean off by one semantic

            float qmdTolerance       = 1.01F;
            float topHeightTolerance = 1.01F;
            float volumeTolerance    = 1.01F;

            for (int periodIndex = 0; periodIndex < trajectory.PlanningPeriods; ++periodIndex)
            {
                Assert.IsTrue(trajectory.DensityByPeriod[periodIndex].BasalAreaPerAcre > 0.0F);
                Assert.IsTrue(trajectory.DensityByPeriod[periodIndex].BasalAreaPerAcre <= TestConstant.Maximum.TreeBasalAreaLarger);
                Assert.IsTrue(trajectory.StandingVolume.ScribnerTotal[periodIndex] > minimumStandingVolumeScribner[periodIndex]);
                Assert.IsTrue(trajectory.StandingVolume.ScribnerTotal[periodIndex] < volumeTolerance * minimumStandingVolumeScribner[periodIndex]);

                OrganonStand stand       = trajectory.StandByPeriod[periodIndex] ?? throw new NotSupportedException("Stand information missing for period " + periodIndex + ".");
                float        qmd         = stand.GetQuadraticMeanDiameter();
                float        topHeight   = stand.GetTopHeight();
                int          treeRecords = stand.GetTreeRecordCount();

                Assert.IsTrue((stand.Name != null) && (trajectory.Name != null) && stand.Name.StartsWith(trajectory.Name));
                Assert.IsTrue(qmd > minimumQmd[periodIndex]);
                Assert.IsTrue(qmd < qmdTolerance * minimumQmd[periodIndex]);
                Assert.IsTrue(topHeight > minimumTopHeight[periodIndex]);
                Assert.IsTrue(topHeight < topHeightTolerance * minimumTopHeight[periodIndex]);
                Assert.IsTrue(treeRecords > 0);
                Assert.IsTrue(treeRecords < 666);

                // TODO: check qmd against QMD from basal area
            }
        }
Beispiel #5
0
        public override TimeSpan Run()
        {
            if (this.ChangeToExchangeAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ChangeToExchangeAfter));
            }
            if (this.FinalMultiplier < this.IntitialMultiplier)
            {
                throw new ArgumentOutOfRangeException(nameof(this.FinalMultiplier));
            }
            if (this.Objective.HarvestPeriodSelection != HarvestPeriodSelection.NoneOrLast)
            {
                throw new NotSupportedException(nameof(this.Objective.HarvestPeriodSelection));
            }
            if (this.LowerWaterAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.LowerWaterAfter));
            }
            if (this.StopAfter < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.StopAfter));
            }
            int initialTreeRecordCount = this.GetInitialTreeRecordCount();

            if (initialTreeRecordCount < 2)
            {
                throw new NotSupportedException();
            }

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            this.EvaluateInitialSelection(this.Iterations);
            if (this.RainRate.HasValue == false)
            {
                this.RainRate = (this.FinalMultiplier - this.IntitialMultiplier) * this.BestObjectiveFunction / this.Iterations;
            }
            if ((this.RainRate.HasValue == false) || (this.RainRate.Value <= 0.0))
            {
                throw new ArgumentOutOfRangeException(nameof(this.RainRate));
            }

            float acceptedObjectiveFunction = this.BestObjectiveFunction;
            //float harvestPeriodScalingFactor = ((float)this.CurrentTrajectory.HarvestPeriods - Constant.RoundToZeroTolerance) / (float)byte.MaxValue;
            float treeIndexScalingFactor = ((float)initialTreeRecordCount - Constant.RoundTowardsZeroTolerance) / (float)UInt16.MaxValue;

            // initial selection is considered iteration 0, so loop starts with iteration 1
            OrganonStandTrajectory candidateTrajectory = new OrganonStandTrajectory(this.CurrentTrajectory);
            float hillClimbingThreshold = this.BestObjectiveFunction;
            int   iterationsSinceBestObjectiveImproved = 0;
            int   iterationsSinceObjectiveImprovedOrMoveTypeChanged   = 0;
            int   iterationsSinceObjectiveImprovedOrWaterLevelLowered = 0;
            float waterLevel = this.IntitialMultiplier * this.BestObjectiveFunction;

            for (int iteration = 1; iteration < this.Iterations; ++iteration, waterLevel += this.RainRate.Value)
            {
                int firstTreeIndex            = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                int firstCurrentHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(firstTreeIndex);
                int firstCandidateHarvestPeriod;
                int secondTreeIndex = -1;
                switch (this.MoveType)
                {
                case MoveType.OneOpt:
                    firstCandidateHarvestPeriod = firstCurrentHarvestPeriod == 0 ? this.CurrentTrajectory.HarvestPeriods - 1 : 0;
                    candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                    break;

                case MoveType.TwoOptExchange:
                    secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                    firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                    while (firstCandidateHarvestPeriod == firstCurrentHarvestPeriod)
                    {
                        // retry until a modifying exchange is found
                        // This also excludes the case where a tree is exchanged with itself.
                        // BUGBUG: infinite loop if all trees have the same selection
                        secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                        firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                    }
                    candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                    candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCurrentHarvestPeriod);
                    break;

                default:
                    throw new NotSupportedException();
                }
                //int candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                //while (candidateHarvestPeriod == currentHarvestPeriod)
                //{
                //    candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                //}
                Debug.Assert(firstCandidateHarvestPeriod >= 0);

                candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                candidateTrajectory.Simulate();
                ++iterationsSinceBestObjectiveImproved;
                ++iterationsSinceObjectiveImprovedOrMoveTypeChanged;
                ++iterationsSinceObjectiveImprovedOrWaterLevelLowered;

                float candidateObjectiveFunction = this.GetObjectiveFunction(candidateTrajectory);
                if ((candidateObjectiveFunction > waterLevel) || (candidateObjectiveFunction > hillClimbingThreshold))
                {
                    // accept move
                    acceptedObjectiveFunction = candidateObjectiveFunction;
                    this.CurrentTrajectory.CopyFrom(candidateTrajectory);
                    hillClimbingThreshold = candidateObjectiveFunction;
                    iterationsSinceObjectiveImprovedOrMoveTypeChanged   = 0;
                    iterationsSinceObjectiveImprovedOrWaterLevelLowered = 0;

                    if (candidateObjectiveFunction > this.BestObjectiveFunction)
                    {
                        this.BestObjectiveFunction = candidateObjectiveFunction;
                        this.BestTrajectory.CopyFrom(this.CurrentTrajectory);

                        iterationsSinceBestObjectiveImproved = 0;
                    }
                }
                else
                {
                    // undo move
                    switch (this.MoveType)
                    {
                    case MoveType.OneOpt:
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                        break;

                    case MoveType.TwoOptExchange:
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                        candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCandidateHarvestPeriod);
                        break;

                    default:
                        throw new NotSupportedException();
                    }
                }

                this.AcceptedObjectiveFunctionByMove.Add(acceptedObjectiveFunction);
                this.CandidateObjectiveFunctionByMove.Add(candidateObjectiveFunction);
                this.MoveLog.TreeIDByMove.Add(firstTreeIndex);

                if (iterationsSinceBestObjectiveImproved > this.StopAfter)
                {
                    break;
                }
                else if (iterationsSinceObjectiveImprovedOrMoveTypeChanged > this.ChangeToExchangeAfter)
                {
                    // will fire repeatedly but no importa since this is idempotent
                    this.MoveType = MoveType.TwoOptExchange;
                    iterationsSinceObjectiveImprovedOrMoveTypeChanged = 0;
                }
                else if (iterationsSinceObjectiveImprovedOrWaterLevelLowered > this.LowerWaterAfter)
                {
                    // could also adjust rain rate but there but does not seem to be a clear need to do so
                    waterLevel            = (1.0F - this.LowerWaterBy) * this.BestObjectiveFunction;
                    hillClimbingThreshold = waterLevel;
                    iterationsSinceObjectiveImprovedOrWaterLevelLowered = 0;
                }
            }

            stopwatch.Stop();
            return(stopwatch.Elapsed);
        }
Beispiel #6
0
        public override TimeSpan Run()
        {
            if (this.MaximumIterations < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.MaximumIterations));
            }
            if (this.Objective.HarvestPeriodSelection != HarvestPeriodSelection.NoneOrLast)
            {
                throw new NotSupportedException(nameof(this.Objective.HarvestPeriodSelection));
            }

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            int initialTreeRecordCount = this.GetInitialTreeRecordCount();

            this.EvaluateInitialSelection(this.MaximumIterations * initialTreeRecordCount);

            float acceptedObjectiveFunction            = this.BestObjectiveFunction;
            float previousBestObjectiveFunction        = this.BestObjectiveFunction;
            OrganonStandTrajectory candidateTrajectory = new OrganonStandTrajectory(this.CurrentTrajectory);

            int[] treeIndices = Heuristic.CreateSequentialArray(initialTreeRecordCount);
            for (int iteration = 0; iteration < this.MaximumIterations; ++iteration)
            {
                // randomize on every iteration since a single randomization against the order of the data has little effect
                if (this.IsStochastic)
                {
                    this.Pseudorandom.Shuffle(treeIndices);
                }

                for (int iterationMoveIndex = 0; iterationMoveIndex < initialTreeRecordCount; ++iterationMoveIndex)
                {
                    // evaluate other cut option
                    int treeIndex              = treeIndices[iterationMoveIndex];
                    int currentHarvestPeriod   = this.CurrentTrajectory.GetTreeSelection(treeIndex);
                    int candidateHarvestPeriod = currentHarvestPeriod == 0 ? this.CurrentTrajectory.HarvestPeriods - 1 : 0;
                    candidateTrajectory.SetTreeSelection(treeIndex, candidateHarvestPeriod);
                    candidateTrajectory.Simulate();

                    float candidateObjectiveFunction = this.GetObjectiveFunction(candidateTrajectory);
                    if (candidateObjectiveFunction > acceptedObjectiveFunction)
                    {
                        // accept change of no cut-cut decision if it improves upon the best solution
                        acceptedObjectiveFunction = candidateObjectiveFunction;
                        this.CurrentTrajectory.CopyFrom(candidateTrajectory);
                    }
                    else
                    {
                        // otherwise, revert changes candidate trajectory for considering next tree's move
                        candidateTrajectory.SetTreeSelection(treeIndex, currentHarvestPeriod);
                    }

                    this.AcceptedObjectiveFunctionByMove.Add(acceptedObjectiveFunction);
                    this.CandidateObjectiveFunctionByMove.Add(candidateObjectiveFunction);
                    this.MoveLog.TreeIDByMove.Add(treeIndex);
                }

                if (acceptedObjectiveFunction <= previousBestObjectiveFunction)
                {
                    // convergence: stop if no improvement
                    break;
                }
                previousBestObjectiveFunction = acceptedObjectiveFunction;
            }

            this.BestObjectiveFunction = acceptedObjectiveFunction;
            this.BestTrajectory.CopyFrom(this.CurrentTrajectory);

            stopwatch.Stop();
            return(stopwatch.Elapsed);
        }
Beispiel #7
0
        public void NelderTrajectory()
        {
            int  expectedUnthinnedTreeRecordCount = 661;
            int  lastPeriod      = 9;
            bool useScaledVolume = false;

            PlotsWithHeight      nelder        = PublicApi.GetNelder();
            OrganonConfiguration configuration = OrganonTest.CreateOrganonConfiguration(new OrganonVariantNwo());
            OrganonStand         stand         = nelder.ToOrganonStand(configuration, 20, 130.0F);

            stand.PlantingDensityInTreesPerHectare = TestConstant.NelderReplantingDensityInTreesPerHectare;

            OrganonStandTrajectory unthinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume);

            unthinnedTrajectory.Simulate();
            foreach (Stand?unthinnedStand in unthinnedTrajectory.StandByPeriod)
            {
                AssertNullable.IsNotNull(unthinnedStand);
                Assert.IsTrue(unthinnedStand.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount);
            }

            int thinPeriod = 3;

            configuration.Treatments.Harvests.Add(new ThinByPrescription(thinPeriod)
            {
                FromAbovePercentage    = 20.0F,
                ProportionalPercentage = 15.0F,
                FromBelowPercentage    = 10.0F
            });
            OrganonStandTrajectory thinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume);

            AssertNullable.IsNotNull(thinnedTrajectory.StandByPeriod[0]);
            Assert.IsTrue(thinnedTrajectory.StandByPeriod[0] !.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount);

            thinnedTrajectory.Simulate();

            for (int periodIndex = 0; periodIndex < thinPeriod; ++periodIndex)
            {
                Stand?unthinnedStand = thinnedTrajectory.StandByPeriod[periodIndex];
                AssertNullable.IsNotNull(unthinnedStand);
                Assert.IsTrue(unthinnedStand.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount);
            }
            int expectedThinnedTreeRecordCount = 328; // must be updated if prescription changes

            for (int periodIndex = thinPeriod; periodIndex < thinnedTrajectory.PlanningPeriods; ++periodIndex)
            {
                Stand?thinnedStand = thinnedTrajectory.StandByPeriod[periodIndex];
                AssertNullable.IsNotNull(thinnedStand);
                Assert.IsTrue(thinnedStand.GetTreeRecordCount() == expectedThinnedTreeRecordCount);
            }

            // verify unthinned trajectory
            //                                          0      1      2      3       4       5       6       7       8       9
            float[] minimumUnthinnedQmd = new float[] { 6.61F, 8.17F, 9.52F, 10.67F, 11.68F, 12.56F, 13.34F, 14.05F, 14.69F, 15.28F };      // in
            //                                                0      1      2      3      4       5       6       7       8       9
            float[] minimumUnthinnedTopHeight = new float[] { 54.1F, 67.9F, 80.3F, 91.6F, 101.9F, 111.3F, 119.9F, 127.8F, 135.0F, 141.7F }; // ft
            float[] minimumUnthinnedVolume;
            if (unthinnedTrajectory.UseScaledVolume)
            {
                minimumUnthinnedVolume = new float[] { 9.758F, 19.01F, 31.07F, 47.24F, 62.21F, 75.09F, 89.64F, 103.7F, 116.5F, 128.9F }; // Poudel 2018 + Scribner long log net MBF/ha
            }
            else
            {
                //                                     0       1       2       3       4       5       6       7       8       9
                minimumUnthinnedVolume = new float[] { 4.428F, 15.02F, 30.49F, 48.39F, 66.72F, 84.45F, 101.1F, 116.4F, 130.6F, 143.6F }; // FIA SV6x32 MBF/ha
            }
            PublicApi.Verify(unthinnedTrajectory, minimumUnthinnedQmd, minimumUnthinnedTopHeight, minimumUnthinnedVolume, 0, lastPeriod, 0, 0, configuration.Variant.TimeStepInYears);

            // verify thinned trajectory
            //                                        0      1      2      3       4       5       6       7       8       9
            float[] minimumThinnedQmd = new float[] { 6.61F, 8.19F, 9.53F, 11.81F, 13.44F, 14.80F, 15.99F, 17.05F, 18.00F, 18.85F };     // in
            //                                              0      1      2      3      4      5       6       7       8       9
            float[] minimumThinnedTopHeight = new float[] { 54.1F, 67.9F, 80.3F, 88.3F, 98.4F, 108.0F, 116.9F, 125.0F, 132.5F, 139.4F }; // ft
            float[] minimumThinnedVolume;
            if (thinnedTrajectory.UseScaledVolume)
            {
                minimumThinnedVolume = new float[] { 9.758F, 19.01F, 31.07F, 28.25F, 41.77F, 54.37F, 68.44F, 85.10F, 100.4F, 114.7F }; // Poudel 2018 + Scribner long log net MBF/ha
            }
            else
            {
                //                                   0       1       2       3       4       5       6       7       8       9
                minimumThinnedVolume = new float[] { 4.428F, 15.02F, 30.50F, 30.64F, 47.26F, 64.81F, 82.54F, 99.87F, 116.3F, 131.7F }; // FIA MBF/ha
            }
            PublicApi.Verify(thinnedTrajectory, minimumThinnedQmd, minimumThinnedTopHeight, minimumThinnedVolume, thinPeriod, lastPeriod, 200, 400, configuration.Variant.TimeStepInYears);
            PublicApi.Verify(thinnedTrajectory, minimumThinnedVolume, thinPeriod);
        }
Beispiel #8
0
        public override TimeSpan Run()
        {
            if ((this.Alpha <= 0.0) || (this.Alpha >= 1.0))
            {
                throw new ArgumentOutOfRangeException(nameof(this.Alpha));
            }
            if (this.ChangeToExchangeAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ChangeToExchangeAfter));
            }
            if (this.FinalProbability < 0.0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.FinalProbability));
            }
            if (this.InitialProbability < this.FinalProbability)
            {
                throw new ArgumentOutOfRangeException(nameof(this.InitialProbability));
            }
            if (this.Iterations < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.Iterations));
            }
            if (this.IterationsPerTemperature < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.IterationsPerTemperature));
            }
            if (this.Objective.HarvestPeriodSelection != HarvestPeriodSelection.NoneOrLast)
            {
                throw new NotSupportedException(nameof(this.Objective.HarvestPeriodSelection));
            }
            if (this.ProbabilityWindowLength < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ProbabilityWindowLength));
            }
            if (this.ReheatAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ReheatAfter));
            }
            if (this.ReheatBy < 0.0F)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ReheatBy));
            }

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            this.EvaluateInitialSelection(this.Iterations);

            float acceptedObjectiveFunction = this.BestObjectiveFunction;
            //float harvestPeriodScalingFactor = ((float)this.CurrentTrajectory.HarvestPeriods - Constant.RoundToZeroTolerance) / (float)byte.MaxValue;
            int   iterationsSinceMoveTypeOrObjectiveChange     = 0;
            int   iterationsSinceReheatOrBestObjectiveImproved = 0;
            float meanAcceptanceProbability      = this.InitialProbability;
            float movingAverageOfObjectiveChange = -1.0F;
            float movingAverageMemory            = 1.0F - 1.0F / this.ProbabilityWindowLength;
            float treeIndexScalingFactor         = ((float)this.GetInitialTreeRecordCount() - Constant.RoundTowardsZeroTolerance) / (float)UInt16.MaxValue;

            OrganonStandTrajectory candidateTrajectory = new OrganonStandTrajectory(this.CurrentTrajectory);

            for (int iteration = 1; (iteration < this.Iterations) && (meanAcceptanceProbability >= this.FinalProbability); meanAcceptanceProbability *= this.Alpha)
            {
                float logMeanAcceptanceProbability = Single.NegativeInfinity;
                if (meanAcceptanceProbability > 0.0F)
                {
                    logMeanAcceptanceProbability = MathV.Ln(meanAcceptanceProbability);
                }

                for (int iterationAtTemperature = 0; iterationAtTemperature < this.IterationsPerTemperature; ++iteration, ++iterationAtTemperature)
                {
                    int firstTreeIndex            = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                    int firstCurrentHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(firstTreeIndex);
                    int firstCandidateHarvestPeriod;
                    int secondTreeIndex = -1;
                    switch (this.MoveType)
                    {
                    case MoveType.OneOpt:
                        firstCandidateHarvestPeriod = firstCurrentHarvestPeriod == 0 ? this.CurrentTrajectory.HarvestPeriods - 1 : 0;
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                        break;

                    case MoveType.TwoOptExchange:
                        secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                        firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                        while (firstCandidateHarvestPeriod == firstCurrentHarvestPeriod)
                        {
                            // retry until a modifying exchange is found
                            // This also excludes the case where a tree is exchanged with itself.
                            // BUGBUG: infinite loop if all trees have the same selection
                            secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                            firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                        }
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                        candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCurrentHarvestPeriod);
                        break;

                    default:
                        throw new NotSupportedException();
                    }
                    //int candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                    //while (candidateHarvestPeriod == currentHarvestPeriod)
                    //{
                    //    candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                    //}
                    Debug.Assert(firstCandidateHarvestPeriod >= 0);

                    candidateTrajectory.Simulate();
                    ++iterationsSinceMoveTypeOrObjectiveChange;
                    ++iterationsSinceReheatOrBestObjectiveImproved;

                    float candidateObjectiveFunction = this.GetObjectiveFunction(candidateTrajectory);

                    bool acceptMove = candidateObjectiveFunction > acceptedObjectiveFunction;
                    // require at least one improving move be accepted to set moving average before accepting disimproving moves
                    if ((acceptMove == false) && (logMeanAcceptanceProbability > Single.NegativeInfinity) && (movingAverageOfObjectiveChange > 0.0F))
                    {
                        // objective function increase is negative and log of acceptance probability is negative or zero, so exponent is positive or zero
                        float objectiveFunctionIncrease = candidateObjectiveFunction - acceptedObjectiveFunction;
                        float exponent = logMeanAcceptanceProbability * objectiveFunctionIncrease / movingAverageOfObjectiveChange;
                        Debug.Assert(exponent >= 0.0F);
                        if (exponent < 10.0F)
                        {
                            // exponent is small enough not to round acceptance probabilities down to zero
                            // 1/e^10 accepts 1 in 22,026 moves.
                            float acceptanceProbability = 1.0F / MathV.Exp(exponent);
                            float moveProbability       = this.GetPseudorandomByteAsProbability();
                            if (moveProbability < acceptanceProbability)
                            {
                                acceptMove = true;
                            }
                        }
                    }

                    if (acceptMove)
                    {
                        float objectiveFunctionChange = MathF.Abs(acceptedObjectiveFunction - candidateObjectiveFunction);
                        if (movingAverageOfObjectiveChange < 0.0F)
                        {
                            // acceptance of first move
                            movingAverageOfObjectiveChange = objectiveFunctionChange;
                        }
                        else
                        {
                            // all subsequent acceptances
                            movingAverageOfObjectiveChange = movingAverageMemory * movingAverageOfObjectiveChange + (1.0F - movingAverageMemory) * objectiveFunctionChange;
                        }

                        acceptedObjectiveFunction = candidateObjectiveFunction;
                        this.CurrentTrajectory.CopyFrom(candidateTrajectory);
                        if (acceptedObjectiveFunction > this.BestObjectiveFunction)
                        {
                            this.BestObjectiveFunction = acceptedObjectiveFunction;
                            this.BestTrajectory.CopyFrom(this.CurrentTrajectory);
                            iterationsSinceReheatOrBestObjectiveImproved = 0;
                        }

                        iterationsSinceMoveTypeOrObjectiveChange = 0;
                        Debug.Assert(movingAverageOfObjectiveChange > 0.0F);
                    }
                    else
                    {
                        // undo move
                        switch (this.MoveType)
                        {
                        case MoveType.OneOpt:
                            candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                            break;

                        case MoveType.TwoOptExchange:
                            candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                            candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCandidateHarvestPeriod);
                            break;

                        default:
                            throw new NotSupportedException();
                        }
                    }

                    this.AcceptedObjectiveFunctionByMove.Add(acceptedObjectiveFunction);
                    this.CandidateObjectiveFunctionByMove.Add(candidateObjectiveFunction);
                    this.MoveLog.TreeIDByMove.Add(firstTreeIndex);

                    if (iterationsSinceMoveTypeOrObjectiveChange > this.ChangeToExchangeAfter)
                    {
                        this.MoveType = MoveType.TwoOptExchange;
                        iterationsSinceMoveTypeOrObjectiveChange = 0;
                    }
                    if (iterationsSinceReheatOrBestObjectiveImproved > this.ReheatAfter)
                    {
                        // while it's unlikely alpha would be close enough to 1 and reheat intervals short enough to drive the acceptance probability
                        // above one, it is possible
                        meanAcceptanceProbability    = Math.Min(meanAcceptanceProbability + this.ReheatBy, 1.0F);
                        logMeanAcceptanceProbability = MathV.Ln(meanAcceptanceProbability);
                        iterationsSinceReheatOrBestObjectiveImproved = 0;
                    }
                }
            }

            stopwatch.Stop();
            return(stopwatch.Elapsed);
        }
Beispiel #9
0
        protected override void ProcessRecord()
        {
            if (this.Runs !.Count < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.Runs));
            }

            using StreamWriter writer = this.GetWriter();

            // for now, perform no reduction when Object.ReferenceEquals(lowestSolution, highestSolution) is true
            StringBuilder line = new StringBuilder();

            if (this.ShouldWriteHeader())
            {
                if ((this.Runs[0].HighestSolution == null) || (this.Runs[0].HighestHeuristicParameters == null) || (this.Runs[0].LowestSolution == null))
                {
                    throw new NotSupportedException("Cannot generate header because first run is missing a highest solution, lowest solution, or highest solution parameters.");
                }

                line.Append("stand,heuristic,thin age,rotation," + this.Runs[0].HighestHeuristicParameters !.GetCsvHeader() + ",iteration,count");

                string            lowestMoveLogHeader = "lowest move log";
                IHeuristicMoveLog?lowestMoveLog       = this.Runs[0].LowestSolution !.GetMoveLog();
                if (lowestMoveLog != null)
                {
                    lowestMoveLogHeader = lowestMoveLog.GetCsvHeader("lowest ");
                }
                line.Append("," + lowestMoveLogHeader);

                line.Append(",lowest,lowest candidate,min,percentile 2.5,percentile 5,lower quartile,median,mean,upper quartile,percentile 95,percentile 97.5,max");

                string            highestMoveLogHeader = "highest move log";
                IHeuristicMoveLog?highestMoveLog       = this.Runs[0].HighestSolution !.GetMoveLog();
                if (highestMoveLog != null)
                {
                    highestMoveLogHeader = highestMoveLog.GetCsvHeader("highest ");
                }
                line.Append("," + highestMoveLogHeader);

                line.Append(",highest,highest candidate");
                writer.WriteLine(line);
            }

            for (int runIndex = 0; runIndex < this.Runs.Count; ++runIndex)
            {
                HeuristicSolutionDistribution distribution = this.Runs[runIndex];
                Heuristic?highestHeuristic = distribution.HighestSolution;
                Heuristic?lowestHeuristic  = distribution.LowestSolution;
                if ((distribution.HighestHeuristicParameters == null) || (highestHeuristic == null) || (lowestHeuristic == null))
                {
                    throw new NotSupportedException("Solution distribution for run " + runIndex + " is missing a highest or lowest solution or highest solution parameters.");
                }
                IHeuristicMoveLog?highestMoveLog = highestHeuristic.GetMoveLog();
                IHeuristicMoveLog?lowestMoveLog  = lowestHeuristic.GetMoveLog();
                // for now, assume highest and lowest solutions used the same parameters
                OrganonStandTrajectory highestTrajectory = highestHeuristic.BestTrajectory;
                string runPrefix = highestTrajectory.Name + "," + highestHeuristic.GetName() + "," + highestTrajectory.GetFirstHarvestAge() + "," + highestTrajectory.GetRotationLength() + "," + distribution.HighestHeuristicParameters.GetCsvValues();

                Debug.Assert(distribution.CountByMove.Count >= lowestHeuristic.AcceptedObjectiveFunctionByMove.Count);
                Debug.Assert(distribution.CountByMove.Count == distribution.MinimumObjectiveFunctionByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.TwoPointFivePercentileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.FifthPercentileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.LowerQuartileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.MedianObjectiveFunctionByMove.Count);
                Debug.Assert(distribution.CountByMove.Count == distribution.MeanObjectiveFunctionByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.NinetyFifthPercentileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.NinetySevenPointFivePercentileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= distribution.UpperQuartileByMove.Count);
                Debug.Assert(distribution.CountByMove.Count == distribution.MaximumObjectiveFunctionByMove.Count);
                Debug.Assert(distribution.CountByMove.Count >= highestHeuristic.AcceptedObjectiveFunctionByMove.Count);
                for (int moveIndex = 0; moveIndex < distribution.CountByMove.Count; moveIndex += this.Step)
                {
                    line.Clear();

                    string runsWithMoveAtIndex = distribution.CountByMove[moveIndex].ToString(CultureInfo.InvariantCulture);

                    string?lowestMove = null;
                    if ((lowestMoveLog != null) && (lowestMoveLog.Count > moveIndex))
                    {
                        lowestMove = lowestMoveLog.GetCsvValues(moveIndex);
                    }
                    string?lowestObjectiveFunction = null;
                    if (lowestHeuristic.AcceptedObjectiveFunctionByMove.Count > moveIndex)
                    {
                        lowestObjectiveFunction = lowestHeuristic.AcceptedObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?lowestObjectiveFunctionForMove = null;
                    if (lowestHeuristic.CandidateObjectiveFunctionByMove.Count > moveIndex)
                    {
                        lowestObjectiveFunctionForMove = lowestHeuristic.CandidateObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }

                    string minObjectiveFunction           = distribution.MinimumObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    string?lowerQuartileObjectiveFunction = null;
                    if (moveIndex < distribution.LowerQuartileByMove.Count)
                    {
                        lowerQuartileObjectiveFunction = distribution.LowerQuartileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?twoPointFivePercentileObjectiveFunction = null;
                    if (moveIndex < distribution.TwoPointFivePercentileByMove.Count)
                    {
                        twoPointFivePercentileObjectiveFunction = distribution.TwoPointFivePercentileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?fifthPercentileObjectiveFunction = null;
                    if (moveIndex < distribution.FifthPercentileByMove.Count)
                    {
                        fifthPercentileObjectiveFunction = distribution.FifthPercentileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?medianObjectiveFunction = null;
                    if (moveIndex < distribution.MedianObjectiveFunctionByMove.Count)
                    {
                        medianObjectiveFunction = distribution.MedianObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string meanObjectiveFunction          = distribution.MeanObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    string?upperQuartileObjectiveFunction = null;
                    if (moveIndex < distribution.UpperQuartileByMove.Count)
                    {
                        upperQuartileObjectiveFunction = distribution.UpperQuartileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?ninetyFifthPercentileObjectiveFunction = null;
                    if (moveIndex < distribution.NinetyFifthPercentileByMove.Count)
                    {
                        ninetyFifthPercentileObjectiveFunction = distribution.NinetyFifthPercentileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?ninetySevenPointFivePercentileObjectiveFunction = null;
                    if (moveIndex < distribution.NinetySevenPointFivePercentileByMove.Count)
                    {
                        ninetySevenPointFivePercentileObjectiveFunction = distribution.NinetySevenPointFivePercentileByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string maxObjectiveFunction = distribution.MaximumObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);

                    string?highestMove = null;
                    if ((highestMoveLog != null) && (highestMoveLog.Count > moveIndex))
                    {
                        highestMove = highestMoveLog.GetCsvValues(moveIndex);
                    }
                    string highestObjectiveFunction = String.Empty;
                    if (highestHeuristic.AcceptedObjectiveFunctionByMove.Count > moveIndex)
                    {
                        highestObjectiveFunction = highestHeuristic.AcceptedObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }
                    string?highestObjectiveFunctionForMove = null;
                    if (highestHeuristic.CandidateObjectiveFunctionByMove.Count > moveIndex)
                    {
                        highestObjectiveFunctionForMove = highestHeuristic.CandidateObjectiveFunctionByMove[moveIndex].ToString(CultureInfo.InvariantCulture);
                    }

                    line.Append(runPrefix + "," +
                                moveIndex + "," +
                                runsWithMoveAtIndex + "," +
                                lowestMove + "," +
                                lowestObjectiveFunction + "," +
                                lowestObjectiveFunctionForMove + "," +
                                minObjectiveFunction + "," +
                                twoPointFivePercentileObjectiveFunction + "," +
                                fifthPercentileObjectiveFunction + "," +
                                lowerQuartileObjectiveFunction + "," +
                                medianObjectiveFunction + "," +
                                meanObjectiveFunction + "," +
                                upperQuartileObjectiveFunction + "," +
                                ninetyFifthPercentileObjectiveFunction + "," +
                                ninetySevenPointFivePercentileObjectiveFunction + "," +
                                maxObjectiveFunction + "," +
                                highestMove + "," +
                                highestObjectiveFunction + "," +
                                highestObjectiveFunctionForMove);
                    writer.WriteLine(line);
                }
            }
        }
Beispiel #10
0
        public override TimeSpan Run()
        {
            if ((this.Alpha < 0.0F) || (this.Alpha > 1.0F))
            {
                throw new ArgumentOutOfRangeException(nameof(this.Alpha));
            }
            if (this.ChangeToExchangeAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.ChangeToExchangeAfter));
            }
            if (this.FixedDeviation < 0.0F)
            {
                throw new ArgumentOutOfRangeException(nameof(this.FixedDeviation));
            }
            if (this.FixedIncrease < 0.0F)
            {
                throw new ArgumentOutOfRangeException(nameof(this.FixedIncrease));
            }
            if (this.IncreaseAfter < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(this.IncreaseAfter));
            }
            if (this.Objective.HarvestPeriodSelection != HarvestPeriodSelection.NoneOrLast)
            {
                throw new NotSupportedException(nameof(this.Objective.HarvestPeriodSelection));
            }
            if ((this.RelativeDeviation < 0.0) || (this.RelativeDeviation > 1.0))
            {
                throw new ArgumentOutOfRangeException(nameof(this.RelativeDeviation));
            }
            if ((this.RelativeIncrease < 0.0) || (this.RelativeIncrease > 1.0))
            {
                throw new ArgumentOutOfRangeException(nameof(this.RelativeIncrease));
            }
            if (this.StopAfter < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(this.StopAfter));
            }

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            this.EvaluateInitialSelection(this.Iterations);

            float acceptedObjectiveFunction = this.BestObjectiveFunction;
            //float harvestPeriodScalingFactor = ((float)this.CurrentTrajectory.HarvestPeriods - Constant.RoundToZeroTolerance) / (float)byte.MaxValue;
            int   iterationsSinceBestObjectiveImproved     = 0;
            int   iterationsSinceObjectiveImprovedOrReheat = 0;
            float previousObjectiveFunction = Single.MinValue;
            float treeIndexScalingFactor    = ((float)this.GetInitialTreeRecordCount() - Constant.RoundTowardsZeroTolerance) / (float)UInt16.MaxValue;

            OrganonStandTrajectory candidateTrajectory = new OrganonStandTrajectory(this.CurrentTrajectory);
            float deviation = this.RelativeDeviation * MathF.Abs(this.BestObjectiveFunction) + this.FixedDeviation;

            for (int iteration = 1; (iteration < this.Iterations) && (iterationsSinceBestObjectiveImproved < this.StopAfter); deviation *= this.Alpha, ++iteration)
            {
                int firstTreeIndex            = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                int firstCurrentHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(firstTreeIndex);
                int firstCandidateHarvestPeriod;
                int secondTreeIndex = -1;
                switch (this.MoveType)
                {
                case MoveType.OneOpt:
                    firstCandidateHarvestPeriod = firstCurrentHarvestPeriod == 0 ? this.CurrentTrajectory.HarvestPeriods - 1 : 0;
                    candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                    break;

                case MoveType.TwoOptExchange:
                    secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                    firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                    while (firstCandidateHarvestPeriod == firstCurrentHarvestPeriod)
                    {
                        // retry until a modifying exchange is found
                        // This also excludes the case where a tree is exchanged with itself.
                        // BUGBUG: infinite loop if all trees have the same selection
                        secondTreeIndex             = (int)(treeIndexScalingFactor * this.GetTwoPseudorandomBytesAsFloat());
                        firstCandidateHarvestPeriod = this.CurrentTrajectory.GetTreeSelection(secondTreeIndex);
                    }
                    candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                    candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCurrentHarvestPeriod);
                    break;

                default:
                    throw new NotSupportedException();
                }
                //int candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                //while (candidateHarvestPeriod == currentHarvestPeriod)
                //{
                //    candidateHarvestPeriod = (int)(harvestPeriodScalingFactor * this.GetPseudorandomByteAsFloat());
                //}
                Debug.Assert(firstCandidateHarvestPeriod >= 0);

                candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCandidateHarvestPeriod);
                candidateTrajectory.Simulate();
                ++iterationsSinceBestObjectiveImproved;
                ++iterationsSinceObjectiveImprovedOrReheat;

                float candidateObjectiveFunction         = this.GetObjectiveFunction(candidateTrajectory);
                float minimumAcceptableObjectiveFunction = this.BestObjectiveFunction - deviation;
                if ((candidateObjectiveFunction > minimumAcceptableObjectiveFunction) || (candidateObjectiveFunction > previousObjectiveFunction))
                {
                    // accept move
                    acceptedObjectiveFunction = candidateObjectiveFunction;
                    this.CurrentTrajectory.CopyFrom(candidateTrajectory);
                    iterationsSinceObjectiveImprovedOrReheat = 0;

                    if (acceptedObjectiveFunction > this.BestObjectiveFunction)
                    {
                        this.BestObjectiveFunction = acceptedObjectiveFunction;
                        this.BestTrajectory.CopyFrom(this.CurrentTrajectory);
                        iterationsSinceBestObjectiveImproved = 0;
                    }
                }
                else
                {
                    // undo move
                    switch (this.MoveType)
                    {
                    case MoveType.OneOpt:
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                        break;

                    case MoveType.TwoOptExchange:
                        candidateTrajectory.SetTreeSelection(firstTreeIndex, firstCurrentHarvestPeriod);
                        candidateTrajectory.SetTreeSelection(secondTreeIndex, firstCandidateHarvestPeriod);
                        break;

                    default:
                        throw new NotSupportedException();
                    }
                }

                this.AcceptedObjectiveFunctionByMove.Add(acceptedObjectiveFunction);
                this.CandidateObjectiveFunctionByMove.Add(candidateObjectiveFunction);
                this.MoveLog.TreeIDByMove.Add(firstTreeIndex);

                if (iterationsSinceBestObjectiveImproved > this.ChangeToExchangeAfter)
                {
                    this.MoveType = MoveType.TwoOptExchange;
                }
                if (iterationsSinceObjectiveImprovedOrReheat == this.IncreaseAfter)
                {
                    deviation += this.RelativeIncrease * MathF.Abs(this.BestObjectiveFunction) + this.FixedIncrease;
                    iterationsSinceObjectiveImprovedOrReheat = 0;
                }
                previousObjectiveFunction = acceptedObjectiveFunction;
            }

            stopwatch.Stop();
            return(stopwatch.Elapsed);
        }
Beispiel #11
0
        protected override void ProcessRecord()
        {
            using StreamWriter writer = this.GetWriter();

            StringBuilder line = new StringBuilder();
            if (this.ShouldWriteHeader())
            {
                line.Append("period");
                // harvest volume headers
                for (int runIndex = 0; runIndex < this.Runs!.Count; ++runIndex)
                {
                    Heuristic? highestSolution = this.Runs[runIndex].HighestSolution;
                    if (highestSolution == null)
                    {
                        throw new NotSupportedException("Cannot write harvest becaue no heuristic solution was provided for run " + runIndex + ".");
                    }

                    OrganonStandTrajectory bestTrajectory = highestSolution.BestTrajectory;
                    line.Append("," + bestTrajectory.Name + "harvest");
                }
                // standing volume headers
                for (int runIndex = 0; runIndex < this.Runs.Count; ++runIndex)
                {
                    OrganonStandTrajectory bestTrajectory = this.Runs[runIndex].HighestSolution!.BestTrajectory;
                    line.Append("," + bestTrajectory.Name + "standing");
                }
                writer.WriteLine(line);
            }

            int maxPlanningPeriod = 0;
            for (int runIndex = 0; runIndex < this.Runs!.Count; ++runIndex)
            {
                Heuristic? highestSolution = this.Runs[runIndex].HighestSolution;
                if (highestSolution == null)
                {
                    throw new NotSupportedException("Cannot write harvest becaue no heuristic solution was provided for run " + runIndex + ".");
                }

                OrganonStandTrajectory bestTrajectory = highestSolution.BestTrajectory;
                maxPlanningPeriod = Math.Max(maxPlanningPeriod, bestTrajectory.PlanningPeriods);
            }
            for (int periodIndex = 0; periodIndex < maxPlanningPeriod; ++periodIndex)
            {
                line.Clear();
                line.Append(periodIndex);

                for (int runIndex = 0; runIndex < this.Runs.Count; ++runIndex)
                {
                    Heuristic heuristic = this.Runs[runIndex].HighestSolution!;
                    float harvestVolumeScibner = heuristic.BestTrajectory.ThinningVolume.ScribnerTotal[periodIndex];
                    line.Append("," + harvestVolumeScibner.ToString(CultureInfo.InvariantCulture));
                }

                for (int runIndex = 0; runIndex < this.Runs.Count; ++runIndex)
                {
                    Heuristic heuristic = this.Runs[runIndex].HighestSolution!;
                    float standingVolumeScribner = heuristic.BestTrajectory.StandingVolume.ScribnerTotal[periodIndex];
                    line.Append("," + standingVolumeScribner.ToString(CultureInfo.InvariantCulture));
                }

                writer.WriteLine(line);
            }
        }