pumpkin-propagators 0.5.0

The propagators of the Pumpkin constraint programming solver.
Documentation
//! Contains common methods for all of the propagators of the cumulative constraint; this includes
//! methods for propagating but also methods related to creating the
//! input parameters.
use std::rc::Rc;

use enumset::enum_set;
use pumpkin_core::propagation::DomainEvent;
use pumpkin_core::propagation::DomainEvents;
use pumpkin_core::propagation::Domains;
use pumpkin_core::propagation::EventsToRegister;
use pumpkin_core::propagation::LocalId;
use pumpkin_core::propagation::PropagatorConstructorContext;
use pumpkin_core::propagation::ReadDomains;
use pumpkin_core::variables::IntegerVariable;

use crate::cumulative::ArgTask;
use crate::cumulative::Task;

/// Based on the [`ArgTask`]s which are passed, it creates and returns [`Task`]s which have been
/// registered for [`DomainEvents`].
///
/// It sorts [`Task`]s on non-decreasing resource usage and removes [`Task`]s with resource usage 0.
pub(crate) fn create_tasks<Var: IntegerVariable + 'static>(
    arg_tasks: &[ArgTask<Var>],
) -> Vec<Task<Var>> {
    // We order the tasks by non-increasing resource usage, this allows certain optimizations
    let mut ordered_tasks = arg_tasks.to_vec();
    ordered_tasks.sort_by_key(|task| -task.resource_usage);

    let mut id = 0;
    ordered_tasks
        .iter()
        .filter_map(|x| {
            // We only add tasks which have a non-zero resource usage
            if x.resource_usage > 0 {
                let return_value = Some(Task {
                    start_variable: x.start_time.clone(),
                    processing_time: x.processing_time,
                    resource_usage: x.resource_usage,
                    id: LocalId::from(id),
                });

                id += 1;
                return_value
            } else {
                None
            }
        })
        .collect::<Vec<Task<Var>>>()
}

pub(crate) fn register_tasks<Var: IntegerVariable + 'static>(
    tasks: &[Rc<Task<Var>>],
    mut context: PropagatorConstructorContext<'_>,
    register_backtrack: bool,
) -> EventsToRegister {
    let mut registration = EventsToRegister::builder();

    for task in tasks.iter() {
        registration = registration.add(
            &task.start_variable,
            DomainEvents::new(enum_set!(
                DomainEvent::LowerBound | DomainEvent::UpperBound | DomainEvent::Assign
            )),
            task.id,
        );

        if register_backtrack {
            context.register_backtrack(
                task.start_variable.clone(),
                DomainEvents::new(enum_set!(
                    DomainEvent::LowerBound | DomainEvent::UpperBound | DomainEvent::Assign
                )),
                task.id,
            );
        }
    }

    registration.build()
}

/// Updates the bounds of the provided [`Task`] to those stored in
/// `context`.
pub(crate) fn update_bounds_task<Var: IntegerVariable + 'static>(
    context: Domains,
    bounds: &mut [(i32, i32)],
    task: &Rc<Task<Var>>,
) {
    bounds[task.id.unpack() as usize] = (
        context.lower_bound(&task.start_variable),
        context.upper_bound(&task.start_variable),
    );
}

/// Determines whether the stored bounds are equal when propagation occurs
pub(crate) fn check_bounds_equal_at_propagation<Var: IntegerVariable + 'static>(
    context: Domains,
    tasks: &[Rc<Task<Var>>],
    bounds: &[(i32, i32)],
) -> bool {
    tasks.iter().all(|current| {
        bounds[current.id.unpack() as usize]
            == (
                context.lower_bound(&current.start_variable),
                context.upper_bound(&current.start_variable),
            )
    })
}