431 | | [[Image(1_MTVRP_operator_featuremodel.png, 600px)]] |
432 | | |
433 | | The {{{SingleDepotOperator}}}, {{{CapcaitatedOperator}}} and {{{TimeWindowedOperator}}} represent '''VRP variants''' or '''parts of VRP variants'''. The {{{AlbaOperator}}} implements all three of them and therefore can be used for all of them. In comparison, the {{{GVROperator}}} (not show in the diagram) does not implement {{{MultiDepotOperator}}}(also not shown) and therefore is not compatible with variants that use multiple depots. |
| 431 | [[Image(1_MTVRP_operator_featuremodel.png, 800px)]] |
| 432 | |
| 433 | The {{{SingleDepotOperator}}}, {{{CapcaitatedOperator}}} and {{{TimeWindowedOperator}}} represent ''VRP variants'' or ''parts of VRP variants''. The {{{AlbaOperator}}} implements all three of them and therefore can be used for all of them. In comparison, the {{{GVROperator}}} (not show in the diagram) does not implement {{{MultiDepotOperator}}}(also not shown) and therefore is not compatible with variants that use multiple depots. |
| 479 | |
| 480 | The implementation of the new {{{MultiTripIterativeInsertionCreator}}} is very easy. We use the functionality of the {{{IterativeInsertionCreator}}} of the {{{PotvinEncoding}}} to create a {{{PotvinEncoding}}} and convert it with the method from the previous section of the tutorial. |
| 481 | |
| 482 | Note that all initial solution candidates created with no delimiters, because random delimiters would mainly yield infeasible solutions, which are hard to repair afterwards. |
| 483 | |
| 484 | {{{ |
| 485 | #!cs |
| 486 | [Item("MultiTripIterativeInsertionCreator", "Creates a randomly initialized VRP solution.")] |
| 487 | [StorableClass] |
| 488 | public sealed class MultiTripIterativeInsertionCreator |
| 489 | : PotvinCreator, IStochasticOperator, IMultiTripOperator { |
| 490 | |
| 491 | #region IStochasticOperator Members |
| 492 | public ILookupParameter<IRandom> RandomParameter { |
| 493 | get { return (LookupParameter<IRandom>)Parameters["Random"]; } |
| 494 | } |
| 495 | #endregion |
| 496 | |
| 497 | [StorableConstructor] |
| 498 | private MultiTripIterativeInsertionCreator(bool deserializing) |
| 499 | : base(deserializing) { } |
| 500 | |
| 501 | public MultiTripIterativeInsertionCreator() |
| 502 | : base() { |
| 503 | Parameters.Add(new LookupParameter<IRandom>("Random", "The pseudo random number generator.")); |
| 504 | } |
| 505 | |
| 506 | public override IDeepCloneable Clone(Cloner cloner) { |
| 507 | return new MultiTripIterativeInsertionCreator(this, cloner); |
| 508 | } |
| 509 | |
| 510 | private MultiTripIterativeInsertionCreator(MultiTripIterativeInsertionCreator original, Cloner cloner) |
| 511 | : base(original, cloner) { |
| 512 | } |
| 513 | |
| 514 | public override IOperation InstrumentedApply() { |
| 515 | PotvinEncoding singleTripSolution = IterativeInsertionCreator.CreateSolution( |
| 516 | ProblemInstanceParameter.ActualValue, RandomParameter.ActualValue, false); |
| 517 | |
| 518 | VRPToursParameter.ActualValue = MultiTripEncoding.ConvertFrom(ProblemInstanceParameter.ActualValue, singleTripSolution); |
| 519 | |
| 520 | return base.InstrumentedApply(); |
| 521 | } |
| 522 | } |
| 523 | }}} |
| 524 | |
| 525 | A more intelligent {{{Creator}}}, which implements a heuristic to place reasonable delimiters for initial solutions would be nice and would help the algorithm a lot, but is not part of this tutorial. |
| 526 | |
| 528 | |
| 529 | The {{{MultiTripManipulator}}} randomly flips a delimiter in a random tour. This is a very simple mutation and many more implementations are possible, for example to shift a delimiter within a tour. Also a more intelligent manipulators are possible, for example a manipulator who looks for good delimiter positions in long sub-tours, or one who tries to remove delimiters in short sub-tours. |
| 530 | |
| 531 | {{{ |
| 532 | #!cs |
| 533 | [Item("MultiTripManipulator", "The manimulation operator which flips a random tour delimiter in a multi trip VRP.")] |
| 534 | [StorableClass] |
| 535 | public class MultiTripManipulator |
| 536 | : PotvinManipulator, IMultiTripOperator { |
| 537 | |
| 538 | [StorableConstructor] |
| 539 | private MultiTripManipulator(bool deserializing) : base(deserializing) { } |
| 540 | |
| 541 | public MultiTripManipulator() : base() { } |
| 542 | |
| 543 | public override IDeepCloneable Clone(Cloner cloner) { |
| 544 | return new MultiTripManipulator(this, cloner); |
| 545 | } |
| 546 | |
| 547 | private MultiTripManipulator(MultiTripManipulator original, Cloner cloner) |
| 548 | : base(original, cloner) { |
| 549 | } |
| 550 | |
| 551 | protected override void Manipulate(IRandom random, PotvinEncoding individual) { |
| 552 | var multiTripIndividual = individual as MultiTripEncoding; |
| 553 | if (multiTripIndividual != null && multiTripIndividual.Tours.Count > 0) { |
| 554 | int tour = random.Next(individual.Tours.Count); |
| 555 | |
| 556 | if (multiTripIndividual.Tours[tour].Stops.Count > 0) { |
| 557 | int position = random.Next(multiTripIndividual.Tours[tour].Stops.Count); |
| 558 | |
| 559 | multiTripIndividual.FlipDelimiter(tour, position); |
| 560 | } |
| 561 | } |
| 562 | } |
| 563 | } |
| 564 | }}} |
| 565 | |
| 568 | To implement a new {{{Crossover}}} which only combines two sets of ''delimiters'' would not yield good results. Therefore we implement a crossover that first performs a {{{PotvinRouteBasedCrossover}}} and afterwards copy the delimiters from the copied tour into the new replaced tour of the new solution. |
| 569 | |
| 570 | Unfortunately, the implementation of the crossover does not return which route was replaced, so we have to look it up first with a small helper method. Then we copy the delimiters from the old tour onto the new, replaced tour. |
| 571 | |
| 572 | {{{ |
| 573 | #!cs |
| 574 | [Item("MultiTripRBX", "The RBX crossover for multi-trip VRP representations.")] |
| 575 | [StorableClass] |
| 576 | public class MultiTripRBX |
| 577 | : PotvinCrossover, IMultiTripOperator { |
| 578 | |
| 579 | [StorableConstructor] |
| 580 | private MultiTripRBX(bool deserializing) : base(deserializing) { } |
| 581 | |
| 582 | public MultiTripRBX() |
| 583 | : base() { } |
| 584 | |
| 585 | public override IDeepCloneable Clone(Cloner cloner) { |
| 586 | return new MultiTripRBX(this, cloner); |
| 587 | } |
| 588 | |
| 589 | private MultiTripRBX(MultiTripRBX original, Cloner cloner) |
| 590 | : base(original, cloner) { |
| 591 | } |
| 592 | |
| 593 | private int FindReplacedTour(PotvinEncoding original, PotvinEncoding manipulated) { |
| 594 | int replacedTour = -1; |
| 595 | for (int tourIdx = 0; tourIdx < original.Tours.Count; tourIdx++) { |
| 596 | if (original.Tours[tourIdx].Stops.Count != manipulated.Tours[tourIdx].Stops.Count) { |
| 597 | replacedTour = tourIdx; |
| 598 | break; |
| 599 | } |
| 600 | |
| 601 | for (int stopIdx = 0; stopIdx < original.Tours[tourIdx].Stops.Count; stopIdx++) { |
| 602 | if (original.Tours[tourIdx].Stops[stopIdx] != manipulated.Tours[tourIdx].Stops[stopIdx]) { |
| 603 | replacedTour = tourIdx; |
| 604 | break; |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | if (replacedTour >= 0) |
| 609 | break; |
| 610 | } |
| 611 | |
| 612 | return replacedTour; |
| 613 | } |
| 614 | |
| 615 | protected override PotvinEncoding Crossover(IRandom random, PotvinEncoding parent1, PotvinEncoding parent2) { |
| 616 | MultiTripEncoding p1 = parent1 as MultiTripEncoding; |
| 617 | MultiTripEncoding p2 = parent2 as MultiTripEncoding; |
| 618 | |
| 619 | PotvinEncoding child = PotvinRouteBasedCrossover.Apply(random, parent1, parent2, ProblemInstance, AllowInfeasibleSolutions.Value.Value); |
| 620 | MultiTripEncoding ch = child as MultiTripEncoding; |
| 621 | |
| 622 | if (ch != null && p1 != null) { |
| 623 | int tourIdx = FindReplacedTour(parent2, child); |
| 624 | if (tourIdx != -1) { |
| 625 | ch.TripDelimiters[tourIdx] = p1.TripDelimiters[tourIdx]; |
| 626 | } |
| 627 | } |
| 628 | |
| 629 | return child; |
| 630 | } |
| 631 | } |
| 632 | }}} |
| 633 | |
| 637 | |
| 638 | Before we can run the {{{MultiTrip}}} variant inside an algorithm we need to configure the algorithm beforehand. This tutorial provides step by step instructions on how to setup the algorithm and load the problem instance. To avoid this procedure you could write a new {{{VRPDataInterpreter}}} (see [wiki:UsersHowtosImplementANewVRPProblemInstance How to ... implement a new VRP ProblemInstance]). In the attachments of this tutorial you also find a configured ready-to-run algorithm. |
| 639 | |
| 640 | 1. Create a new {{{Genetic Algorithm}}} (you can chose a variant like Offspring Selection GA too, then the configuration changes slightly) |
| 641 | a. Set a new {{{Vehicle Routing Problem}}} |
| 642 | b. Set the {{{ProblemInstance}}} to a new {{{MultiTripVRPInstance}}} |
| 643 | 2. Configure the {{{Genetic Algorithm}}} |
| 644 | a. In the {{{Problem}}} set the {{{SolutionCreator}}} to {{{MultiTripIterativeInsertionCreator}}} (if not already set automatically) |
| 645 | b. In the {{{Algorithm Parameters}}} set the {{{Crossover}}} to {{{MultiTripRBX }}}(if you like, you can use the other {{{PotvinCrossovers}}} as well in a {{{MultiVRPSolutionCrossover}}}) |
| 646 | c. Set the {{{Mutator}}} to a {{{MultiVRPSolutionManipulator}}} |
| 647 | i. Enable the {{{MultiTripManipulator}}}, {{{PotvinOneLevelExchange}}} and {{{PotvinTwoLevelExchange}}} manipulator |
| 648 | i. If you want, increase the ''propability'' of the {{{MultiTripManipulator}}} (e.g. to 2) |
| 649 | d. If you want, activate the {{{CapacityRelaxion}}} in the Analyzers |
| 650 | e. Set the MutationProbability to 25% |
| 651 | f. Set the PopulationSize to 20 (or higher for better results) |
| 652 | g. Set the Generations to 2000 (or higher for better results) |
| 653 | 3. Open a separate {{{Vehicle Routing Problem}}} |
| 654 | a. Open a {{{CVRP problem}}} (your own or from a library) (e.g. Augerat P-n50-k7) |
| 655 | b. Open the original {{GA ProblemInstance}}} and the {{{ProblemInstance}} from the new separate problem side by side |
| 656 | c. Copy the {{{Coordinates}}} and {{{Demands}}} to the empty {{{MultiTripVRPInstance}}} by drag and drop them |
| 657 | d. Set the remaining parameters (capacity, vehicles, ...) |
| 658 | e. Set the {{{MaxDistance}}} (e.g. to 500) |
| 659 | 4. Run the Algorithm |
| 660 | |