コード例 #1
0
 @Override
 public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes) {
   List<VehicleRoute> routes = new ArrayList<VehicleRoute>(vehicleRoutes);
   for (VehicleRoute route : routes) {
     if (route.isEmpty()) {
       fleetManager.unlock(route.getVehicle());
       vehicleRoutes.remove(route);
     }
   }
 }
コード例 #2
0
ファイル: VrpXMLWriter.java プロジェクト: homerquan/jsprit
  public void write(String filename) {
    if (!filename.endsWith(".xml")) filename += ".xml";
    log.info("write vrp: " + filename);
    XMLConf xmlConfig = new XMLConf();
    xmlConfig.setFileName(filename);
    xmlConfig.setRootElementName("problem");
    xmlConfig.setAttributeSplittingDisabled(true);
    xmlConfig.setDelimiterParsingDisabled(true);

    writeProblemType(xmlConfig);
    writeVehiclesAndTheirTypes(xmlConfig);

    // might be sorted?
    List<Job> jobs = new ArrayList<Job>();
    jobs.addAll(vrp.getJobs().values());
    for (VehicleRoute r : vrp.getInitialVehicleRoutes()) {
      jobs.addAll(r.getTourActivities().getJobs());
    }

    writeServices(xmlConfig, jobs);
    writeShipments(xmlConfig, jobs);

    writeInitialRoutes(xmlConfig);
    writeSolutions(xmlConfig);

    OutputFormat format = new OutputFormat();
    format.setIndenting(true);
    format.setIndent(5);

    try {
      Document document = xmlConfig.createDoc();

      Element element = document.getDocumentElement();
      element.setAttribute("xmlns", "http://www.w3schools.com");
      element.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
      element.setAttribute("xsi:schemaLocation", "http://www.w3schools.com vrp_xml_schema.xsd");

    } catch (ConfigurationException e) {
      logger.error("Exception:", e);
      e.printStackTrace();
      System.exit(1);
    }

    try {
      Writer out = new FileWriter(filename);
      XMLSerializer serializer = new XMLSerializer(out, format);
      serializer.serialize(xmlConfig.getDocument());
      out.close();
    } catch (IOException e) {
      logger.error("Exception:", e);
      e.printStackTrace();
      System.exit(1);
    }
  }
コード例 #3
0
 private boolean hasActivityIn(Collection<VehicleRoute> routes, String jobId) {
   boolean isInRoute = false;
   for (VehicleRoute route : routes) {
     for (TourActivity act : route.getActivities()) {
       if (act instanceof TourActivity.JobActivity) {
         if (((TourActivity.JobActivity) act).getJob().getId().equals(jobId)) isInRoute = true;
       }
     }
   }
   return isInRoute;
 }
 private double getDeltaAbsoluteFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
   Capacity load = Capacity.addup(getCurrentMaxLoadInRoute(route), job.getSize());
   //		double load = getCurrentMaxLoadInRoute(route) + job.getCapacityDemand();
   double currentFix = 0.0;
   if (route.getVehicle() != null) {
     if (!(route.getVehicle() instanceof NoVehicle)) {
       currentFix += route.getVehicle().getType().getVehicleCostParams().fix;
     }
   }
   if (!newVehicle.getType().getCapacityDimensions().isGreaterOrEqual(load)) {
     return Double.MAX_VALUE;
   }
   return newVehicle.getType().getVehicleCostParams().fix - currentFix;
 }
コード例 #5
0
 @Override
 public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) {
   if (job instanceof Service) {
     route.setVehicleAndDepartureTime(
         iData.getSelectedVehicle(), iData.getVehicleDepartureTime());
     if (!iData.getSelectedVehicle().isReturnToDepot()) {
       if (iData.getDeliveryInsertionIndex()
           >= route.getTourActivities().getActivities().size()) {
         setEndLocation(route, (Service) job);
       }
     }
     TourActivity activity = vehicleRoutingProblem.copyAndGetActivities(job).get(0);
     route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), activity);
   } else delegator.handleJobInsertion(job, iData, route);
 }
