Skip to content

Commit

Permalink
minor updates
Browse files Browse the repository at this point in the history
  • Loading branch information
harshangrjn committed Sep 12, 2024
1 parent 2f1c91f commit 25a9f37
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 75 deletions.
2 changes: 1 addition & 1 deletion examples/run_examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ end
#-------------------------------#
# User-defined params #
#-------------------------------#
num_nodes = 12
num_nodes = 8
instance = 1
data_dict, augment_budget = data_I(num_nodes, instance)
# data_dict, augment_budget = data_SLAM("CSAIL") # "CSAIL", "intel", "ais2klinik"
Expand Down
126 changes: 57 additions & 69 deletions src/data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,63 +129,52 @@ function parse_file(file_path::String)
end

function _pre_process_data(data_dict::Dict{String,Any})
num_edges_existing = 0
num_edges_to_augment = 0

num_nodes = data_dict["num_nodes"]
adjacency_base_graph = data_dict["adjacency_base_graph"]
adjacency_augment_graph = data_dict["adjacency_augment_graph"]

for i in 1:(num_nodes-1)
for j in (i+1):num_nodes
if isapprox(abs(adjacency_base_graph[i, j]), 0, atol = 1E-6)
adjacency_base_graph[i, j] = 0
end
if isapprox(abs(adjacency_augment_graph[i, j]), 0, atol = 1E-6)
adjacency_augment_graph[i, j] = 0
end
if !(isapprox(
adjacency_base_graph[i, j],
adjacency_base_graph[j, i],
atol = 1E-5,
))
Memento.error("Adjacency matrix of the base graph has to be symmetric")
end
if !(isapprox(
adjacency_augment_graph[i, j],
adjacency_augment_graph[j, i],
atol = 1E-5,
))
Memento.error("Adjacency matrix of the augment graph has to be symmetric")
end
if !(isapprox(adjacency_base_graph[i, j], 0, atol = 1E-6))
num_edges_existing += 1
end
if !(isapprox(adjacency_augment_graph[i, j], 0, atol = 1E-6))
num_edges_to_augment += 1
end
num_edges_existing, num_edges_to_augment = 0, 0

for i in 1:num_nodes-1, j in i+1:num_nodes
adjacency_base_graph[i, j] =
isapprox(abs(adjacency_base_graph[i, j]), 0, atol = 1E-6) ? 0 :
adjacency_base_graph[i, j]
adjacency_augment_graph[i, j] =
isapprox(abs(adjacency_augment_graph[i, j]), 0, atol = 1E-6) ? 0 :
adjacency_augment_graph[i, j]

if !isapprox(adjacency_base_graph[i, j], adjacency_base_graph[j, i], atol = 1E-5)
Memento.error(_LOGGER, "Adjacency matrix of the base graph must be symmetric")
end
if !isapprox(
adjacency_augment_graph[i, j],
adjacency_augment_graph[j, i],
atol = 1E-5,
)
Memento.error(
_LOGGER,
"Adjacency matrix of the augment graph must be symmetric",
)
end
end

# Base graph connectivity
is_base_graph_connected = false
if num_edges_existing > 0
!(isapprox(
abs(LOpt.algebraic_connectivity(adjacency_base_graph)),
0,
atol = 1E-6,
)) && (is_base_graph_connected = true)
num_edges_existing +=
!isapprox(adjacency_base_graph[i, j], 0, atol = 1E-6) ? 1 : 0
num_edges_to_augment +=
!isapprox(adjacency_augment_graph[i, j], 0, atol = 1E-6) ? 1 : 0
end

data_dict_new = Dict{String,Any}()
data_dict_new["num_nodes"] = num_nodes
data_dict_new["adjacency_base_graph"] = adjacency_base_graph
data_dict_new["adjacency_augment_graph"] = adjacency_augment_graph
data_dict_new["num_edges_existing"] = num_edges_existing
data_dict_new["num_edges_to_augment"] = num_edges_to_augment
data_dict_new["is_base_graph_connected"] = is_base_graph_connected
is_base_graph_connected =
(num_edges_existing > 0) &&
!isapprox(abs(LOpt.algebraic_connectivity(adjacency_base_graph)), 0, atol = 1E-6)

return data_dict_new
return Dict{String,Any}(
"num_nodes" => num_nodes,
"adjacency_base_graph" => adjacency_base_graph,
"adjacency_augment_graph" => adjacency_augment_graph,
"num_edges_existing" => num_edges_existing,
"num_edges_to_augment" => num_edges_to_augment,
"is_base_graph_connected" => is_base_graph_connected,
)
end

"""
Expand Down Expand Up @@ -213,57 +202,56 @@ end
Given the pre-processed data dictionary, this function detects any infeasibility before
building the optimization model.
"""

