| Title: | Multi-Objective Minimum Spanning Tree via NSGA-II with Local Search |
|---|---|
| Description: | Solves the Multi-Criteria Minimum Spanning Tree (mc-MST) problem on complete weighted graphs by combining the Non-dominated Sorting Genetic Algorithm II (NSGA-II) with optional Pareto local search operators. Chromosomes are represented as Prufer sequences so that every random individual decodes to a valid spanning tree (Cayley's theorem), avoiding repair operators. Four solver variants are provided: pure NSGA-II ("base"), Path Relinking ("PR"), Pareto Local Search ("PLS"), and Tabu Search ("TS"). The package supports 2 and 3 objective formulations and provides convenience functions to plot Pareto fronts and best-compromise spanning trees. This package is the reference implementation of the method described in Parraga-Alava, Inostroza-Ponta and Dorn (2017) <doi:10.1109/CEC.2017.7969432>. |
| Authors: | Jorge A. Parraga-Alava [aut, cre] (ORCID: <https://orcid.org/0000-0001-8558-9122>) |
| Maintainer: | Jorge A. Parraga-Alava <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.1 |
| Built: | 2026-06-22 19:37:08 UTC |
| Source: | https://github.com/cran/momst |
The momst package implements the Multi-Criteria Minimum Spanning Tree (mc-MST) algorithm using the NSGA-II (Non-dominated Sorting Genetic Algorithm II). Four variants are supported depending on the local search operator applied after each generation:
"base"Pure NSGA-II without local search.
"PR"NSGA-II plus Path Relinking.
"PLS"NSGA-II plus Pareto Local Search.
"TS"NSGA-II plus Tabu Search.
Chromosomes are encoded as Prufer sequences, taking advantage of Cayley's theorem (every sequence of length n-2 with values in {1,...,n} decodes to a unique spanning tree of n nodes). This bijection makes every random chromosome a valid solution, avoiding repair operators.
The main entry point is run_momst.
This package is the reference implementation of the method described in:
Parraga-Alava, J., Inostroza-Ponta, M., & Dorn, M. (2017). Using local search strategies to improve the performance of NSGA-II for the Multi-Criteria Minimum Spanning Tree problem. In 2017 IEEE Congress on Evolutionary Computation (CEC) (pp. 1818-1825). IEEE. doi:10.1109/CEC.2017.7969432
Jorge A. Parraga-Alava [email protected]
Useful links:
Report bugs at https://github.com/jorgeklz/momst/issues
Apply the Configured Local-Search Variant
apply_local_search( instance, pareto_pop, num_obj, n, variant, pop_size, lookup = NULL, verbose = FALSE )apply_local_search( instance, pareto_pop, num_obj, n, variant, pop_size, lookup = NULL, verbose = FALSE )
instance |
Edge-list |
pareto_pop |
Integer matrix. |
num_obj |
Integer. |
n |
Integer. |
variant |
One of |
pop_size |
Integer. |
lookup |
Optional lookup. |
verbose |
Logical. |
data.frame.
Constructs one n x n matrix per objective so that the weight of any
edge (i, j) can be queried in O(1) via standard matrix indexing.
build_weight_lookup(instance, n, num_obj)build_weight_lookup(instance, n, num_obj)
instance |
|
n |
Integer. Number of nodes. |
num_obj |
Integer. Number of objectives (2 or 3). |
A list of length num_obj; element k is the symmetric
n x n weight matrix of objective k.
inst <- generate_instance(10, 2, seed = 1) L <- build_weight_lookup(inst, 10, 2) L[[1]][1, 2]inst <- generate_instance(10, 2, seed = 1) L <- build_weight_lookup(inst, 10, 2) L[[1]][1, 2]
Compute Multi-Objective Costs for a Population
compute_objectives(instance, chromosomes, num_obj, lookup = NULL)compute_objectives(instance, chromosomes, num_obj, lookup = NULL)
instance |
Edge-list |
chromosomes |
Numeric matrix |
num_obj |
Integer. |
lookup |
Optional list returned by |
Numeric matrix [pop_size x (n - 2 + num_obj)].
inst <- generate_instance(10, 2, seed = 1) lk <- build_weight_lookup(inst, 10, 2) pop <- generate_prufer_population(10, 5) compute_objectives(inst, pop, 2, lk)inst <- generate_instance(10, 2, seed = 1) lk <- build_weight_lookup(inst, 10, 2) pop <- generate_prufer_population(10, 5) compute_objectives(inst, pop, 2, lk)
Returns the edge list of the spanning tree encoded by a Prufer sequence
of length n - 2. Uses Wang's pointer-based linear-time algorithm,
replacing the classic O(n^2) "search smallest leaf each step" approach.
decode_prufer(seq_prufer, n)decode_prufer(seq_prufer, n)
seq_prufer |
Integer vector of length |
n |
Integer. Number of nodes of the underlying graph. |
Integer matrix of dimensions (n - 1) x 2; each row is an
undirected edge (from, to).
decode_prufer(c(3, 1, 5), n = 5)decode_prufer(c(3, 1, 5), n = 5)
Produces a complete undirected weighted graph with random multi-objective edge weights. Each edge gets two or three independent uniform weights, one per objective.
generate_instance( n, num_obj, range_a = c(10, 100), range_b = c(10, 50), range_c = c(30, 200), seed = NULL )generate_instance( n, num_obj, range_a = c(10, 100), range_b = c(10, 50), range_c = c(30, 200), seed = NULL )
n |
Integer. Number of nodes of the graph (must be at least 3). |
num_obj |
Integer in {2, 3}. Number of objectives. |
range_a |
Numeric vector |
range_b |
Numeric vector |
range_c |
Numeric vector |
seed |
Optional integer. If supplied, the RNG seed is fixed before sampling and restored afterwards, leaving the global RNG untouched. |
A data.frame with n*(n-1)/2 rows and columns
from, to, weight_1, weight_2 (and
weight_3 when num_obj == 3).
inst <- generate_instance(n = 10, num_obj = 2, seed = 12345) head(inst)inst <- generate_instance(n = 10, num_obj = 2, seed = 12345) head(inst)
Generate an Initial Prufer-Encoded Population
generate_prufer_population(n, pop_size)generate_prufer_population(n, pop_size)
n |
Integer. Number of nodes. |
pop_size |
Integer. Number of individuals to generate. |
Integer matrix of dimensions pop_size x (n - 2).
generate_prufer_population(n = 6, pop_size = 4)generate_prufer_population(n = 6, pop_size = 4)
Assign Pareto Rank and Crowding Distance
non_dominated_crowding(population, num_obj)non_dominated_crowding(population, num_obj)
population |
Numeric matrix |
num_obj |
Integer. |
Matrix with extra columns rankingIndex and density.
Pareto Local Search
pareto_local_search( instance, pareto_pop, num_obj, n, neighbour_frac = 0.1, pop_size, lookup = NULL, verbose = FALSE )pareto_local_search( instance, pareto_pop, num_obj, n, neighbour_frac = 0.1, pop_size, lookup = NULL, verbose = FALSE )
instance |
Edge-list |
pareto_pop |
Integer matrix |
num_obj |
Integer. |
n |
Integer. |
neighbour_frac |
Numeric. |
pop_size |
Integer. |
lookup |
Optional lookup. |
verbose |
Logical. |
data.frame of chromosomes.
Path Relinking on the Current Pareto Front
path_relinking( instance, pareto_pop, num_obj, n, pop_size, lookup = NULL, verbose = FALSE )path_relinking( instance, pareto_pop, num_obj, n, pop_size, lookup = NULL, verbose = FALSE )
instance |
Edge-list |
pareto_pop |
Integer matrix |
num_obj |
Integer. |
n |
Integer. |
pop_size |
Integer. |
lookup |
Optional lookup. |
verbose |
Logical. |
data.frame of non-dominated chromosomes.
Plot the Best-Compromise Spanning Tree
plot_best_tree(result, n)plot_best_tree(result, n)
result |
List returned by |
n |
Integer. |
Invisible NULL.
Plot a Pareto Front (2-objective case)
plot_pareto_front(result, show_dominated = FALSE)plot_pareto_front(result, show_dominated = FALSE)
result |
List returned by |
show_dominated |
Logical. |
Invisible NULL.
Random Mutation on Prufer Sequences
random_mutation(population, pop_size, mut_rate)random_mutation(population, pop_size, mut_rate)
population |
Numeric matrix |
pop_size |
Integer. |
mut_rate |
Numeric in \[0, 1\]. |
Integer matrix [pop_size x (n - 2)].
Single entry point that replaces the original main.R script.
run_momst( instance = NULL, instance_file = NULL, n = 10L, num_obj = 2L, variant = c("base", "PR", "PLS", "TS"), iterations = 10L, pop_size = 50L, tour_size = 2L, cross_rate = 0.8, mut_rate = 0.05, max_generations = 100L, convergence_window = 10L, range_a = c(10, 100), range_b = c(10, 50), range_c = c(30, 200), save_dir = NULL, verbose = TRUE, seed = NULL )run_momst( instance = NULL, instance_file = NULL, n = 10L, num_obj = 2L, variant = c("base", "PR", "PLS", "TS"), iterations = 10L, pop_size = 50L, tour_size = 2L, cross_rate = 0.8, mut_rate = 0.05, max_generations = 100L, convergence_window = 10L, range_a = c(10, 100), range_b = c(10, 50), range_c = c(30, 200), save_dir = NULL, verbose = TRUE, seed = NULL )
instance |
Optional |
instance_file |
Optional path. |
n |
Integer. |
num_obj |
Integer in {2, 3}. |
variant |
One of |
iterations |
Integer or two-length integer |
pop_size |
Integer (must be even). |
tour_size |
Integer. |
cross_rate |
Numeric in \[0, 1\]. |
mut_rate |
Numeric in \[0, 1\]. |
max_generations |
Integer. |
convergence_window |
Integer. |
range_a, range_b, range_c
|
Weight ranges for instance generation. |
save_dir |
Optional directory for per-iteration result files. |
verbose |
Logical. |
seed |
Optional integer. |
Invisible list with the solution data.
Parraga-Alava, J., Inostroza-Ponta, M., & Dorn, M. (2017). Using local search strategies to improve the performance of NSGA-II for the Multi-Criteria Minimum Spanning Tree problem. In 2017 IEEE Congress on Evolutionary Computation (CEC) (pp. 1818-1825). IEEE. doi:10.1109/CEC.2017.7969432
res <- run_momst(n = 10, num_obj = 2, iterations = 3, pop_size = 20, max_generations = 30, variant = "base", seed = 1) head(res$global_pareto)res <- run_momst(n = 10, num_obj = 2, iterations = 3, pop_size = 20, max_generations = 30, variant = "base", seed = 1) head(res$global_pareto)
Tabu Search on the Current Pareto Front
tabu_search( instance, pareto_pop, num_obj, n, neighbour_frac = 0.05, pop_size, lookup = NULL, verbose = FALSE )tabu_search( instance, pareto_pop, num_obj, n, neighbour_frac = 0.05, pop_size, lookup = NULL, verbose = FALSE )
instance |
Edge-list |
pareto_pop |
Integer matrix |
num_obj |
Integer. |
n |
Integer. |
neighbour_frac |
Numeric. |
pop_size |
Integer. |
lookup |
Optional lookup. |
verbose |
Logical. |
data.frame of non-dominated chromosomes.
Tournament Selection
tournament_selection(population, pop_size, tour_size)tournament_selection(population, pop_size, tour_size)
population |
Matrix with last two columns being |
pop_size |
Integer. |
tour_size |
Integer. |
Selected subpopulation matrix.
Uniform Crossover for Prufer Sequences
uniform_crossover(pool, pop_size, cross_rate)uniform_crossover(pool, pop_size, cross_rate)
pool |
Numeric matrix |
pop_size |
Integer (must be even). |
cross_rate |
Numeric in \[0, 1\]. |
Integer matrix [pop_size x (n - 2)].