コード例 #6
0
 public void visit(TourActivity activity) {
   for (Vehicle vehicle : vehicles) {
     double latestArrTimeAtPrevAct =
         latest_arrTimes_at_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()];
     Location prevLocation = location_of_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()];
     double potentialLatestArrivalTimeAtCurrAct =
         latestArrTimeAtPrevAct
             - transportCosts.getBackwardTransportTime(
                 activity.getLocation(),
                 prevLocation,
                 latestArrTimeAtPrevAct,
                 route.getDriver(),
                 vehicle)
             - activity.getOperationTime();
     double latestArrivalTime =
         Math.min(
             activity.getTheoreticalLatestOperationStartTime(),
             potentialLatestArrivalTimeAtCurrAct);
     if (latestArrivalTime < activity.getTheoreticalEarliestOperationStartTime()) {
       stateManager.putTypedInternalRouteState(
           route, vehicle, InternalStates.SWITCH_NOT_FEASIBLE, true);
     }
     stateManager.putInternalTypedActivityState(
         activity, vehicle, InternalStates.LATEST_OPERATION_START_TIME, latestArrivalTime);
     latest_arrTimes_at_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()] = latestArrivalTime;
     location_of_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()] = activity.getLocation();
   }
 }
コード例 #7
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void futureMaxLoatAtAct1ShouldBe15() {
   stateManager.informInsertionStarts(Arrays.asList(serviceRoute), Collections.<Job>emptyList());
   Capacity atAct1 =
       stateManager.getActivityState(
           serviceRoute.getActivities().get(0), InternalStates.FUTURE_MAXLOAD, Capacity.class);
   assertEquals(15, atAct1.get(0));
 }
コード例 #8
0
 @Override
 public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) {
   if (job instanceof Shipment) {
     List<AbstractActivity> acts = vehicleRoutingProblem.copyAndGetActivities(job);
     TourActivity pickupShipment = acts.get(0);
     TourActivity deliverShipment = acts.get(1);
     route.setVehicleAndDepartureTime(
         iData.getSelectedVehicle(), iData.getVehicleDepartureTime());
     if (!iData.getSelectedVehicle().isReturnToDepot()) {
       if (iData.getDeliveryInsertionIndex() >= route.getActivities().size()) {
         setEndLocation(route, (Shipment) job);
       }
     }
     route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), deliverShipment);
     route.getTourActivities().addActivity(iData.getPickupInsertionIndex(), pickupShipment);
   } else delegator.handleJobInsertion(job, iData, route);
 }
