我刚刚开始使用求解器,但我仍然几乎不知道自己在做什么,但是:
假设我有各种各样的 JOB Shop Scheduling 问题,并且我遵循了一个教程并得到了一个可行的解决方案,现在我正在尝试对此进行扩展:
我有一组要在 X 资源(机器)上完成的任务,我想创建一个时间表,其中 a) 任务尽可能快地完成 b) 我想尽量减少这样做所需的时间,同时考虑到类型开关。
同一台机器可以做不同的处理不同类型的任务,但是在切换类型的时候,有一个1h的时间段机器需要Setup,但是我不知道怎么处理这个约束,这是我从教程中得到的.
private void InitializeGoal()
{
Goal goal = model.AddGoal("goal", GoalKind.Minimize, makespan); // (34)
}
/// <summary>Create the model.
/// </summary>
/// <param name="project">The project to be scheduled.</param>
public void Initialize(Project project)
{
context = SolverContext.GetContext();
context.ClearModel();
model = context.CreateModel();
int eventCount = project.Tasks.Count;
// we will fill these in in the remainder of the post.
InitializeSets(project.Tasks.Count, eventCount, project.Resources.Count);
InitializeParameters(project.Tasks, project.Resources);
InitializeDecisions();
InitializeGoal();
InitializeConstraints(project, eventCount);
}
private void InitializeSets(int taskCount, int eventCount, int resourceCount)
{
tasks = new Set(Domain.Real, "tasks");
events = new Set(0, eventCount, 1);
events1ToN = new Set(1, eventCount, 1); // starting from event 1.
}
private void InitializeParameters(ICollection<Task> t, ICollection<Resource> r)
{
entrega = new Parameter(Domain.Integer, "entrega", tasks);
entrega.SetBinding(t, "Entrega", "ID");
molde = new Parameter(Domain.Integer, "molde", tasks);
molde.SetBinding(t, "Molde", "ID");
duration = new Parameter(Domain.RealNonnegative, "duration", tasks);
duration.SetBinding(t, "Duration", "ID");
model.AddParameters(duration,molde,entrega);
}
private void InitializeDecisions()
{
makespan = new Decision(Domain.RealNonnegative, "makespan");
isActive = new Decision(Domain.IntegerRange(0, 1), "isActive", tasks, events);
start = new Decision(Domain.RealNonnegative, "start", events);
modelSwitch = new Decision(Domain.Boolean, "modelSwitch", tasks);
model.AddDecisions(makespan, isActive, start, modelSwitch);
}
private void InitializeConstraints(Project project, int eventCount)
{
// Establish the makespan: the maximum finish time over all activities.
model.AddConstraint("c35",
Model.ForEach(events1ToN, e =>
Model.ForEach(tasks, i =>
makespan >= start[e] + (isActive[i, e] - isActive[i, e - 1]) * duration[i] )
));
model.AddConstraint("ValidDeliveryDate",
Model.ForEach(tasks, e => start[e] + duration[e] <= entrega[e]));
// The first event starts at time 0.
model.AddConstraint("c_36", start[0] == 0);
// Link the isActive decision to the starts of each event and task durations.
model.AddConstraint("c_37",
Model.ForEach(events1ToN, e =>
Model.ForEachWhere(events, f =>
Model.ForEach(tasks, i =>
start[f] >= start[e] + ((isActive[i, e] - isActive[i, e - 1]) - (isActive[i, f] - isActive[i, f - 1]) - 1) * duration[i]
), f => f > e)));
// Link the isActive decision with the first event. This constraint is missing in the original
// paper.
model.AddConstraint("c_37_e0",
Model.ForEach(events1ToN, f =>
Model.ForEach(tasks, i =>
start[f] >= start[0] + (isActive[i, 0] - (isActive[i, f] - isActive[i, f - 1]) - 1) * duration[i]
)));
// Order the events.
model.AddConstraint("c_38", Model.ForEach(events1ToN, e => start[e] >= start[e - 1]));
// Ensure adjacency of events for an in-progress task.
SumTermBuilder sum = new SumTermBuilder(eventCount);
for (int i = 0; i < project.Tasks.Count; i++)
{
for (int e = 1; e < eventCount; e++)
{
sum.Add(isActive[i, e - 1]);
model.AddConstraint("c_39_" + i + "_" + e,
sum.ToTerm() <= e * (1 - (isActive[i, e] - isActive[i, e - 1])));
}
sum.Clear();
}
sum = new SumTermBuilder(eventCount);
for (int i = 0; i < project.Tasks.Count; i++)
{
for (int e = 1; e < eventCount; e++)
{
for (int e1 = e; e1 < eventCount; e1++)
{
sum.Add(isActive[i, e1]); // (it's inefficient to reconstruct this for each value of e.)
}
model.AddConstraint("c_40_" + i + "_" + e,
sum.ToTerm() <= e * (1 + (isActive[i, e] - isActive[i, e - 1])));
sum.Clear();
}
}
// All activities must be active during the project.
model.AddConstraint("c_41", Model.ForEach(tasks, i =>
Model.Sum(Model.ForEach(events, e => isActive[i, e])) >= 1));
// A link (i, j) means that the start of task j must be after the finish of task i.
int c42 = 0;
foreach (TaskDependency link in project.Dependencies)
{
int i = link.Source.ID;
int j = link.Destination.ID;
sum = new SumTermBuilder(eventCount);
for (int e = 0; e < eventCount; e++)
{
sum.Add(isActive[j, e]); // sum now has isActive[j, 0] .. isActive[j, e].
model.AddConstraint("c_42_" + c42++, isActive[i, e] + sum.ToTerm() <= 1 + e * (1 - isActive[i, e]));
}
}
// Resource usage during each event must not exceed resource capacity.
Dictionary<Resource, int> resToId = new Dictionary<Resource, int>(project.Resources.Count);
for (int k = 0; k < project.Resources.Count; k++)
{
resToId[project.Resources[k]] = k;
}
SumTermBuilder[] totalResWork = new SumTermBuilder[project.Resources.Count];
int c43 = 0;
for (int e = 0; e < eventCount; e++)
{
for (int taskID = 0; taskID < project.Tasks.Count; taskID++)
{
foreach (var assn in project.Tasks[taskID].Assignments)
{
int resID = resToId[assn.Resource];
if (totalResWork[resID] == null)
{
totalResWork[resID] = new SumTermBuilder(5); // most tasks will have <= 4 assignments.
}
totalResWork[resID].Add(assn.Units * isActive[taskID, e]);
}
}
for (int resID = 0; resID < totalResWork.Length; resID++)
{
if (totalResWork[resID] != null)
{
model.AddConstraint("c43_" + c43++, totalResWork[resID].ToTerm() <= project.Resources[resID].MaxUnits);
totalResWork[resID] = null; // note: memory churn...
}
}
}
}
我添加了约束来验证任务是否在交付数据之前完成(在本例中为 entrega):
model.AddConstraint("ValidDeliveryDate",
Model.ForEach(tasks, e => start[e] + duration[e] <= entrega[e]));
它似乎可以正常工作,但现在我想考虑任务 Molde(Aka 类型),基本上,我想减少任务类型之间的切换次数以及完成此任务所需的时间。
我认为最快的方法是更改任务持续时间以适应切换时间,但我不知道如何/在哪里这样做。
我真的不知道所有这些求解器/优化问题,但是项目提出了这可能有用的地方,因此非常感谢任何见解/解释。
编辑:我想到了一个可能的解决方案,即添加一个约束,在两个连续的任务上强制执行该约束,如果 Molde 变量发生变化,则引入 1h 间隙,但我也不知道如何实现。