Using the Nextmv Decision Stack
Runners
Runners are needed to deploy your app. Runners read data into a state structure, run a solver or simulator, and get decisions as data. See the deployment overview page for more information on prebuilt runners available for use with the Nextmv Decision Stack.
Apps
Nextmv apps are industry-specific applications that can be used as is or extended as needed to meet your needs for your specific use case. For example, see the engines overview page for a list of prebuilt filters and constraints available for use with the Dispatch App for routing a fleet of vehicles.
Apps are available on GitHub (contact support@nextmv.io for access).
In each app, the cmd directory contains the main.go, cmd.sh and supporting
data files needed to run the app. Apps provide a starting point for solving
real-world problems using our solvers and engines. If you are just getting
started, check out our n-queens app.
Engines
Engines are Nextmv's implementations of high-performance algorithms that handle common tasks. These can be used directly or as components in your own decision models similar to plugin architectures.
Engines each have a struct defining its expected input. Engines implement Hop's
model.State interface, and can be passed directly to Hop's solver
constructors. By convention, engines provide the following exported types and
functions for constructing models:
Input: a struct defining required model input.DefaultModel: a function that converts anInput, and any other required data, into a Hop model.DefaultSolver: a function that converts anInput, and any other required data, into a Hop solver.DefaultOptions: a function to configure default solver options for a model.
Additionally, for fleet routing problems, engines provide:
DefaultAssigner: a function that converts anInput, and any other required data, into a Hop assigner.
Default solver vs model
There are typically 3 usage patterns for engines:
| Problem | Data | Usage Pattern |
|---|---|---|
| Standard | Adheres to Input structure | Install and run DefaultSolver directly |
| Standard | Can be transformed into Input structure | Modify main, build, and run DefaultSolver |
| Extension | Adheres to (or can be transformed into) Input structure | Instantiate a DefaultModel, extend standard model, build, and run new solver |
"Standard" here refers to the standard modeling problem represented by an engine. For example, a Traveling Salesperson Problem (TSP) is our standard vehicle engine that can be used standalone (standard) or nested inside of a knapsack problem (extension).
As mentioned above, a user can install and run the DefaultSolver directly. For
example, the package github.com/nextmv-io/code/engines/route provides a
default routing model for the Capacitated Vehicle Routing Problem (CVRP).
go install github.com/nextmv-io/code/engines/cmd/route
$(go env GOPATH)/bin/route -hop.runner.input.path input.json \
-hop.runner.output.path output.json \
-hop.solver.limits.solutions 1
If the input data does not conform to the required Input format, the main
method for the user model can be modified to transform input data into the
required format for using the DefaultSolver directly.
package main
import (
"github.com/nextmv-io/code/hop/run/cli"
"github.com/nextmv-io/code/engines/route/vehicle"
)
func main() {
cli.Run(vehicle.DefaultSolver)
}
The DefaultSolver configuration can be found in vehicle/default.go and shows
that a cost matrix is the standard measure for this TSP.
Modelers may, however, choose to use engines as components and build extensions
around them rather than using the solver directly. Calling DefaultModel rather
than DefaultSolver gives the modeler access to the exported components of the
underlying model which allows a user to build and test models without having to
dive into the individual model source.
Constraints
Our engines can be configured with prebuilt, commonly used constraints as well as a constraint interface for building custom constraints unique to your business.
You can find a full list of constraints for each model in the Engines' package docs.
Here is an example of a model with capacity, precedence (pickups happen before dropoffs), window (locations must be visited within cost windows), and max length (route must be less than or equal to the max) constraints for routing vehicles.
constraints = append(
constraints,
[]vehicle.Constraint{
vehicle.Capacity(capacity, capacityChange),
vehicle.Precedence(precedence),
vehicle.Window(startCost, windows, windowMeasure)
vehicle.MaxLength(routeMeasure, max, true)
},
)
Our clustering models can also have constraints. For example you may want to dictate how many points are in a cluster (cardinality) or their combined weight (capacity). Here is an example using both.
model, err := kmeans.DefaultModel(
k,
measure,
kmeans.Cardinality(5),
kmeans.Capacity(capacity, weights),
)
Measures
Measures tell the model how to access the cost of an arc or connection between
two locations. Engines can be used with a predefined collection of measures
(e.g., haversine and matrix) which give users flexibility in how routes are
compared. Measures also allow you to scale, override, or make cost between
locations constant.
In the example below, the measure will tell the Vehicle model that the cost
between any two locations is time (haversine distance scaled by 1/speed) as a
default. The override cost will be 0 if the driver is going from any location to
the end of their route. This is useful for any routing model that does not
specify an end location for the driver or does not return to a depot (think
real-time gig economy).
Check out all our measures through the engine package docs.
// Use speed to get drive time to/from all points and make to a driver start 0
measure := measure.Override(
// Calculate drive time, assumes travel speed is 9m/s
measure.Scale(measure.Indexed(HaversineByPoint(),points), 1.0/9.0),
measure.Constant(0),
func(from, to int) bool {
return driverDomain.Contains(to)
},
)
Solvers and simulators
While you can use Nextmv solvers and simulators directly, we recommend starting with an app or engine first. See the best practices for more information on using Nextmv solvers and simulators with apps and engines.