Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
81477e1
Fix evaluation of moves that leave a route empty (without clients) (#…
N-Wouda Mar 31, 2026
6d709e1
Bump configure-pages and deploy-pages actions (#1091)
N-Wouda Apr 2, 2026
059c87a
Fix a few more Zizmor-flagged workflow concerns (#1092)
N-Wouda Apr 3, 2026
25a595b
Exclude releases from <7 days ago from dependency resolution (#1093)
N-Wouda Apr 3, 2026
87eb81d
Clear when update leaves route empty (#1094)
N-Wouda Apr 3, 2026
1b361f5
Split off data components to separate files (#1096)
N-Wouda Apr 16, 2026
b74eaa1
Phrasing PyVRP Enterprise
N-Wouda May 4, 2026
b22be0c
Track unplanned clients (#1103)
N-Wouda May 5, 2026
51810ca
Bump upload-pages-artifact to v5 (#1104)
N-Wouda May 5, 2026
493670c
Add an AI policy, and a FAQ entry about modifying the C+ components (…
N-Wouda May 7, 2026
d29a1e1
Add Rybbit analytics to docs pages
N-Wouda May 8, 2026
9b47db8
SI1: Data model + Python API
FormelessPuppy41 May 19, 2026
0b8ce95
SI2: bindings and cpp backend
FormelessPuppy41 May 19, 2026
607dd8c
SI3: Validation and Defaults
FormelessPuppy41 May 19, 2026
50dee62
SI4: Model-to-data matrix build path
FormelessPuppy41 May 20, 2026
7702982
SI5: Core route evaluation
FormelessPuppy41 May 20, 2026
1c5d3a8
SI6: Local search evaluation
FormelessPuppy41 May 20, 2026
8958571
benchmark
FormelessPuppy41 May 20, 2026
8de2803
benchmark
FormelessPuppy41 May 20, 2026
5dc7d1a
SI7: testing
FormelessPuppy41 May 20, 2026
ec00e5c
SI8: docs
FormelessPuppy41 May 20, 2026
61215d0
Merge branch 'main' into edge_demand
FormelessPuppy41 May 20, 2026
ffb68be
remove docs
FormelessPuppy41 May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions .github/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@

# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
community a harassment-free experience for everyone. We pledge to act and
interact in ways that contribute to a healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Being respectful of differing opinions, experiences, and each other's time
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
Expand All @@ -37,6 +30,21 @@ Examples of unacceptable behavior include:
* Other conduct which could reasonably be considered inappropriate in a
professional setting

## AI Policy

Using LLMs to assist in writing, translation, and software development is fine,
as long as the final output - whether that be code or text - is correct and up
to standards.

Please do not submit PRs largely based on "AI slop" code. If you cannot explain
an implementation without going back to the LLM, do not submit it. Maintainer
time is scarce, and we ask that you respect that by not forcing maintainers to
be the human-reviewer-in-the-loop for a code generation tool. In any case,
non-trivial code generation tool use must be disclosed in the PR.

Issues and PRs that look like unreviewed LLM output will be closed without
detailed feedback, and constitute a violation of this Code of Conduct.

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
Expand Down
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ In particular:
- Docstring additions must render correctly, including escapes and LaTeX.
- Finally, it is essential that all contributions in this PR are license-compatible with PyVRP's MIT license.
Please check that this PR can be included into PyVRP under the MIT license.
- You must disclose the use of LLMs/generative AI if you used such tools to write code.

</details>
16 changes: 9 additions & 7 deletions .github/workflows/CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions: {}

jobs:
build:
name: Build wheels on ${{ matrix.os }}
Expand All @@ -18,15 +20,15 @@ jobs:
matrix:
os: [ ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-15 ]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Build wheels
uses: pypa/cibuildwheel@v3.2.0
uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1
with:
package-dir: .
output-dir: dist
- uses: actions/upload-artifact@v7 # upload all wheels
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dist-${{ matrix.os }}
path: ./dist/*
Expand All @@ -39,16 +41,16 @@ jobs:
permissions:
id-token: write # for trusted publishing
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.7.5"
version: "0.11.3"
enable-cache: true
python-version: "3.13"
- uses: actions/download-artifact@v4 # download previously built wheels
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: dist-*
merge-multiple: true
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions: {}

jobs:
build:
name: >
Expand All @@ -32,22 +34,22 @@ jobs:
compiler-version: '20'
python-version: '3.14'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.7.5"
version: "0.11.3"
enable-cache: true
- name: Install Python dependencies
run: uv sync --no-install-project
- name: Cache pre-commit
uses: actions/cache@v5
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
id: cache-pre-commit
with:
path: ~/.cache/pre-commit/
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/CodSpeed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,24 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions: {}

jobs:
bench:
name: Run benchmarks
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.13'
- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.7.5"
version: "0.11.3"
enable-cache: true
- name: Install Python dependencies
run: uv sync --no-install-project
Expand Down Expand Up @@ -56,7 +58,7 @@ jobs:
- name: Run benchmarks with CodSpeed
if: ${{ secrets.CODSPEED_TOKEN != '' }}
# We evaluate the microbenchmarks using the release build.
uses: CodSpeedHQ/action@v3
uses: CodSpeedHQ/action@76578c2a7ddd928664caa737f0e962e3085d4e7c # v3.8.1
with:
token: ${{ secrets.CODSPEED_TOKEN }}
run: uv run pytest benchmarks/ --codspeed
Expand Down
14 changes: 8 additions & 6 deletions .github/workflows/DOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ concurrency:
group: "pages"
cancel-in-progress: true

permissions: {}

jobs:
build:
name: "Build documentation"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.7.5"
version: "0.11.3"
enable-cache: true
python-version: "3.13"
- name: Update package index
Expand All @@ -45,7 +47,7 @@ jobs:
- name: Build documentation
run: uv run make html --directory=docs
- name: Upload documentation
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: 'docs/build/html'

Expand All @@ -64,4 +66,4 @@ jobs:
steps:
- name: Deploy documentation
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pip install pyvrp
The documentation is available [here][1].

> [!TIP]
> Looking for professional support or enterprise features? [RoutingLab](https://routinglab.tech) provides consulting, custom development, and FastVRP - a production-ready route optimisation API built on PyVRP.
> Looking for professional support or enterprise features? [RoutingLab](https://routinglab.tech) provides consulting, custom development, and PyVRP Enterprise - an extended and production-ready version of PyVRP.

### Tutorials

Expand Down
124 changes: 124 additions & 0 deletions benchmarks/search/test_EdgeDemands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import numpy as np
import pytest

from pyvrp import (
Client,
CostEvaluator,
Depot,
Location,
ProblemData,
RandomNumberGenerator,
Solution,
VehicleType,
)
from pyvrp.search import (
Exchange11,
LocalSearch,
PerturbationManager,
PerturbationParams,
)

NUM_CLIENTS = 90
SEED = 42


def _distance_matrix(size: int) -> np.ndarray:
idx = np.arange(size, dtype=np.int64)
mat = np.abs(idx[:, None] - idx[None, :]) + 1
np.fill_diagonal(mat, 0)
return mat


def _edge_demand_matrix(size: int) -> np.ndarray:
frm = np.arange(size, dtype=np.int64)[:, None]
to = np.arange(size, dtype=np.int64)[None, :]

# Deterministic directed pattern with non-zero off-diagonals.
mat = ((frm * 17 + to * 31) % 5 == 0).astype(np.int64)
np.fill_diagonal(mat, 0)
return mat


def _make_data(with_edge_demands: bool) -> ProblemData:
size = NUM_CLIENTS + 1 # includes depot

locs = [Location(idx, idx % 13) for idx in range(size)]
clients = [Client(client, delivery=[1]) for client in range(1, size)]
depots = [Depot(0)]

# Keep capacity large so edge demands do not change objective values.
veh_types = [VehicleType(num_available=3, capacity=[1_000_000])]
dist = _distance_matrix(size)
dur = dist.copy()

try:
if with_edge_demands:
return ProblemData(
locations=locs,
clients=clients,
depots=depots,
vehicle_types=veh_types,
distance_matrices=[dist],
duration_matrices=[dur],
edge_demand_matrices=[[_edge_demand_matrix(size)]],
)

return ProblemData(
locations=locs,
clients=clients,
depots=depots,
vehicle_types=veh_types,
distance_matrices=[dist],
duration_matrices=[dur],
)
except TypeError:
if with_edge_demands:
pytest.skip(
"This PyVRP build does not support edge_demand_matrices yet."
)
raise


def _make_search(data: ProblemData):
rng = RandomNumberGenerator(seed=SEED)
neighbours = [
[nb for nb in range(data.num_clients) if nb != client]
for client in range(data.num_clients)
]
ls = LocalSearch(
data,
rng,
neighbours,
PerturbationManager(PerturbationParams(0, 0)),
)
ls.add_operator(Exchange11(data))

# Multiple routes improve move opportunities while keeping deterministic
# route structure across benchmark rounds.
routes = [
list(range(0, 30)),
list(range(30, 60)),
list(range(60, data.num_clients)),
]
sol = Solution(data, routes)
cost_eval = CostEvaluator([1], 0, 0)

return ls, sol, cost_eval


@pytest.mark.parametrize(
"with_edge_demands",
[False, True],
ids=["without_edge_demands", "with_edge_demands"],
)
def test_exchange11_edge_demand_overhead(with_edge_demands, benchmark):
"""
Benchmarks local-search runtime with and without edge demand matrices.

For legacy comparison, run this benchmark on ``main``:
``without_edge_demands`` there is case (1), and on this branch it is case
(2). Case (3) is ``with_edge_demands`` on this branch.
"""
data = _make_data(with_edge_demands)
ls, sol, cost_eval = _make_search(data)
benchmark(ls, sol, cost_eval, exhaustive=True)
10 changes: 10 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,13 @@ def linkcode_resolve(domain: str, info: dict) -> str | None:

# -- Options for EPUB output
epub_show_urls = "footnote"


def setup(app):
# Set up Rybbit analytics.
kwargs = {"data-site-id": "aeb758c34279"}
app.add_js_file(
"https://app.rybbit.io/api/script.js",
loading_method="defer",
**kwargs,
)
1 change: 1 addition & 0 deletions docs/source/dev/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ This greatly reduces the job of maintaining and releasing the software.
- If you are adding new functionality, you need to add it to the documentation by editing (or creating) the appropriate file in ``docs/source/``.
- Make sure your documentation changes parse correctly.
See the documentation in the ``docs/`` directory for details on how to build the documentation locally.
- Disclose the use of LLMs/generative AI if you used such tools to develop the feature in your pull request.

.. note::

Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ It can be installed through *pip* via

.. tip::

Looking for professional support or enterprise features? `RoutingLab <https://routinglab.tech>`_ provides consulting, custom development, and FastVRP - a production-ready route optimisation API built on PyVRP.
Looking for professional support or enterprise features? `RoutingLab <https://routinglab.tech>`_ provides consulting, custom development, and PyVRP Enterprise - an extended and production-ready version of PyVRP.

Contents
--------
Expand Down
Loading
Loading