首页 > 编程问答 >OR-Tools CP-SAT:如何为动态能力和任务建模

OR-Tools CP-SAT:如何为动态能力和任务建模

时间:2024-05-31 13:27:13浏览次数:25  
标签:c# or-tools constraint-programming cp-sat

我正在处理作业车间调度问题,其中我有一些定义为时间间隔变量的任务:

IntervalVar taskInterval = model.NewIntervalVar(start, duration, end, $"interval_{task.WorkOrder_Id}_{task.TaskId}");

每个任务都可以在某个工作站上完成,每个工作站都有自己的日历。

List<UserCalendarsDTO> userCalendarsDTOs = userCalendars.Where(x => x.WorkProviderCode == task.UserWorkStation & x.IdCategory == CalendarGroupCategoryEnum.Available).                                                                OrderBy(x => x.Date).ToList();//we can only planfor (int calendarID = 0; calendarID < userCalendarsDTOs.Count() - 1; ++calendarID){ //add daily constraints...

因此,今天我只能期望工作站 A 有 1 项任务,而明天我希望看到 2 项任务。

我正在查看 AddCumulative 约束,但在我看来,它只能用于 IntervalVar 的静态集合。这并不令人满意,因为我不知道任务何时开始(今天或明天,所以容量必须是 1 或 2)。我查看了问题 OR-tools:

此外,C# 中 AddCumulative 的用法与 Python 中的不同,因此如果能提供相关参考资料,我将不胜感激。

我曾尝试将时间间隔动态添加到集合中,但我没有找到任何方法或工具函数可以做到这一点。


说的对,AddCumulative 约束在 C# 中需要一个静态的间隔变量集。但是,可以使用它来模拟随时间变化的容量,方法是为每个时间步创建一个“虚拟”间隔变量,并使用它来调整该时间步的总容量。

以下是如何使用 C# 和 Google OR-Tools 实现这一点的示例:

using Google.OrTools.Sat;

public class JobShopScheduling
{
    public static void Solve()
    {
        // 数据
        var tasks = new List<(int WorkStation, int Duration, int Day)>
        {
            (0, 1, 0), // 工作站 0,持续时间 1,第 0 天
            (0, 1, 1), // 工作站 0,持续时间 1,第 1 天
            (1, 2, 0), // 工作站 1,持续时间 2,第 0 天
        };
        var numWorkstations = 2;
        var horizon = 2; // 规划范围(天数)

        // 创建模型
        var model = new CpModel();

        // 创建间隔变量
        var taskIntervals = new IntervalVar[tasks.Count];
        for (int i = 0; i < tasks.Count; i++)
        {
            var (workStation, duration, day) = tasks[i];
            var start = model.NewIntVar(day, horizon - 1, $"start_{i}");
            var end = model.NewIntVar(day, horizon, $"end_{i}");
            taskIntervals[i] = model.NewIntervalVar(start, duration, end, $"interval_{i}");
        }

        // 创建虚拟间隔变量以模拟动态容量
        var capacityIntervals = new IntervalVar[numWorkstations, horizon];
        for (int w = 0; w < numWorkstations; w++)
        {
            for (int t = 0; t < horizon; t++)
            {
                // 获取该时间步的容量
                var capacity = GetCapacity(w, t); // 此函数返回时间步 t 工作站 w 的容量

                // 创建虚拟间隔变量
                var start = model.NewConstant(t);
                var end = model.NewConstant(t + 1);
                var interval = model.NewOptionalIntervalVar(start, 1, end, true, $"capacity_{w}_{t}");

                // 如果容量大于 0,则强制虚拟间隔变量为 present
                model.Add(interval.PresenceLiteral == (capacity > 0));

                // 将虚拟间隔变量添加到列表中
                capacityIntervals[w, t] = interval;
            }
        }

        // 为每个工作站添加累积约束
        for (int w = 0; w < numWorkstations; w++)
        {
            // 收集该工作站的所有任务间隔变量
            var workstationTasks = taskIntervals.Where((interval, i) => tasks[i].WorkStation == w).ToList();

            // 创建一个包含所有相关间隔变量的列表
            var allIntervals = workstationTasks.Concat(Enumerable.Range(0, horizon).Select(t => capacityIntervals[w, t])).ToList();

            // 获取每个间隔变量的容量
            var demands = workstationTasks.Select(_ => 1).Concat(Enumerable.Range(0, horizon).Select(t => -GetCapacity(w, t))).ToList();

            // 添加累积约束
            model.AddCumulative(allIntervals.ToArray(), demands.ToArray(), 0, $"cumulative_{w}");
        }

        // 定义目标函数(例如,最小化 makespan)
        var makespan = model.NewIntVar(0, horizon, "makespan");
        model.AddMaxEquality(makespan, taskIntervals.Select(interval => interval.EndExpr).ToArray());
        model.Minimize(makespan);

        // 创建求解器并求解模型
        var solver = new CpSolver();
        var status = solver.Solve(model);

        // 打印解决方案
        if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible)
        {
            Console.WriteLine("Solution:");
            for (int i = 0; i < tasks.Count; i++)
            {
                Console.WriteLine($"  Task {i}: start = {solver.Value(taskIntervals[i].StartExpr)}, end = {solver.Value(taskIntervals[i].EndExpr)}");
            }
            Console.WriteLine($"Makespan: {solver.Value(makespan)}");
        }
        else
        {
            Console.WriteLine("No solution found.");
        }
    }

    // 此函数返回时间步 t 工作站 w 的容量
    private static int GetCapacity(int workstation, int timestep)
    {
        // 在此处实现的逻辑以根据工作站和时间步检索容量
        // 例如,可以从数据库或文件中读取容量数据

        // 示例:
        if (workstation == 0 && timestep == 0) return 1; // 工作站 0 在第 0 天的容量为 1
        if (workstation == 0 && timestep == 1) return 2; // 工作站 0 在第 1 天的容量为 2
        if (workstation == 1) return 1; // 工作站 1 的容量始终为 1
        return 0;
    }
}

