Skip to content

Commit

Permalink
Merge pull request #28 from jalving/update_jll_version
Browse files Browse the repository at this point in the history
Update jll version; update readme; do code formatting
  • Loading branch information
SebastianSchlag authored Nov 26, 2023
2 parents a4f967f + e00fd4e commit bea720d
Show file tree
Hide file tree
Showing 11 changed files with 454 additions and 223 deletions.
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "KaHyPar"
uuid = "2a6221f6-aa48-11e9-3542-2d9e0ef01880"
version = "0.3.0"
version = "0.3.1"

[deps]
KaHyPar_jll = "87a0c12d-51e1-52a8-b1ed-2b00825fe6a4"
Expand All @@ -9,7 +9,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
KaHyPar_jll = "= 1.3.0"
KaHyPar_jll = "= 1.3.3"
julia = "1.6"

[extras]
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,43 @@ KaHyPar is a  
pkg> add KaHyPar
```

## Usage
KaHyPar.jl natively accepts an incidence matrix as a Julia sparse matrix.
The following snippet shows how to define and partition a simple hypergraph that contains 7 vertices and 4 hyperedges.

```julia
using KaHyPar
using SparseArrays

# setup incidence matrix
# I and J represent non-zero coordinates in the incidence matrix
I = [1, 3, 1, 2, 4, 5, 4, 5, 7, 3, 6, 7]
J = [1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4]
V = Int.(ones(length(I))) # can technically be any non-zero value

# create the incidence matrix
A = sparse(I, J, V)

# create a KaHyPar hypergraph
h = KaHyPar.HyperGraph(A)

# partition with default edge-cut configuration with maximum imbalance of 10%
KaHyPar.partition(h, 2; configuration=:edge_cut, imbalance=0.1)