コード例 #9
0
  public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute) {
    insertionListeners.informBeforeJobInsertion(job, insertionData, vehicleRoute);

    if (insertionData == null || (insertionData instanceof NoInsertionFound))
      throw new IllegalStateException("insertionData null. cannot insert job.");
    if (job == null) throw new IllegalStateException("cannot insert null-job");
    if (!(vehicleRoute.getVehicle().getId().equals(insertionData.getSelectedVehicle().getId()))) {
      insertionListeners.informVehicleSwitched(
          vehicleRoute, vehicleRoute.getVehicle(), insertionData.getSelectedVehicle());
      vehicleRoute.setVehicleAndDepartureTime(
          insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
    }
    jobInsertionHandler.handleJobInsertion(job, insertionData, vehicleRoute);

    insertionListeners.informJobInserted(
        job, vehicleRoute, insertionData.getInsertionCost(), insertionData.getAdditionalTime());
  }
コード例 #10
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void when_shipmentroute_loadAtAct3ShouldBe10() {
   stateManager.informInsertionStarts(Arrays.asList(shipment_route), Collections.<Job>emptyList());
   Capacity atAct =
       stateManager.getActivityState(
           shipment_route.getActivities().get(2), InternalStates.LOAD, Capacity.class);
   assertEquals(10, atAct.get(0));
 }
コード例 #11
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void when_shipmentroute_pastMaxLoatAtAct4ShouldBe15() {
   stateManager.informInsertionStarts(Arrays.asList(shipment_route), Collections.<Job>emptyList());
   Capacity atAct =
       stateManager.getActivityState(
           shipment_route.getActivities().get(3), InternalStates.PAST_MAXLOAD, Capacity.class);
   assertEquals(15, atAct.get(0));
 }
コード例 #12
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void pastMaxLoatAtAct2ShouldBe10() {
   stateManager.informInsertionStarts(Arrays.asList(serviceRoute), Collections.<Job>emptyList());
   Capacity atAct2 =
       stateManager.getActivityState(
           serviceRoute.getActivities().get(1), InternalStates.PAST_MAXLOAD, Capacity.class);
   assertEquals(15, atAct2.get(0));
 }
コード例 #13
0
  @Test
  public void whenSolvingProblem2_nuActsShouldBe6() {

    VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
    new VrpXMLReader(vrpBuilder)
        .read("src/test/resources/simpleProblem_inclShipments_iniRoutes.xml");
    VehicleRoutingProblem vrp = vrpBuilder.build();

    VehicleRoutingAlgorithm vra = new SchrimpfFactory().createAlgorithm(vrp);
    Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
    VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions);

    int nuActs = 0;
    for (VehicleRoute r : solution.getRoutes()) {
      nuActs += r.getActivities().size();
    }
    assertEquals(6, nuActs);
  }
コード例 #14
0
 @Override
 public void visit(VehicleRoute route) {
   begin(route);
   Iterator<TourActivity> revIterator = route.getTourActivities().reverseActivityIterator();
   while (revIterator.hasNext()) {
     visit(revIterator.next());
   }
   finish();
 }
コード例 #15
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void when_pdroute_loadAtAct2ShouldBe10() {
   stateManager.informInsertionStarts(
       Arrays.asList(pickup_delivery_route), Collections.<Job>emptyList());
   Capacity atAct2 =
       stateManager.getActivityState(
           pickup_delivery_route.getActivities().get(1), InternalStates.LOAD, Capacity.class);
   assertEquals(10, atAct2.get(0));
 }
コード例 #16
0
ファイル: LoadStateTest.java プロジェクト: JasonSung/jsprit
 @Test
 public void when_pdroute_pastMaxLoatAtAct1ShouldBe15() {
   stateManager.informInsertionStarts(
       Arrays.asList(pickup_delivery_route), Collections.<Job>emptyList());
   Capacity atAct1 =
       stateManager.getActivityState(
           pickup_delivery_route.getActivities().get(0),
           InternalStates.PAST_MAXLOAD,
           Capacity.class);
   assertEquals(15, atAct1.get(0));
 }
コード例 #17
0
  @Test
  public void whenSolvingProblem2_nuJobsInSolutionShouldBe4() {

    VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance();
    new VrpXMLReader(vrpBuilder)
        .read("src/test/resources/simpleProblem_inclShipments_iniRoutes.xml");
    VehicleRoutingProblem vrp = vrpBuilder.build();

    VehicleRoutingAlgorithm vra = new SchrimpfFactory().createAlgorithm(vrp);
    Collection<VehicleRoutingProblemSolution> solutions = vra.searchSolutions();
    VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions);

    SolutionPrinter.print(vrp, solution, SolutionPrinter.Print.VERBOSE);

    int jobsInSolution = 0;
    for (VehicleRoute r : solution.getRoutes()) {
      jobsInSolution += r.getTourActivities().jobSize();
    }
    assertEquals(4, jobsInSolution);
  }
