Expressions

Expression functions for extracting values from interval variables.

Basic Accessors

start_of

pycsp3_scheduling.expressions.interval_expr.start_of(interval, absent_value=0)[source]

Return an expression representing the start time of an interval.

If the interval is absent (optional and not selected), returns absent_value.

Parameters:
  • interval (IntervalVar) – The interval variable.

  • absent_value (int) – Value to return if interval is absent (default: 0).

Returns:

An expression representing the start time.

Return type:

IntervalExpr

Example

>>> task = IntervalVar(size=10, name="task")
>>> expr = start_of(task)
>>> # Can be used in constraints: start_of(task) >= 5

end_of

pycsp3_scheduling.expressions.interval_expr.end_of(interval, absent_value=0)[source]

Return an expression representing the end time of an interval.

If the interval is absent (optional and not selected), returns absent_value.

FIXME: end_of() still returns an internal IntervalExpr; for pycsp3 objectives use end_time() for now.

Parameters:
  • interval (IntervalVar) – The interval variable.

  • absent_value (int) – Value to return if interval is absent (default: 0).

Returns:

An expression representing the end time.

Return type:

IntervalExpr

Example

>>> task = IntervalVar(size=10, name="task")
>>> expr = end_of(task)
>>> # Can be used in constraints: end_of(task) <= 100

size_of

pycsp3_scheduling.expressions.interval_expr.size_of(interval, absent_value=0)[source]

Return an expression representing the size (duration) of an interval.

If the interval is absent (optional and not selected), returns absent_value.

Parameters:
  • interval (IntervalVar) – The interval variable.

  • absent_value (int) – Value to return if interval is absent (default: 0).

Returns:

An expression representing the size.

Return type:

IntervalExpr

Example

>>> task = IntervalVar(size=(5, 20), name="task")
>>> expr = size_of(task)
>>> # Can be used in constraints: size_of(task) >= 10

length_of

pycsp3_scheduling.expressions.interval_expr.length_of(interval, absent_value=0)[source]

Return an expression representing the length of an interval.

Length can differ from size when intensity functions are used. If the interval is absent, returns absent_value.

Parameters:
  • interval (IntervalVar) – The interval variable.

  • absent_value (int) – Value to return if interval is absent (default: 0).

Returns:

An expression representing the length.

Return type:

IntervalExpr

Example

>>> task = IntervalVar(size=10, length=(8, 12), name="task")
>>> expr = length_of(task)

presence_of

pycsp3_scheduling.expressions.interval_expr.presence_of(interval)[source]

Return a boolean expression representing whether the interval is present.

For mandatory intervals, this is always true. For optional intervals, this is a decision variable.

Parameters:

interval (IntervalVar) – The interval variable.

Returns:

A boolean expression (0 or 1) for presence.

Return type:

IntervalExpr

Example

>>> task = IntervalVar(size=10, optional=True, name="task")
>>> expr = presence_of(task)
>>> # Can be used: presence_of(task) == 1 means task is selected

overlap_length

pycsp3_scheduling.expressions.interval_expr.overlap_length(interval1, interval2, absent_value=0)[source]

Return an expression for the overlap length between two intervals.

The overlap is max(0, min(end1, end2) - max(start1, start2)). If either interval is absent, returns absent_value.

Parameters:
  • interval1 (IntervalVar) – First interval variable.

  • interval2 (IntervalVar) – Second interval variable.

  • absent_value (int) – Value to return if either interval is absent.

Returns:

An expression representing the overlap length.

Return type:

IntervalExpr

Example

>>> task1 = IntervalVar(size=10, name="task1")
>>> task2 = IntervalVar(size=15, name="task2")
>>> expr = overlap_length(task1, task2)
>>> # expr == 0 means no overlap

Utility Functions

expr_min

pycsp3_scheduling.expressions.interval_expr.expr_min(*args)[source]

Return the minimum of multiple expressions.

Parameters:

*args (IntervalExpr | int) – Expressions or integers to take minimum of.

Returns:

An expression representing the minimum.

Return type:

IntervalExpr

expr_max

pycsp3_scheduling.expressions.interval_expr.expr_max(*args)[source]

Return the maximum of multiple expressions.

Parameters:

*args (IntervalExpr | int) – Expressions or integers to take maximum of.

Returns:

An expression representing the maximum.

Return type:

IntervalExpr

IntervalExpr Class

class pycsp3_scheduling.expressions.interval_expr.IntervalExpr(expr_type, interval=None, absent_value=0, operands=<factory>, value=None, _id=-1)[source]

