using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Core.Networks; using HeuristicLab.Data; using HeuristicLab.Encodings.RealVectorEncoding; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Problems.DataAnalysis; using HeuristicLab.Problems.TestFunctions; namespace HeuristicLab.Networks.IntegratedOptimization.SurrogateModeling { [Item("Surrogate Modeling Orchestrator", "")] [StorableClass] public sealed class SurrogateModelingOrchestrator : OrchestratorNode { private readonly object aoProblemSync = new object(); private readonly object mbProblemSync = new object(); private readonly object bestSolutionSync = new object(); private readonly ManualResetEventSlim mbSignal = new ManualResetEventSlim(false); private readonly ManualResetEventSlim aoSignal = new ManualResetEventSlim(false); private CancellationTokenSource cts; [Storable] public ExecutionState ExecutionState { get; set; } [Storable] public IRegressionProblem CurrentModelBuilderProblem { get; set; } [Storable] public AssistedOptimizerProblem CurrentAssistedOptimizerProblem { get; set; } [Storable] public IScope EstimatedBestSolution { get; set; } #region Port Names private const string FullEvaluatorOrchPortName = "Full Evaluator Orchestration"; private const string FullEvaluatorEvalPortName = "Full Evaluator Evaluation"; private const string FullEvaluatorExploitationEvalPortName = "Full Evaluator Exploitation"; private const string FullEvaluatorExplorationEvalPortName = "Full Evaluator Exploration"; private const string FullEvaluatorUpdatePortName = "Full Evaluator Update"; private const string PointGeneratorOrchPortName = "Point Generator Orchestration"; private const string PointGeneratorEvalPortName = "Point Generator Evaluation"; private const string ModelBuilderOrchPortName = "Model Builder Orchestration"; private const string ModelBuilderEvalPortName = "Model Builder Evaluation"; private const string ModelBuilderUpdatePortName = "Model Builder Update"; private const string AssistedOptimizerOrchPortName = "Assisted Optimizer Orchestration"; private const string AssistedOptimizerEvalPortName = "Assisted Optimizer Evaluation"; private const string AssistedOptimizerUpdatePortName = "Assisted Optimizer Update"; #endregion #region Ports public IMessagePort FullEvaluatorOrchPort { get { return (IMessagePort)Ports[FullEvaluatorOrchPortName]; } } public IMessagePort FullEvaluatorEvalPort { get { return (IMessagePort)Ports[FullEvaluatorEvalPortName]; } } public IMessagePort FullEvaluatorExploitationEvalPort { get { return (IMessagePort)Ports[FullEvaluatorExploitationEvalPortName]; } } public IMessagePort FullEvaluatorExplorationEvalPort { get { return (IMessagePort)Ports[FullEvaluatorExplorationEvalPortName]; } } public IMessagePort FullEvaluatorUpdatePort { get { return (IMessagePort)Ports[FullEvaluatorUpdatePortName]; } } public IMessagePort PointGeneratorOrchPort { get { return (IMessagePort)Ports[PointGeneratorOrchPortName]; } } public IMessagePort PointGeneratorEvalPort { get { return (IMessagePort)Ports[PointGeneratorEvalPortName]; } } public IMessagePort ModelBuilderOrchPort { get { return (IMessagePort)Ports[ModelBuilderOrchPortName]; } } public IMessagePort ModelBuilderEvalPort { get { return (IMessagePort)Ports[ModelBuilderEvalPortName]; } } public IMessagePort ModelBuilderUpdatePort { get { return (IMessagePort)Ports[ModelBuilderUpdatePortName]; } } public IMessagePort AssistedOptimizerOrchPort { get { return (IMessagePort)Ports[AssistedOptimizerOrchPortName]; } } public IMessagePort AssistedOptimizerEvalPort { get { return (IMessagePort)Ports[AssistedOptimizerEvalPortName]; } } public IMessagePort AssistedOptimizerUpdatePort { get { return (IMessagePort)Ports[AssistedOptimizerUpdatePortName]; } } #endregion #region Parameter Names private const string TestFunctionProblemParameterName = "TestFunctionProblem"; private const string PointGeneratorProblemParameterName = "PointGeneratorProblem"; private const string ModelBuilderRegressionProblemParameterName = "ModelBuilderRegressionProblem"; private const string AssistedOptimizerProblemParameterName = "AssistedOptimizerProblem"; private const string MinPointsParameterName = "MinPoints"; private const string MaxPointsParameterName = "MaxPoints"; private const string MinModelQualityParameterName = "MinModelQuality"; #endregion #region Parameters private IValueParameter TestFunctionProblemParameter { get { return (IValueParameter)Parameters[TestFunctionProblemParameterName]; } } private IValueParameter PointGeneratorProblemParameter { get { return (IValueParameter)Parameters[PointGeneratorProblemParameterName]; } } private IValueParameter ModelBuilderRegressionProblemParameter { get { return (IValueParameter)Parameters[ModelBuilderRegressionProblemParameterName]; } } private IValueParameter AssistedOptimizerProblemParameter { get { return (IValueParameter)Parameters[AssistedOptimizerProblemParameterName]; } } private IValueParameter MinPointsParameter { get { return (IValueParameter)Parameters[MinPointsParameterName]; } } private IValueParameter MaxPointsParameter { get { return (IValueParameter)Parameters[MaxPointsParameterName]; } } private IValueParameter MinModelQualityParameter { get { return (IValueParameter)Parameters[MinModelQualityParameterName]; } } #endregion #region Parameter Properties private SingleObjectiveTestFunctionProblem TestFunctionProblem { get { return TestFunctionProblemParameter.Value; } set { TestFunctionProblemParameter.Value = value; } } private PointGeneratorProblem PointGeneratorProblem { get { return PointGeneratorProblemParameter.Value; } set { PointGeneratorProblemParameter.Value = value; } } private IRegressionProblem ModelBuilderRegressionProblem { get { return ModelBuilderRegressionProblemParameter.Value; } set { ModelBuilderRegressionProblemParameter.Value = value; } } private AssistedOptimizerProblem AssistedOptimizerProblem { get { return AssistedOptimizerProblemParameter.Value; } set { AssistedOptimizerProblemParameter.Value = value; } } private IntValue MinPoints { get { return MinPointsParameter.Value; } set { MinPointsParameter.Value = value; } } private IntValue MaxPoints { get { return MaxPointsParameter.Value; } set { MaxPointsParameter.Value = value; } } private DoubleValue MinModelQuality { get { return MinModelQualityParameter.Value; } set { MinModelQualityParameter.Value = value; } } #endregion #region Constructors & Cloning [StorableConstructor] private SurrogateModelingOrchestrator(bool deserializing) : base(deserializing) { } private SurrogateModelingOrchestrator(SurrogateModelingOrchestrator original, Cloner cloner) : base(original, cloner) { RegisterPortEvents(); } public SurrogateModelingOrchestrator() : this("Surrogate Modeling Orchestrator") { } public SurrogateModelingOrchestrator(string name) : this(name, "") { } public SurrogateModelingOrchestrator(string name, string description) : base(name, description) { AddParameters(); AddFullEvaluatorPorts(); AddPointGeneratorPorts(); AddModelBuilderPorts(); AddAssistedOptimizerPorts(); RegisterPortEvents(); } public override IDeepCloneable Clone(Cloner cloner) { return new SurrogateModelingOrchestrator(this, cloner); } #endregion [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterPortEvents(); } public override void Prepare(bool clearRuns = false) { if (ExecutionState == ExecutionState.Started) throw new InvalidOperationException("Prepare not allowed in execution state " + ExecutionState); ExecutionState = ExecutionState.Prepared; EstimatedBestSolution = null; Results.Clear(); var orchMsg = OrchestrationMessage.Prepare; if (clearRuns) orchMsg |= OrchestrationMessage.ClearRuns; var tfProblem = (SingleObjectiveTestFunctionProblem)TestFunctionProblem.Clone(); var pgProblem = (PointGeneratorProblem)PointGeneratorProblem.Clone(); var mbProblem = (IRegressionProblem)ModelBuilderRegressionProblem.Clone(); var aoProblem = (AssistedOptimizerProblem)AssistedOptimizerProblem.Clone(); pgProblem.Encoding.Bounds = tfProblem.Bounds; pgProblem.Encoding.Length = tfProblem.ProblemSize.Value; pgProblem.NotifyEvaluation = (point, quality) => { var evalPort = (IMessagePort)PointGeneratorOrchPort.ConnectedPort.Parent.Ports["EvaluationPort"]; var evalMsg = evalPort.PrepareMessage(); evalMsg["RealVector"] = point; evalMsg["Quality"] = new DoubleValue(quality); evalPort.SendMessage(evalMsg); }; var fullEvaluatorNode = (FullEvaluatorNode)FullEvaluatorOrchPort.ConnectedPort.Parent; fullEvaluatorNode.Algorithm.NotifyEvaluation = (point, quality) => { var evalPort = (IMessagePort)fullEvaluatorNode.Ports["EvaluationPort"]; var evalMsg = evalPort.PrepareMessage(); evalMsg["RealVector"] = point; evalMsg["Quality"] = new DoubleValue(quality); evalPort.SendMessage(evalMsg); }; var variableNames = Enumerable.Range(0, tfProblem.ProblemSize.Value).Select(x => x.ToString("X00#")).Concat(new[] { "TARGET" }); var variableValues = variableNames.Select(x => new List()); var modifiableDataset = new ModifiableDataset(variableNames, variableValues); mbProblem.ProblemData = new RegressionProblemData(modifiableDataset, variableNames.Except(new[] { "TARGET" }), variableNames.Last()); aoProblem.Encoding.Bounds = tfProblem.Bounds; aoProblem.Encoding.Length = tfProblem.ProblemSize.Value; aoProblem.NotifyEvaluation = (point, quality) => { var evalPort = (IMessagePort)AssistedOptimizerOrchPort.ConnectedPort.Parent.Ports["EvaluationPort"]; var evalMsg = evalPort.PrepareMessage(); evalMsg["RealVector"] = point; evalMsg["Quality"] = new DoubleValue(quality); evalPort.SendMessage(evalMsg); }; CurrentModelBuilderProblem = mbProblem; CurrentAssistedOptimizerProblem = aoProblem; var tasks = new[] { SendOrchestrationMessageAsync(FullEvaluatorOrchPort, orchMsg, tfProblem), SendOrchestrationMessageAsync(PointGeneratorOrchPort, orchMsg, pgProblem), SendOrchestrationMessageAsync(ModelBuilderOrchPort, orchMsg, mbProblem), SendOrchestrationMessageAsync(AssistedOptimizerOrchPort, orchMsg, aoProblem) }; Task.WhenAll(tasks); } public override void Start() { if (ExecutionState != ExecutionState.Prepared) throw new InvalidOperationException("Start not allowed in execution state " + ExecutionState); ExecutionState = ExecutionState.Started; bool mbReady = CurrentModelBuilderProblem.ProblemData.Dataset.Rows >= MinPoints.Value; if (mbReady) mbSignal.Set(); else mbSignal.Reset(); bool aoReady = CurrentAssistedOptimizerProblem.RegressionSolution != null; if (aoReady) aoSignal.Set(); else aoSignal.Reset(); if (cts != null) { cts.Cancel(); cts.Dispose(); } cts = new CancellationTokenSource(); var tasks = new[] { StartFullEvaluatorAsync(), StartPointGeneratorMonitoringAsync(), StartModelBuilderMonitoringAsync(), StartAssistedOptimizerMonitoringAsync() }; Task.WhenAll(tasks); } public override void Pause() { if (ExecutionState != ExecutionState.Started) throw new InvalidOperationException("Pause not allowed in execution state " + ExecutionState); ExecutionState = ExecutionState.Paused; cts.Cancel(); var tasks = new[] { SendOrchestrationMessageAsync(PointGeneratorOrchPort, OrchestrationMessage.Pause), SendOrchestrationMessageAsync(FullEvaluatorOrchPort, OrchestrationMessage.Pause), SendOrchestrationMessageAsync(ModelBuilderOrchPort, OrchestrationMessage.Pause), SendOrchestrationMessageAsync(AssistedOptimizerOrchPort, OrchestrationMessage.Pause) }; Task.WhenAll(tasks); } public override void Stop() { if (ExecutionState != ExecutionState.Started && ExecutionState != ExecutionState.Paused) throw new InvalidOperationException("Stop not allowed in execution state " + ExecutionState); ExecutionState = ExecutionState.Stopped; cts.Cancel(); var tasks = new[] { SendOrchestrationMessageAsync(PointGeneratorOrchPort, OrchestrationMessage.Stop), SendOrchestrationMessageAsync(FullEvaluatorOrchPort, OrchestrationMessage.Stop), SendOrchestrationMessageAsync(ModelBuilderOrchPort, OrchestrationMessage.Stop), SendOrchestrationMessageAsync(AssistedOptimizerOrchPort, OrchestrationMessage.Stop) }; Task.WhenAll(tasks); } #region Helpers private void AddParameters() { var tfProblem = new SingleObjectiveTestFunctionProblem(); var pgProblem = new PointGeneratorProblem(); var mbProblem = new RegressionProblem(); var aoProblem = new AssistedOptimizerProblem(); var variableNames = "DUMMY".ToEnumerable(); var variableValues = new List().ToEnumerable(); var modifiableDataset = new ModifiableDataset(variableNames, variableValues); mbProblem.ProblemData = new RegressionProblemData(modifiableDataset, variableNames, variableNames.Last()); Parameters.Add(new ValueParameter(TestFunctionProblemParameterName, tfProblem)); Parameters.Add(new ValueParameter(PointGeneratorProblemParameterName, pgProblem)); Parameters.Add(new ValueParameter(ModelBuilderRegressionProblemParameterName, mbProblem)); Parameters.Add(new ValueParameter(AssistedOptimizerProblemParameterName, aoProblem)); Parameters.Add(new ValueParameter(MinPointsParameterName, new IntValue(1))); Parameters.Add(new ValueParameter(MaxPointsParameterName, new IntValue(int.MaxValue))); Parameters.Add(new ValueParameter(MinModelQualityParameterName, new DoubleValue(0.95))); } private void AddFullEvaluatorPorts() { var orchPort = CreateOrchestrationPort(FullEvaluatorOrchPortName); Ports.Add(orchPort); var evalPort = CreateEvaluationPort(FullEvaluatorEvalPortName, "RealVector", "Quality"); Ports.Add(evalPort); var exploitationPort = CreateEvaluationPort(FullEvaluatorExploitationEvalPortName, "RealVector", "Quality"); Ports.Add(exploitationPort); var explorationPort = CreateEvaluationPort(FullEvaluatorExplorationEvalPortName, "RealVector", "Quality"); Ports.Add(explorationPort); var updatePort = new ConfigurationPort(FullEvaluatorUpdatePortName) { Parameters = { new PortParameter("RegressionSolution") { Type = PortParameterType.Output } } }; Ports.Add(updatePort); } private void AddPointGeneratorPorts() { var orchPort = CreateOrchestrationPort(PointGeneratorOrchPortName); Ports.Add(orchPort); var evalPort = CreateEvaluationPort(PointGeneratorEvalPortName, "RealVector", "Quality"); Ports.Add(evalPort); } private void AddModelBuilderPorts() { var orchPort = CreateOrchestrationPort(ModelBuilderOrchPortName); Ports.Add(orchPort); var evalPort = CreateEvaluationPort(ModelBuilderEvalPortName, "RegressionSolution", "Quality"); Ports.Add(evalPort); var updatePort = new ConfigurationPort(ModelBuilderUpdatePortName) { Parameters = { new PortParameter("ProblemData") { Type = PortParameterType.Output } } }; Ports.Add(updatePort); } private void AddAssistedOptimizerPorts() { var orchPort = CreateOrchestrationPort(AssistedOptimizerOrchPortName); Ports.Add(orchPort); var evalPort = CreateEvaluationPort(AssistedOptimizerEvalPortName, "RealVector", "Quality"); Ports.Add(evalPort); var updatePort = new ConfigurationPort(AssistedOptimizerUpdatePortName) { Parameters = { new PortParameter("RegressionSolution") { Type = PortParameterType.Output } } }; Ports.Add(updatePort); } private void RegisterPortEvents() { PointGeneratorEvalPort.MessageReceived += PointGeneratorEvalPort_MessageReceived; ModelBuilderEvalPort.MessageReceived += ModelBuilderEvalPort_MessageReceived; AssistedOptimizerEvalPort.MessageReceived += AssistedOptimizerEvalPort_MessageReceived; FullEvaluatorEvalPort.MessageReceived += FullEvaluatorEvalPort_MessageReceived; } private IMessage SendOrchestrationMessage(IMessagePort orchPort, OrchestrationMessage message, IProblem problem = null) { var msg = orchPort.PrepareMessage(); msg["OrchestrationMessage"] = new EnumValue(message); if (problem != null) msg["Problem"] = problem; orchPort.SendMessage(msg); return msg; } private async Task SendOrchestrationMessageAsync(IMessagePort orchPort, OrchestrationMessage message, IProblem problem = null) { var msg = orchPort.PrepareMessage(); msg["OrchestrationMessage"] = new EnumValue(message); if (problem != null) msg["Problem"] = problem; await orchPort.SendMessageAsync(msg); return msg; } private Task StartFullEvaluatorAsync() { return SendOrchestrationMessageAsync(FullEvaluatorOrchPort, OrchestrationMessage.Start); } private Task StartPointGeneratorMonitoringAsync() { return AsyncHelper.DoAsync(ct => { var startOrchMsg = OrchestrationMessage.Start; var prepareOrchMsg = OrchestrationMessage.Prepare | OrchestrationMessage.ClearRuns; while (!ct.IsCancellationRequested) { SendOrchestrationMessage(PointGeneratorOrchPort, prepareOrchMsg); SendOrchestrationMessage(PointGeneratorOrchPort, startOrchMsg); } }, cts.Token); } private Task StartModelBuilderMonitoringAsync() { return AsyncHelper.DoAsync(ct => { IMessage message; var startOrchMsg = OrchestrationMessage.Start; var prepareOrchMsg = OrchestrationMessage.Prepare | OrchestrationMessage.ClearRuns; mbSignal.Wait(cts.Token); IRegressionProblem mbProblem; while (!ct.IsCancellationRequested) { lock (mbProblemSync) mbProblem = (IRegressionProblem)CurrentModelBuilderProblem.Clone(); SendOrchestrationMessage(ModelBuilderOrchPort, prepareOrchMsg, mbProblem); message = SendOrchestrationMessage(ModelBuilderOrchPort, startOrchMsg); var results = (ResultCollection)message["Results"]; var bestSolution = results.Select(x => x.Value).OfType().SingleOrDefault(); var clonedSolution = (IRegressionSolution)bestSolution.Clone(); var evalMsg = ModelBuilderEvalPort.PrepareMessage(); evalMsg["RegressionSolution"] = clonedSolution; evalMsg["Quality"] = new DoubleValue(clonedSolution.TrainingRSquared); ModelBuilderEvalPort.ReceiveMessage(evalMsg, ct); } }, cts.Token); } private Task StartAssistedOptimizerMonitoringAsync() { return AsyncHelper.DoAsync(ct => { IMessage message; var prepareOrchMsg = OrchestrationMessage.Prepare | OrchestrationMessage.ClearRuns; var startOrchMsg = OrchestrationMessage.Start; aoSignal.Wait(cts.Token); AssistedOptimizerProblem aoProblem; while (!ct.IsCancellationRequested) { lock (aoProblemSync) aoProblem = (AssistedOptimizerProblem)CurrentAssistedOptimizerProblem.Clone(); aoProblem.NotifyEvaluation = (point, quality) => { var evalPort = (IMessagePort)AssistedOptimizerOrchPort.ConnectedPort.Parent.Ports["EvaluationPort"]; var msg = evalPort.PrepareMessage(); msg["RealVector"] = point; msg["Quality"] = new DoubleValue(quality); evalPort.SendMessage(msg); }; SendOrchestrationMessage(AssistedOptimizerOrchPort, prepareOrchMsg, aoProblem); message = SendOrchestrationMessage(AssistedOptimizerOrchPort, startOrchMsg); var results = (ResultCollection)message["Results"]; var bestSolution = (RealVector)results["BestSolution"].Value.Clone(); var bestQuality = (DoubleValue)results["BestQuality"].Value.Clone(); var evalMsg = AssistedOptimizerEvalPort.PrepareMessage(); evalMsg["RealVector"] = bestSolution; evalMsg["Quality"] = bestQuality; AssistedOptimizerEvalPort.ReceiveMessage(evalMsg, ct); } }, cts.Token); } private void AddRowToDataset(IRegressionProblem problem, IEnumerable row) { var problemData = problem.ProblemData; var dataset = problemData.Dataset; var r = row.ToArray(); var modifiableDataset = dataset as ModifiableDataset ?? ((Dataset)dataset).ToModifiable(); if (modifiableDataset.Rows > MaxPoints.Value) { var targetValues = modifiableDataset.GetDoubleValues(problemData.TargetVariable).ToList(); var maxTargetValue = targetValues.Max(); if (maxTargetValue > (double)r.Last()) { int index = targetValues.IndexOf(maxTargetValue); modifiableDataset.ReplaceRow(index, row); } } else modifiableDataset.AddRow(row); problemData = new RegressionProblemData(modifiableDataset, problemData.AllowedInputVariables, problemData.TargetVariable); problemData.TrainingPartition.End = modifiableDataset.Rows; problemData.TestPartition.Start = modifiableDataset.Rows; problem.ProblemData = problemData; } private static bool IsBetter(double oldValue, double newValue, bool maximization) { return maximization ? newValue > oldValue : newValue < oldValue; } #endregion #region Event Handlers private void PointGeneratorEvalPort_MessageReceived(object sender, EventArgs e) { var evalMsg = e.Value; // just forward message to full evaluator FullEvaluatorExplorationEvalPort.SendMessage(evalMsg); } private void FullEvaluatorEvalPort_MessageReceived(object sender, EventArgs e) { var evalMsg = e.Value; var realVector = (RealVector)evalMsg["RealVector"]; var quality = (DoubleValue)evalMsg["Quality"]; if (realVector.Length == 0 && double.IsNaN(quality.Value)) { Stop(); return; } var sample = realVector.ToList(); sample.Add(quality.Value); var row = sample.Select(x => (object)x).ToList(); lock (mbProblemSync) { AddRowToDataset(CurrentModelBuilderProblem, row); if (!mbSignal.IsSet && CurrentModelBuilderProblem.ProblemData.Dataset.Rows >= MinPoints.Value) mbSignal.Set(); } //lock (aoProblemSync) { // if (CurrentAssistedOptimizerProblem.RegressionSolution != null) // CurrentAssistedOptimizerProblem.RegressionSolution.ProblemData = CurrentModelBuilderProblem.ProblemData; //} } private void ModelBuilderEvalPort_MessageReceived(object sender, EventArgs e) { var evalMsg = e.Value; var solution = (IRegressionSolution)evalMsg["RegressionSolution"]; var quality = (DoubleValue)evalMsg["Quality"]; if (solution.TrainingRSquared < MinModelQuality.Value) return; lock (aoProblemSync) { CurrentAssistedOptimizerProblem.RegressionSolution = solution; if (!aoSignal.IsSet) aoSignal.Set(); else { if (AssistedOptimizerUpdatePort.ConnectedPort != null) { var updateMsg = AssistedOptimizerUpdatePort.PrepareMessage(); updateMsg["RegressionSolution"] = (IRegressionSolution)CurrentAssistedOptimizerProblem.RegressionSolution.Clone(); AssistedOptimizerUpdatePort.SendMessage(updateMsg); } if (FullEvaluatorUpdatePort.ConnectedPort != null) { var updateMsg = FullEvaluatorUpdatePort.PrepareMessage(); updateMsg["RegressionSolution"] = (IRegressionSolution)CurrentAssistedOptimizerProblem.RegressionSolution.Clone(); FullEvaluatorUpdatePort.SendMessage(updateMsg); } } } } private void AssistedOptimizerEvalPort_MessageReceived(object sender, EventArgs e) { var evalMsg = e.Value; // just forward message to full evaluator FullEvaluatorExploitationEvalPort.SendMessage(evalMsg); var realVector = (RealVector)evalMsg["RealVector"]; var quality = (DoubleValue)evalMsg["Quality"]; lock (bestSolutionSync) { if (EstimatedBestSolution == null) { EstimatedBestSolution = new Scope("BestSolution") { Variables = { new Variable("RealVector", realVector), new Variable("Quality", quality) } }; } else { var oldRealVectorVariable = EstimatedBestSolution.Variables["RealVector"]; var oldQualityVariable = EstimatedBestSolution.Variables["Quality"]; var oldQuality = (DoubleValue)oldQualityVariable.Value; if (IsBetter(oldQuality.Value, quality.Value, false)) { oldRealVectorVariable.Value = realVector; oldQualityVariable.Value = quality; } } } } #endregion } }