nextmv Docs

Release Notes

Release Notes

This page details relevant updates for each release. As always, you can find release specific implementation details in the package docs as well for that version.

Upgrading to v0.7.1

Nextmv version 0.7.1 introduces a few changes that extend existing functionality and improve efficiency. The changes introduced with this release are not breaking and optional for Nextmv customers to upgrade. A summary of added functionality is included below.

Hop fleet extensions

Exposed new information in Hop fleet

Assigned vehicles and locations

The new Assigned() (int, model.IntDomain) function gives access to the set of locations and the vehicle those locations were assigned to in the previous call to Next(). The function can be called on a fleet state and will return an index and an IntDomain. Note, if no locations were assigned, then an empty domain will be returned by the function. Previously, accessing this information required ranging and recomputing over all vehicles. We recommend using the new Assigned function for a more efficient approach.

Vehicle state

The new Vehicle(index int) vehicle.State function gives fast access to the state of a particular vehicle. The function takes an index of a vehicle (this can be passed from Assigned) and returns the vehicle state.

Added flexible assigner framework

This version introduces a flexible assigner framework to Hop to facilitate the addition of commonly used filters or constraints. To use the new assigner framework, you will need to specify three things:

  • Location selector: Takes in state and returns an IntDomain representing the next locations to be assigned to vehicles. If nil, the minimum from the domain of remaining location indices will be taken by default.
  • Location grouper: Specifies for a given index location, all locations (including the index location itself) that must be assigned with the same vehicle (e.g., if a pickup is assigned to a vehicle, then the drop off must also be assigned to that vehicle). A default location grouper function is provided for convenience.
  • Vehicle filter: Takes in a list of vehicles, locations, and routes for those vehicles and returns an IntDomain representing valid combinations of vehicles and locations to be assigned next. New vehicle filters are detailed below.

Added new vehicle filters

Three vehicle filters are now supported in Hop fleet and made available through the assigner framework. The following vehicle filters are available:

  • Max route length filter: Prevents the assignment of locations to vehicles that will exceed a max route length (measured in time or distance between two locations).
  • Capacity filter: Prevents the assignment of locations to vehicles that will exceed vehicle capacity constraints (e.g., a car with only one seat for a passenger will not be assigned to a pickup location with >1 passenger).
  • Attributes filter: Prevents the assignment of locations to vehicles that are not eligible due to compatibility of vehicle and location attributes (e.g., a location with a pick up that requires refrigeration will not be assigned to a vehicle without refrigeration)

Note, because vehicle filters serve as a pre-filter they also help to improve search efficiency. We recommend customers use the built-in vehicle filters (when appropriate) for this reason.

Extended value function

Penalty cost for unassigned locations can now be included in the value function. The penalty cost will help to direct the search algorithm away from unassigned locations in solutions. Unassignment is still allowed, but can be penalized.

To add penalties for unassigned locations, specify an integer slice of UnassignedPenalities on the fleet struct. Locations can have different penalties (e.g., if there is a high priority locations that must be served, they can have higher penalties).

If no penalty is provided, the solver defaults to previous behavior (tries to assign everything, but some locations may still be unassigned).

Added access to the underlying vehicle model

A new function transform func(vehicle.State) vehicle.State has been added to CustomRouter to provide access to the underlying vehicle model. This enables constraints such as earliness and lateness to be considered as part of the vehicle model search to find improving results faster. ETAs and time windows can now be tracked when planning a vehicle route as well.

Hop vehicle model extensions

Exposed new information in Hop vehicle

Accumulated cost

The accumulated cost for each stop in a route can be accessed using vehicle.State.Cost(index int) float64. This simplifies the calculation of an ETA as part of the value function.

Vehicle state constraints

The constraints of a vehicle state can be accessed using vehicle.State.Constraints(). This new method returns the constraints of vehicle at the state it was called on.

Cost on a window constraint

Window constraints specify hard time windows that need to be met. If a vehicle arrives early, a wait time will be enforced. The new vehicle.Coster interface specifies a Cost(index int) int method implemented by the Window constraint that captures both the accumulated measure and enforced wait time.

Added max route length constraint

The vehicle max route length constraint requires a measure, max route length, and a boolean to ignore the triangular inequality as input. Most customers using their own costs (e.g., a matrix measurer) will want to set ignoreTriangular to TRUE. See the package docs for additional details.

Simplified measure specifications

Previously, separate point and index interfaces were implemented for each form of measurement (e.g., haversineByPoint, haversineByIndex, EuclideanByPoint, EuclideanByIndex, etc.). As we add new measures, we are moving to a more scalable system where only ByPoint is specified, and ByIndex is dynamically generated.

