/// <summary> /// Creates the final <see cref="AdapterDataRecord{GurobiResult}"/>. /// </summary> /// <param name="timeStamp">The time stamp.</param> /// <param name="model">The <see cref="GRBModel"/>.</param> /// <param name="result">The <see cref="GurobiResult"/>.</param> /// <returns>The final <see cref="AdapterDataRecord{GurobiResult}"/>.</returns> private AdapterDataRecord <GurobiResult> CreateFinalAdapterDataRecord(DateTime timeStamp, GRBModel model, GurobiResult result) { if (this._instanceFeatures == null) { throw new ArgumentNullException(nameof(this._instanceFeatures)); } var finalRuntimeFeatures = this.GetFinalGurobiRuntimeFeatures(model, result); var lastRuntimeFeatures = this._lastRuntimeFeatures ?? finalRuntimeFeatures; var adapterFeatures = GurobiUtils.ComposeAdapterFeatures(finalRuntimeFeatures, lastRuntimeFeatures, this._instanceFeatures); var adapterFeaturesHeader = GurobiUtils.ComposeAdapterFeaturesHeader(finalRuntimeFeatures, lastRuntimeFeatures, this._instanceFeatures); this._lastRuntimeFeatures = finalRuntimeFeatures.Copy(); return(new AdapterDataRecord <GurobiResult>( "Gurobi901", result.TargetAlgorithmStatus, // The cpu time is not recordable, because all parallel Gurobi runs are started in the same process in this implementation. TimeSpan.FromSeconds(0), this.GetCurrentRuntime(timeStamp), timeStamp, adapterFeaturesHeader, adapterFeatures, result)); }
/// <summary> /// Creates the current <see cref="AdapterDataRecord{GurobiResult}"/>. /// </summary> /// <param name="timeStamp">The time stamp.</param> /// <returns>The current <see cref="AdapterDataRecord{GurobiResult}"/>.</returns> private AdapterDataRecord <GurobiResult> CreateCurrentAdapterDataRecord(DateTime timeStamp) { if (this._instanceFeatures == null) { throw new ArgumentNullException(nameof(this._instanceFeatures)); } var currentRuntimeFeatures = this.GetCurrentGurobiRuntimeFeatures(); var lastRuntimeFeatures = this._lastRuntimeFeatures ?? currentRuntimeFeatures; var adapterFeatures = GurobiUtils.ComposeAdapterFeatures(currentRuntimeFeatures, lastRuntimeFeatures, this._instanceFeatures); var adapterFeaturesHeader = GurobiUtils.ComposeAdapterFeaturesHeader(currentRuntimeFeatures, lastRuntimeFeatures, this._instanceFeatures); this._lastRuntimeFeatures = currentRuntimeFeatures.Copy(); return(new AdapterDataRecord <GurobiResult>( "Gurobi901", TargetAlgorithmStatus.Running, // The cpu time is not recordable, because all parallel Gurobi runs are started in the same process in this implementation. TimeSpan.FromSeconds(0), this.GetCurrentRuntime(timeStamp), timeStamp, adapterFeaturesHeader, adapterFeatures, new GurobiResult( currentRuntimeFeatures.MipGap, this._runnerConfiguration.CpuTimeout, TargetAlgorithmStatus.CancelledByGrayBox, currentRuntimeFeatures.FeasibleSolutionsCount > 0))); }
public void CorrectGrayBoxFeaturesAreSelected() { var gurobiGrayBoxMethods = new GurobiGrayBoxMethods(); var runtimeFeatures = new GurobiRuntimeFeatures(DateTime.Now); var instanceFeatures = new GurobiInstanceFeatures(); var adapterFeatures = GurobiUtils.ComposeAdapterFeatures(runtimeFeatures, runtimeFeatures, instanceFeatures); var adapterFeaturesHeader = GurobiUtils.ComposeAdapterFeaturesHeader(runtimeFeatures, runtimeFeatures, instanceFeatures); var result = new GurobiResult(double.NaN, TimeSpan.MaxValue, TargetAlgorithmStatus.CancelledByTimeout, false); var adapterDataRecord = new AdapterDataRecord <GurobiResult>( "Gurobi901", TargetAlgorithmStatus.Running, TimeSpan.Zero, TimeSpan.Zero, DateTime.Now, adapterFeaturesHeader, adapterFeatures, result); var tunerDataRecord = new TunerDataRecord <GurobiResult>( "Node", 0, 0, "Instance", 0.5, new[] { "Genome" }, (GenomeDoubleRepresentation) new[] { 0D }, result); var dataRecord = new DataRecord <GurobiResult>(tunerDataRecord, adapterDataRecord); var featureNames = gurobiGrayBoxMethods.GetGrayBoxFeatureNamesFromDataRecord(dataRecord); var correctFeatureNames = new[] { "ExpendedWallClockTime", "RuntimeFeature_CuttingPlanesCount_Current", "RuntimeFeature_ExploredNodeCount_Current", "RuntimeFeature_FeasibleSolutionsCount_Current", "RuntimeFeature_MipGap_Current", "RuntimeFeature_SimplexIterationsCount_Current", "RuntimeFeature_UnexploredNodeCount_Current", "RuntimeFeature_CuttingPlanesCount_Last", "RuntimeFeature_ExploredNodeCount_Last", "RuntimeFeature_FeasibleSolutionsCount_Last", "RuntimeFeature_MipGap_Last", "RuntimeFeature_SimplexIterationsCount_Last", "RuntimeFeature_UnexploredNodeCount_Last", "InstanceFeature_NumberOfIntegerVariables", "InstanceFeature_NumberOfLinearConstraints", "InstanceFeature_NumberOfNonZeroCoefficients", "InstanceFeature_NumberOfVariables", }; featureNames.SequenceEqual(correctFeatureNames).ShouldBeTrue(); }
/// <summary> /// Composes the given adapter features to a single array. /// </summary> /// <param name="currentRuntimeFeatures">The current runtime features.</param> /// <param name="lastRuntimeFeatures">The last runtime features.</param> /// <param name="instanceFeatures">The instance features.</param> /// <returns>The composed array.</returns> public static double[] ComposeAdapterFeatures( GurobiRuntimeFeatures currentRuntimeFeatures, GurobiRuntimeFeatures lastRuntimeFeatures, GurobiInstanceFeatures instanceFeatures) { return(currentRuntimeFeatures.ToArray() .Concat(lastRuntimeFeatures.ToArray()) .Concat(instanceFeatures.ToArray()) .ToArray()); }
/// <summary> /// Composes the header of the given adapter features to a single array. /// </summary> /// <param name="currentRuntimeFeatures">The current runtime features.</param> /// <param name="lastRuntimeFeatures">The last runtime features.</param> /// <param name="instanceFeatures">The instance features.</param> /// <returns>The composed array.</returns> public static string[] ComposeAdapterFeaturesHeader( GurobiRuntimeFeatures currentRuntimeFeatures, GurobiRuntimeFeatures lastRuntimeFeatures, GurobiInstanceFeatures instanceFeatures) { return(currentRuntimeFeatures.GetHeader("RuntimeFeature_", "_Current") .Concat(lastRuntimeFeatures.GetHeader("RuntimeFeature_", "_Last")) .Concat(instanceFeatures.GetHeader("InstanceFeature_")) .ToArray()); }
/// <summary> /// Creates a task that runs Gurobi on the given instance. /// </summary> /// <param name="instance"> /// Instance to run on. /// </param> /// <param name="cancellationToken"> /// The cancellation token given to the task. NOTE: In this implementation, we ignore this cancellation token, since Gurobi uses its own cancellation token in the callback. /// </param> /// <returns> /// A task that returns the run's runtime, gap, feasibility and completion status onto return. /// </returns> public Task <GurobiResult> Run(InstanceSeedFile instance, CancellationToken cancellationToken) { // Check if the runner has already been disposed. if (this._hasAlreadyBeenDisposed) { throw new ObjectDisposedException("GurobiRunner", "Called Run on a disposed GurobiRunner."); } // Continue if it hasn't. // ReSharper disable once MethodSupportsCancellation var solveTask = Task.Run( () => { this._recordTimer = null; try { // Prepare Gurobi model: Use configured _environment and the given instance, // then add a cancellation token source and the Gurobi callback for cancellation. var instanceFile = new FileInfo(instance.Path); if (!File.Exists(instanceFile.FullName)) { throw new FileNotFoundException($"Instance {instanceFile.FullName} not found!"); } LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Setting MIPGap to {this._runnerConfiguration.TerminationMipGap}"); this._environment.MIPGap = this._runnerConfiguration.TerminationMipGap; LoggingHelper.WriteLine(VerbosityLevel.Debug, $"Current Seed: {instance.Seed}"); this._environment.Seed = instance.Seed; this._environment.TimeLimit = this._runnerConfiguration.CpuTimeout.TotalSeconds; if (!Directory.Exists("GurobiLog")) { Directory.CreateDirectory("GurobiLog"); } var instanceFileNameWithoutExtension = GurobiUtils.GetFileNameWithoutGurobiExtension(instanceFile); this._environment.LogFile = "GurobiLog" + Path.DirectorySeparatorChar + $"GurobiRunner_{DateTime.Now:yy-MM-dd_HH-mm-ss-ffff}_" + instanceFileNameWithoutExtension + $"_{Guid.NewGuid()}.log"; var model = new GRBModel(this._environment, instanceFile.FullName) { ModelName = instanceFileNameWithoutExtension }; if (GurobiRunner.TryToGetMstFile(instanceFile.DirectoryName, instanceFileNameWithoutExtension, out var mstFileFullName)) { model.Read(mstFileFullName); } this._startTimeStamp = DateTime.Now; this._targetAlgorithmStatus = TargetAlgorithmStatus.Running; this._innerCancellationTokenSource = new CancellationTokenSource(this._runnerConfiguration.CpuTimeout); this._grbCallback = new GurobiCallback( this._innerCancellationTokenSource.Token, this._tunerConfiguration.EnableDataRecording, this._startTimeStamp); model.SetCallback(this._grbCallback); if (this._tunerConfiguration.EnableDataRecording) { this._lastRuntimeFeatures = null; this.SetGurobiInstanceFeatures(model); // Start record timer. var autoResetEvent = new AutoResetEvent(false); this._recordTimer = new Timer( (timerCallback) => { var currentTimeStamp = DateTime.Now; if (currentTimeStamp - this._startTimeStamp < this._runnerConfiguration.CpuTimeout) { // Write current line to data log. var currentAdapterData = this.CreateCurrentAdapterDataRecord(currentTimeStamp); this.OnNewDataRecord?.Invoke(this, currentAdapterData); } }, autoResetEvent, TimeSpan.FromSeconds(0), this._tunerConfiguration.DataRecordUpdateInterval); } // Optimize. This step may be aborted in the callback. model.Optimize(); if (this._targetAlgorithmStatus != TargetAlgorithmStatus.CancelledByGrayBox) { this._recordTimer?.Dispose(); this._targetAlgorithmStatus = this.GetIsRunInterrupted(model) ? TargetAlgorithmStatus.CancelledByTimeout : TargetAlgorithmStatus.Finished; } var finalTimeStamp = DateTime.Now; var result = this.CreateGurobiResult(finalTimeStamp, model); if (this._tunerConfiguration.EnableDataRecording) { // Write last line to data log. var lastAdapterData = this.CreateFinalAdapterDataRecord(finalTimeStamp, model, result); this.OnNewDataRecord?.Invoke(this, lastAdapterData); } // Before returning, dispose of Gurobi model. model.Dispose(); return(result); } finally { this._recordTimer?.Dispose(); } }); return(solveTask); }