Python

Using pip

This is the easiest way to start using the solver's latest version:

pip install vrp-cli
python examples/python-interop/example.py # test example

See python code example in repo or in next section.

Using maturin

You can use maturin tool to build solver locally for you. Here are the steps:

  1. Create a virtual environment and install maturin (and pydantic):

    cd vrp-cli # directory of the crate where with python bindings are located
    python3 -m venv .venv
    source .venv/bin/activate
    pip install -U pip maturin[patchelf] pydantic
    pip freeze
    
  2. Use maturin to build and install the solver library in your current environment:

    maturin develop --release --features "py_bindings"
    
  3. Import and use the library in your python code:

import vrp_cli
import pragmatic_types as prg
import config_types as cfg
import json
from pydantic.json import pydantic_encoder

# if you want to use approximation, you can skip this definition and pass empty list later
# also there is a get_locations method to get list of locations in expected order.
# you can use this list to fetch routing matrix externally
matrix = prg.RoutingMatrix(
    profile='normal_car',
    durations=[0, 609, 981, 906, 813, 0, 371, 590, 1055, 514, 0, 439, 948, 511, 463, 0],
    distances=[0, 3840, 5994, 5333, 4696, 0, 2154, 3226, 5763, 2674, 0, 2145, 5112, 2470, 2152, 0]
)


# specify termination criteria: max running time in seconds or max amount of refinement generations
config = cfg.Config(
    termination=cfg.Termination(
        maxTime=5,
        maxGenerations=1000
    )
)

# specify test problem
problem = prg.Problem(
    plan=prg.Plan(
        jobs=[
            prg.Job(
                id='delivery_job1',
                deliveries=[
                    prg.JobTask(
                        places=[
                            prg.JobPlace(
                                location=prg.Location(lat=52.52599, lng=13.45413),
                                duration=300,
                                times=[['2019-07-04T09:00:00Z', '2019-07-04T18:00:00Z']]
                            ),
                        ],
                        demand=[1]
                    )
                ]
            ),
            prg.Job(
                id='pickup_job2',
                pickups=[
                    prg.JobTask(
                        places=[
                            prg.JobPlace(
                                location=prg.Location(lat=52.5225, lng=13.4095),
                                duration=240,
                                times=[['2019-07-04T10:00:00Z', '2019-07-04T16:00:00Z']]
                            )
                        ],
                        demand=[1]
                    )
                ]
            ),
            prg.Job(
                id="pickup_delivery_job3",
                pickups=[
                    prg.JobTask(
                        places=[
                            prg.JobPlace(
                                location=prg.Location(lat=52.5225, lng=13.4095),
                                duration=300,
                                tag="p1"
                            )
                        ],
                        demand=[1]
                    )
                ],
                deliveries=[
                    prg.JobTask(
                        places=[
                            prg.JobPlace(
                                location=prg.Location(lat=52.5165, lng=13.3808),
                                duration=300,
                                tag="d1"
                            ),
                        ],
                        demand=[1]
                    )
                ]
            )
        ]
    ),
    fleet=prg.Fleet(
        vehicles=[
            prg.VehicleType(
                typeId='vehicle',
                vehicleIds=['vehicle_1'],
                profile=prg.VehicleProfile(matrix='normal_car'),
                costs=prg.VehicleCosts(fixed=22, distance=0.0002, time=0.005),
                shifts=[
                    prg.VehicleShift(
                        start=prg.VehicleShiftStart(
                            earliest="2019-07-04T09:00:00Z",
                            location=prg.Location(lat=52.5316, lng=13.3884),
                        ),
                        end=prg.VehicleShiftEnd(
                            latest="2019-07-04T18:00:00Z",
                            location=prg.Location(lat=52.5316, lng=13.3884),
                        )
                    )
                ],
                capacity=[10]
            )
        ],
        profiles=[prg.RoutingProfile(name='normal_car')]
    )

)

# run solver and deserialize result into solution model
solution = prg.Solution(**json.loads(vrp_cli.solve_pragmatic(
    problem=json.dumps(problem, default=pydantic_encoder),
    matrices=[json.dumps(matrix, default=pydantic_encoder)],
    config=json.dumps(config, default=pydantic_encoder),
)))

print(solution)

You can check the project repository for complete example.

Please note, that type wrappers, defined in examples with pydantic, are incomplete. However, it should be enough to get started, and you can tweak them according to the documentation or rust source code.

Using local build

Another way to run the solver, built locally, from python is to use subprocess to run vrp-cli directly:

import subprocess
import json

# NOTE: ensure that paths are correct on your environment
cli_path = "./target/release/vrp-cli"
problem_path = "./examples/data/pragmatic/simple.basic.problem.json"
solution_path = "./examples/data/pragmatic/simple.basic.solution.json"
geojson_solution_path = "./examples/data/pragmatic/simple.basic.solution.geojson"

class Deserializer:
    @classmethod
    def from_dict(cls, dict):
        obj = cls()
        obj.__dict__.update(dict)
        return obj

class SolverClient:
    def __init__(self, cli_path):
        self.cli_path = cli_path

    def solve_pragmatic(self, problem_path, solution_path, geojson_solution_path):
        # NOTE: modify example to pass matrix, config, initial solution, etc.
        p = subprocess.run([self.cli_path, 'solve', 'pragmatic', problem_path,
            '-o', solution_path, '-g', geojson_solution_path, '--log'])

        if p.returncode == 0:
            with open(solution_path, 'r') as f:
                solution_str = f.read()
                return json.loads(solution_str, object_hook=Deserializer.from_dict)
        else:
            pass

solver = SolverClient(cli_path)
solution = solver.solve_pragmatic(problem_path, solution_path, geojson_solution_path)

print(f"Total cost is {solution.statistic.cost}, tours: {len(solution.tours)}")

Please note, that the solver expects file paths instead of json strings as input arguments.