Search code examples
javajob-schedulingor-toolscp-sat

Or-tools: How to use BoolVar/ Change bounds of exisitng IntVar


I wanted to expand the job shop problem for the CP-Sat Solver by adding optional tasks,that don't have to be executed. But my problem is that I don't really know how "remove" a task from the model if a BoolVar is true.

I added a boolean optional to the class Task and a BoolVar active to the class TaskType

public class Task {
    int machine;
    int[] duration;
    String name;
    boolean optional;
    Task(int machine, int[] duration, String name, boolean optional) {
        this.machine = machine;
        this.duration = duration;
        this.name = name;
        this.optional = optional;
    }
}

public class TaskType {
    IntVar start;
    IntVar end;
    IntervalVar interval;
    BoolVar active;
}

My first idea was that if taskType.active == false, then I don't add the taskType to the allTasks list, so it isn't considered by the model as a task that needs to be executed. But to my understanding, there is no way to compare a BoolVar because it's state is technically undefined (if you know what I mean?)

In the part where we create the taskTypes in the link above, (a part of) my code looks as follows:

if(task.optional) {
    taskType.active = model.newBoolVar(task.name + "_activeStatus");
}

taskType.interval = model.newIntervalVar(
      taskType.start,
      model.newIntVar(task.duration[0], task.duration[1], task.name + "_duration"),
      taskType.end,
      "interval" + suffix);

task.duration is an int array, which has the minimum duration in duration[0] and the maximum duration in duration[1].

At this point I honestly don't know how to proceed. I thought about setting the duration of taskType.duration to zero if taskType.active == true, maybe by using onlyEnforceIf() , so that it doesn't effect the final duration of all tasks, and then filter the tasks out in the output. But then again, I haven't found a way to use onlyEnforceIf() this way.

My problem essentially is that I want to make the taskType "irrelevant" if its BoolVar active is true, maybe by changing an existing IntVars bounds (but I also haven't found out if that is even possible).

Maybe some of you have a hint for me, thanks in advance.


Solution

  • Without analyzing your code, it seems you are going the wrong way.

    IntervalVar supports optionality out of the box.

    "An important aspect is optionality: an IntervalVar can be performed or not. If unperformed, then it simply does not exist, and its characteristics cannot be accessed any more. An interval var is automatically marked as unperformed when it is not consistent anymore (start greater than end, duration < 0...)"

    So this activity-controlling bool-var already exists as part of the variable if setup in this way.

    (They also contain start- and end intvars which is another thing which looks strange in your question)

    While there are much more C++ and Python examples, this java example does something with optionality.

    One of those python examples would be Gate scheduling where you for example see:

        # Create an optional copy of interval to be executed on machine 1.
        start1 = model.new_int_var(0, horizon, "start_%i_on_m1" % i)
        end1 = model.new_int_var(0, horizon, "end_%i_on_m1" % i)
        interval1 = model.new_optional_interval_var(
            start1,
            duration,
            end1,
            ~performed_on_m0,
            "interval_%i_on_m1" % i,
        )
        intervals1.append(interval1)
    
        # We only propagate the constraint if the tasks is performed on the machine.
        model.add(start0 == start).only_enforce_if(performed_on_m0)
        model.add(start1 == start).only_enforce_if(~performed_on_m0)