コード例 #18
0
ファイル: VrpXMLWriter.java プロジェクト: homerquan/jsprit
 private void writeInitialRoutes(XMLConf xmlConfig) {
   if (vrp.getInitialVehicleRoutes().isEmpty()) return;
   String path = "initialRoutes.route";
   int routeCounter = 0;
   for (VehicleRoute route : vrp.getInitialVehicleRoutes()) {
     xmlConfig.setProperty(path + "(" + routeCounter + ").driverId", route.getDriver().getId());
     xmlConfig.setProperty(path + "(" + routeCounter + ").vehicleId", route.getVehicle().getId());
     xmlConfig.setProperty(path + "(" + routeCounter + ").start", route.getStart().getEndTime());
     int actCounter = 0;
     for (TourActivity act : route.getTourActivities().getActivities()) {
       xmlConfig.setProperty(
           path + "(" + routeCounter + ").act(" + actCounter + ")[@type]", act.getName());
       if (act instanceof JobActivity) {
         Job job = ((JobActivity) act).getJob();
         if (job instanceof Service) {
           xmlConfig.setProperty(
               path + "(" + routeCounter + ").act(" + actCounter + ").serviceId", job.getId());
         } else if (job instanceof Shipment) {
           xmlConfig.setProperty(
               path + "(" + routeCounter + ").act(" + actCounter + ").shipmentId", job.getId());
         } else {
           throw new IllegalStateException(
               "cannot write solution correctly since job-type is not know. make sure you use either service or shipment, or another writer");
         }
       }
       xmlConfig.setProperty(
           path + "(" + routeCounter + ").act(" + actCounter + ").arrTime", act.getArrTime());
       xmlConfig.setProperty(
           path + "(" + routeCounter + ").act(" + actCounter + ").endTime", act.getEndTime());
       actCounter++;
     }
     xmlConfig.setProperty(path + "(" + routeCounter + ").end", route.getEnd().getArrTime());
     routeCounter++;
   }
 }
 private double getDeltaRelativeFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
   Capacity currentLoad = getCurrentMaxLoadInRoute(route);
   //		int currentLoad = getCurrentMaxLoadInRoute(route);
   Capacity load = Capacity.addup(currentLoad, job.getSize());
   //		double load = currentLoad + job.getCapacityDemand();
   double currentRelFix = 0.0;
   if (route.getVehicle() != null) {
     if (!(route.getVehicle() instanceof NoVehicle)) {
       currentRelFix +=
           route.getVehicle().getType().getVehicleCostParams().fix
               * Capacity.divide(
                   currentLoad, route.getVehicle().getType().getCapacityDimensions());
     }
   }
   if (!newVehicle.getType().getCapacityDimensions().isGreaterOrEqual(load)) {
     return Double.MAX_VALUE;
   }
   double relativeFixCost =
       newVehicle.getType().getVehicleCostParams().fix
               * (Capacity.divide(load, newVehicle.getType().getCapacityDimensions()))
           - currentRelFix;
   return relativeFixCost;
 }
