/// <summary> /// Creates a cancellable task that runs Gurobi on the given instance. /// </summary> /// <param name="instance">Instance to run on.</param> /// <param name="cancellationToken"> /// Token that is regurlarly checked for cancellation. /// If cancellation is detected, the task will be stopped. /// </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. var solveTask = Task.Run( () => { // Prepare Gurobi model: Use configured _environment and the given instance, // then add a callback for cancellation. var instanceFile = new FileInfo(instance.Path); if (!File.Exists(instanceFile.FullName)) { throw new Exception(string.Format("Instance {0} not found!", instance.Path)); } 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; var fileName = Path.GetFileNameWithoutExtension(instance.Path); if (!Directory.Exists("GurobiLog")) { Directory.CreateDirectory("GurobiLog"); } this._environment.LogFile = $"GurobiLog/GurobiRunner_{DateTime.Now:yy-MM-dd_HH-mm-ss-ffff}_" + fileName + ".log"; var model = new GRBModel(this._environment, instance.Path) { ModelName = instanceFile.Name }; var mstfileName = instance.Path.Substring(0, instance.Path.Length - instanceFile.Extension.Length) + ".mst"; if (File.Exists(mstfileName)) { model.Read(mstfileName); } model.SetCallback(new GurobiCallback(cancellationToken)); // Optimize. This step may be aborted in the callback. model.Optimize(); var result = this.CreateGurobiResult(model); // Before returning, dispose of Gurobi model. model.Dispose(); return(result); }, cancellationToken); return(solveTask); }
/// <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); }