Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding multiple capacity vehicle capability #29

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
23 changes: 21 additions & 2 deletions src/adaptors.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define NODE_OR_TOOLS_ADAPTORS_F1FF74E9681C_H

#include <nan.h>

#include <iostream>
#include "types.h"

// We adapt matrices and vectors for or-tools since it expects them to have specific signatures and types.
Expand Down Expand Up @@ -134,11 +134,30 @@ template <typename Vector> inline auto makeVectorFromJsNumberArray(v8::Local<v8:

if (!num->IsNumber())
throw std::runtime_error{"Expected array element of types Number"};

//std::cout<<"hello"<<std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debug prints and the include above have to go before we can merge this

vec.at(atIdx) = Nan::To<std::int32_t>(num).FromJust();
}

return vec;
}

// our function for changing js array into vector of long long int

template <typename Vector> inline auto makeVectorFromJsNumberArray1(v8::Local<v8::Array> array) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this function needed?

const int64 len = array->Length();

Vector vec(len);

for (int64 atIdx = 0; atIdx < len; ++atIdx) {
auto num = Nan::Get(array, atIdx).ToLocalChecked();

if (!num->IsNumber())
throw std::runtime_error{"Expected array element of types Number"};
//std::cout<<"hello"<<std::endl;
vec.at(atIdx) = Nan::To<int64_t>(num).FromJust();
}

return vec;
}