コード例 #20
0
ファイル: VrpXMLWriter.java プロジェクト: homerquan/jsprit
 private void writeSolutions(XMLConf xmlConfig) {
   if (solutions == null) return;
   String solutionPath = "solutions.solution";
   int counter = 0;
   for (VehicleRoutingProblemSolution solution : solutions) {
     xmlConfig.setProperty(solutionPath + "(" + counter + ").cost", solution.getCost());
     int routeCounter = 0;
     for (VehicleRoute route : solution.getRoutes()) {
       //				xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter
       // + ").cost", route.getCost());
       xmlConfig.setProperty(
           solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").driverId",
           route.getDriver().getId());
       xmlConfig.setProperty(
           solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").vehicleId",
           route.getVehicle().getId());
       xmlConfig.setProperty(
           solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").start",
           route.getStart().getEndTime());
       int actCounter = 0;
       for (TourActivity act : route.getTourActivities().getActivities()) {
         xmlConfig.setProperty(
             solutionPath
                 + "("
                 + counter
                 + ").routes.route("
                 + routeCounter
                 + ").act("
                 + actCounter
                 + ")[@type]",
             act.getName());
         if (act instanceof JobActivity) {
           Job job = ((JobActivity) act).getJob();
           if (job instanceof Service) {
             xmlConfig.setProperty(
                 solutionPath
                     + "("
                     + counter
                     + ").routes.route("
                     + routeCounter
                     + ").act("
                     + actCounter
                     + ").serviceId",
                 job.getId());
           } else if (job instanceof Shipment) {
             xmlConfig.setProperty(
                 solutionPath
                     + "("
                     + counter
                     + ").routes.route("
                     + routeCounter
                     + ").act("
                     + actCounter
                     + ").shipmentId",
                 job.getId());
           } else {
             throw new IllegalStateException(
                 "cannot write solution correctly since job-type is not know. make sure you use either service or shipment, or another writer");
           }
         }
         xmlConfig.setProperty(
             solutionPath
                 + "("
                 + counter
                 + ").routes.route("
                 + routeCounter
                 + ").act("
                 + actCounter
                 + ").arrTime",
             act.getArrTime());
         xmlConfig.setProperty(
             solutionPath
                 + "("
                 + counter
                 + ").routes.route("
                 + routeCounter
                 + ").act("
                 + actCounter
                 + ").endTime",
             act.getEndTime());
         actCounter++;
       }
       xmlConfig.setProperty(
           solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").end",
           route.getEnd().getArrTime());
       routeCounter++;
     }
     int unassignedJobCounter = 0;
     for (Job unassignedJob : solution.getUnassignedJobs()) {
       xmlConfig.setProperty(
           solutionPath
               + "("
               + counter
               + ").unassignedJobs.job("
               + unassignedJobCounter
               + ")[@id]",
           unassignedJob.getId());
       unassignedJobCounter++;
     }
     counter++;
   }
 }
コード例 #21
0
ファイル: SolutionPrinter.java プロジェクト: kostas1/jsprit
 private static String getVehicleString(VehicleRoute route) {
   return route.getVehicle().getId();
 }
コード例 #22
0
 private void setEndLocation(VehicleRoute route, Shipment shipment) {
   route.getEnd().setLocationId(shipment.getDeliveryLocation().getId());
 }
コード例 #23
0
 @Override
 public Collection<Vehicle> get(VehicleRoute route) {
   return Arrays.asList(route.getVehicle());
 }
コード例 #24
0
  static ScoredJob getScoredJob(
      Collection<VehicleRoute> routes,
      Job unassignedJob,
      JobInsertionCostsCalculator insertionCostsCalculator,
      ScoringFunction scoringFunction) {
    InsertionData best = null;
    InsertionData secondBest = null;
    VehicleRoute bestRoute = null;

    double benchmark = Double.MAX_VALUE;
    for (VehicleRoute route : routes) {
      if (secondBest != null) {
        benchmark = secondBest.getInsertionCost();
      }
      InsertionData iData =
          insertionCostsCalculator.getInsertionData(
              route,
              unassignedJob,
              NO_NEW_VEHICLE_YET,
              NO_NEW_DEPARTURE_TIME_YET,
              NO_NEW_DRIVER_YET,
              benchmark);
      if (iData instanceof InsertionData.NoInsertionFound) continue;
      if (best == null) {
        best = iData;
        bestRoute = route;
      } else if (iData.getInsertionCost() < best.getInsertionCost()) {
        secondBest = best;
        best = iData;
        bestRoute = route;
      } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
        secondBest = iData;
      }
    }

    VehicleRoute emptyRoute = VehicleRoute.emptyRoute();
    InsertionData iData =
        insertionCostsCalculator.getInsertionData(
            emptyRoute,
            unassignedJob,
            NO_NEW_VEHICLE_YET,
            NO_NEW_DEPARTURE_TIME_YET,
            NO_NEW_DRIVER_YET,
            benchmark);
    if (!(iData instanceof InsertionData.NoInsertionFound)) {
      if (best == null) {
        best = iData;
        bestRoute = emptyRoute;
      } else if (iData.getInsertionCost() < best.getInsertionCost()) {
        secondBest = best;
        best = iData;
        bestRoute = emptyRoute;
      } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
        secondBest = iData;
      }
    }
    if (best == null) {
      return new RegretInsertion.BadJob(unassignedJob);
    }
    double score = score(unassignedJob, best, secondBest, scoringFunction);
    ScoredJob scoredJob;
    if (bestRoute == emptyRoute) {
      scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, true);
    } else scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, false);
    return scoredJob;
  }