Use the new Indexed function to generate a ByIndex measure: Indexed(m ByPoint, points []Point) ByIndex. The function takes a ByPoint measure and a slice of points as input and returns a ByIndex measure.

While switching to the new implementation of indexed measures is not required to upgrade, we strongly recommend customers do so. The change will be required in future releases.

Support for Alternate locations

This release adds support for alternate location handling. The Vehicle input schema has a new field AlternateLocations of type IntDomain. This domain specifies a set of locations one of which has to be visited by the vehicle. One usage scenario is when the vehicle should go on a break, and there are multiple break locations. The model will choose the closest (as determined by the given measure) one. Additionally we added Alternate() (index, location int), which gives access to the index in the route at which the alternate location can be found as well as the actual location that is used from the set of alternate locations.

Introducing Nextmv apps

Apps provide a faster way to get started with Nextmv and can be used off-the-shelf or customized to your needs. The first Nextmv App is now available in the apps repo on GitHub. The first application is Dispatch, a base multi-vehicle fleet routing app with no pre-specified constraints. This base app is ready to use with Hop v0.7.1 and can be easily extended to include the new functionality detailed above. Stay tuned as we continue to add new apps.

Upgrading to v0.7.0

Nextmv version 0.7.0 introduces a few important changes that improve time to first feasible solution, help with memory use management, and align product releases.

This release introduces breaking changes. All Nextmv customers migrating to Hop v0.7.0 will need to make a few changes to their models to ensure smooth operations.

Updating Import Paths

Before downloading v0.7.0, it is important to note that we’ve combined Hop and HopM into a single GitHub repository called code. This means import paths have changed (github.com/nextmv-io/hop is now github.com/nextmv-io/code/hop) and this must be reflected in your go get command.

go get github.com/nextmv-io/code/hop
go get github.com/nextmv-io/code/hopm

You’ll need to replace the old import paths manually inside your models. Under Files, you’ll need to find, edit, and replace the old import paths with the new ones. It’s also good practice to do a go mod tidy once you’ve made the import updates.

Version 0.6.6 and beforeVersion 0.7.0 and above
github.com/nextmv-io/hopgithub.com/nextmv-io/code/hop
github.com/nextmv-io/hopmgithub.com/nextmv-io/code/hopm

Updating Next Method for Expansion Limits

We’ve introduced expansion limits in version 0.7.0 to improve time to first feasible solution and better manage memory use. This enhancement has a direct impact on the Next method. If you try go build, you will see a build error that shows the Next method is expecting a model.Expander but is instead returning []model.State.

Models without HopM Components

Any user with a custom model that does not embed a HopM component should update their Next method to return an Eager expander.

We'll look at our n-queens model as an example.

For v0.6.6 and before:

// Next places a queen in each available column of the most constrained row.
func (b board) Next() []model.State {
next := []model.State{}
if row, ok := model.IntDomains(b).Smallest(); ok {
for it := b[row].Iterator(); it.Next(); {
next = append(next, b.place(row, it.Value()))
}
}
return next
}

For v0.7.0 with Expanders:

// Next places a queen in each available column of the most constrained row.
func (b board) Next() model.Expander {
next := []model.State{}
if row, ok := model.IntDomains(b).Smallest(); ok {
for it := b[row].Iterator(); it.Next(); {
next = append(next, b.place(row, it.Value()))
}
}
return expand.Eager(next...)
}

We use an Eager expander to maintain the same state space we had before this release. We could also use Lazy to return less child states. See How Hop Hops for more details on Expanders.

Models with HopM Components

Any user with an embedded Hop component should update their Next method to return a Lazy expander.

We'll look at our dispatch model as an example. When we embed a HopM component like Fleet and need to access HopM's state for value function changes, our Next method is a wrapped HopM state.

For v0.6.6 and before:

func (s state) Next() []model.State {
next := []model.State{}
for _, n := range s.fleetState.Next() {
next = append(next, state{
fleetState: n.(fleet.State),
outputTransformer: s.outputTransformer,
})
}
return next
}

For v0.7.0 with Expanders:

func (s state) Next() model.Expander {
expander := s.fleetState.Next()
if expander == nil {
return nil
}
return expand.Lazy(func() model.State {
if next := expander.Expand(); next != nil {
return state{
fleetState: next.(fleet.State),
outputTransformer: s.outputTransformer,
}
}
return nil
})
}

We use an Lazy expander to limit the number of states returned by default. See How Hop Hops for more details on Expanders.

Updating CLI and ENV Options

Beginning with version 0.7.0, the hop.runner.output.solutions environment variable will now default to last instead of all. This default setting tells Hop to only output the final improving solution in a Hop model. If this is the desired behavior of your model, all you need to do is remove this line of code from your command line or env. If you desire to have it return all, you will need to set the runner options accordingly.