Bases: object

Base class for interval-related expressions.

These expressions represent values derived from interval variables that can be used in constraints and objectives.

expr_type

The type of expression.

Type:

ExprType

interval

The interval variable (if applicable).

Type:

IntervalVar | None

absent_value

Value to use when interval is absent.

Type:

int

operands

Child expressions for compound expressions.

Type:

list[IntervalExpr]

value

Constant value (for literals).

Type:

int | None

expr_type: ExprType
interval: IntervalVar | None = None
absent_value: int = 0
operands: list[IntervalExpr]
value: int | None = None
__post_init__()[source]

Assign unique ID.

__add__(other)[source]

Add two expressions or expression and constant.

__radd__(other)[source]

Right addition.

__sub__(other)[source]

Subtract two expressions or expression and constant.

__rsub__(other)[source]

Right subtraction.

__mul__(other)[source]

Multiply two expressions or expression and constant.

__rmul__(other)[source]

Right multiplication.

__truediv__(other)[source]

Divide two expressions or expression and constant.

__neg__()[source]

Negate expression.

__abs__()[source]

Absolute value of expression.

__eq__(other)[source]

Equality comparison.

__ne__(other)[source]

Inequality comparison.

__lt__(other)[source]

Less than comparison.

__le__(other)[source]

Less than or equal comparison.

__gt__(other)[source]

Greater than comparison.

__ge__(other)[source]

Greater than or equal comparison.

__hash__()[source]

Hash based on unique ID.

__repr__()[source]

String representation.

get_intervals()[source]

Get all interval variables referenced by this expression.

is_comparison()[source]

Check if this is a comparison expression (constraint).

__init__(expr_type, interval=None, absent_value=0, operands=<factory>, value=None, _id=-1)

Sequence Accessor Expressions

These functions access properties of neighboring intervals in a sequence.

Next Accessors

Function

Description

start_of_next(seq, iv, last_val, absent_val)

Start time of next interval

end_of_next(seq, iv, last_val, absent_val)

End time of next interval

size_of_next(seq, iv, last_val, absent_val)

Size of next interval

length_of_next(seq, iv, last_val, absent_val)

Length of next interval

next_arg(seq, iv, last_val, absent_val)

ID of next interval

Previous Accessors

Function

Description

start_of_prev(seq, iv, first_val, absent_val)

Start time of previous interval

end_of_prev(seq, iv, first_val, absent_val)

End time of previous interval

size_of_prev(seq, iv, first_val, absent_val)

Size of previous interval

length_of_prev(seq, iv, first_val, absent_val)

Length of previous interval

prev_arg(seq, iv, first_val, absent_val)

ID of previous interval

Element Expressions

ElementArray

A 1D array wrapper that supports transparent array[variable] indexing syntax.

class pycsp3_scheduling.expressions.element.ElementArray(data)[source]

Bases: Sequence

A wrapper that allows transparent array[variable_index] syntax.

This class wraps a list of values and overrides __getitem__ to create pycsp3 element expressions when indexed with a variable or expression.

Unlike regular Python lists, ElementArray supports: - Integer indexing (returns the value directly) - Variable indexing (returns a pycsp3 element expression)

Example

from pycsp3_scheduling.expressions import ElementArray

# Create an ElementArray from costs costs = ElementArray([10, 20, 30, 40, 50])

# Index with a constant - returns 30 costs[2]

# Index with a variable/expression - returns element expression idx = next_arg(route, interval) cost = costs[idx] # Creates element(costs, idx) automatically

# Use in objective minimize(Sum(costs[next_arg(route, v)] for v in visits))

data

The underlying list of values.

_var_array

Cached pycsp3 VarArray for element constraints.

__init__(data)[source]

Initialize an ElementArray.

Parameters:

data (list[int | float]) – A list of numeric values.

property data: list[int | float]

The underlying data list.

__len__()[source]

Return the number of elements.

__getitem__(index: int) int | float[source]
__getitem__(index: Any) Any

Index the array, supporting both constants and variables.

Parameters:

index (int | Any) – An integer constant or a pycsp3 variable/expression.

Returns:

Returns the value at that index. - If index is a variable/expression: Returns a pycsp3 element expression.

Return type:

  • If index is an int

Example

arr = ElementArray([10, 20, 30]) arr[1] # Returns 20 arr[var_idx] # Returns element expression

__iter__()[source]

Iterate over the values.

