Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 38 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Contributing to VRP-GPU

Thank you for contributing. This document describes the repository boundaries
and the checks expected before a pull request.
Thank you for contributing. This document defines the repository boundaries
and evidence required for a pull request. Start at the repository root unless
a command explicitly changes directory.

## Development environments

Expand All @@ -12,9 +13,19 @@ The repository intentionally has two Cargo workspaces:
- `crates/vrp-gpu-kernel` is a standalone, unpublished workspace. It uses the
pinned nightly and cuda-oxide toolchain to regenerate `kernel.ptx`.

Do not introduce a root-workspace dependency on the kernel package. Generated
PTX is copied into `crates/vrp-gpu/kernel.ptx` and committed with its source
change.
For CPU algorithms, parsing, documentation and the CUDA **host** code, install
stable Rust with `rustfmt` and `clippy`, plus `just` and `cargo-deny` for the
full gate. The public crate declares MSRV 1.88; test compatibility against
that version when changing public code or dependencies. No CUDA compiler is
needed for CPU changes.

For kernel source changes, use the nightly pinned in
`crates/vrp-gpu-kernel/rust-toolchain.toml`, the pinned cuda-oxide Git revision,
and the toolchain requirements of that revision. The kernel workspace is
excluded from the stable root workspace. See
[kernel development](docs/kernel-development.md) for setup, regeneration and
hardware checks. Do not add a dependency from the root workspace to the
kernel package.

## Git workflow

Expand All @@ -25,15 +36,18 @@ change.
- `hotfix/<slug>`: critical fixes branched from `main`.

Use atomic Conventional Commits such as `feat(gpu): ...` or
`docs(release): ...`. Every commit must contain a Developer Certificate of
Origin trailer and a cryptographic signature:
`docs(release): ...`. Write code, documentation, commit messages and PRs in
English. Every commit must contain a Developer Certificate of Origin trailer
and a cryptographic signature:

```sh
git commit -s -S
```

Feature branches merge into `develop` through a pull request using a non-fast-
forward merge.
Feature branches merge into `develop` through a pull request using a
non-fast-forward merge. Keep data-only benchmark records distinct from the
implementation that produced them. Never rewrite another contributor's
published commits to add a signature.

## Quality gates

Expand All @@ -44,8 +58,14 @@ just ci
```

This checks formatting, all targets and features, Clippy, tests, rustdoc,
package contents and dependency policy. To run parts independently, use
`just --list`.
package contents and dependency policy. `cargo test` skips the existing GPU
hardware tests because they are marked `#[ignore]`; passing `just ci`
alone is not evidence of GPU execution. To run parts independently, use
`just --list`. The package and dependency-policy stages may need network
access for the crates.io index or advisory database.

Use the [validation matrix](docs/testing.md) to select additional checks for
CPU, GPU, package or documentation changes and to report exactly what ran.

Changes to kernel source or the PTX artifact also require:

Expand All @@ -58,6 +78,7 @@ cmp vrp_gpu_kernel.ptx ../vrp-gpu/kernel.ptx

GPU behavior must be checked against the CPU reference on supported hardware.
Hardware-only tests stay marked `#[ignore]` so CPU-only CI remains portable.
Regenerate PTX from the pinned source; never edit the checked-in PTX by hand.

## Pull requests

Expand All @@ -69,4 +90,10 @@ A pull request should:
4. Keep unrelated refactoring in separate commits or pull requests.
5. Avoid performance claims without a reproducible benchmark record.

For a fork that changes public behavior, also state the supported input model,
error and panic behavior, numerical tolerance, and any new hardware assumption.
Tests should compare GPU output with the CPU oracle and, for 2-opt changes,
with a full route-cost recomputation. Do not infer support for another NVIDIA
architecture from the existence of an `sm_120` artifact.

