def test_initial_routes(): horizon = 20000 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) m = d.generate_solver_space_matrix(m) x_m = d.insert_nodes_for_breaks(m) v = V.Vehicles(5, horizon) trip_chains_B = IR.initial_routes_2(d, v.vehicles, x_m) initial_routes_B = [tcv for tcv in trip_chains_B.values()] (assignment, routing, manager) = MR.model_run(d, x_m, v.vehicles, 10000, None, initial_routes_B) assert assignment assert assignment.ObjectiveValue() == 43516 for vehicle in v.vehicles: vehicle_id = vehicle.index original_chain = trip_chains_B[vehicle_id] index = routing.Start(vehicle_id) plan_output = [] route_time = 0 while not routing.IsEnd(index): # drop 0 node = manager.IndexToNode(index) if node != 0: plan_output.append(node) index = assignment.Value(routing.NextVar(index)) # print(original_chain) # print(plan_output) assert original_chain == plan_output
def test_insert_nodes_for_breaks(): # now test the break generator inside demand # first read in the test demand 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) # read in travel time # assume a mile a minute (60mph), so m in miles === m in minutes # convert to solver space m = d.generate_solver_space_matrix(m) # this should drastically trim travel matrix, since only one # demand is feasible before 10000 assert len(m) == 3 newtimes = d.insert_nodes_for_breaks(m) assert len(newtimes) > len(m) # expect to have added 4 nodes between 0-1, 5 nodes between 1-2, 2 # nodes 2-0 plus original 2 assert len(newtimes) == 11 + 3 # check each row for reasonableness count_new_nodes = 0 count_nodes = 0 feasible_index = d.demand.feasible for idx in d.demand.index[feasible_index]: print(idx) origin = d.demand.loc[idx,'origin'] dest = d.demand.loc[idx,'destination'] print(origin,dest,idx) # 5 nodes link to origin---depot plus 4 break nodes assert len(newtimes.loc[:,origin].index[newtimes.loc[:,origin].notna()]) == 5 # 6 nodes link to destination---origin plus 5 break nodes assert len(newtimes.loc[:,dest].index[newtimes.loc[:,dest].notna()]) == 6
def test_load_demand(): d = reader.load_demand_from_csv('test/data/demand.csv') assert (d.early.min() > 0) assert (d.early.max() < 10000) assert (d.late.min() > 0) assert (d.late.max() < 10000) assert (len(d) == 5)
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
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')
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')
def test_demand(): horizon = 10000 putime = 20 dotime = 25 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) d_alt = D.Demand(odpairs, m, horizon * 10, putime, dotime) assert (d.demand.early.min() > 0) assert (d.demand.early.max() < horizon) assert (d.demand.late.min() > 0) assert (d.demand.late.max() < horizon) # test that horizon weeded out OD pairs assert (len(d.equivalence) == 2) assert (len(d_alt.equivalence) == 10) listing = d.get_node_list() assert len(listing) == 2 for i in listing: assert isinstance(i, np.int64) # print(d.demand.loc[:,['feasible','from_node','to_node','origin','destination']]) assert d.get_map_node(0) == 0 assert d.get_map_node( 1) == 5 # first few are not feasible with short horizon assert d.get_map_node(2) == 2 assert d.get_map_node( 3) == -1 # only one pickup pair is feasible with short horizon # loading time is 15 as default assert d.get_service_time(1) == 15 # unloading time is 15 as default assert d.get_service_time(2) == 15 assert d.get_service_time(0) == 0 assert d.get_service_time(3) == 0 # loading time is 20 in alternate assert d_alt.get_service_time(1) == 20 # unloading time is 25 in alternate assert d_alt.get_service_time(6) == 25 # and long horizon has more pairs # other functions tested in test_evaluators, as they involve other modules? node_list = d.get_node_list() assert node_list[0] == 1 assert node_list[1] == 2 assert d.get_demand_number(1) == 3 assert d_alt.get_demand_number(1) == 0 assert d.get_demand_number(2) == 3 assert d.get_demand_number(0) == -1 assert d.get_demand_number(3) == -1 assert d.get_demand(0) == 0 assert d.get_demand(1) == 1 assert d.get_demand(2) == -1 assert d.get_demand(3) == 0 mm = d.generate_solver_space_matrix(m) assert mm.max().max() > 0 assert len(mm.index) == 3 assert mm.loc[0, 0] == 0 assert mm.loc[0, 1] == 930 assert np.isnan(mm.loc[0, 2]) mm = d_alt.generate_solver_space_matrix(m) assert mm.max().max() > 0 assert len(mm.index) == 11 # 5 nodes plus depot assert mm.loc[0, 0] == 0 assert mm.loc[0, 1] == 1150 assert np.isnan(mm.loc[6, 1]) assert np.isnan(mm.loc[0, 6]) mm_ex = d_alt.insert_nodes_for_breaks(mm) for idx in mm.index: assert mm.loc[idx, idx] == mm_ex.loc[idx, idx] assert len(mm_ex.index) > len(mm.index) # travel times from new nodes to all nodes is less than max of original matrix new_nodes = range(len(mm.index), len(mm_ex.index)) assert mm_ex.loc[new_nodes, :].max().max() < mm.max().max() # and all to new, ditto assert mm_ex.loc[:, new_nodes].max().max() < mm.max().max() break_chain = d_alt.get_break_node_chain(0, 1) new_node_start = len(mm.index) assert len(break_chain) == 5 assert break_chain[0] == new_node_start + 1 assert break_chain[1] == new_node_start + 0 assert break_chain[2] == new_node_start + 3 assert break_chain[3] == new_node_start + 2 assert break_chain[4] == new_node_start + 4 assert d_alt.get_break_node(0) == None bn8 = d_alt.get_break_node(new_node_start) bn10 = d_alt.get_break_node(new_node_start + 1) bn8.node = new_node_start bn8.origin = 0 bn8.destination = bn10.node bn8.break_time = 30 bn8.drive_time_restore = -480 bn10.node = new_node_start + 1 bn10.origin = 0 bn10.destination = 1 bn10.break_time = 600 bn10.drive_time_restore = -660
def test_output(): horizon = 20000 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) m = d.generate_solver_space_matrix(m) v = V.Vehicles(5, horizon) # (assignment,routing,manager) = MR.model_run_nobreaks3(d,m,v) (assignment, routing, manager) = MR.model_run_nobreaks(d, m, v.vehicles) assert assignment out = io.StringIO() err = io.StringIO() args = MockArgs() with redirected(out=out, err=err): out.flush() err.flush() SO.print_solution(d, m, m, v, manager, routing, assignment, horizon, 0, args) output = out.getvalue() expected_output = "" assert output == expected_output assert filecmp.cmp(output_file, expected_file) # make sure output file was created as directed assert os.path.exists(args.summary_output) # write details again, and this time there should be a _1 version of args.summary_output assert not os.path.exists(second_output_file) SO.print_solution(d, m, m, v, manager, routing, assignment, horizon, 0, args) # created alternate named file assert os.path.exists(second_output_file) assert filecmp.cmp(output_file, second_output_file) # now try again without the file out = io.StringIO() err = io.StringIO() args.summary_output = None with redirected(out=out, err=err): out.flush() err.flush() SO.print_solution(d, m, m, v, manager, routing, assignment, horizon, 0, args) output = out.getvalue() f = open(expected_file, "r", encoding="utf-8") expected_output = f.read() assert output == expected_output assert not os.path.exists(third_output_file) os.unlink(output_file) os.unlink(second_output_file) # reset args to dump output file args = MockArgs() # test when run with breaks x_m = d.insert_nodes_for_breaks(m) trip_chains = IR.initial_routes_2(d, v.vehicles, x_m) initial_routes = [v for v in trip_chains.values()] (assignment, routing, manager) = MR.model_run(d, x_m, v.vehicles, 10000, None, initial_routes) SO.print_solution(d, x_m, x_m, v, manager, routing, assignment, horizon, 10000, args) assert filecmp.cmp(output_file, expected_breaks_file) os.unlink(output_file)