def test_weighted_instance(): """Checks that the capped formulations agree on the optimal result for an instance with weighted edges. """ EPS = 0.000001 d, ndds = read_with_ndds("test-fixtures/100-random-weights") fns = [ k_ip.optimise_hpief_prime, k_ip.optimise_hpief_2prime, k_ip.optimise_hpief_prime_full_red, k_ip.optimise_hpief_2prime_full_red, k_ip.optimise_picef, k_ip.optimise_ccf, k_ip.optimise_eef, k_ip.optimise_eef_full_red ] for max_cycle in [0, 1, 2, 3, 4]: for max_chain in [0, 1, 2, 3]: opt_result_0 = fns[0](k_ip.OptConfig(d, ndds, max_cycle, max_chain)) k_utils.check_validity(opt_result_0, d, ndds, max_cycle, max_chain) for fn in fns[1:]: opt_result = fn(k_ip.OptConfig(d, ndds, max_cycle, max_chain)) k_utils.check_validity(opt_result, d, ndds, max_cycle, max_chain) print(max_cycle, max_chain, opt_result.total_score, \ opt_result.ip_model.obj_val, opt_result_0.total_score, fn) assert abs(opt_result.total_score - opt_result_0.total_score) < EPS
def test_relabelled_solve(): """Checks whether the vertex-ordering heuristic affects the result for a weighted instance """ EPS = 0.000001 d, ndds = read_with_ndds("test-fixtures/100-random-weights") for max_cycle in [0, 3]: for max_chain in [0, 5]: opt_result_0 = k_ip.optimise_picef( k_ip.OptConfig(d, ndds, max_cycle, max_chain)) print(opt_result_0.total_score) opt_result = k_ip.optimise_relabelled( k_ip.optimise_picef, k_ip.OptConfig(d, ndds, max_cycle, max_chain)) print(" ", opt_result.total_score) assert abs(opt_result.total_score - opt_result_0.total_score) < EPS
def test_weighted_instance_failure_aware(): """Checks that the CF and PICEF formulations agree on the optimal result for an instance with weighted edges, using failure-aware solving. """ EPS = 0.000001 d, ndds = read_with_ndds("test-fixtures/100-random-weights") fns = [k_ip.optimise_picef, k_ip.optimise_ccf] for max_cycle in [0, 1, 2, 3, 4]: for max_chain in [0, 1, 2, 3]: cfg = k_ip.OptConfig(d, ndds, max_cycle, max_chain, edge_success_prob=0.7) opt_result_cf = k_ip.optimise_ccf(cfg) opt_result_picef = k_ip.optimise_picef(cfg) opt_result_cf_relabelled = k_ip.optimise_relabelled( k_ip.optimise_ccf, cfg) opt_result_picef_relabelled = k_ip.optimise_relabelled( k_ip.optimise_picef, cfg) assert abs(opt_result_cf.total_score - opt_result_picef.total_score) < EPS assert abs(opt_result_cf.total_score - opt_result_cf_relabelled.total_score) < EPS assert abs(opt_result_cf.total_score - opt_result_picef_relabelled.total_score) < EPS
def test_instance_with_chain_with_option(): graph, altruists = read_digraph_file("test-fixtures/test-alt2.json") cfg = k_ip.OptConfig(graph, ndds=altruists, max_cycle=3, max_chain=2) opt_result = k_ip.optimise_picef_nhs(cfg) eq_(len(opt_result.cycles), 0) eq_(len(opt_result.chains), 1) assert_almost_equal(opt_result.total_score, 9.098)
def test_instance_with_threeway_with_backarc_max_matchable(): graph, altruists = read_digraph_file("test-fixtures/test3.json") cfg = k_ip.OptConfig(graph, ndds=altruists, max_cycle=3, max_chain=2) cfg._constrain_maximal = True opt_result = k_ip.optimise_picef_nhs(cfg) eq_(len(opt_result.cycles), 1) assert_almost_equal(opt_result.total_score, 12.147)
def test_instance_with_twocycle_and_threecycle_max_matchable(): graph, altruists = read_digraph_file("test-fixtures/test1.json") cfg = k_ip.OptConfig(graph, ndds=altruists, max_cycle=3, max_chain=2) cfg._constrain_maximal = True opt_result = k_ip.optimise_picef_nhs(cfg) eq_(len(opt_result.cycles), 1) eq_(opt_result.total_score, 8.098)
def test_instance_with_threeway_with_backarc_or_threeway_without(): graph, altruists = read_digraph_file("test-fixtures/test4.json") opt_result = k_ip.optimise_picef_nhs( k_ip.OptConfig(graph, ndds=altruists, max_cycle=3, max_chain=2)) eq_(len(opt_result.cycles), 1) assert_almost_equal(opt_result.total_score, 12.147) cycle = [v.patient_id() for v in opt_result.cycles[0]] eq_(cycle, [1, 2, 4])
def test_instance_with_two_2cycles(): d, ndds = read_with_ndds("test-fixtures/two_2cycles") fns = [ k_ip.optimise_uuef, k_ip.optimise_eef, k_ip.optimise_eef_full_red, k_ip.optimise_hpief_prime_full_red, k_ip.optimise_hpief_2prime_full_red, k_ip.optimise_hpief_prime, k_ip.optimise_hpief_2prime, k_ip.optimise_picef, k_ip.optimise_ccf ] for fn in fns: opt_result = fn(k_ip.OptConfig(d, ndds, 3, 0)) assert len(opt_result.cycles) == 1 assert opt_result.total_score == 40
def test_preflib_instance_with_zero_chain_cap(): d, ndds = read_with_ndds("test-fixtures/MD-00001-00000100") fns = [ k_ip.optimise_eef, k_ip.optimise_eef_full_red, k_ip.optimise_hpief_prime_full_red, k_ip.optimise_hpief_2prime_full_red, k_ip.optimise_hpief_prime, k_ip.optimise_hpief_2prime, k_ip.optimise_eef, k_ip.optimise_eef_full_red, k_ip.optimise_picef, k_ip.optimise_ccf ] for fn in fns: max_cycle = 4 max_chain = 0 opt_result = fn(k_ip.OptConfig(d, ndds, max_cycle, max_chain)) k_utils.check_validity(opt_result, d, ndds, max_cycle, max_chain) print(fn) assert opt_result.total_score == 39
def test_single_cycle_instance(): d, ndds = read_with_ndds("test-fixtures/one-cycle") fns = [ k_ip.optimise_uuef, k_ip.optimise_hpief_prime, k_ip.optimise_hpief_2prime, k_ip.optimise_hpief_prime_full_red, k_ip.optimise_hpief_2prime_full_red, k_ip.optimise_eef, k_ip.optimise_eef_full_red, k_ip.optimise_picef, k_ip.optimise_ccf ] for max_chain in [0, 1, 2]: for fn in fns: opt_result = fn(k_ip.OptConfig(d, ndds, 3, max_chain)) assert len(opt_result.cycles) == 1 if fn == k_ip.optimise_uuef or max_chain > 0: assert len(opt_result.chains) == 1 else: assert len(opt_result.chains) == 0
def test_chains_only_instance(): d, ndds = read_with_ndds("test-fixtures/no-cycles") fns = [ k_ip.optimise_uuef, k_ip.optimise_hpief_prime, k_ip.optimise_hpief_2prime, k_ip.optimise_hpief_prime_full_red, k_ip.optimise_hpief_2prime_full_red, k_ip.optimise_eef, k_ip.optimise_eef_full_red, k_ip.optimise_picef, k_ip.optimise_ccf ] for max_chain in [0, 1, 2]: for fn in fns: opt_result = fn(k_ip.OptConfig(d, ndds, 3, max_chain, None)) eq_(len(opt_result.cycles), 0) if fn == k_ip.optimise_uuef or max_chain > 0: eq_(len(opt_result.chains), 2) else: eq_(len(opt_result.chains), 0)
def test_failure_aware(): # Use instance with a 3-cycle and a 3-chain d, ndds = read_with_ndds("test-fixtures/3cycle_and_3chain") fns = [k_ip.optimise_picef, k_ip.optimise_ccf] for fn in fns: for max_cycle, max_chain, expected_score in [(3, 2, 3 * .125 + .75), (3, 3, 3 * .125 + .875), (0, 3, .875), (3, 0, 3 * .125)]: opt_result = fn( k_ip.OptConfig(d, ndds, max_cycle, max_chain, edge_success_prob=.5)) # 3 edges * .5**3 for the cycle; .5 + .5**2 for the chain assert opt_result.total_score == expected_score k_utils.check_validity(opt_result, d, ndds, max_cycle, max_chain)
def test_failure_aware(): # Use instance with a 3-cycle and a 3-chain d, ndds = read_with_ndds("test-fixtures/3cycle_and_3chain") fns = [k_ip.optimise_picef, k_ip.optimise_ccf] edge_weight = 1 * 0.5 for fn in fns: for max_cycle, max_chain, expected_score in [ (3, 0, 1.5), (3, 2, 1.5 + 0.5 + 0.25), (3, 3, 1.5 + 0.5 + 0.25 + 0.125), (0, 3, 0.5 + 0.25 + 0.125) ]: opt_result = fn( k_ip.OptConfig(d, ndds, max_cycle, max_chain, edge_success_prob=.5)) assert_almost_equal(opt_result.total_score, expected_score) LOGGER.debug("%s got %s, expected %s", fn, opt_result.total_score, expected_score) k_utils.check_validity(opt_result, d, ndds, max_cycle, max_chain)
def start(): parser = argparse.ArgumentParser("Solve a kidney-exchange instance") parser.add_argument("cycle_cap", type=int, help="The maximum permitted cycle length") parser.add_argument( "chain_cap", type=int, help="The maximum permitted number of edges in a chain") parser.add_argument( "formulation", help= "The IP formulation (uef, eef, eef_full_red, hpief_prime, hpief_2prime, hpief_prime_full_red, hpief_2prime_full_red, picef, cf, nhs, nhs_chains)" ) parser.add_argument( "--use-relabelled", "-r", required=False, action="store_true", help="Relabel vertices in descending order of in-deg + out-deg") parser.add_argument( "--eef-alt-constraints", "-e", required=False, action="store_true", help= "Use slightly-modified EEF constraints (ignored for other formulations)" ) parser.add_argument( "--timelimit", "-t", required=False, default=None, type=float, help="IP solver time limit in seconds (default: no time limit)") parser.add_argument("--verbose", "-v", required=False, action="store_true", help="Log Gurobi output to screen and log file") parser.add_argument( "--edge-success-prob", "-p", required=False, type=float, default=1.0, help="Edge success probability, for failure-aware matching. " + "This can only be used with PICEF and cycle formulation. (default: 1)") parser.add_argument("--lp-file", "-l", required=False, default=None, metavar='FILE', help="Write the IP model to FILE, then exit.") parser.add_argument( "--details", required=False, default=None, metavar='FILE', help="Write a summary of the problem to FILE, then exit.") parser.add_argument("--size", "-s", required=False, action='store_true', help="Only maximise number of transplants") parser.add_argument("--relax", "-x", required=False, action='store_true', help="Solve the LP relaxation.") parser.add_argument("--output", "-o", required=False, metavar='FILE', help="Write solution to FILE") parser.add_argument("--std-output", required=False, action='store_true', help="Write solution to stdout") parser.add_argument("--xml", required=False, metavar='FILE', help="Write solution as XML to FILE") parser.add_argument( "--matchable", required=False, action="store_true", help="Find and use maximally matchable edges (For NHS option only)") parser.add_argument("--debug", "-d", required=False, action="store_true", help="Enable debugging output") args = parser.parse_args() if args.debug: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(name)s ' '%(message)s') else: logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s:%(name)s ' '%(message)s') args.formulation = args.formulation.lower() input_lines = [line for line in sys.stdin if len(line.strip()) > 0] if input_lines[0].strip() == "{": # JSON input digraph, altruists = readers.read_digraph_json(input_lines) elif "xml" in input_lines[0] or "<data>" in input_lines[0]: digraph, altruists = readers.read_digraph_xml(input_lines) else: n_digraph_edges = int(input_lines[0].split()[1]) digraph_lines = input_lines[:n_digraph_edges + 2] digraph = readers.read_digraph(digraph_lines) if len(input_lines) > len(digraph_lines): ndd_lines = input_lines[n_digraph_edges + 2:] altruists = readers.read_ndds(ndd_lines, digraph) else: altruists = [] start_time = time.time() cfg = kidney_ip.OptConfig(digraph, altruists, args.cycle_cap, args.chain_cap, args.verbose, args.timelimit, args.edge_success_prob, args.eef_alt_constraints, args.lp_file, args.relax, args.size, args.details) if args.matchable: cfg._constrain_maximal = True opt_solution = solve_kep(cfg, args.formulation, args.use_relabelled) time_taken = time.time() - start_time if args.std_output: print("formulation: {}".format(args.formulation)) if opt_solution: print("formulation_name: {}".format(opt_solution.formulation_name)) print("using_relabelled: {}".format(args.use_relabelled)) print("cycle_cap: {}".format(args.cycle_cap)) print("chain_cap: {}".format(args.chain_cap)) print("edge_success_prob: {}".format(args.edge_success_prob)) if opt_solution: print("ip_time_limit: {}".format(args.timelimit)) print("ip_vars: {}".format(opt_solution.ip_model.numVars)) print("ip_constrs: {}".format(opt_solution.ip_model.numConstrs)) print("total_time: {}".format(time_taken)) if opt_solution: print("ip_solve_time: {}".format(opt_solution.ip_model.runtime)) print("solver_status: {}".format(opt_solution.ip_model.status)) print("total_score: {}".format(opt_solution.total_score)) if args.xml and opt_solution: opt_solution.as_xml(args.xml) if opt_solution: if args.output: with open(args.output, 'w') as outfile: outfile.write(str(opt_solution)) elif args.std_output: opt_solution.display()
def test_instance_with_two_threeway_or_three_twoway(): graph, altruists = read_digraph_file("test-fixtures/test2.json") opt_result = k_ip.optimise_picef_nhs( k_ip.OptConfig(graph, ndds=altruists, max_cycle=3, max_chain=2)) eq_(len(opt_result.cycles), 3) assert_almost_equal(opt_result.total_score, 24.294)