N-Wouda / Euro-NeurIPS-2022

OptiML's contribution to the EURO meets NeurIPS 2022 vehicle routing competition.
Other
16 stars 2 forks source link

Single static solver builder #114

Closed jmhvandoorn closed 2 years ago

jmhvandoorn commented 2 years ago

Since we now are building the static solver in 3-4 different places, I would propose making a single function that handles this.

Something like:

def solve_static(
    instance,
    config,
    *,
    seed=1,
    max_runtime=None,
    max_iterations=None,
    initial_solutions=()
):
    config = hgspy.Config(seed=seed, **config["params"])
    params = hgspy.Params(config, **tools.inst_to_vars(instance))

    rng = hgspy.XorShift128(seed=seed)
    pop = hgspy.Population(params, rng)

    for sol in initial_solutions:
        pop.add_individual(hgspy.Individual(params, sol))

    ls = hgspy.LocalSearch(params, rng)

    for op in config["node_ops"]:
        ls.add_node_operator(node_ops[op](params))

    for op in config["route_ops"]:
        ls.add_route_operator(route_ops[op](params))

    algo = hgspy.GeneticAlgorithm(params, rng, pop, ls)

    for op in config["crossover_ops"]:
        algo.add_crossover_operator(crossover_ops[op])

    if max_runtime is not None:
        stop = hgspy.stop.MaxRuntime(max_runtime)
    else:
        stop = hgspy.stop.MaxIterations(max_iterations)

    return algo.run(stop)
jmhvandoorn commented 2 years ago

We that only still need to find a way to return a copy of the results instead of the results directly.

@N-Wouda do you have a suggestion on how?

Because simply using pythons copy/deepcopy functions do not work on this custom c++ result object. (as they rely on pickle)

N-Wouda commented 2 years ago

ls.add_node_operator(node_ops[op](params))

I doubt this works: on the cpp side, addNodeOperator takes a reference to a node operator, not a node operator itself. The object exists only for the duration of this call, and will not be available when the algorithm actually wants to use it later. The lists I used originally were there for this reason.

We that only still need to find a way to return a copy of the results instead of the results directly.

Result needs to take a copy of an individual on the cpp side. The object itself is fine on the py side, but copying it there won't do anything to solve this. I have the required changes on the cpp side in a stash that I will commit to the relevant branch once it exists.

jmhvandoorn commented 2 years ago

I doubt this works: on the cpp side, addNodeOperator takes a reference to a node operator, not a node operator itself. The object exists only for the duration of this call, and will not be available when the algorithm actually wants to use it later. The lists I used originally were there for this reason.

I see, never occurred to me in python that there was a difference in those two things, but I get your issue😅.

However, my point was mainly to keep the operator insertion dynamic, decided by the config file. That still holds, but then should be something like:

    node_ops = [node_ops[op](params) for op in config["node_ops"]]

    for op in node_ops:
        ls.add_node_operator(op)

What I believe is prefered over

    node_ops = [getattr(hgspy.operators, op)(params) for op in config["node_ops"]]
jmhvandoorn commented 2 years ago

Does the same hold for adding individuals to the population?

for sol in initial_solutions:
        pop.add_individual(hgspy.Individual(params, sol))
jmhvandoorn commented 2 years ago

Result needs to take a copy of an individual on the cpp side. The object itself is fine on the py side, but copying it there won't do anything to solve this. I have the required changes on the cpp side in a stash that I will commit to the relevant branch once it exists.

Please add this to #117

N-Wouda commented 2 years ago

122 closed this.