# partition with default connectivity configuration with maximum imbalance of 10%
KaHyPar.partition(h, 2; configuration=:connectivity, imbalance=0.1)
```

Configuration files may also be used to define the partition options like the following snippet. Some
sample configuration files can be found [here](https://github.com/kahypar/KaHyPar.jl/tree/master/src/config).
```julia
# partition with given configuration filepath
KaHyPar.partition(h, 2; configuration="km1_rKaHyPar_sea20.ini")
```

It is also possible to partition with node and edge weights, set target block weights, set fixed vertices, or run improvement on existing partitions. The Julia API
is not documented, but the [test files](https://github.com/kahypar/KaHyPar.jl/tree/master/test) show how to use the aforementioned features.

## License

This Julia wrapper package is released under MIT License.
Expand Down
163 changes: 125 additions & 38 deletions src/KaHyPar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using LinearAlgebra
using Libdl
using KaHyPar_jll: libkahypar

const default_configuration = joinpath(@__DIR__,"config/cut_kKaHyPar_sea20.ini")
const default_configuration = joinpath(@__DIR__, "config/cut_kKaHyPar_sea20.ini")

# KaHyPar C API
include("kahypar_h.jl")
Expand All @@ -27,56 +27,80 @@ mutable struct HyperGraph
end

#Julia HyperGraph object constructor
function HyperGraph(num_vertices,edge_indices,hyperedges,vertex_weights,edge_weights)
context = kahypar_context_new()
function HyperGraph(num_vertices, edge_indices, hyperedges, vertex_weights, edge_weights)
context = kahypar_context_new()
num_edges = kahypar_hyperedge_id_t(length(edge_indices) - 1)
hypergraph = HyperGraph(context,nothing,num_vertices,edge_indices,hyperedges,vertex_weights,edge_weights,nothing)
hypergraph = HyperGraph(
context,
nothing,
num_vertices,
edge_indices,
hyperedges,
vertex_weights,
edge_weights,
nothing,
)
return hypergraph
end

HyperGraph(num_vertices,edge_indices,hyperedges) = HyperGraph(num_vertices,edge_indices,hyperedges,kahypar_hypernode_weight_t.(ones(num_vertices)),
kahypar_hyperedge_weight_t.(ones(length(edge_indices) - 1)),nothing)

function HyperGraph(num_vertices, edge_indices, hyperedges)
return HyperGraph(
num_vertices,
edge_indices,
hyperedges,
kahypar_hypernode_weight_t.(ones(num_vertices)),
kahypar_hyperedge_weight_t.(ones(length(edge_indices) - 1)),
nothing,
)
end

"""
KaHyPar.HyperGraph(A::SparseMatrixCSC,vertex_weights::Vector{Int64},edge_weights::Vector{Int64})
Create Hypergraph from a sparse matrix representing the incidence matrix (rows are nodes, columns are edges)
"""
function HyperGraph(A::SparseMatrixCSC,vertex_weights::Vector,edge_weights::Vector)
N_v,N_e = size(A)
function HyperGraph(A::SparseMatrixCSC, vertex_weights::Vector, edge_weights::Vector)
N_v, N_e = size(A)
@assert length(vertex_weights) == N_v
@assert length(edge_weights) == N_e

edge_indices = Vector{Csize_t}(undef, N_e+1)
edge_indices = Vector{Csize_t}(undef, N_e + 1)
edge_indices[1] = 0 #must start at zero for C interface
hyperedges = Vector{kahypar_hyperedge_id_t}(undef, nnz(A))
hyperedge_i = 0
@inbounds for j in 1:N_e
n_rows = 0
for k in A.colptr[j] : (A.colptr[j+1] - 1)
for k in A.colptr[j]:(A.colptr[j + 1] - 1)
i = A.rowval[k]
n_rows += 1
hyperedge_i += 1
hyperedges[hyperedge_i] = i - 1 #subtract 1 for C interface
end
edge_indices[j+1] = edge_indices[j] + n_rows
edge_indices[j + 1] = edge_indices[j] + n_rows
end
resize!(hyperedges, hyperedge_i)

return HyperGraph(kahypar_hypernode_id_t(N_v),edge_indices,hyperedges,kahypar_hypernode_weight_t.(vertex_weights),kahypar_hyperedge_weight_t.(edge_weights))
return HyperGraph(
kahypar_hypernode_id_t(N_v),
edge_indices,
hyperedges,
kahypar_hypernode_weight_t.(vertex_weights),
kahypar_hyperedge_weight_t.(edge_weights),
)
end

#Default weights are 1
function HyperGraph(A::SparseMatrixCSC)
_check_structure(A) && error("Incidence matrix contains an empty column (i.e. a hyperedge not connected to any vertices). KaHyPar does not support empty hyperedges.")
N_v,N_e = size(A)
_check_structure(A) && error(
"Incidence matrix contains an empty column (i.e. a hyperedge not connected to any vertices). KaHyPar does not support empty hyperedges.",
)
N_v, N_e = size(A)
vertex_weights = kahypar_hypernode_id_t.(ones(N_v))
edge_weights = kahypar_hyperedge_id_t.(ones(N_e))
return HyperGraph(A,vertex_weights,edge_weights)
return HyperGraph(A, vertex_weights, edge_weights)
end

function _check_structure(A::SparseMatrixCSC)
return any(sum(A,dims = 1) .== 0) #check whether any columns (hyperedges) are empty
return any(sum(A; dims=1) .== 0) #check whether any columns (hyperedges) are empty
end

@deprecate hypergraph HyperGraph
Expand All @@ -85,74 +109,137 @@ end
KaHyPar.partition(H, kparts; options_file = "")
Partition the hypergraph `H` in `k` parts. Returns a partition vector
"""
partition(H, kparts;imbalance = 0.03, configuration = default_configuration) = partition(HyperGraph(H), kparts,imbalance = imbalance, configuration = configuration)
function partition(H, kparts; imbalance=0.03, configuration=default_configuration)
return partition(
HyperGraph(H), kparts; imbalance=imbalance, configuration=configuration
)
end

#Simple partition wrapper. We create a new context, load the file, partition the hypergraph, and free the context.
function partition(H::HyperGraph, kparts::Integer; imbalance::Number = 0.03, configuration::Union{Nothing,Symbol,String} = nothing)
function partition(
H::HyperGraph,
kparts::Integer;
imbalance::Number=0.03,
configuration::Union{Nothing,Symbol,String}=nothing,
)
objective = Cint(0)
parts = Vector{kahypar_partition_id_t}(undef, H.n_vertices)
num_hyperedges = kahypar_hyperedge_id_t(length(H.edge_indices) - 1)
if isa(configuration,Symbol)
if isa(configuration, Symbol)
if configuration == :edge_cut
config_file = joinpath(@__DIR__ ,"config/cut_kKaHyPar_sea20.ini")
config_file = joinpath(@__DIR__, "config/cut_kKaHyPar_sea20.ini")
elseif configuration == :connectivity
config_file = joinpath(@__DIR__ ,"config/km1_kKaHyPar_sea20.ini")
config_file = joinpath(@__DIR__, "config/km1_kKaHyPar_sea20.ini")
else
error("Unsupported configuration option given")
end
H.config = config_file
elseif isa(configuration,String)
elseif isa(configuration, String)
H.config = configuration
elseif configuration == nothing && H.config == nothing
H.config = default_configuration
end
kahypar_configure_context_from_file(H.context,H.config)
kahypar_configure_context_from_file(H.context, H.config)
if H.k_hypergraph == nothing
kahypar_partition(H.n_vertices, num_hyperedges, Cdouble(imbalance), kahypar_partition_id_t(kparts),H.v_weights, H.e_weights, H.edge_indices,H.hyperedges, objective, H.context, parts)
kahypar_partition(
H.n_vertices,
num_hyperedges,
Cdouble(imbalance),
kahypar_partition_id_t(kparts),
H.v_weights,
H.e_weights,
H.edge_indices,
H.hyperedges,
objective,
H.context,
parts,
)
else
kahypar_partition_hypergraph(H.k_hypergraph,kahypar_partition_id_t(kparts),Cdouble(imbalance),objective,H.context,parts)
kahypar_partition_hypergraph(
H.k_hypergraph,
kahypar_partition_id_t(kparts),
Cdouble(imbalance),
objective,
H.context,
parts,
)
end
#kahypar_context_free(context)
return Int.(parts) #typecast result back to julia Integer
end

#Load a partitioning configuration from a file
function set_config_file(H::HyperGraph,config_file::String)
function set_config_file(H::HyperGraph, config_file::String)
H.config = config_file
return nothing
end

#Improve an existing partition
function improve_partition(H::HyperGraph, kparts::Integer, input_partition::Vector;num_iterations::Int64 = 10, imbalance::Number = 0.03)
function improve_partition(
H::HyperGraph,
kparts::Integer,
input_partition::Vector;
num_iterations::Int64=10,
imbalance::Number=0.03,
)
objective = Cint(0)
parts = Vector{kahypar_partition_id_t}(undef, H.n_vertices)
num_hyperedges = kahypar_hyperedge_id_t(length(H.edge_indices) - 1)
input_partition = kahypar_partition_id_t.(input_partition)
if H.k_hypergraph == nothing
kahypar_improve_partition(H.n_vertices,num_hyperedges,Cdouble(imbalance),kahypar_partition_id_t(kparts),
H.v_weights, H.e_weights, H.edge_indices,H.hyperedges,input_partition,num_iterations,objective, H.context, parts)
kahypar_improve_partition(
H.n_vertices,
num_hyperedges,
Cdouble(imbalance),
kahypar_partition_id_t(kparts),
H.v_weights,
H.e_weights,
H.edge_indices,
H.hyperedges,
input_partition,
num_iterations,
objective,
H.context,
parts,
)
else #we already have a hypergraph object
kahypar_improve_hypergraph_partition(H.k_hypergraph,kahypar_partition_id_t(kparts),Cdouble(imbalance),objective,
H.context,input_partition,num_iterations,parts)
kahypar_improve_hypergraph_partition(
H.k_hypergraph,
kahypar_partition_id_t(kparts),
Cdouble(imbalance),
objective,
H.context,
input_partition,
num_iterations,
parts,
)
end
return Int.(parts) #typecast result back to julia Integer
end

function set_target_block_weights(H::HyperGraph,block_weights::Vector{Int64})
@assert length(block_weights) <= H.n_vertices "Number of block weights ($(length(block_weights))) must be less than or equal to number of vertices ($(H.n_vertices)) "
function set_target_block_weights(H::HyperGraph, block_weights::Vector{Int64})
@assert length(block_weights) <= H.n_vertices "Number of block weights ($(length(block_weights))) must be less than or equal to number of vertices ($(H.n_vertices)) "
@assert sum(block_weights) >= sum(H.v_weights) "Sum of individual part weights must be greater than sum of vertex weights"
n_blocks = kahypar_hypernode_id_t(length(block_weights))
block_weights = kahypar_hypernode_weight_t.(block_weights)
kahypar_set_custom_target_block_weights(n_blocks,block_weights,H.context)
kahypar_set_custom_target_block_weights(n_blocks, block_weights, H.context)
return nothing
end

#Fix vertices
function fix_vertices(H::HyperGraph,num_blocks::Int64,fixed_vertex_blocks::Vector{Int64})
function fix_vertices(H::HyperGraph, num_blocks::Int64, fixed_vertex_blocks::Vector{Int64})
num_edges = kahypar_hyperedge_id_t(length(H.edge_indices) - 1)
k_hypergraph = kahypar_create_hypergraph(kahypar_partition_id_t(num_blocks),H.n_vertices,num_edges,H.edge_indices,H.hyperedges,H.e_weights,H.v_weights)
k_hypergraph = kahypar_create_hypergraph(
kahypar_partition_id_t(num_blocks),
H.n_vertices,
num_edges,
H.edge_indices,
H.hyperedges,
H.e_weights,
H.v_weights,
)
H.k_hypergraph = k_hypergraph
kahypar_set_fixed_vertices(H.k_hypergraph,kahypar_partition_id_t.(fixed_vertex_blocks))
kahypar_set_fixed_vertices(H.k_hypergraph, kahypar_partition_id_t.(fixed_vertex_blocks))
return nothing
end

Expand Down
Loading

2 comments on commit bea720d

@jalving
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/95978

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.1 -m "<description of version>" bea720d2fdc84cd02648d156f500965a91cafe06
git push origin v0.3.1

Please sign in to comment.