Sebastian Jose
Sebastian Jose

Reputation: 309

How to write alternative constraint with interval variable in or-tools?

How to write alternative constraint with interval variable in or-tools? I thought something like this would work but the method AddAlternative doesn´t exist. Another question is how to know if interval variable is active

from ortools.sat.python import cp_model

# Crear el modelo
model = cp_model.CpModel()

# Crear la variable de intervalo
interval = model.NewIntervalVar(start, end, duration, 'interval')

# Crear los intervalos alternativos
alt_interval1 = model.NewIntervalVar(start1, end1, duration1, 'alt_interval1')
alt_interval2 = model.NewIntervalVar(start2, end2, duration2, 'alt_interval2')

# Agregar los intervalos alternativos a la variable de intervalo
model.AddAlternative(interval, [alt_interval1, alt_interval2])

# Resolver el modelo
solver = cp_model.CpSolver()
status = solver.Solve(model)

My code is:

# VARIABLES#
x = [
    model.NewIntervalVar(
        start=model.NewIntVar(es, lf, f"start_{row.id_pozo}"),
        size=model.NewIntVar(es, lf, f"size_{row.id_pozo}"),
        end=model.NewIntVar(es, lf, f"end_{row.id_pozo}"),
        name="pozo_intv_{}".format(row.id_pozo),
    )
    for row in pozos.itertuples()
]

y = [
    model.NewOptionalIntervalVar(
        start=model.NewIntVar(row.dia_inicio, row.dia_fin, f"start_{idx}"),
        size=row.tiempo_total,
        end=model.NewIntVar(row.dia_inicio, row.dia_fin, f"end_{idx}"),
        is_present=True,
        name="pte_intv_{}".format(idx),
    )
    for idx, row in pozo_time_equipment.iterrows()
]

Upvotes: 1

Views: 1100

Answers (1)

DobbyTheElf
DobbyTheElf

Reputation: 789

This is a flexible job shop problem. In the OR tools source there is a working example. The basics are, firstly creating the main interval for each task:

    # Create main interval for the task.
    suffix_name = '_j%i_t%i' % (job_id, task_id)
    start = model.NewIntVar(0, horizon, 'start' + suffix_name)
    duration = model.NewIntVar(min_duration, max_duration,
                               'duration' + suffix_name)
    end = model.NewIntVar(0, horizon, 'end' + suffix_name)
    interval = model.NewIntervalVar(start, duration, end,
                                    'interval' + suffix_name)

Then for each alternative, create an additional interval:

            alt_suffix = '_j%i_t%i_a%i' % (job_id, task_id, alt_id)
            l_presence = model.NewBoolVar('presence' + alt_suffix)
            l_start = model.NewIntVar(0, horizon, 'start' + alt_suffix)
            l_duration = task[alt_id][0]
            l_end = model.NewIntVar(0, horizon, 'end' + alt_suffix)
            l_interval = model.NewOptionalIntervalVar(
                l_start, l_duration, l_end, l_presence,
                'interval' + alt_suffix)
            l_presences.append(l_presence)

The alternative is linked to the main interval, only if the alternative is selected:

            # Link the primary/global variables with the local ones.
            model.Add(start == l_start).OnlyEnforceIf(l_presence)
            model.Add(duration == l_duration).OnlyEnforceIf(l_presence)
            model.Add(end == l_end).OnlyEnforceIf(l_presence)

Then finally, add a constraint to ensure that only one of the alternatives is selected:

        # Select exactly one presence variable.
        model.AddExactlyOne(l_presences)

Upvotes: 4

Related Questions