コード例 #1
0
def test_create_time_matrix():
    matrix = reader.load_matrix_from_csv('test/data/matrix.csv')
    hours_matrix = reader.travel_time(60,matrix)
    assert (hours_matrix.loc[0,0] == 0)
    assert (hours_matrix.loc[0,1] == math.floor(21.15))
    minutes_matrix = reader.travel_time(1,matrix)
    assert (minutes_matrix.loc[0,0] == 0)
    assert (minutes_matrix.loc[0,1] == 1269.0) # a mile a minute
コード例 #2
0
def test_time_callback():
    pickup_time = 20
    dropoff_time = 10
    horizon = 10000
    m = reader.load_matrix_from_csv('test/data/matrix.csv')
    odpairs = reader.load_demand_from_csv('test/data/demand.csv')
    d = D.Demand(odpairs,
                 m,
                 horizon,
                 pickup_time=pickup_time,
                 dropoff_time=dropoff_time)
    max_distance = m.max().max()
    m_m = reader.travel_time(1, m)
    max_time = m_m.max().max()

    # test fixing travel time matrix into model space
    m_m = d.generate_solver_space_matrix(m_m)
    assert m_m.ndim == 2
    # did I get it right?  the time from
    # node 0 to node 1 is depot to node 5, is 930
    assert m_m.loc[0, 1] == m.loc[0, d.get_map_node(1)]

    # with horizon of 10000, only one OD pair is feasible
    assert len(m_m.index) == 3  # 2 nodes, 1 depot
    assert len(m_m.loc[0]) == 3

    demand_callback = E.create_demand_callback(m.index, d)

    manager = MockManager()

    assert demand_callback(manager, 0) == 0
    assert demand_callback(manager, 1) == 1
    assert demand_callback(manager, 2) == -1
    assert demand_callback(manager, 3) == 0

    # Distance tests, no pickup or dropoff times
    dist_callback = E.create_dist_callback(m_m, d)

    assert dist_callback(manager, 0, 0) == 0  # depot to depot is zero
    # Again that first line in demand is mapnode 7 to mapnode 9
    # so node 0 to node 1 is depot to node 7, is 930
    assert dist_callback(manager, 0, 1) == m.loc[0, d.get_map_node(1)]
    assert dist_callback(manager, 1, 2) == m.loc[d.get_map_node(1),
                                                 d.get_map_node(2)]
    assert dist_callback(manager, 2, 0) == m.loc[d.get_map_node(2), 0]
    # everything else is forbidden
    assert dist_callback(manager, 0, 2) > max_distance  # can't skip pickup
    assert dist_callback(manager, 2, 1) > max_distance  # can't go backwards
    assert dist_callback(manager, 1, 0) > max_distance  # can't skip dropoff

    # Time tests add pickup and delivery time

    # simple time callback with original minutes matrix
    time_callback = E.create_time_callback2(m_m, d)

    assert time_callback(manager, 0, 0) == 0
    # note that pickup time at node, dropoff time at node is transit
    # time across node, so is only added in when node is origin
    assert time_callback(manager, 0, 1) == m.loc[0, d.get_map_node(1)]
    assert time_callback(
        manager, 1,
        2) == m.loc[d.get_map_node(1), d.get_map_node(2)] + pickup_time
    assert time_callback(manager, 2,
                         0) == m.loc[d.get_map_node(2), 0] + dropoff_time
    # everything else is forbidden
    assert time_callback(manager, 0, 2) > max_time  # can't skip pickup
    assert time_callback(manager, 2, 1) > max_time  # can't go backwards
    assert time_callback(manager, 1, 0) > max_time  # can't skip dropoff

    # more extensive time callback with break nodes too
    m_m_more = d.insert_nodes_for_breaks(m_m)

    print('len m_m_more is ', len(m_m_more))

    assert len(m_m_more) > len(m_m)
    time_callback = E.create_time_callback2(m_m_more, d)
    # the following are same as above, should not have changed
    assert time_callback(manager, 0, 0) == 0
    assert time_callback(manager, 0, 1) == m.loc[0, d.get_map_node(1)]
    assert time_callback(
        manager, 1,
        2) == m.loc[d.get_map_node(1), d.get_map_node(2)] + pickup_time
    assert time_callback(manager, 2,
                         0) == m.loc[d.get_map_node(2), 0] + dropoff_time
    assert time_callback(manager, 0, 2) > max_time  # can't skip pickup
    assert time_callback(manager, 2, 1) > max_time  # can't go backwards
    assert time_callback(manager, 1, 0) > max_time  # can't skip dropoff

    # now test new nodes, 3+
    assert len(m_m_more.index) == 14

    # test long break from 0 to 1
    assert time_callback(manager, 0, 3) == 660
    # short break
    assert time_callback(manager, 0, 4) == 480
    # short break happens
    assert time_callback(manager, 4, 3) == (660 - 480) + 30
    # long break happens
    assert time_callback(manager, 3,
                         1) == (m.loc[0, d.get_map_node(1)] - 660) + 600
    # can't go
    assert time_callback(manager, 3, 4) > max_time

    # test the drive callback accumulator
    drive_callback = partial(
        E.create_drive_callback(m_m_more, d, 11 * 60, 10 * 60), manager)

    # drive callback is just the drive time, but gets reset at long breaks
    bn11 = d.get_break_node(3)
    assert bn11.drive_time_restore() == -660

    assert drive_callback(0, 0) == 0
    # no pickup, no dropoff time added to drive dimension
    assert drive_callback(0, 1) == m.loc[0, d.get_map_node(1)]
    assert drive_callback(1, 2) == m.loc[d.get_map_node(1), d.get_map_node(2)]
    assert drive_callback(2, 0) == m.loc[d.get_map_node(2), 0]
    assert drive_callback(0, 2) > max_time  # can't skip pickup
    assert drive_callback(2, 1) > max_time  # can't go backwards
    assert drive_callback(1, 0) > max_time  # can't skip dropoff

    # now test break nodes
    # test long break from 0 to 1
    assert drive_callback(0, 3) == 660  # just drive, haven't taken break
    # short break
    assert drive_callback(0, 4) == 480  # just drive, haven't taken break
    # short break happens, no impact
    assert drive_callback(4, 3) == (660 - 480)  # no 30 minute break added
    # long break happens
    assert drive_callback(
        3,
        1) == (m.loc[0, d.get_map_node(1)] - 660) + bn11.drive_time_restore()
    # can't go
    assert drive_callback(3, 4) > max_time

    # now short break callback
    short_callback = partial(
        E.create_short_break_callback(m_m_more, d, 8 * 60, 30), manager)

    # drive callback is just the drive time, but gets reset at long breaks
    bn8 = d.get_break_node(4)
    assert bn8.drive_time_restore() == -480
    assert short_callback(0, 0) == 0
    # no pickup, no dropoff time added to drive dimension
    assert short_callback(0, 1) == m.loc[0, d.get_map_node(1)]
    assert short_callback(1, 2) == m.loc[d.get_map_node(1), d.get_map_node(2)]
    assert short_callback(2, 0) == m.loc[d.get_map_node(2), 0]
    assert short_callback(0, 2) > max_time  # can't skip pickup
    assert short_callback(2, 1) > max_time  # can't go backwards
    assert short_callback(1, 0) > max_time  # can't skip dropoff

    # now test break nodes
    # test long break from 0 to 1
    assert short_callback(0, 3) == 660  # just drive, haven't taken break
    # short break
    assert short_callback(0, 4) == 480  # just drive, haven't taken break
    # short break happens, triggers restore of 480
    assert short_callback(4, 3) == (660 - 480) + bn8.drive_time_restore()
    # long break happens.  Okay, the source code is lame, but it also restores 660 - 480
    assert short_callback(
        3, 1) == (m.loc[0, d.get_map_node(1)] -
                  660) + (bn11.drive_time_restore() - bn8.drive_time_restore())
    # can't go
    assert short_callback(3, 4) > max_time
