Source code for pycsp3_scheduling.constraints.bounds

"""
Bounds constraints for interval variables.

This module provides convenience constraints for setting time bounds on intervals:

1. **release_date(interval, time)**: Interval cannot start before given time
2. **deadline(interval, time)**: Interval must complete by given time
3. **time_window(interval, earliest_start, latest_end)**: Combined release + deadline

All constraints return pycsp3 Node objects that can be used with satisfy().
"""

from __future__ import annotations

from pycsp3_scheduling.constraints._pycsp3 import (
    _build_end_expr,
    _get_node_builders,
    _validate_interval,
    presence_var,
    start_var,
)
from pycsp3_scheduling.variables.interval import IntervalVar


# =============================================================================
# Bounds Constraints
# =============================================================================


[docs] def release_date(interval: IntervalVar, time: int) -> list: """ Constrain an interval to not start before a given time. The interval's start time must be >= the release date. Args: interval: The interval variable to constrain. time: The earliest allowed start time (release date). Returns: List of pycsp3 constraint nodes. Example: >>> task = IntervalVar(size=10, name="task") >>> satisfy(release_date(task, 8)) # Cannot start before time 8 """ _validate_interval(interval, "release_date") if not isinstance(time, int): raise TypeError(f"release_date: time must be an integer, got {type(time).__name__}") Node, TypeNode = _get_node_builders() start = start_var(interval) constraint = Node.build(TypeNode.GE, start, time) # Handle optional intervals if interval.optional: pres = presence_var(interval) # (presence == 0) OR (start >= time) not_present = Node.build(TypeNode.EQ, pres, 0) constraint = Node.build(TypeNode.OR, not_present, constraint) return [constraint]
[docs] def deadline(interval: IntervalVar, time: int) -> list: """ Constrain an interval to complete by a given time. The interval's end time must be <= the deadline. Args: interval: The interval variable to constrain. time: The latest allowed end time (deadline). Returns: List of pycsp3 constraint nodes. Example: >>> task = IntervalVar(size=10, name="task") >>> satisfy(deadline(task, 50)) # Must finish by time 50 """ _validate_interval(interval, "deadline") if not isinstance(time, int): raise TypeError(f"deadline: time must be an integer, got {type(time).__name__}") Node, TypeNode = _get_node_builders() end = _build_end_expr(interval, Node, TypeNode) constraint = Node.build(TypeNode.LE, end, time) # Handle optional intervals if interval.optional: pres = presence_var(interval) # (presence == 0) OR (end <= time) not_present = Node.build(TypeNode.EQ, pres, 0) constraint = Node.build(TypeNode.OR, not_present, constraint) return [constraint]
[docs] def time_window(interval: IntervalVar, earliest_start: int, latest_end: int) -> list: """ Constrain an interval to execute within a time window. Combines release_date and deadline constraints. Args: interval: The interval variable to constrain. earliest_start: The earliest allowed start time. latest_end: The latest allowed end time. Returns: List of pycsp3 constraint nodes. Raises: ValueError: If earliest_start > latest_end. Example: >>> delivery = IntervalVar(size=30, name="delivery") >>> # Delivery must occur during business hours (9am-5pm as minutes) >>> satisfy(time_window(delivery, earliest_start=9*60, latest_end=17*60)) """ _validate_interval(interval, "time_window") if not isinstance(earliest_start, int): raise TypeError( f"time_window: earliest_start must be an integer, got {type(earliest_start).__name__}" ) if not isinstance(latest_end, int): raise TypeError( f"time_window: latest_end must be an integer, got {type(latest_end).__name__}" ) if earliest_start > latest_end: raise ValueError( f"time_window: earliest_start ({earliest_start}) must be <= latest_end ({latest_end})" ) # Combine release_date and deadline constraints return release_date(interval, earliest_start) + deadline(interval, latest_end)