在此示例中,我们创建了额外的虚拟间隔变量 capacityIntervals 来表示每个时间步的容量。这些间隔变量的持续时间为 1,并且如果该时间步的容量大于 0,则强制它们为 present。然后,我们使用 AddCumulative 约束来确保每个时间步使用的总容量不超过可用容量。

GetCapacity 函数用于检索每个时间步每个工作站的容量。可以根据的特定用例调整此函数。

这种方法允许使用 AddCumulative 约束来模拟随时间变化的容量,即使 C# 中的约束需要一个静态的间隔变量集也是如此。

标签:
c#,or-tools,constraint-programming,cp-sat
From: 78539870

相关文章

  • Leedcode-反转字符串 II
    自己写的:classSolution:defreverseStr(self,s:str,k:int)->str:#初始化两个空列表:s_li用于存储切分后的字符串片段,res用于存储处理后的片段s_li=[]res=[]#遍历字符串,步长为2*k,切分成每2*k个字符一组的片段并存储在s_li......
  • Leedcode-最长特殊序列 Ⅰ
    自己写的:classSolution:#getMinimumDifference方法接收一个二叉树的根节点root,并返回树中所有节点值的最小差值defgetMinimumDifference(self,root:Optional[TreeNode])->int:#初始化一个列表用于存储树中的节点值myli=[]#使......
  • Leedcode-二叉搜索树的最小绝对差
    自己写的:classSolution:#getMinimumDifference方法接收一个二叉树的根节点root,并返回树中所有节点值的最小差值defgetMinimumDifference(self,root:Optional[TreeNode])->int:#初始化一个列表用于存储树中的节点值myli=[]#使......
  • HTML网页规划与设计【冬季奥林匹克运动会——带报告5200字】HTML+CSS+JavaScript
    ......
  • HTML学生个人网站作业设计:基于HTML+CSS+JavaScript制作简单响应式个人博客HTML模板(8
    ......
  • 彻底关闭解决Windows Defender实时防护(MsMpEng.exe、Antimalware Service Executable
    彻底关闭解决WindowsDefender实时防护MsMpEng.exe、AntimalwareServiceExecutable占用CPU和内存过多win11有效解决方法常规方法步骤一、修改注册表步骤二、组策略关闭WindowsDefender防病毒程序根治方法直接删除WindowsDefender实时防护功能简述解决过程Antima......
  • 创建和管理一个 CA 及证书的生命周期
    0使用openssl或者gmssl,提交markdown格式文档和转化后的pdf1创建一个根CA,包括生成私钥和根证书。分析证书和0015,0034标准的符合情况2为一台服务器生成一个私钥和证书签署请求(CSR)。3使用根CA对服务器的CSR进行签名,生成服务器证书。4吊销该服务器的证书。5提交生成的......
  • Xilinx FPGA NVMe A4S Host Controller, 高性能NVMe A4S主机控制器IP
    NVMeA4SHostControllerIP1     介绍NVMeA4SHostControllerIP可以连接高速存储PCIeSSD,无需CPU和外部存储器,自动加速处理所有的NVMe协议命令,具备独立的数据写入AXI4-Stream/FIFO接口和数据读取AXI4-Stream/FIFO接口,适合于高性能、顺序访问的应用,比如视频记录、信号......
  • 用.NET代码生成JSON Schema 验证器
    问题对于验证复杂JSON数据是否合法的需求,通常的解决方式是标准JSONSchema,.Net下有对应的JSONSchema实现库。应用程序通常需要将标准JSONschema传入实现库,来做后续的数据验证。这里有一种情况,就是如果使用者不太了解标准JSONSchema格式,但又希望能在自己的service中使用其强大......
  • 深入解析C#中的模式匹配:简洁高效的功能探索
    模式匹配是编程领域中一种强大的工具,用于检测表达式是否符合特定条件,C#通过一系列丰富且灵活的模式表达式与语句,极大地简化了这一过程。本文将逐一剖析C#提供的模式匹配特性,揭示其背后的简洁逻辑与强大功能。C#模式匹配核心组件C#模式匹配围绕两个基本构建块展开:表达式/语句和......