Publication and release steps are documented in [docs/releasing.md](docs/releasing.md).
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
[![CI](https://github.com/pedrozaz/vrp-gpu/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/pedrozaz/vrp-gpu/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

VRP-GPU is an experimental Rust library for vehicle-routing heuristics with an
optional CUDA execution path. The CPU implementation is the correctness
reference; versioned PTX kernels accelerate supported local-search operations
when the `gpu` feature is enabled.
VRP-GPU is an experimental Rust library for capacitated vehicle routing (CVRP).
It parses Solomon-style instances, builds routes with a greedy nearest-neighbor
heuristic, and provides CPU 2-opt local search. An optional CUDA path evaluates
2-opt candidates and selects one improving move. The CPU implementation is the
correctness reference for GPU validation.

> **Status:** active alpha. The crate has not been published to crates.io yet,
> and public APIs may change before the first stable release.
Expand Down Expand Up @@ -42,22 +43,28 @@ vrp-gpu = { git = "https://github.com/pedrozaz/vrp-gpu", features = ["gpu"] }

The default feature set is CPU-only. See the
[crate-specific README](crates/vrp-gpu/README.md) for a minimal example and the
public feature contract.
public feature contract. The current internal CLI prints a placeholder message;
use the library API for actual routing work.

## Compatibility

- **Rust:** MSRV 1.88 for the supported feature set; edition 2024.
- **CPU path:** does not require CUDA or an NVIDIA GPU.
- **GPU path:** currently developed and hardware-validated on Linux with an
NVIDIA GeForce RTX 5060 Ti (`sm_120`). Broader hardware support is not yet
claimed.
claimed. A CUDA driver and a device able to load the checked-in PTX are
required at runtime; enabling the Cargo feature alone does not supply them.
- **Kernel development:** uses the toolchain pinned in
`crates/vrp-gpu-kernel/rust-toolchain.toml` and the pinned cuda-oxide revision.

## Documentation

- [Library usage and API contract](crates/vrp-gpu/README.md)
- [Input model and algorithm semantics](docs/user-guide.md)
- [Architecture and publication boundaries](docs/architecture.md)
- [Deterministic 2-opt reduction](docs/2opt-gpu-reduction.md)
- [Validation matrix and evidence rules](docs/testing.md)
- [Kernel development and PTX regeneration](docs/kernel-development.md)
- [Release and crates.io checklist](docs/releasing.md)
- [Benchmark record format](docs/benchmarks/README.md)
- [Changelog](CHANGELOG.md)
Expand Down
107 changes: 90 additions & 17 deletions crates/vrp-gpu/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
# vrp-gpu

`vrp-gpu` provides vehicle-routing data structures, deterministic CPU reference
heuristics and opt-in CUDA acceleration. The current API parses Solomon-style
CVRP instances, constructs feasible routes and evaluates or selects 2-opt moves.
`vrp-gpu` provides CVRP data structures, CPU reference heuristics and optional
CUDA evaluation of 2-opt moves. It parses Solomon-style text, constructs routes
with a greedy nearest-neighbor heuristic, and improves routes on the CPU.
The GPU API computes candidate deltas or selects a single move; it does not run
a complete GPU search loop or mutate a route.

The crate is experimental and has not been published to crates.io yet.

## Features

| Feature | Default | Effect |
| --- | --- | --- |
| `gpu` | No | Enables CUDA driver orchestration through `cudarc` and the embedded `sm_120` PTX artifact |
| `gpu` | No | Exposes `local_search::gpu`, adds `cudarc`, and embeds the checked-in PTX targeting `sm_120` |

Without `gpu`, the library has no third-party runtime dependencies.
Without `gpu`, the library has no third-party runtime dependencies. Enabling
`gpu` requires a working NVIDIA driver when a GPU operation is executed. The
PTX is precompiled; consumers do not need cuda-oxide or a nightly Rust compiler.

## Installation

During development, use the Git repository:
Before publication, use the Git repository and pin a tested revision for a
reproducible application build:

```toml
[dependencies]
vrp-gpu = { git = "https://github.com/pedrozaz/vrp-gpu" }
vrp-gpu = { git = "https://github.com/pedrozaz/vrp-gpu", rev = "<tested-commit>" }
```

After the first crates.io release, the dependency will use the normal registry
form:
After an actual crates.io release, use the published version. The following is
an example of the intended form, not a currently available release:

```toml
[dependencies]
Expand All @@ -36,7 +41,11 @@ vrp-gpu = "0.1"
```rust
use std::error::Error;

use vrp_gpu::{construct::nearest_neighbor, instance::SolomonInstance};
use vrp_gpu::{
construct::nearest_neighbor,
instance::SolomonInstance,
local_search::cpu::two_opt,
};

fn main() -> Result<(), Box<dyn Error>> {
let input = "\
Expand All @@ -49,22 +58,86 @@ CUSTOMER
2 0 1 5 0 100 0
";
let instance: SolomonInstance = input.parse()?;
let solution = nearest_neighbor(&instance);
let mut solution = nearest_neighbor(&instance);

assert!(solution.is_feasible(&instance));
let before = solution.total_distance(&instance);
two_opt(&mut solution, &instance);
assert!(solution.is_feasible(&instance));
assert!(solution.total_distance(&instance) <= before);
Ok(())
}
```

`nearest_neighbor` panics on malformed instances, a customer whose demand
exceeds vehicle capacity, or exhaustion of the configured fleet. Fleet
exhaustion only means this greedy construction failed. If instance data are
constructed or changed through public fields, call `SolomonInstance::validate`
before passing them to CPU routines.

To use the optional GPU API, enable the feature in the dependency:

```toml
[dependencies]
vrp-gpu = { git = "https://github.com/pedrozaz/vrp-gpu", rev = "<tested-commit>", features = ["gpu"] }
```

Replace `<tested-commit>` with the exact Git commit you validated.
`gpu::evaluate_two_opt_deltas` returns a row-major `n × n` matrix with computed
`f32` deltas for valid `i < j` and positive infinity elsewhere. Extreme finite
input distances can still yield non-finite arithmetic results.
`gpu::best_two_opt_move` returns the best finite negative delta as
`Option<TwoOptMove>`. Exact ties choose
the lowest row-major `(i, j)`. Both functions return `GpuEvaluationError` for
invalid input, size overflow or CUDA driver failure. Empty and singleton routes
return without creating a CUDA context. A returned move is only a proposal:
apply it with `cpu::apply_two_opt` if your application accepts it.

After enabling `gpu`, one selection and application can be written as follows.
The documentation build compiles this example without running it because a
GPU and driver are required:

```rust,no_run
#[cfg(feature = "gpu")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
use vrp_gpu::{
instance::SolomonInstance,
local_search::{cpu, gpu},
solution::Route,
};

let input = r#"DEMO
VEHICLE
1 10
CUSTOMER
0 0 0 0 0 100 0
1 1 0 0 0 100 0
2 0 1 0 0 100 0
3 2 1 0 0 100 0
"#;
let instance: SolomonInstance = input.parse()?;
let mut route = Route::from_nodes(vec![1, 2, 3]);
if let Some(best) = gpu::best_two_opt_move(&route, &instance)? {
cpu::apply_two_opt(&mut route, best.i, best.j);
}
Ok(())
}

#[cfg(not(feature = "gpu"))]
fn main() {}
```

Enable `gpu` to access `local_search::gpu`. GPU entry points validate their
inputs before using CUDA, while empty and singleton routes use CPU-only fast
paths. The current PTX artifact is hardware-validated on an NVIDIA GeForce RTX
5060 Ti (`sm_120`); compatibility with other architectures is not yet claimed.
Each nontrivial GPU call currently creates a CUDA context, loads the embedded
PTX and transfers the matrix and route. There is no reusable device session or
GPU convergence loop. The checked-in artifact targets `sm_120` and has been
hardware-validated on an NVIDIA GeForce RTX 5060 Ti. Other devices have no
compatibility claim. See the [input and algorithm guide](https://github.com/pedrozaz/vrp-gpu/blob/develop/docs/user-guide.md)
for exact model and numerical contracts.

## Minimum supported Rust version

The supported feature set requires Rust 1.88 or newer. Raising the MSRV is a
release-level change and must be recorded in the changelog.
The supported feature set requires Rust 1.88 or newer (edition 2024). Raising
the MSRV is a release-level change and must be recorded in the changelog.

See the [repository documentation](https://github.com/pedrozaz/vrp-gpu/tree/develop/docs)
for architecture, kernel contracts and release procedures.
Expand Down
28 changes: 28 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ Only `vrp-gpu` is intended for crates.io. The other packages use project-
qualified names to avoid collisions with unrelated crates and are explicitly
marked `publish = false`.

`vrp-gpu-cli` is currently a placeholder, not a supported solver command.
`vrp-gpu-bench` is an internal tool whose protocols and results must be
documented under [benchmark records](benchmarks/README.md). Read the
[library guide](user-guide.md) for the public APIs.

## Runtime boundary

The default feature set is CPU-only. Enabling `gpu` adds `cudarc` and exposes
Expand All @@ -43,6 +48,8 @@ vrp-gpu-kernel (nightly + cuda-oxide, development only)

Kernel source and generated PTX must change together. The checked-in artifact
is part of the public package and therefore part of its compatibility surface.
The PTX currently declares `.target sm_120`. The package includes this one
artifact; it does not compile PTX for the consumer's GPU at build time.

## Correctness boundary

Expand All @@ -51,12 +58,30 @@ only after parity tests compare candidate deltas and deterministic selection
against that reference. Floating-point comparisons use documented tolerances;
tie-breaking uses exact row-major order.

The shared input representation is a row-major `f32` distance matrix of N²
entries plus customer IDs in route order. The implemented 2-opt delta formula
changes two boundary edges and assumes symmetric distances. CPU best-move
selection evaluates O(n²) candidate pairs for a route of n customers, using
O(1) additional selection state. The GPU evaluator materializes n² `f32`
deltas on device; the reduction repeatedly shrinks them to one candidate.
The full-matrix GPU API also downloads n² deltas. These are algorithmic and
allocation bounds, not measured speedup claims.

The GPU host functions validate the instance and route, create a context,
load the embedded module, transfer inputs, launch kernels and synchronize on
each nontrivial call. There is no persistent context cache or GPU route
convergence API. `best_two_opt_move` returns a proposal; applying it and
repeating search remain the caller's responsibility.

## Public API boundary

Public modules live in `crates/vrp-gpu/src`. Workspace binaries may depend on
the library, but the library must not depend on them or on the kernel workspace.
New public APIs require rustdoc, tests and a changelog entry. Feature-gated APIs
must continue to allow the default CPU-only package to compile without CUDA.
The public model currently covers capacitated routing only. Solomon time-window
and service fields are retained but not enforced by route construction,
feasibility checks or 2-opt. See [model semantics](user-guide.md).

## Package contents

Expand All @@ -68,3 +93,6 @@ to the archive. Validate the exact archive before a release with:
cargo package -p vrp-gpu --list
cargo package -p vrp-gpu --locked
```

See [kernel development](kernel-development.md) for PTX regeneration and
[testing](testing.md) for the evidence expected of a change.
Loading
Loading