function _detect_infeasbility_in_data(data::Dict{String,Any})
num_nodes = data["num_nodes"]
num_edges_existing = data["num_edges_existing"]
num_edges_to_augment = data["num_edges_to_augment"]
augment_budget = data["augment_budget"]

if data["num_edges_to_augment"] == 0
Memento.error(
_LOGGER,
"At least one edge has to be specified to be able to augment to the base graph",
)
# Assuming undirected graph
elseif data["num_edges_existing"] == num_nodes * (num_nodes - 1) / 2
if num_edges_to_augment == 0
Memento.error(_LOGGER, "At least one edge is required for augmentation.")
elseif num_edges_existing == num_nodes * (num_nodes - 1) / 2
Memento.error(
_LOGGER,
"Input graph is already a complete graph; augmentation is unnecessary",
"The graph is already complete; augmentation is unnecessary.",
)
elseif (data["num_edges_existing"] == 0) && (data["augment_budget"] < (num_nodes - 1))
elseif num_edges_existing == 0 && augment_budget < (num_nodes - 1)
Memento.error(
_LOGGER,
"Detected trivial solutions with disconnected graphs. `augment_budget` may be insufficient.",
"Graph is disconnected; `augment_budget` may be insufficient.",
)
elseif !isinteger(data["augment_budget"]) || (data["augment_budget"] < -1E-6)
Memento.error(_LOGGER, "Edge augmentation budget has to be a positive integer")
elseif !isinteger(augment_budget) || augment_budget < 0
Memento.error(_LOGGER, "Augmentation budget must be a positive integer.")
end

# Detect mutually exclusive sets of edges between base and augment graph
# Check mutually exclusive edge sets
if maximum(
(data["adjacency_augment_graph"] .> 0) + (data["adjacency_base_graph"] .> 0),
) > 1
Memento.error(
_LOGGER,
"Edge sets of base and augment graphs have to be mutually exclusive",
"Edge sets of base and augment graphs must be mutually exclusive.",
)
end

# Detect free vertices
# Check for free vertices
A = data["adjacency_augment_graph"] + data["adjacency_base_graph"]
for i in 1:num_nodes
if isapprox(sum(A[i, :]), 0, atol = 1E-6)
Memento.error(
_LOGGER,
"Detected trivial solutions with disconnected graphs due to free vertices.",
"Free vertices detected, leading to disconnected graphs.",
)
end
end

# Detect tour infeasibility
if (data["graph_type"] == "hamiltonian_cycle") && (data["num_edges_existing"] == 0)
if !(data["num_edges_to_augment"] >= data["num_nodes"]) ||
!(data["augment_budget"] == data["num_nodes"])
# Check Hamiltonian cycle feasibility
if data["graph_type"] == "hamiltonian_cycle" && num_edges_existing == 0
if !(num_edges_to_augment >= num_nodes && augment_budget == num_nodes)
Memento.error(
_LOGGER,
"Detected infeasibility due to the number of augmentation edges incompatible for a hamiltonian cycle",
"Infeasibility due to incompatible augmentation for Hamiltonian cycle.",
)
end
end
Expand Down
9 changes: 5 additions & 4 deletions src/utility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,11 @@ this function returns the Cheeger's constant (or isoperimetric number of the gra
(`S` and `S_complement`) of the graph.
References:
(I) Somisetty, N., Nagarajan, H. and Darbha, S., 2024, December. Spectral Graph Theoretic Methods for Enhancing
Network Robustness in Robot Localization. In 63nd IEEE conference on decision and control (CDC). IEEE.
(II) Mohar, B., 1989. Isoperimetric numbers of graphs. Journal of combinatorial theory,
Series B, 47(3), pp.274-291. Link: https://doi.org/10.1016/0095-8956(89)90029-4
(I) Somisetty, N., Nagarajan, H. and Darbha, S., "Spectral Graph Theoretic Methods for Enhancing
Network Robustness in Robot Localization". In 63nd IEEE conference on decision and control (CDC). IEEE, 2024.
(II) Mohar, B., Isoperimetric numbers of graphs. Journal of combinatorial theory,
Series B, 47(3), pp.274-291, 1989. Link: https://doi.org/10.1016/0095-8956(89)90029-4
"""
function cheeger_constant(
G::Matrix{<:Number},
Expand Down
7 changes: 6 additions & 1 deletion test/utility_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ end
G[i, j] = 1.0
G[j, i] = G[i, j]
end
cheeger = LOpt.cheeger_constant(G, glpk_optimizer)

cheeger =
LOpt.cheeger_constant(G, glpk_optimizer, formulation_type = "set_cardinality")
@test isapprox(cheeger["cheeger_constant"], 5 / 3, atol = 1E-6)

cheeger = LOpt.cheeger_constant(G, glpk_optimizer, formulation_type = "set_volume")
@test isapprox(cheeger["cheeger_constant"], 5 / 9, atol = 1E-6)
end

0 comments on commit 25a9f37

Please sign in to comment.