## ams_version=1.0 Model Main_Flow_Shop { Comment: { "Asynchronous solver sessions Problem type: MIP (medium) Keywords: Parallel solver sessions, asynchronous, GMP, Threads Description: This example demonstrates how asynchronous solver sessions can be used to solve multiple instances of the FlowShop problem in parallel. For each solver session you can specify the amount of threads to be used by the solver. That way you can, e.g., solve two instances in parallel by using two solver sessions whereby each solver session uses 4 threads. Note: Multiple threads are only supported by CPLEX and Gurobi." } DeclarationSection Declaration_Model { Parameter MaxJobs { Text: "Number of elements in the set Jobs"; Range: integer; } Set Jobs { Text: "Set of all Jobs"; Index: j, j2; Definition: ElementRange(1,MaxJobs, prefix: "Job-"); } Set Schedules { Text: "Set of all schedules"; Index: s; Definition: ElementRange(1,MaxJobs, prefix: "Pos-"); } Parameter MaxMachines { Text: "Number of elements in the set Machines"; Range: integer; } Set Machines { Text: "Set of all machines"; Index: m; Definition: ElementRange(1,MaxMachines, prefix: "M-"); } Parameter ProcesTime { IndexDomain: (j,m); Text: "Time required to process job j on machine m"; } Variable JobSchedule { IndexDomain: (j,s); Text: "Determining the place of the job"; Range: binary; } Variable StartTime { IndexDomain: (s,m); Text: "The time at which job in schedule position s commences processing on machine m"; Range: nonnegative; } Constraint OneJobPerSchedule { IndexDomain: (s); Text: "Only one job is related to every schedule"; Definition: sum(j, JobSchedule(j,s)) = 1; } Constraint OneSchedulePerJob { IndexDomain: (j); Text: "Only one schedule is related to every job"; Definition: sum(s, JobSchedule(j,s)) = 1; } Constraint MachineStartTime { IndexDomain: (s,m) | m <> last(Machines); Text: "The commencement of schedule s on machine m+1 is no earlier then its finish on machine m"; Definition: StartTime(s,m + 1) >= StartTime(s,m) + sum(j, ProcesTime(j,m)*JobSchedule(j,s)); } Constraint ScheduleStartTime { IndexDomain: (s,m) | s <> last(Schedules); Text: "The commencement of schedule s + 1 on machine m is no earlier then the finish time of job j on machine m"; Definition: StartTime(s + 1,m) >= StartTime(s,m) + sum(j, ProcesTime(j,m)*JobSchedule(j,s)); } Variable TimeSpan { Text: "Total time to process all jobs on all machines"; Definition: StartTime(Last(s),last(m)) + sum(j, ProcesTime(j,last(m))*JobSchedule(j,Last(s))); } MathematicalProgram FlowShop { Objective: TimeSpan; Direction: minimize; Constraints: AllConstraints; Variables: AllVariables; Type: MIP; } } DeclarationSection Declaration_GMP { Set Runs { SubsetOf: Integers; Index: n; Definition: { { 1 .. 22 } } } ElementParameter RunGMP { IndexDomain: n; Range: AllGeneratedMathematicalPrograms; } ElementParameter RunSolverSession { IndexDomain: n; Range: AllSolverSessions; } StringParameter RunName { IndexDomain: n; } ElementParameter sessionA { Range: AllSolverSessions; Comment: "Solver session A"; } ElementParameter sessionB { Range: AllSolverSessions; Comment: "Solver session B"; } ElementParameter SessionA_Solver { Range: SolversMIP; } ElementParameter SessionB_Solver { Range: SolversMIP; } ElementParameter pwA { Range: AllProgressCategories; Comment: "Progress category for session A"; } ElementParameter pwB { Range: AllProgressCategories; Comment: "Progress category for session B"; } ElementParameter FinishedSession { Range: AllSolverSessions; } Set ActiveSolverSessions { SubsetOf: AllSolverSessions; } Set UserSolverSessions { SubsetOf: AllSolverSessions; Index: uss; Definition: { AllSolverSessions - Element( AllSolverSessions, 21 ) - Element( AllSolverSessions, 22 ) - Element( AllSolverSessions, 23 ) } } Parameter SessionNr { Range: integer; } } DeclarationSection Declaration_Page { StringParameter BeginTime; Quantity SI_Time_Duration { BaseUnit: s; Conversions: { second -> s : # -> #, tick -> s : # -> # / 100 } } Parameter sessionStart { IndexDomain: IndexSolverSessions; Unit: s; Comment: "Start time of sessions"; } Parameter sessionEnd { IndexDomain: IndexSolverSessions; Unit: s; Comment: "End time of sessions"; } ElementParameter sessionColor { IndexDomain: IndexSolverSessions; Range: AllColors; Comment: { "Color of sessions. The color is determined by the solver session to which the session was assigned: A (blue) or B (yellow)." } } ElementParameter ColorA { Range: AllColors; InitialData: 'blue'; } ElementParameter ColorB { Range: AllColors; InitialData: 'yellow'; } StringParameter sessionStringA { Definition: "Session A"; } StringParameter sessionStringB { Definition: "Session B"; } Set SolversMIP { SubsetOf: AllSolvers; Index: sm; } Parameter ThreadLimitA { Range: integer; Default: 1; } Parameter ThreadLimitB { Range: integer; Default: 1; } } Procedure MainInitialization { Body: { MaxJobs := 9; MaxMachines := 12; ProcesTime(j,m) := round(Uniform(1,20)); for (IndexSolvers) do if ( FindString(IndexSolvers, "CPLEX") and not FindString(IndexSolvers, "ODH") ) then SolversMIP += {IndexSolvers}; elseif ( FindString(IndexSolvers, "GUROBI") ) then SolversMIP += {IndexSolvers}; elseif ( FindString(IndexSolvers, "CBC") ) then SolversMIP += {IndexSolvers}; SessionA_Solver := IndexSolvers; SessionB_Solver := IndexSolvers; endif; endfor; for (sm) do if ( FindString(sm, "Gurobi") ) then SessionA_Solver := sm; SessionB_Solver := sm; endif; endfor; for (sm) do if ( FindString(sm, "CPLEX") ) then SessionA_Solver := sm; SessionB_Solver := sm; endif; endfor; } } Procedure MainExecution { Body: { ! Generate the problems and corresponding solver sessions. for ( n ) do ProcesTime('Job-1',m) := round(Uniform(1,20)); RunName(n) := "s" + n; RunGMP(n) := GMP::Instance::Generate( FlowShop, RunName(n) ); RunSolverSession(n) := GMP::Instance::CreateSolverSession( RunGMP(n), Name: RunName(n) ); endfor; SessionNr := 1; BeginTime := CurrentToString("%c%y-%m-%d %H:%M:%S:%t"); empty SessionEnd, sessionStart; PageRefreshAll; while ( SessionNr <= card(n) ) do if ( SessionNr = 1 ) then ! Start with first two solver sessions sessionA := GMP::Instance::CreateSolverSession( RunGMP(1), Name: RunName(1), SessionA_Solver ); sessionB := GMP::Instance::CreateSolverSession( RunGMP(2), Name: RunName(2), SessionB_Solver ); pwA := GMP::SolverSession::CreateProgressCategory( sessionA ); pwB := GMP::SolverSession::CreateProgressCategory( sessionB ); sessionStart(sessionA) := CurrentToMoment([tick],BeginTime); sessionStart(sessionB) := CurrentToMoment([tick],BeginTime); SetThreadLimit( sessionA, ThreadLimitA ); SetThreadLimit( sessionB, ThreadLimitB ); GMP::SolverSession::AsynchronousExecute( sessionA ); GMP::SolverSession::AsynchronousExecute( sessionB ); SessionColor(sessionA) := ColorA; SessionColor(sessionB) := ColorB; SessionNr += 2; ActiveSolverSessions := sessionA + sessionB; elseif ( FinishedSession = sessionA ) then ! Session A ended; take next session to replace it SessionEnd(sessionA) := CurrentToMoment([tick],BeginTime); if ( SessionNr <= card(n)-2 ) then sessionA := GMP::Instance::CreateSolverSession( RunGMP(SessionNr), Name: RunName(SessionNr), SessionA_Solver ); sessionStart(sessionA) := CurrentToMoment([tick],BeginTime); GMP::ProgressWindow::Transfer( pwA, sessionA ); SetThreadLimit( sessionA, ThreadLimitA ); GMP::SolverSession::AsynchronousExecute( sessionA ); SessionColor(sessionA) := ColorA; ActiveSolverSessions := sessionA + sessionB; else ActiveSolverSessions := sessionB; endif; SessionNr += 1; else ! Session B ended; take next session to replace it SessionEnd(sessionB) := CurrentToMoment([tick],BeginTime); if ( SessionNr <= card(n)-2 ) then sessionB := GMP::Instance::CreateSolverSession( RunGMP(SessionNr), Name: RunName(SessionNr), SessionB_Solver ); sessionStart(sessionB) := CurrentToMoment([tick],BeginTime); GMP::ProgressWindow::Transfer( pwB, sessionB ); SetThreadLimit( sessionB, ThreadLimitB ); GMP::SolverSession::AsynchronousExecute( sessionB ); SessionColor(sessionB) := ColorB; ActiveSolverSessions := sessionA + sessionB; else ActiveSolverSessions := sessionA; endif; SessionNr += 1; endif; FinishedSession := GMP::SolverSession::WaitForSingleCompletion( ActiveSolverSessions ); PageRefreshAll; endwhile; } } Procedure SetThreadLimit { Arguments: (session,ThreadLimit); Body: { SessionSolver := GMP::SolverSession::GetSolver( session ); if ( FindString(SessionSolver, "CPLEX") ) then GMP::SolverSession::SetOptionValue( session, "Global Thread Limit", ThreadLimit ); elseif ( FindString(SessionSolver, "GUROBI") ) then GMP::SolverSession::SetOptionValue( session, "Thread Limit", ThreadLimit ); endif; } Comment: "Note: CBC cannot use multiple threads."; ElementParameter Session { Range: AllSolverSessions; Property: Input; } Parameter ThreadLimit { Range: integer; Property: Input; } ElementParameter SessionSolver { Range: AllSolvers; } } Procedure MainTermination { Body: { return 1; } } }