コード例 #25
0
 private void setEndLocation(VehicleRoute route, Service service) {
   route.getEnd().setLocationId(service.getLocation().getId());
 }
コード例 #26
0
ファイル: SolutionPrinter.java プロジェクト: kostas1/jsprit
 private static void printVerbose(
     PrintWriter out, VehicleRoutingProblem problem, VehicleRoutingProblemSolution solution) {
   String leftAlgin = "| %-7s | %-20s | %-21s | %-15s | %-15s | %-15s | %-15s |%n";
   out.format(
       "+--------------------------------------------------------------------------------------------------------------------------------+%n");
   out.printf(
       "| detailed solution                                                                                                              |%n");
   out.format(
       "+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+%n");
   out.printf(
       "| route   | vehicle              | activity              | job             | arrTime         | endTime         | costs           |%n");
   int routeNu = 1;
   for (VehicleRoute route : solution.getRoutes()) {
     out.format(
         "+---------+----------------------+-----------------------+-----------------+-----------------+-----------------+-----------------+%n");
     double costs = 0;
     out.format(
         leftAlgin,
         routeNu,
         getVehicleString(route),
         route.getStart().getName(),
         "-",
         "undef",
         Math.round(route.getStart().getEndTime()),
         Math.round(costs));
     TourActivity prevAct = route.getStart();
     for (TourActivity act : route.getActivities()) {
       String jobId;
       if (act instanceof JobActivity) {
         jobId = ((JobActivity) act).getJob().getId();
       } else {
         jobId = "-";
       }
       double c =
           problem
               .getTransportCosts()
               .getTransportCost(
                   prevAct.getLocation(),
                   act.getLocation(),
                   prevAct.getEndTime(),
                   route.getDriver(),
                   route.getVehicle());
       c +=
           problem
               .getActivityCosts()
               .getActivityCost(act, act.getArrTime(), route.getDriver(), route.getVehicle());
       costs += c;
       out.format(
           leftAlgin,
           routeNu,
           getVehicleString(route),
           act.getName(),
           jobId,
           Math.round(act.getArrTime()),
           Math.round(act.getEndTime()),
           Math.round(costs));
       prevAct = act;
     }
     double c =
         problem
             .getTransportCosts()
             .getTransportCost(
                 prevAct.getLocation(),
                 route.getEnd().getLocation(),
                 prevAct.getEndTime(),
                 route.getDriver(),
                 route.getVehicle());
     c +=
         problem
             .getActivityCosts()
             .getActivityCost(
                 route.getEnd(),
                 route.getEnd().getArrTime(),
                 route.getDriver(),
                 route.getVehicle());
     costs += c;
     out.format(
         leftAlgin,
         routeNu,
         getVehicleString(route),
         route.getEnd().getName(),
         "-",
         Math.round(route.getEnd().getArrTime()),
         "undef",
         Math.round(costs));
     routeNu++;
   }
   out.format(
       "+--------------------------------------------------------------------------------------------------------------------------------+%n");
   if (!solution.getUnassignedJobs().isEmpty()) {
     out.format("+----------------+%n");
     out.format("| unassignedJobs |%n");
     out.format("+----------------+%n");
     String unassignedJobAlgin = "| %-14s |%n";
     for (Job j : solution.getUnassignedJobs()) {
       out.format(unassignedJobAlgin, j.getId());
     }
     out.format("+----------------+%n");
   }
 }