ElementMatrix

A 2D matrix that supports indexing with pycsp3 expressions, similar to CP Optimizer’s IloNumArray2. Supports both M[i, j] (tuple) and M[i][j] (chained) syntax.

class pycsp3_scheduling.expressions.element.ElementMatrix(matrix, last_value=0, absent_value=0, _flat_vars=None, _n_rows=-1, _n_cols=-1, _last_type=-1, _absent_type=-1)[source]

Bases: object

A 2D matrix that can be indexed with expressions.

This class wraps a 2D list of values and provides element-style indexing where indices can be pycsp3 variables or expressions. It’s designed to work with next_arg() for computing transition costs in scheduling.

The matrix supports two special values for boundary cases: - last_value: Used when an interval is the last in its sequence - absent_value: Used when an interval is absent (optional and not selected)

Example (CP Optimizer pattern):

# Travel distance matrix indexed by customer types M = ElementMatrix(

matrix=travel_times, # 2D list [from_type][to_type] last_value=depot_distances, # 1D list or scalar for return to depot absent_value=0, # 0 cost if interval is absent

)

# Objective: minimize total travel distance for k in vehicles:

for i in intervals:

cost = M[type_i, next_arg(route[k], visit[k][i])]

matrix

2D list of transition costs [from_type][to_type].

Type:

list[list[int | float]]

last_value

Value(s) when next is “last” (end of sequence). Can be a scalar or 1D list indexed by from_type.

Type:

int | float | list[int | float]

absent_value

Value when interval is absent (scalar or 1D list).

Type:

int | float | list[int | float]

n_rows

Number of rows (from_types).

n_cols

Number of columns (to_types).

matrix: list[list[int | float]]
last_value: int | float | list[int | float] = 0
absent_value: int | float | list[int | float] = 0
__post_init__()[source]

Validate and setup the matrix.

property n_rows: int

Number of rows (from_types).

property n_cols: int

Number of columns (to_types), excluding special types.

property last_type: int

Type index representing ‘last’ (end of sequence).

property absent_type: int

Type index representing ‘absent’ (interval not scheduled).

property total_cols: int

Total columns including special types (last, absent).

build_extended_matrix()[source]

Build the full matrix including last and absent columns.

Returns:

  • columns 0..n_cols-1 are regular transitions

  • column n_cols is the “last” value

  • column n_cols+1 is the “absent” value

Return type:

Matrix of shape [n_rows][n_cols + 2] where

__getitem__(index)[source]

Index the matrix with expressions.

Supports two syntaxes: - M[row, col] - tuple indexing - M[row][col] - chained indexing (row returns a proxy)

Parameters:

index (int | tuple | Any) – Can be: - tuple (row, col): Returns element at (row, col) - int: Returns an _ElementMatrixRowProxy for chained indexing - variable/expression: Returns a proxy for deferred column access

Returns:

A pycsp3 element expression for matrix[row][col] - If int/variable: A proxy for M[row][col] chained access

Return type:

  • If tuple

Example

M = ElementMatrix(travel_times, last_value=depot, absent_value=0) cost = M[type_i, next_arg(route, interval)] # tuple syntax cost = M[type_i][next_arg(route, interval)] # chained syntax

get_value(row, col)[source]

Get a constant value from the matrix (no expression).

Use this for debugging or when indices are known constants. For variable indices, use __getitem__ instead.

__init__(matrix, last_value=0, absent_value=0, _flat_vars=None, _n_rows=-1, _n_cols=-1, _last_type=-1, _absent_type=-1)

element

pycsp3_scheduling.expressions.element.element(array, index)[source]

Create an element expression for array indexing with a variable index.

This provides a clean way to access array[index] where index is a pycsp3 variable or expression.

Parameters:
  • array (Sequence) – A list of values or VarArray.

  • index (Any) – A pycsp3 variable, expression, or integer.

Returns:

A pycsp3 element expression.

Return type:

Any

Example

costs = [10, 20, 30, 40, 50] idx = next_arg(route, interval) cost = element(costs, idx) # Returns costs[next_arg(…)]

element2d

pycsp3_scheduling.expressions.element.element2d(matrix, row_idx, col_idx)[source]

Create an element expression for 2D array indexing with variable indices.

This provides matrix[row][col] access where row and col can be expressions.

Parameters:
  • matrix (Sequence[Sequence]) – A 2D list of values.

  • row_idx (Any) – Row index (variable or expression).

  • col_idx (Any) – Column index (variable or expression).