コード例 #3
0
def main():
    """Entry point of the program."""
    parser = argparse.ArgumentParser(
        description=
        'Solve assignment of truck load routing problem, give hours of service rules and a specified list of origins and destinations'
    )
    # parser.add_argument('--resume_file',type=str,dest='resumefile',
    #                     help="resume a failed solver run from this file")
    parser.add_argument('-m,--matrixfile',
                        type=str,
                        dest='matrixfile',
                        help='CSV file for travel matrix (distances)')
    parser.add_argument(
        '-d,--demandfile',
        type=str,
        dest='demand',
        help='CSV file for demand pairs (origin, dest, time windows)')
    parser.add_argument('-o,--vehicleoutput',
                        type=str,
                        dest='vehicle_output',
                        default='vehicle_output.csv',
                        help='CSV file for dumping output')
    parser.add_argument(
        '--demandoutput',
        type=str,
        dest='demand_output',
        default='demand_output.csv',
        help=
        'CSV file for dumping output for demand details (including invalid demands, etc)'
    )
    parser.add_argument(
        '--summaryoutput',
        type=str,
        dest='summary_output',
        help=
        'A file for dumping the human-readable summary output for the assignment'
    )
    parser.add_argument(
        '--speed',
        type=float,
        dest='speed',
        default=55.0,
        help=
        'Average speed, miles per hour.  Default is 55 (miles per hour).  Distance unit should match that of the matrix of distances.  The time part should be per hours'
    )
    parser.add_argument(
        '--maxtime',
        type=int,
        dest='horizon',
        default=10080,
        help='Max time in minutes.  Default is 10080 minutes, which is 7 days.'
    )

    parser.add_argument('-v,--vehicles',
                        type=int,
                        dest='numvehicles',
                        default=100,
                        help='Number of vehicles to create.  Default is 100.')
    parser.add_argument(
        '--pickup_time',
        type=int,
        dest='pickup_time',
        default=15,
        help='Pick up time in minutes.  Default is 15 minutes.')
    parser.add_argument(
        '--dropoff_time',
        type=int,
        dest='dropoff_time',
        default=15,
        help='Drop off time in minutes.  Default is 15 minutes.')

    parser.add_argument(
        '-t, --timelimit',
        type=int,
        dest='timelimit',
        default=5,
        help='Maximum run time for solver, in minutes.  Default is 5 minutes.')

    parser.add_argument(
        '--narrow_destination_timewindows',
        type=bool,
        dest='destination_time_windows',
        default=True,
        help=
        "If true, limit destination node time windows based on travel time from corresponding origin.  If false, destination nodes time windows are 0 to args.horizon.  Default true (limit the time window)."
    )

    parser.add_argument(
        '--drive_dim_start_value',
        type=int,
        dest='drive_dimension_start_value',
        default=1000,
        help=
        "Due to internal solver mechanics, the drive dimension can't go below zero (it gets truncated at zero).  So to get around this, the starting point for the drive time dimension has to be greater than zero.  The default is 1000.  Change it with this variable"
    )

    parser.add_argument('--debug',
                        type=bool,
                        dest='debug',
                        default=False,
                        help="Turn on some print statements.")

    parser.add_argument(
        '--noroutes',
        type=bool,
        dest='noroutes',
        default=False,
        help="Disable generating initial routes.  Not recommended")
    args = parser.parse_args()

    print('read in distance matrix')
    matrix = reader.load_matrix_from_csv(args.matrixfile)
    minutes_matrix = reader.travel_time(args.speed / 60, matrix)

    print('read in demand data')
    odpairs = reader.load_demand_from_csv(args.demand)
    d = D.Demand(odpairs, minutes_matrix, args.horizon)

    # convert nodes to solver space from input map space
    mm = d.generate_solver_space_matrix(minutes_matrix, args.horizon)

    # create dummy nodes for breaks
    expanded_mm = d.insert_nodes_for_breaks(mm)

    # copy new nodes to distance matrix
    expanded_m = reader.travel_time(60 / args.speed, expanded_mm)
    # print('original matrix of',len(matrix.index),'expanded to ',len(expanded_m.index))

    # vehicles:
    vehicles = V.Vehicles(args.numvehicles, args.horizon)

    # Create the routing index manager.

    # number of nodes is now given by the travel time matrix
    # probably should refactor to put time under control of
    # demand class
    num_nodes = len(expanded_mm.index)
    print('After augmenting network with break nodes, solving with ',
          num_nodes, 'nodes')
    #print(d.demand.loc[d.demand.feasible,:])
    print(d.demand.loc[:, [
        'from_node', 'to_node', 'early', 'late', 'pickup_time', 'dropoff_time',
        'round_trip', 'depot_origin', 'earliest_destination', 'feasible',
        'origin', 'destination'
    ]])

    initial_routes = None
    trip_chains = {}

    trip_chainsb = IR.initial_routes_2(d, vehicles.vehicles, expanded_mm)
    initial_routesb = [v for v in trip_chainsb.values()]
    (assB, routing, manager) = MR.model_run(d, expanded_mm, vehicles.vehicles,
                                            args.drive_dimension_start_value,
                                            None, initial_routesb,
                                            args.timelimit)
    # 1201918

    # # set up initial routes by creating a lot of little problems
    # for d_idx in d.demand.index:
    #     # depot to pickup
    #     record = d.demand.loc[d_idx]
    #     if not record.feasible:
    #         continue
    #     nodes = MR.use_nodes(record,d)
    #     (subassignment,minirouting,minimanager) = MR.model_run(d,expanded_mm,[vehicles.vehicles[0]],args.drive_dimension_start_value,nodes)
    #     trip_chains[record.origin] = MR.get_route(0,subassignment,minirouting,minimanager)
    # initial_routes = [v for v in trip_chains.values()]
    # (assA,routingA,managerA) = MR.model_run(d,expanded_mm,vehicles.vehicles,args.drive_dimension_start_value,None,initial_routes)
    # 1201918 ---  same, but slower, so don't do it

    if assB:
        assignment = assB
        ## save the assignment, (Google Protobuf format)
        #save_file_base = os.path.realpath(__file__).split('.')[0]
        #if routing.WriteAssignment(save_file_base + '_assignment.ass'):
        #    print('succesfully wrote assignment to file ' + save_file_base +
        #          '_assignment.ass')

        #print(expanded_mm)
        print('The Objective Value is {0}'.format(assignment.ObjectiveValue()))
        print('details:')

        SO.print_solution(d, expanded_m, expanded_mm, vehicles, manager,
                          routing, assignment, args.horizon,
                          args.drive_dimension_start_value, args)

        SO.csv_output(d, expanded_m, expanded_mm, vehicles, manager, routing,
                      assignment, args.horizon, args.vehicle_output)
        SO.csv_demand_output(d, expanded_m, expanded_mm, vehicles, manager,
                             routing, assignment, args.horizon,
                             args.demand_output)

    else:
        print('assignment failed')
