https://github.com/lmmx/polars-scheduler
Schedule recurring events with constraints
https://github.com/lmmx/polars-scheduler
polars-dataframe scheduling-algorithms
Last synced: about 2 months ago
JSON representation
Schedule recurring events with constraints
- Host: GitHub
- URL: https://github.com/lmmx/polars-scheduler
- Owner: lmmx
- Created: 2025-03-08T20:17:46.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2025-03-24T22:04:21.000Z (about 2 months ago)
- Last Synced: 2025-03-24T23:19:40.458Z (about 2 months ago)
- Topics: polars-dataframe, scheduling-algorithms
- Language: Rust
- Homepage: https://pypi.org/project/polars-scheduler/
- Size: 120 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Polars Scheduler
[](https://pepy.tech/project/polars-scheduler)
[](https://github.com/astral-sh/uv)
[](https://pdm.fming.dev)
[](https://pypi.org/project/polars-scheduler)
[](https://pypi.org/project/polars-scheduler)
[](https://pypi.python.org/pypi/polars-scheduler)
[](https://results.pre-commit.ci/latest/github/lmmx/polars-scheduler/master)A Polars plugin for easily scheduling recurring events with constraints.
## Installation
```bash
pip install polars-scheduler[polars]
```On older CPUs run:
```bash
pip install polars-scheduler[polars-lts-cpu]
```## Features
- **Powerful Constraint System**: Schedule events with constraints like "≥6h apart", "≥1h before food", or "≥2h after medicine"
- **Time Windows**: Define preferred time windows for events (e.g., breakfast at "08:00", or dinner between "18:00-20:00")
- **Optimization Strategies**: Choose between "earliest" (soonest possible scheduling) or "latest" (just-in-time)
- **Smart Distribution**: Automatically distributes multiple daily instances across different time windows
- **Mixed Integer Linear Programming**: Uses MILP solver to find optimal schedules that satisfy all constraints## Usage
The plugin adds a `scheduler` namespace to Polars DataFrames with methods for registering events and constraints.
Here is a full demo showing how to:
- create a schedule
- add events with constraints
- generate both an "earliest" and a "latest" schedule```python
import polars as pl
from polars_scheduler import Scheduler# Create a new empty schedule
scheduler = Scheduler()# Add simple meal and medication schedule
scheduler.add(
event="chicken",
category="food",
unit="meal",
frequency="3x daily",
windows=["08:00", "12:00-14:00", "19:00"],
)scheduler.add(
event="vitamin",
category="supplement",
unit="pill",
frequency="1x daily",
constraints=["≥1h after food"],
)scheduler.add(
event="antibiotic",
category="medication",
unit="pill",
frequency="2x daily",
constraints=["≥6h apart", "≥1h before food"],
)scheduler.add(
event="probiotic",
category="supplement",
unit="capsule",
frequency="1x daily",
constraints=["≥2h after antibiotic"],
)scheduler.add(
event="protein shake",
category="supplement",
unit="gram",
amount=30,
frequency="1x daily",
constraints=[],
windows=["11:00"],
note="mix with 300ml water",
)scheduler.add(
event="ginger",
category="supplement",
unit="shot",
frequency="1x daily",
constraints=["≥1h before food"],
)# Print the original schedule
print("--- Schedule Constraints ---")
print(scheduler._df)# Generate an optimized schedule (Earliest)
result = scheduler.create(
strategy="earliest",
day_start="07:00",
day_end="22:00",
)print("\n--- Optimized Schedule (Earliest) ---")
print(result.select(["entity_name", "instance", "time_hhmm", "Category"]))# Generate an optimized schedule (Latest)
result_latest = scheduler.create(
strategy="latest",
day_start="07:00",
day_end="22:00",
)print("\n--- Latest Schedule ---")
print(result_latest.select(["entity_name", "instance", "time_hhmm", "Category"]))
```Example output:
```
--- Schedule Constraints ---
shape: (6, 9)
┌────────────┬────────────┬─────────┬────────┬───┬───────────┬────────────┬────────────┬───────────┐
│ Event ┆ Category ┆ Unit ┆ Amount ┆ … ┆ Frequency ┆ Constraint ┆ Windows ┆ Note │
│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ s ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ f64 ┆ ┆ str ┆ --- ┆ list[str] ┆ str │
│ ┆ ┆ ┆ ┆ ┆ ┆ list[str] ┆ ┆ │
╞════════════╪════════════╪═════════╪════════╪═══╪═══════════╪════════════╪════════════╪═══════════╡
│ chicken ┆ food ┆ meal ┆ null ┆ … ┆ 3x daily ┆ [] ┆ ["08:00", ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ ┆ "12:00-14: ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 00", ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ ┆ "19:0… ┆ │
│ vitamin ┆ supplement ┆ pill ┆ null ┆ … ┆ 1x daily ┆ ["≥1h ┆ [] ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ after ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ food"] ┆ ┆ │
│ antibiotic ┆ medication ┆ pill ┆ null ┆ … ┆ 2x daily ┆ ["≥6h ┆ [] ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ apart", ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ "≥1h ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ before ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ food… ┆ ┆ │
│ probiotic ┆ supplement ┆ capsule ┆ null ┆ … ┆ 1x daily ┆ ["≥2h ┆ [] ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ after anti ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ biotic"] ┆ ┆ │
│ protein ┆ supplement ┆ gram ┆ 30.0 ┆ … ┆ 1x daily ┆ [] ┆ ["11:00"] ┆ mix with │
│ shake ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 300ml │
│ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ water │
│ ginger ┆ supplement ┆ shot ┆ null ┆ … ┆ 1x daily ┆ ["≥1h ┆ [] ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ before ┆ ┆ │
│ ┆ ┆ ┆ ┆ ┆ ┆ food"] ┆ ┆ │
└────────────┴────────────┴─────────┴────────┴───┴───────────┴────────────┴────────────┴───────────┘--- Optimized Schedule (Earliest) ---
shape: (9, 4)
┌───────────────┬──────────┬───────────┬────────────┐
│ entity_name ┆ instance ┆ time_hhmm ┆ Category │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i32 ┆ str ┆ str │
╞═══════════════╪══════════╪═══════════╪════════════╡
│ vitamin ┆ 1 ┆ 07:00 ┆ supplement │
│ antibiotic ┆ 1 ┆ 07:00 ┆ medication │
│ probiotic ┆ 1 ┆ 07:00 ┆ supplement │
│ protein shake ┆ 1 ┆ 07:00 ┆ supplement │
│ ginger ┆ 1 ┆ 07:00 ┆ supplement │
│ chicken ┆ 3 ┆ 07:30 ┆ food │
│ chicken ┆ 1 ┆ 11:30 ┆ food │
│ antibiotic ┆ 2 ┆ 13:00 ┆ medication │
│ chicken ┆ 2 ┆ 18:30 ┆ food │
└───────────────┴──────────┴───────────┴────────────┘--- Latest Schedule ---
shape: (9, 4)
┌───────────────┬──────────┬───────────┬────────────┐
│ entity_name ┆ instance ┆ time_hhmm ┆ Category │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i32 ┆ str ┆ str │
╞═══════════════╪══════════╪═══════════╪════════════╡
│ chicken ┆ 1 ┆ 08:30 ┆ food │
│ chicken ┆ 3 ┆ 14:30 ┆ food │
│ antibiotic ┆ 1 ┆ 16:00 ┆ medication │
│ chicken ┆ 2 ┆ 19:30 ┆ food │
│ protein shake ┆ 1 ┆ 22:00 ┆ supplement │
│ antibiotic ┆ 2 ┆ 22:00 ┆ medication │
│ vitamin ┆ 1 ┆ 22:00 ┆ supplement │
│ ginger ┆ 1 ┆ 22:00 ┆ supplement │
│ probiotic ┆ 1 ┆ 22:00 ┆ supplement │
└───────────────┴──────────┴───────────┴────────────┘
```## Constraint Types
The scheduler supports several constraint types:
- **Apart constraint**: `"≥6h apart"` - Ensures that multiple instances of the same entity are scheduled at least 6 hours apart
- **Before constraint**: `"≥1h before food"` - Ensures that an entity is scheduled at least 1 hour before any entity in the "food" category
- **After constraint**: `"≥2h after medication"` - Ensures that an entity is scheduled at least 2 hours after any entity in the "medication" categoryWhen both "before" and "after" constraints are specified for the same entity-category pair, they are combined as an "OR" constraint using a big-M formulation, not an "AND" constraint (which would often be impossible to satisfy).
## Window Specifications
Windows can be specified in two formats:
- **Anchor**: `"08:00"` - A specific time point
- **Range**: `"18:00-20:00"` - A time rangeWhen multiple instances of the same entity need to be scheduled, the solver will try to distribute them across different windows.
## Optimization Strategies
- **Earliest**: Places events as early as possible while satisfying all constraints
- **Latest**: Places events as late as possible while satisfying all constraints## Standalone CLI Tool
The project also includes a standalone command-line tool for scheduling:
```bash
cd scheduler-cli
cargo run -- --strategy earliest --start=07:00 --end=22:00
```## Development
To build the project:
1. Build the core library:
```bash
cd scheduler-core
cargo build
```2. Build the CLI tool:
```bash
cd scheduler-cli
cargo build
```3. Build the Python bindings:
```bash
cd polars-scheduler-py
maturin develop
```## License
MIT License