Returns:

A pycsp3 element expression.

Return type:

Any

Example

travel = [[0, 10, 20], [10, 0, 15], [20, 15, 0]] i = type_of(interval_from) j = next_arg(route, interval_from) cost = element2d(travel, i, j)

Usage Examples

Basic Expression Usage

from pycsp3_scheduling import IntervalVar, start_of, end_of, size_of, presence_of

task = IntervalVar(size=(5, 15), optional=True, name="task")

# Get expressions (internal representation)
start_expr = start_of(task)
end_expr = end_of(task)
size_expr = size_of(task)
presence_expr = presence_of(task)

# With absent value for optional intervals
start_expr = start_of(task, absent_value=-1)

Expression Arithmetic

from pycsp3_scheduling import start_of, end_of

task1 = IntervalVar(size=10, name="task1")
task2 = IntervalVar(size=15, name="task2")

# Arithmetic operations
gap = start_of(task2) - end_of(task1)
total_duration = size_of(task1) + size_of(task2)

Comparison Expressions

from pycsp3_scheduling import start_of, end_of

# Build constraint expressions
must_start_early = start_of(task) <= 100
must_end_late = end_of(task) >= 50

Min/Max of Multiple Expressions

from pycsp3_scheduling import expr_min, expr_max, start_of, end_of

tasks = [IntervalVar(size=10, name=f"task{i}") for i in range(3)]

# Earliest start
earliest = expr_min(*(start_of(t) for t in tasks))

# Latest end (makespan)
makespan = expr_max(*(end_of(t) for t in tasks))

next_arg for Distance Objectives (VRPTW Pattern)

The next_arg function returns the ID of the next interval in a sequence, enabling CP Optimizer-style distance objectives. Similar to pycsp3’s maximum_arg pattern:

from pycsp3 import minimize, Sum
from pycsp3_scheduling import IntervalVar, SequenceVar
from pycsp3_scheduling.expressions.sequence_expr import next_arg
from pycsp3_scheduling.expressions.element import ElementMatrix

# Create intervals with IDs (types)
visits = [IntervalVar(size=5, optional=True, name=f"v{i}") for i in range(n)]
route = SequenceVar(intervals=visits, types=list(range(n)), name="route")

# Build cost matrix with boundary values
M = ElementMatrix(
    matrix=travel_costs,           # [from_id][to_id] distances
    last_value=depot_return_costs, # Cost when interval is last
    absent_value=0,                # No cost when interval is absent
)

# Objective: minimize total transition costs
distance_terms = []
for i, visit in enumerate(visits):
    next_id = next_arg(
        route, visit,
        last_value=M.last_type,      # Column index for "last"
        absent_value=M.absent_type,  # Column index for "absent"
    )
    distance_terms.append(M[i, next_id])

minimize(Sum(distance_terms))

ElementMatrix for Transition Costs

from pycsp3_scheduling.expressions.element import ElementMatrix

# Travel distance matrix between customers
travel_costs = [
    [0, 10, 15],   # From customer 0
    [10, 0, 8],    # From customer 1
    [15, 8, 0],    # From customer 2
]

# Return-to-depot distances (per customer)
depot_distances = [20, 18, 22]

# Create matrix with boundary values
M = ElementMatrix(
    matrix=travel_costs,
    last_value=depot_distances,  # When interval is last in sequence
    absent_value=0,              # When interval is not scheduled
)

# Access properties
print(M.n_rows, M.n_cols)  # 3, 3
print(M.last_type)         # 3 (column index for last)
print(M.absent_type)       # 4 (column index for absent)

# Get constant value (debugging)
print(M.get_value(0, 1))        # 10 (travel from 0 to 1)
print(M.get_value(1, M.last_type))  # 18 (return from 1 to depot)

# Use with expressions - both syntaxes work
cost = M[id_i, next_arg(route, interval, M.last_type, M.absent_type)]  # tuple
cost = M[id_i][next_arg(route, interval, M.last_type, M.absent_type)]  # chained

ElementArray for Simple Array Indexing

from pycsp3_scheduling import ElementArray

# Create an array with transparent variable indexing
costs = ElementArray([10, 20, 30, 40, 50])

# Integer indexing - returns value directly
costs[2]  # → 30

# Variable indexing - returns pycsp3 element expression
idx = next_arg(route, interval)
cost = costs[idx]  # Creates element constraint automatically

# Use in objective
minimize(Sum(costs[type_of_next(route, v)] for v in visits))