コード例 #4
0
def main():
    """Entry point of the program."""
    parser = argparse.ArgumentParser(
        description=
        'Solve assignment of truck load routing problem, with specified list of origins and destinations, ignoring hours of service rules'
    )
    # parser.add_argument('--resume_file',type=str,dest='resumefile',
    #                     help="resume a failed solver run from this file")
    parser.add_argument('-m,--matrixfile',
                        type=str,
                        dest='matrixfile',
                        help='CSV file for travel matrix (distances)')
    parser.add_argument(
        '-d,--demandfile',
        type=str,
        dest='demand',
        help='CSV file for demand pairs (origin, dest, time windows)')
    parser.add_argument('-o,--vehicleoutput',
                        type=str,
                        dest='vehicle_output',
                        default='vehicle_output.csv',
                        help='CSV file for dumping output')
    parser.add_argument(
        '--demandoutput',
        type=str,
        dest='demand_output',
        default='demand_output.csv',
        help=
        'CSV file for dumping output for demand details (including invalid demands, etc)'
    )
    parser.add_argument(
        '--summaryoutput',
        type=str,
        dest='summary_output',
        help=
        'A file for dumping the human-readable summary output for the assignment'
    )
    parser.add_argument(
        '--speed',
        type=float,
        dest='speed',
        default=55.0,
        help=
        'Average speed, miles per hour.  Default is 55 (miles per hour).  Distance unit should match that of the matrix of distances.  The time part should be per hours'
    )
    parser.add_argument(
        '--maxtime',
        type=int,
        dest='horizon',
        default=10080,
        help='Max time in minutes.  Default is 10080 minutes, which is 7 days.'
    )

    parser.add_argument('-v,--vehicles',
                        type=int,
                        dest='numvehicles',
                        default=100,
                        help='Number of vehicles to create.  Default is 100.')
    parser.add_argument(
        '--pickup_time',
        type=int,
        dest='pickup_time',
        default=15,
        help='Pick up time in minutes.  Default is 15 minutes.')
    parser.add_argument(
        '--dropoff_time',
        type=int,
        dest='dropoff_time',
        default=15,
        help='Drop off time in minutes.  Default is 15 minutes.')

    parser.add_argument(
        '-t, --timelimit',
        type=int,
        dest='timelimit',
        default=5,
        help='Maximum run time for solver, in minutes.  Default is 5 minutes.')

    parser.add_argument(
        '--initial_routes',
        type=bool,
        dest='initial_routes',
        default=False,
        help=
        "If true, generate initial routes.  Sometimes the solution isn't as good as letting the solver do its thing, but sometimes it is better.  In tests, with all 100 trips active it is slightly better to set initial routes, but with just 50 routes active, the solution is better without initial routes."
    )

    parser.add_argument(
        '--narrow_destination_timewindows',
        type=bool,
        dest='destination_time_windows',
        default=True,
        help=
        "If true, limit destination node time windows based on travel time from corresponding origin.  If false, destination nodes time windows are 0 to args.horizon.  Default true (limit the time window)."
    )

    parser.add_argument('--debug',
                        type=bool,
                        dest='debug',
                        default=False,
                        help="Turn on some print statements.")

    args = parser.parse_args()

    print('read in distance matrix')
    matrix = reader.load_matrix_from_csv(args.matrixfile)
    minutes_matrix = reader.travel_time(args.speed / 60, matrix)

    print('read in demand data')
    odpairs = reader.load_demand_from_csv(args.demand)
    d = D.Demand(odpairs, minutes_matrix, args.horizon, use_breaks=False)

    # convert nodes to solver space from input map space
    expanded_mm = d.generate_solver_space_matrix(minutes_matrix, args.horizon)

    # echo nodes to distance matrix
    expanded_m = reader.travel_time(60 / args.speed, expanded_mm)
    # print('original matrix of',len(matrix.index),'expanded to ',len(expanded_m.index))

    # vehicles:
    vehicles = V.Vehicles(args.numvehicles, args.horizon)

    # number of nodes is now given by the travel time matrix
    # probably should refactor to put time under control of
    # demand class
    num_nodes = len(expanded_mm.index)
    print('Solving with ', num_nodes, 'nodes')
    print(d.demand.loc[:, [
        'from_node', 'to_node', 'early', 'late', 'pickup_time', 'dropoff_time',
        'round_trip', 'depot_origin', 'earliest_destination', 'feasible',
        'origin', 'destination'
    ]])

    initial_routes = None
    trip_chains = {}
    assignment = None
    routing = None
    manager = None
    if args.initial_routes:
        trip_chains = IR.initial_routes_no_breaks(d,
                                                  vehicles.vehicles,
                                                  expanded_mm,
                                                  debug=args.debug)
        initial_routes = [v for v in trip_chains.values()]
        (assignment, routing,
         manager) = MR.model_run_nobreaks(d, expanded_mm, vehicles.vehicles,
                                          None, initial_routes, args)
    else:
        (assignment, routing,
         manager) = MR.model_run_nobreaks(d,
                                          expanded_mm,
                                          vehicles.vehicles,
                                          args=args)

    if assignment:
        ## save the assignment, (Google Protobuf format)
        #save_file_base = os.path.realpath(__file__).split('.')[0]
        #if routing.WriteAssignment(save_file_base + '_assignment.ass'):
        #    print('succesfully wrote assignment to file ' + save_file_base +
        #          '_assignment.ass')

        #print(expanded_mm)
        print('The Objective Value is {0}'.format(assignment.ObjectiveValue()))
        print('details:')

        SO.print_solution(d, expanded_m, expanded_mm, vehicles, manager,
                          routing, assignment, args.horizon, 0, args)
        SO.csv_output(d, expanded_m, expanded_mm, vehicles, manager, routing,
                      assignment, args.horizon, args.vehicle_output)
        SO.csv_demand_output(d, expanded_m, expanded_mm, vehicles, manager,
                             routing, assignment, args.horizon,
                             args.demand_output)

    else:
        print('assignment failed')