示例#1
0
    public void Run()
    {
        var api = new ApiHelper <Ivr7.SolveRequest, Ivr7.SolutionResponse>("ivr7-kt461v8eoaif", configFile);

        // so here we're going to build the model

        // create a solve request
        Ivr7.SolveRequest sr = new Ivr7.SolveRequest();

        sr.Model = new Ivr7.Model(); // initialise the model container

        // we're going to reuse the helpers described in the ivr7basic example. Please see that for a reference.
        ivr7helper.makeDistanceTimeCapDims(sr.Model); // adds distance, time & capacity
        ivr7helper.makeLocations(sr.Model, data);     // adds all the locations to the model
        ivr7helper.makeJobTimeCap(sr.Model, data, ivr7helper.Rep(0, data.Count - 1), ivr7helper.Seq(1, data.Count));
        sr.Model.vehicleCostClasses.Add(ivr7helper.makeVccSimple("vcc1", 1000, 0.01f, 0.01f, 0.01f, 1, 3));
        sr.Model.vehicleClasses.Add(ivr7helper.makeVcSimple("vc1", 1, 1, 1, 1));
        for (int i = 0; i < 4; i++)
        {
            sr.Model.Vehicles.Add(ivr7helper.makeVehicleCap("vehicle_" + i, // unique id for the vehicle.
                                                            "vc1",          // the vehicle class
                                                            "vcc1",         // the vehicle cost class
                                                            2000,           // the capacity of the vehicle
                                                            data[0].id,     // start location for the vehicle
                                                            data[0].id,     // end location for the vehicle
                                                            7 * 60,         // start time: 7 AM
                                                            18 * 60         // end time: 6 PM
                                                            ));
        }

        sr.solveType = Ivr7.SolveRequest.SolveType.Optimise; // Optimise the solve request.

        // now it's just sending the model to the api

        string requestId       = api.Post(sr);       // send the model to the api
        var    initialSolution = api.Get(requestId); // get the response (which it typed, so that's cool)

        // so we can do a few things here, we can add constraints which weren't in the original
        // model, evaluate the same sequence and see if any constraints are broken?
        // sure, this sounds like fun. Lets add some time windows to all the locations and see
        // what that does.
        foreach (var l in sr.Model.Locations)
        {
            var a = new Ivr7.Location.Attribute()
            {
                dimensionId    = "time",
                arrivalWindows = { new Ivr7.Window {
                                       Start = 8 * 60,
                                       End   = 14 * 60
                                   } }
            };
            l.Attributes.Add(a);
        }


        // okay, so now we've added a 08:00 - 14:00 window on all the locations.
        // in order to evaluate our current solution against this new solution
        // we need to convert our current solution to a task sequence (which is done
        // by vehicle)
        // so the only catch here is that the shift-start and shift-end nodes are implicitly already there
        // so all we really need to do is pull out the nodes inbetween. So we filter on only the tasks we're
        // scheduling in the last solution we received.

        foreach (var r in initialSolution.Routes)
        {
            // so the definition is, for a vehicle: list the tasks that vehicle should perform
            // each item in the list is another vehicle. vehicles with no tasks can be omitted
            List <string> tasklist = new List <string>();
            for (int i = 1; i < r.Stops.Count - 1; i++)
            {
                tasklist.Add(r.Stops[i].taskId);
            }
            if (tasklist.Count > 0)
            {
                var ts = new Ivr7.TaskSequence {
                    vehicleId = r.vehicleId
                };
                foreach (var t in tasklist)
                {
                    ts.taskIds.Add(t);
                }
                sr.Model.taskSequences.Add(ts);
            }
        }

        sr.solveType = Ivr7.SolveRequest.SolveType.Evaluate; // now evaluate this sequence with the new constraints

        requestId = api.Post(sr);                            // send the new model to the api
        Solution  = api.Get(requestId);                      // get the response (which it typed, so that's cool)

        // just print the infeasibilities
        ivr7helper.printSolution(Solution, false, false, false, true);

        // so here we should have some constraints which have been broken.
        // We get told which dimension is related (if the constraint is related to a dimension)
        // we also get told which type of constraint (if known) and the degree to which the constraint is broken.

        //  if the constraints are tardy constraints being broken, this means the task starts AFTER the
        //  allowable window. the limit will be often be zero, the value will be the amount by which the
        //  vehicle is late. we can check the arrival time of the task to verify this.
        HashSet <string> infeasibleTasks = new HashSet <string>();

        foreach (var t in Solution.Infeasibilities)
        {
            infeasibleTasks.Add(t.taskId);
        }
        foreach (var r in initialSolution.Routes)
        { // now step through the initial solution and confirm this.
            foreach (var s in r.Stops)
            {
                if (infeasibleTasks.Contains(s.taskId))
                {
                    foreach (var a in s.Attributes)
                    {
                        if (a.dimId == "time")
                        {
                            if (!(a.startValue > 14 * 60))
                            {
                                throw new Exception("Hmmm. a stop was marked as infeasible but it's arrival time looks okay?");
                                // don't worry, this won't happen unless the solver is broken, or you're checking against
                                // the incorrect solution reference.
                            }
                        }
                    }
                }
            }
        }
        // we could try other things: how about we take out all tasks which are infeasible?
        // lets modify the solve request to take out these stops.
        foreach (var ts in sr.Model.taskSequences)
        {
            foreach (var tskId in ts.taskIds.ToArray())
            {
                if (infeasibleTasks.Contains(tskId))
                {
                    ts.taskIds.Remove(tskId); // lol. this is O(n) on the .net list (which is actually an array). For this example it's fine,
                                              // but in general, it's better a linkedlist for these kinds of operators. Although you need a prettty
                                              // massive model to really feel the performance impact of this.
                }
            }
        }
        // okay, but kinda obviously, the pickups were mostly feasible on their time windows, it was
        // just the dropoffs that were a problem. So now we have pickups without a dropoff?
        // what will happen?
        // lets re-run the model and check for infeasibilities
        requestId = api.Post(sr);       // send the new model to the api
        Solution  = api.Get(requestId); // get the response (which it typed, so that's cool)

        // just print the infeasibilities
        ivr7helper.printSolution(Solution, false, false, false, true);
        // so this is again, very intuitive. We find that there are a whole bunch of precendence
        // constraints which are then broken, cumul-pair constraints and task-pair constraints.
        // this is because there's a relation between the pickup and dropoff and either they're BOTH scheduled
        // or BOTH unscheduled. Having one task assigned to a vehicle in the schedule without the other breaks
        // a bunch of constraints.
        // so lets apply the same trick and remove these stops.
        foreach (var t in Solution.Infeasibilities)
        {
            infeasibleTasks.Add(t.taskId);
        }

        foreach (var ts in sr.Model.taskSequences)
        {
            foreach (var tskId in ts.taskIds.ToArray())
            {
                if (infeasibleTasks.Contains(tskId))
                {
                    ts.taskIds.Remove(tskId);
                }
            }
        }

        requestId = api.Post(sr);       // send the new model to the api
        Solution  = api.Get(requestId); // get the response (which it typed, so that's cool)
        // just print the infeasibilities
        ivr7helper.printSolution(Solution, false, false, false, true);
        // great, this is actually an empty table now, which means there aren't any infeasibilities left in the schedule
        // but at what cost did that come? Quite a lot. the solution now is very expensive.
        // We could simply copy the solution, then switch the model back to optimise and re-run it

        var prevSolution = Solution;

        sr.solveType = Ivr7.SolveRequest.SolveType.Optimise;
        requestId    = api.Post(sr);
        Solution     = api.Get(requestId);
        if (prevSolution.Objective < Solution.Objective)
        {
            throw new Exception("Whoa, this doesn't make any sense :-)");
        }

        ivr7helper.printSolution(Solution);
        // with no infeasibilities.
        // for visualisations see the R/python notebook for plots on the same example.

        return;
    }
    public void Run()
    {
        var api = new ApiHelper <Ivr7.SolveRequest, Ivr7.SolutionResponse>("ivr7-kt461v8eoaif", configFile);
        // so here we're going to build the model

        // create a solve request

        var m = new Ivr7.Model(); // initialise the model container

        // we're going to reuse the helpers described in the ivr7basic example. Please see that for a reference.
        ivr7helper.makeDistanceTimeCapDims(m); // adds distance, time & capacity
        ivr7helper.makeLocations(m, data);     // adds all the locations to the model
        ivr7helper.makeJobTimeCap(m, data, ivr7helper.Rep(0, data.Count - 1), ivr7helper.Seq(1, data.Count));
        m.vehicleCostClasses.Add(ivr7helper.makeVccSimple("vcc1", 1000, 0.01f, 0.01f, 0.01f, 1, 3));
        m.vehicleClasses.Add(ivr7helper.makeVcSimple("vc1", 1, 1, 1, 1));
        for (int i = 0; i < 4; i++)
        {
            m.Vehicles.Add(ivr7helper.makeVehicleCap("vehicle_" + i, // unique id for the vehicle.
                                                     "vc1",          // the vehicle class
                                                     "vcc1",         // the vehicle cost class
                                                     2000,           // the capacity of the vehicle
                                                     data[0].id,     // start location for the vehicle
                                                     data[0].id,     // end location for the vehicle
                                                     7 * 60,         // start time: 7 AM
                                                     18 * 60         // end time: 6 PM
                                                     ));
        }

        // okay, so that's a basic model. Lets now use the objects, but submit them to the api
        // through a different mechanism.
        var data_api = new ApiHelper <IVRData.CachedModel, object>("ivrdata-o43e0dvs78zq", configFile);
        // so the data api allows us to push just the data, without anything else.
        var dataModel = new IVRData.CachedModel();

        dataModel.Model = ApiHelper <object, object> .SerialiseObject <Ivr7.Model>(m);

        // epic: we just saved our model as a byte stream into this data payload.
        string modelID = data_api.Post(dataModel);

        var sr = new Ivr7.SolveRequest();                    // we can now make a solve request which references the model we've uploaded

        sr.modelID   = modelID;                              // tell the solve request to use the model we uploaded.
        sr.solveType = Ivr7.SolveRequest.SolveType.Optimise; // Optimise the solve request.

        // now it's just sending the model to the api
        string requestId = api.Post(sr); // send the model to the api

        Solution = api.Get(requestId);   // get the response (which it typed, so that's cool)
        ivr7helper.printSolution(Solution);
        // this also means that because we have a model which is versioned separately from the
        // solve request, we can use the solve request with the task-sequence and have that apply
        // to a model. So lets extract the task sequence from the solved model.


        foreach (var r in Solution.Routes)
        {
            List <string> tasklist = new List <string>();
            for (int i = 1; i < r.Stops.Count - 1; i++)
            {
                tasklist.Add(r.Stops[i].taskId);
            }
            if (tasklist.Count > 0)
            {
                var ts = new Ivr7.TaskSequence {
                    vehicleId = r.vehicleId
                };
                foreach (var t in tasklist)
                {
                    ts.taskIds.Add(t);
                }
                sr.Routes.Add(ts); //NOTE: We're adding the tasks to the solve request, not the model - the task sequence will be
                                   // applied to the model we've referenced in this example
            }
        }

        sr.solveType = Ivr7.SolveRequest.SolveType.Evaluate; // now evaluate this sequence with the model reference (i.e. a minimal data-send)

        requestId = api.Post(sr);
        var EvalSolution = api.Get(requestId);

        // just print the infeasibilities
        ivr7helper.printSolution(EvalSolution);

        if (Math.Abs(EvalSolution.Objective - Solution.Objective) > 0.01f)
        {
            throw new Exception("Evaluation not identical to original solution value?");
        }
        // so this is pretty nice when we think about it. It means that if you want to evaluate
        // several permutations (i.e. modifications on a UI) then you don't have to resend the model each
        // time, you can send it only when the master data is modified (i.e. times, locations, tasks etc)
        // and then just use an evaluate solve request against a particular task-sequence.

        // for visualisations see the R/python notebook for plots on the same example.

        return;
    }