#endif
3 changes: 1 addition & 2 deletions src/tsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ NAN_METHOD(TSP::Solve) try {
const std::int32_t numNodes = self->costs->dim();
const std::int32_t numVehicles = 1; // Always one for TSP

auto* worker = new TSPWorker{self->costs, //
auto worker = new TSPWorker{self->costs, //
new Nan::Callback{userParams.callback}, //
modelParams, //
searchParams, //
numNodes, //
numVehicles, //
userParams.depotNode}; //
Nan::AsyncQueueWorker(worker);

} catch (const std::exception& e) {
return Nan::ThrowError(e.what());
Expand Down
33 changes: 21 additions & 12 deletions src/tsp_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,40 @@
#include <utility>
#include <vector>

struct TSPWorker final : Nan::AsyncWorker {
using Base = Nan::AsyncWorker;
struct TSPWorker final {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove this and no longer initialize the base class below? The base class already stores the callback, there should be no need to store it here.


TSPWorker(std::shared_ptr<const CostMatrix> costs_, Nan::Callback* callback, const RoutingModelParameters& modelParams_,
const RoutingSearchParameters& searchParams_, std::int32_t numNodes, std::int32_t numVehicles,
std::int32_t vehicleDepot)
: Base(callback), costs{std::move(costs_)}, model{numNodes, numVehicles, NodeIndex{vehicleDepot}, modelParams_},
modelParams{modelParams_}, searchParams{searchParams_} {}
: mCallback(callback), costs{std::move(costs_)}, model{numNodes, numVehicles, NodeIndex{vehicleDepot}, modelParams_},
modelParams{modelParams_}, searchParams{searchParams_}
{
Execute();
}

void Execute() override {
void Execute() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please inherit from the NAN AsyncWorker and keep the overrides here and below.

auto costAdaptor = makeBinaryAdaptor(*costs);
auto costEvaluator = makeCallback(costAdaptor);

model.SetArcCostEvaluatorOfAllVehicles(costEvaluator);

const auto* assignment = model.SolveWithParameters(searchParams);

if (!assignment || (model.status() != RoutingModel::Status::ROUTING_SUCCESS))
SetErrorMessage("Unable to find a solution");
std::string jsError;
if (!assignment || (model.status() != RoutingModel::Status::ROUTING_SUCCESS)) {
jsError = "Unable to find a solution";
}

model.AssignmentToRoutes(*assignment, &routes);

if (routes.size() != 1)
SetErrorMessage("Expected route for one vehicle");
if (routes.size() != 1) {
jsError = "Expected route for one vehicle";
}

HandleOKCallback(jsError);
}

void HandleOKCallback() override {
void HandleOKCallback(std::string jsError) {
Nan::HandleScope scope;

const auto& route = routes.front();
Expand All @@ -47,9 +54,10 @@ struct TSPWorker final : Nan::AsyncWorker {
(void)Nan::Set(jsRoute, j, Nan::New<v8::Number>(route[j].value()));

const auto argc = 2u;
v8::Local<v8::Value> argv[argc] = {Nan::Null(), jsRoute};
v8::Local<v8::String> errorStr = v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), jsError.c_str());
v8::Local<v8::Value> argv[argc] = {errorStr, jsRoute};

callback->Call(argc, argv);
mCallback->Call(argc, argv);
}

std::shared_ptr<const CostMatrix> costs; // inc ref count to keep alive for async cb
Expand All @@ -58,6 +66,7 @@ struct TSPWorker final : Nan::AsyncWorker {
RoutingModelParameters modelParams;
RoutingSearchParameters searchParams;

Nan::Callback* mCallback;
// Stores solution until we can translate back to v8 objects
std::vector<std::vector<NodeIndex>> routes;
};
Expand Down
27 changes: 20 additions & 7 deletions src/vrp_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct VRPSearchParams {
std::int32_t numVehicles;
std::int32_t depotNode;
std::int32_t timeHorizon;
std::int32_t vehicleCapacity;
std::vector<int64> vehicleCapacity; // type changed to vector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment


RouteLocks routeLocks;

Expand All @@ -40,8 +40,8 @@ inline auto makeTimeWindowsFrom2dArray(std::int32_t n, v8::Local<v8::Array> arra
if (n < 0)
throw std::runtime_error{"Negative size"};

if (static_cast<std::int32_t>(array->Length()) != n)
throw std::runtime_error{"Array dimension do not match size"};
// if (static_cast<std::int32_t>(array->Length()) != n)
// throw std::runtime_error{"Array dimension do not match size"};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why commented out?


TimeWindows timeWindows(n);

Expand Down Expand Up @@ -78,8 +78,8 @@ inline auto makeRouteLocksFrom2dArray(std::int32_t n, v8::Local<v8::Array> array
if (n < 0)
throw std::runtime_error{"Negative size"};

if (static_cast<std::int32_t>(array->Length()) != n)
throw std::runtime_error{"Array dimension do not match size"};
// if (static_cast<std::int32_t>(array->Length()) != n)
// throw std::runtime_error{"Array dimension do not match size"};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why commented out?


// Note: use (n) for construction because RouteLocks is a weak alias to a std::vector.
// Using vec(n) creates a vector of n items, using vec{n} creates a vector with a single element n.
Expand Down Expand Up @@ -130,6 +130,13 @@ VRPSolverParams::VRPSolverParams(const Nan::FunctionCallbackInfo<v8::Value>& inf
auto timeWindowsVectorOk = !maybeTimeWindowsVector.IsEmpty() && maybeTimeWindowsVector.ToLocalChecked()->IsArray();
auto demandMatrixOk = !maybeDemandMatrix.IsEmpty() && maybeDemandMatrix.ToLocalChecked()->IsArray();


if(!numNodesOk ) throw std::runtime_error{"ERROR TYPE 1"};
else if(!costMatrixOk) throw std::runtime_error{"ERROR TYPE 2"};
else if(!durationMatrixOk) throw std::runtime_error{"ERROR TYPE 3"};
else if(!timeWindowsVectorOk) throw std::runtime_error{"ERROR TYPE 4"};
else if(!demandMatrixOk) throw std::runtime_error{"ERROR TYPE 5"};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on here?


if (!numNodesOk || !costMatrixOk || !durationMatrixOk || !timeWindowsVectorOk || !demandMatrixOk)
throw std::runtime_error{"SolverOptions expects"
" 'numNodes' (Number),"
Expand Down Expand Up @@ -170,7 +177,7 @@ VRPSearchParams::VRPSearchParams(const Nan::FunctionCallbackInfo<v8::Value>& inf
auto numVehiclesOk = !maybeNumVehicles.IsEmpty() && maybeNumVehicles.ToLocalChecked()->IsNumber();
auto depotNodeOk = !maybeDepotNode.IsEmpty() && maybeDepotNode.ToLocalChecked()->IsNumber();
auto timeHorizonOk = !maybeTimeHorizon.IsEmpty() && maybeTimeHorizon.ToLocalChecked()->IsNumber();
auto vehicleCapacityOk = !maybeVehicleCapacity.IsEmpty() && maybeVehicleCapacity.ToLocalChecked()->IsNumber();
auto vehicleCapacityOk = !maybeVehicleCapacity.IsEmpty() && maybeVehicleCapacity.ToLocalChecked()->IsArray(); // IsNumber() changed to IsArray()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment

auto routeLocksOk = !maybeRouteLocks.IsEmpty() && maybeRouteLocks.ToLocalChecked()->IsArray();
auto pickupsOk = !maybePickups.IsEmpty() && maybePickups.ToLocalChecked()->IsArray();
auto deliveriesOk = !maybeDeliveries.IsEmpty() && maybeDeliveries.ToLocalChecked()->IsArray();
Expand All @@ -192,7 +199,9 @@ VRPSearchParams::VRPSearchParams(const Nan::FunctionCallbackInfo<v8::Value>& inf
numVehicles = Nan::To<std::int32_t>(maybeNumVehicles.ToLocalChecked()).FromJust();
depotNode = Nan::To<std::int32_t>(maybeDepotNode.ToLocalChecked()).FromJust();
timeHorizon = Nan::To<std::int32_t>(maybeTimeHorizon.ToLocalChecked()).FromJust();
vehicleCapacity = Nan::To<std::int32_t>(maybeVehicleCapacity.ToLocalChecked()).FromJust();
//vehicleCapacity = Nan::To<std::int32_t>(maybeVehicleCapacity.ToLocalChecked()).FromJust();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why commented out?




auto routeLocksArray = maybeRouteLocks.ToLocalChecked().As<v8::Array>();
routeLocks = makeRouteLocksFrom2dArray(numVehicles, routeLocksArray);
Expand All @@ -203,6 +212,10 @@ VRPSearchParams::VRPSearchParams(const Nan::FunctionCallbackInfo<v8::Value>& inf
auto deliveriesArray = maybeDeliveries.ToLocalChecked().As<v8::Array>();
deliveries = makeVectorFromJsNumberArray<Deliveries>(deliveriesArray);

auto vehicleCapacityArray = maybeVehicleCapacity.ToLocalChecked().As<v8::Array>();
vehicleCapacity = makeVectorFromJsNumberArray1<std::vector<int64> >(vehicleCapacityArray);
// new function call added to make vehicle capacity vector calls function in adapter.h

callback = info[1].As<v8::Function>();
}

Expand Down
42 changes: 37 additions & 5 deletions src/vrp_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct VRPWorker final : Nan::AsyncWorker {
std::int32_t numVehicles_, //
std::int32_t vehicleDepot_, //
std::int32_t timeHorizon_, //
std::int32_t vehicleCapacity_, //
std::vector<int64> vehicleCapacity_, // type changed to vector int64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change from int32s to int64s?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was because the AddDimensionWithVehicleCapacity() takes long long integer array

RouteLocks routeLocks_, //
Pickups pickups_, //
Deliveries deliveries_) //
Expand All @@ -47,7 +47,7 @@ struct VRPWorker final : Nan::AsyncWorker {
numVehicles{numVehicles_},
vehicleDepot{vehicleDepot_},
timeHorizon{timeHorizon_},
vehicleCapacity{vehicleCapacity_},
vehicleCapacity{std::move(vehicleCapacity_)},
routeLocks{std::move(routeLocks_)},
pickups{std::move(pickups_)},
deliveries{std::move(deliveries_)},
Expand Down Expand Up @@ -120,14 +120,38 @@ struct VRPWorker final : Nan::AsyncWorker {
auto demandAdaptor = makeBinaryAdaptor(*demands);
auto demandCallback = makeCallback(demandAdaptor);

const static auto kDimensionCapacity = "capacity";

//std::vector<int64> vehicle_capacities
// vehicle capacity call back

model.AddDimension(demandCallback, /*slack=*/0, vehicleCapacity, /*fix_start_cumul_to_zero=*/true, kDimensionCapacity);
//auto vehicleCapacityAdaptor = makeUnaryAdaptor(vehicleCapacity);
// auto vehicleCapacityCallback = makeCallback(vehicleCapacityAdaptor);

// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution -2");


const static std::string& kDimensionCapacity = "capacity";

//function for handling different capacitated vehicles
model.AddDimensionWithVehicleCapacity(demandCallback, /*slack=*/0, vehicleCapacity, /*fix_start_cumul_to_zero=*/true, kDimensionCapacity);
const auto& capacityDimension = model.GetDimensionOrDie(kDimensionCapacity);

// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution -1");
//old
//const static auto kDimensionCapacity = "capacity";

//model.AddDimension(demandCallback, /*slack=*/0, vehicleCapacity, /*fix_start_cumul_to_zero=*/true, kDimensionCapacity);
// const auto& capacityDimension = model.GetDimensionOrDie(kDimensionCapacity);



// Pickup and Deliveries

auto* solver = model.solver();
// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution 0");

for (std::int32_t atIdx = 0; atIdx < pickups.size(); ++atIdx) {
const auto pickupIndex = model.NodeToIndex(pickups.at(atIdx));
Expand All @@ -144,14 +168,22 @@ struct VRPWorker final : Nan::AsyncWorker {

model.AddPickupAndDelivery(pickups.at(atIdx), deliveries.at(atIdx));
}
// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution 0");

// Done with modifications to the routing model

model.CloseModel();

// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution 1");

// Locking routes into place needs to happen after the model is closed and the underlying vars are established
const auto validLocks = model.ApplyLocksToAllVehicles(routeLocks, /*close_routes=*/false);

// if(model.status() != RoutingModel::Status::ROUTING_SUCCESS)
// return SetErrorMessage("Unable to find a solution 2");

if (!validLocks)
return SetErrorMessage("Invalid locks");

Expand Down Expand Up @@ -238,7 +270,7 @@ struct VRPWorker final : Nan::AsyncWorker {
std::int32_t numVehicles;
std::int32_t vehicleDepot;
std::int32_t timeHorizon;
std::int32_t vehicleCapacity;
std::vector<int64> vehicleCapacity; // changed type of vehicle capacity from int32_t to vector<int64>

const RouteLocks routeLocks;

Expand Down
9 changes: 8 additions & 1 deletion test/vrp.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ tap.test('Test VRP', function(assert) {

var numVehicles = 10;
var timeHorizon = dayEnds - dayStarts;
var vehicleCapacity = 10;

//vehicle capacity array
var vehicleCapacity = new Array(numVehicles);
for (var i = 0; i < numVehicles; i++) {

vehicleCapacity[i]=10;
}


// Dummy lock to let vehicle 0 go to location 2 and 3 first - to test route locks
var routeLocks = new Array(numVehicles);
Expand Down