/// <summary> /// Constructor. /// </summary> /// <param name="optimiser">The optimiser.</param> /// <param name="model">The model.</param> /// <param name="timeOutManager">The timeout manager.</param> /// <param name="convergenceCheckers">Checks for early termination.</param> /// <param name="reportingFrequency">Number of reinsertions between reports on progress.</param> /// <param name="numberOfNewIndividualsPerGeneration">Number of new individuals to generate whenever an individual is evaluated.</param> public ReinsertionAgent( Optimiser optimiser, IModel model, TimeOutManager timeOutManager, Func <Population, bool> convergenceCheckers, int reportingFrequency, int numberOfNewIndividualsPerGeneration) { CancellationSource = new CancellationTokenSource(); this.timeOutManager = timeOutManager; this.convergenceCheckers = convergenceCheckers; NumberGenerated = 0; NumberReinserted = 0; AllEvaluated = new List <Individual>(); ReportingFrequency = reportingFrequency; this.optimiser = optimiser; this.model = model; this.numberOfNewIndividualsPerGeneration = numberOfNewIndividualsPerGeneration; IndividualsForReinsertion = new TransformManyBlock <Individual, Individual>( (Func <Individual, IEnumerable <Individual> >)Process, new ExecutionDataflowBlockOptions { CancellationToken = CancellationSource.Token, MaxDegreeOfParallelism = 1 }); NewIndividuals = new BufferBlock <Individual>( new DataflowBlockOptions { CancellationToken = CancellationSource.Token }); Reports = new BroadcastBlock <Population>(i => i); //Set up link so that new individuals created // are pushed to the output buffer IndividualsForReinsertion.LinkTo( NewIndividuals, new DataflowLinkOptions() { PropagateCompletion = true }); }
/// <summary> /// Initialises all the buffers to be ready. /// </summary> /// <param name="timeOutManager">The <see cref="TimeOutManager"/>.</param> /// <param name="reportingFrequency">The number of reinsertions between reports on the current population</param> /// <param name="numberOfNewIndividualsPerGeneration">The number of new individuals to generate whenever an individual is reinserted.</param> private void setUpAgents( TimeOutManager timeOutManager, int reportingFrequency, int numberOfNewIndividualsPerGeneration) { reinsertionAgent = new ReinsertionAgent( builder.CreateOptimiser(), builder.CreateModel(), timeOutManager, convergenceCheckers, reportingFrequency, numberOfNewIndividualsPerGeneration); evaluationAgent = new EvaluationAgent( evaluator, reinsertionAgent.CancellationSource.Token); //Create link so that newly created individuals from the ReinsertionAgent // are pushed to the EvaluationAgent reinsertionAgent.NewIndividuals.LinkTo( evaluationAgent.IndividualsForEvaluation, new DataflowLinkOptions() { PropagateCompletion = true }); //Create link so that evaluated individuals from the EvaluationAgent // are pushed to the ReinsertionAgent evaluationAgent.EvaluatedIndividuals.LinkTo( reinsertionAgent.IndividualsForReinsertion, new DataflowLinkOptions() { PropagateCompletion = true }); //Create link so that reports from the ReinsertionAgent // are pushed to the reporting delegates reinsertionAgent.Reports.LinkTo(reportingAgent, new DataflowLinkOptions() { PropagateCompletion = true }); }
/// <summary> /// Runs the optimisation. /// </summary> /// <param name="storeAll"><see langword="true"/> to store all individuals evaluated (memory required).</param> /// <param name="reportingFrequency">The number of evaluations between reporting progress.</param> /// <param name="timeOutEvaluations">The maximum number of evaluations before terminating the optimisation.</param> /// <param name="timeOutDuration">The maximum time allowed before terminating the optimisation.</param> /// <param name="newIndividualsPerGeneration">The number of new <see cref="Individual"/>s to generate each time new individuals are generated from the <see cref="Population"/>.</param> public override void Run( bool storeAll = true, int reportingFrequency = 100, int timeOutEvaluations = 0, TimeSpan?timeOutDuration = null, int newIndividualsPerGeneration = 1) { StartTime = DateTime.Now; // Calculate time outs automatically if not provided if (timeOutEvaluations == 0) { var numDims = builder.CreateModel().GetNewDecisionVector().Count; timeOutEvaluations = Math.Min(numDims * 20000, 2000000); } var timeOutDurationNotNull = TimeSpan.MaxValue; if (timeOutDuration != null) { timeOutDurationNotNull = timeOutDuration.Value; } var timeOutManager = new TimeOutManager(timeOutEvaluations, timeOutDurationNotNull); if (newIndividualsPerGeneration <= 0) { throw new ArgumentOutOfRangeException(nameof(newIndividualsPerGeneration), "At least one new individual must be created each generation."); } setUpAgents(timeOutManager, reportingFrequency, newIndividualsPerGeneration); if (reinsertionAgent == null || evaluationAgent == null) { throw new ApplicationException("Failed to initialise TPL buffers."); } reinsertionAgent.SaveAll = storeAll; // Get started var pumpPrimingInds = reinsertionAgent.CreateNewIndividuals( Math.Max(NumberOfIndividualsToStart, newIndividualsPerGeneration)); foreach (var ind in pumpPrimingInds) { reinsertionAgent.NewIndividuals.Post(ind); } // Wait for completion try { Task.WaitAll( reinsertionAgent.IndividualsForReinsertion.Completion, reinsertionAgent.NewIndividuals.Completion, evaluationAgent.EvaluatedIndividuals.Completion, evaluationAgent.IndividualsForEvaluation.Completion); } catch (AggregateException e) when(e.InnerExceptions.All(ie => ie is TaskCanceledException)) { // There is no way to wait for cancellation, // so we have to wait for completion and then // ignore the cancellation errors } FinalPopulation = reinsertionAgent.GetCurrentPopulation(); AllEvaluated = reinsertionAgent.AllEvaluated; if (FinalPopulation.Count <= 0) { return; } // Not really the best found unless the optimiser is elitist BestFound = FinalPopulation.Best(); }