/* Copyright: Copyright (c) MOSEK ApS, Denmark. All rights reserved. File: concurrent1.cs Purpose: Demonstrates a simple implementation of a concurrent optimizer. The concurrent optimizer starts a few parallel optimizations of the same problem using different algorithms, and reports a solution when the first optimizer is ready. This example also demonstrates how to define a simple callback handler that stops the optimizer when requested. */ using System.Threading.Tasks; using System; namespace mosek.example { public class concurrent1 { /** Takes a list of tasks and optimizes then in parallel. The response code and termination code from each optimization is stored in ``res`` and ``trm``. When one task completes with rescode == ok, others are terminated. Returns true if some optimizer returned without error. In this case ``firstOK`` is the index of the first task that returned with rescode == ok. Whether or not this is the task firstOK contains the most valuable answer, is for the caller to decide. */ public static bool optimize(mosek.Task[] tasks, mosek.rescode[] res, mosek.rescode[] trm, out int firstOK) { var n = tasks.Length; var jobs = new System.Threading.Tasks.Task[n]; int firstStop = -1; // Set a callback function var cb = new CallbackProxy(); for (var i = 0; i < n; ++i) tasks[i].set_Progress(cb); // Initialize for (var i = 0; i < n; ++i) { res[i] = mosek.rescode.err_unknown; trm[i] = mosek.rescode.err_unknown; } // Start parallel optimizations, one per task for (var i = 0; i < n; ++i) { int num = i; jobs[i] = System.Threading.Tasks.Task.Factory.StartNew( () => { try { trm[num] = tasks[num].optimize(); res[num] = mosek.rescode.ok; } catch (mosek.Exception e) { trm[num] = mosek.rescode.err_unknown; res[num] = e.Code; } finally { // If this finished with success, inform other tasks to interrupt if (res[num] == mosek.rescode.ok) { if (!cb.Stop) firstStop = num; cb.Stop = true; } } } ); } // Join all threads foreach (var j in jobs) j.Wait(); // For debugging, print res and trm codes for all optimizers for (var i = 0; i < n; ++i) Console.WriteLine("Optimizer {0} res {1} trm {2}", i, res[i], trm[i]); firstOK = firstStop; return cb.Stop; } /** Given a continuous task, set up jobs to optimize it with a list of different solvers. Returns an index, corresponding to the optimization task that is returned as winTask. This is the task with the best possible status of those that finished. If none task is considered successful returns -1. */ public static int optimizeconcurrent(mosek.Task task, int[] optimizers, out mosek.Task winTask, out mosek.rescode winTrm, out mosek.rescode winRes) { var n = optimizers.Length; var tasks = new mosek.Task[n]; var res = new mosek.rescode[n]; var trm = new mosek.rescode[n]; // Clone tasks and choose various optimizers for (var i = 0; i < n; ++i) { tasks[i] = new mosek.Task(task); tasks[i].putintparam(mosek.iparam.optimizer, optimizers[i]); } // Solve tasks in parallel bool success; int firstOK; success = optimize(tasks, res, trm, out firstOK); if (success) { winTask = tasks[firstOK]; winTrm = trm[firstOK]; winRes = res[firstOK]; return firstOK; } else { winTask = null; winTrm = 0; winRes = 0; return -1; } } /** Given a mixed-integer task, set up jobs to optimize it with different values of seed. That will lead to different execution paths of the optimizer. Returns an index, corresponding to the optimization task that is returned as winTask. This is the task with the best value of the objective function. If none task is considered successful returns -1. Typically, the input task would contain a time limit. The two major scenarios are: 1. Some clone ends before time limit - then it has optimum. 2. All clones reach time limit - pick the one with best objective. */ public static int optimizeconcurrentMIO(mosek.Task task, int[] seeds, out mosek.Task winTask, out mosek.rescode winTrm, out mosek.rescode winRes) { var n = seeds.Length; var tasks = new mosek.Task[n]; var res = new mosek.rescode[n]; var trm = new mosek.rescode[n]; // Clone tasks and choose various seeds for the optimizer for (var i = 0; i < n; ++i) { tasks[i] = new mosek.Task(task); tasks[i].putintparam(mosek.iparam.mio_seed, seeds[i]); } // Solve tasks in parallel bool success; int firstOK; success = optimize(tasks, res, trm, out firstOK); if (success) { // Pick the task that ended with res = ok // and contains an integer solution with best objective value mosek.objsense sense = task.getobjsense(); double bestObj = (sense == mosek.objsense.minimize) ? 1.0e+10 : -1.0e+10; int bestPos = -1; for (var i = 0; i < n; ++i) Console.WriteLine("{0} {1} ", i, tasks[i].getprimalobj(mosek.soltype.itg)); for (var i = 0; i < n; ++i) if ((res[i] == mosek.rescode.ok) && (tasks[i].getsolsta(mosek.soltype.itg) == mosek.solsta.prim_feas || tasks[i].getsolsta(mosek.soltype.itg) == mosek.solsta.integer_optimal) && ((sense == mosek.objsense.minimize) ? (tasks[i].getprimalobj(mosek.soltype.itg) < bestObj) : (tasks[i].getprimalobj(mosek.soltype.itg) > bestObj) ) ) { bestObj = tasks[i].getprimalobj(mosek.soltype.itg); bestPos = i; } if (bestPos != -1) { winTask = tasks[bestPos]; winTrm = trm[bestPos]; winRes = res[bestPos]; return bestPos; } } winTask = null; winTrm = 0; winRes = 0; return -1; } /** This is an example of how one can use the methods optimizeconcurrent optimizeconcurrentMIO argv[0] : name of file with input problem argv[1]: (optional) time limit */ public static void Main(string[] argv) { using (var env = new mosek.Env()) { using (var task = new mosek.Task(env)) { if (argv.Length >= 1) { task.readdata(argv[0]); } else { task.readdata("../data/25fv47.mps"); } mosek.rescode res, trm; mosek.Task t; int idx; // Optional time limit if (argv.Length >= 2) { double timeLimit = double.Parse(argv[1]); task.putdouparam(mosek.dparam.optimizer_max_time, timeLimit); } if (task.getnumintvar() == 0) { /* If the problem is continuous optimize it with three continuous optimizers. (Simplex will fail for non-linear problems) */ int[] optimizers = { mosek.optimizertype.conic, mosek.optimizertype.dual_simplex, mosek.optimizertype.primal_simplex }; idx = optimizeconcurrent(task, optimizers, out t, out trm, out res); } else { /* Mixed-integer problem. Try various seeds. */ int[] seeds = { 42, 13, 71749373 }; idx = optimizeconcurrentMIO(task, seeds, out t, out trm, out res); } // Check results and print the best answer if (idx >= 0) { Console.WriteLine("Result from optimizer with index {0}: res {1} trm {2}", idx, res, trm); t.set_Stream(mosek.streamtype.log, new msgclass("")); t.optimizersummary(mosek.streamtype.log); t.solutionsummary(mosek.streamtype.log); } else { Console.WriteLine("All optimizers failed."); } } } } /** Defines a Mosek callback function whose only function is to indicate if the optimizer should be stopped. */ private class CallbackProxy : mosek.Progress { private bool stop; public CallbackProxy() { stop = false; } public override int progressCB(mosek.callbackcode caller) { // Return non-zero implies terminate the optimizer return stop ? 1 : 0; } public bool Stop { get { return stop; } set { stop = value; } } } /** A simple stream handler. */ class msgclass : mosek.Stream { public msgclass (string prfx) { } public override void streamCB (string msg) { Console.Write("{0}", msg); } } } }