Packages

Comprehensive graph file format I/O for the yog graph library - Support for GraphML, GDF, JSON (D3, Cytoscape, VisJs), TGF, LEDA, Pajek, Adjacency List/Matrix, and Matrix Market

Current section

Files

Jump to
yog_io src yog_io@matrix.erl
Raw

src/yog_io@matrix.erl

-module(yog_io@matrix).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog_io/matrix.gleam").
-export([from_matrix/2, to_matrix/1, from_string/1, read/2, serialize/1, write/2]).
-export_type([matrix_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Adjacency Matrix format support.\n"
"\n"
" This module provides functions to convert between graphs and adjacency\n"
" matrices. Adjacency matrices are dense representations where `matrix[i][j]`\n"
" represents the edge from node `i` to node `j`.\n"
"\n"
" ## Format Overview\n"
"\n"
" The file representation is a space-separated text file where each row\n"
" corresponds to a row in the matrix.\n"
"\n"
" ## Example\n"
"\n"
" ```\n"
" 0.0 1.0 0.5\n"
" 1.0 0.0 0.0\n"
" 0.0 0.0 0.0\n"
" ```\n"
"\n"
" ## Usage\n"
"\n"
" ```gleam\n"
" import yog/model.{Undirected}\n"
" import yog_io/matrix as matrix_io\n"
"\n"
" pub fn main() {\n"
" let assert Ok(graph) = matrix_io.read(\"graph.mat\", Undirected)\n"
" }\n"
" ```\n"
).
-type matrix_error() :: {not_square, integer(), integer()} |
{read_error, binary(), binary()} |
{write_error, binary(), binary()}.
-file("src/yog_io/matrix.gleam", 55).
?DOC(
" Creates a graph from an adjacency matrix.\n"
"\n"
" An adjacency matrix is a square matrix where `matrix[i][j]` represents\n"
" the weight of the edge from node `i` to node `j`. A value of `0.0`\n"
" indicates no edge.\n"
).
-spec from_matrix(yog@model:graph_type(), list(list(float()))) -> {ok,
yog@model:graph(nil, float())} |
{error, matrix_error()}.
from_matrix(Graph_type, Matrix) ->
N = erlang:length(Matrix),
case N =:= 0 of
true ->
{ok, yog@model:new(Graph_type)};
false ->
Is_square = gleam@list:all(
Matrix,
fun(Row) -> erlang:length(Row) =:= N end
),
case Is_square of
false ->
{error, {not_square, N, 0}};
true ->
Graph = gleam@int:range(
0,
N - 1,
yog@model:new(Graph_type),
fun(G, I) -> yog@model:add_node(G, I, nil) end
),
Edges = case Graph_type of
undirected ->
gleam@list:index_fold(
Matrix,
[],
fun(Acc, Row@1, I@1) ->
gleam@list:index_fold(
Row@1,
Acc,
fun(Acc2, Weight, J) ->
case (J > I@1) andalso (Weight /= +0.0) of
true ->
[{I@1, J, Weight} | Acc2];
false ->
Acc2
end
end
)
end
);
directed ->
gleam@list:index_fold(
Matrix,
[],
fun(Acc@1, Row@2, I@2) ->
gleam@list:index_fold(
Row@2,
Acc@1,
fun(Acc2@1, Weight@1, J@1) ->
case (I@2 /= J@1) andalso (Weight@1
/= +0.0) of
true ->
[{I@2, J@1, Weight@1} |
Acc2@1];
false ->
Acc2@1
end
end
)
end
)
end,
_pipe = gleam@list:fold(
Edges,
Graph,
fun(G@1, E) ->
{From, To, Weight@2} = E,
yog@model:add_edge_ensure(
G@1,
From,
To,
Weight@2,
nil
)
end
),
{ok, _pipe}
end
end.
-file("src/yog_io/matrix.gleam", 114).
?DOC(
" Exports a graph to an adjacency matrix representation.\n"
"\n"
" Returns a tuple `#(nodes, matrix)` where:\n"
" - `nodes` is a list of node IDs in the order they appear in the matrix\n"
" - `matrix` is the adjacency matrix (list of lists of weights)\n"
).
-spec to_matrix(yog@model:graph(any(), float())) -> {list(integer()),
list(list(float()))}.
to_matrix(Graph) ->
Nodes = begin
_pipe = maps:keys(erlang:element(3, Graph)),
gleam@list:sort(_pipe, fun gleam@int:compare/2)
end,
Matrix = gleam@list:map(
Nodes,
fun(I) -> gleam@list:map(Nodes, fun(J) -> case I =:= J of
true ->
+0.0;
false ->
Weight = begin
_pipe@1 = gleam_stdlib:map_get(
erlang:element(4, Graph),
I
),
_pipe@2 = gleam@result:'try'(
_pipe@1,
fun(_capture) ->
gleam_stdlib:map_get(_capture, J)
end
),
gleam@result:unwrap(_pipe@2, +0.0)
end,
Weight
end end) end
),
{Nodes, Matrix}.
-file("src/yog_io/matrix.gleam", 156).
?DOC(" Parses an adjacency matrix from a string.\n").
-spec from_string(binary()) -> {ok, list(list(float()))} |
{error, matrix_error()}.
from_string(Content) ->
Lines = begin
_pipe = gleam@string:split(Content, <<"\n"/utf8>>),
_pipe@1 = gleam@list:map(_pipe, fun gleam@string:trim/1),
gleam@list:filter(_pipe@1, fun(L) -> L /= <<""/utf8>> end)
end,
gleam@list:try_map(
Lines,
fun(Line) ->
Parts = begin
_pipe@2 = gleam@string:split(Line, <<" "/utf8>>),
gleam@list:filter(_pipe@2, fun(P) -> P /= <<""/utf8>> end)
end,
_pipe@5 = gleam@list:try_map(
Parts,
fun(P@1) -> _pipe@3 = gleam_stdlib:parse_float(P@1),
gleam@result:lazy_or(
_pipe@3,
fun() -> _pipe@4 = gleam_stdlib:parse_int(P@1),
gleam@result:map(_pipe@4, fun erlang:float/1) end
) end
),
gleam@result:replace_error(
_pipe@5,
{read_error,
<<""/utf8>>,
<<"Invalid record in matrix: "/utf8, Line/binary>>}
)
end
).
-file("src/yog_io/matrix.gleam", 137).
?DOC(" Reads an adjacency matrix from a file.\n").
-spec read(binary(), yog@model:graph_type()) -> {ok,
yog@model:graph(nil, float())} |
{error, matrix_error()}.
read(Path, Graph_type) ->
_pipe = simplifile:read(Path),
_pipe@1 = gleam@result:map_error(
_pipe,
fun(Err) -> {read_error, Path, gleam@string:inspect(Err)} end
),
_pipe@2 = gleam@result:'try'(_pipe@1, fun from_string/1),
gleam@result:'try'(
_pipe@2,
fun(_capture) -> from_matrix(Graph_type, _capture) end
).
-file("src/yog_io/matrix.gleam", 176).
?DOC(" Converts a matrix to a space-separated string.\n").
-spec serialize(list(list(float()))) -> binary().
serialize(Matrix) ->
_pipe@1 = gleam@list:map(
Matrix,
fun(Row) ->
_pipe = gleam@list:map(Row, fun gleam_stdlib:float_to_string/1),
gleam@string:join(_pipe, <<" "/utf8>>)
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>).
-file("src/yog_io/matrix.gleam", 148).
?DOC(" Writes an adjacency matrix to a file.\n").
-spec write(binary(), yog@model:graph(any(), float())) -> {ok, nil} |
{error, matrix_error()}.
write(Path, Graph) ->
{_, Matrix} = to_matrix(Graph),
Content = serialize(Matrix),
_pipe = simplifile:write(Path, Content),
gleam@result:map_error(
_pipe,
fun(Err) -> {write_error, Path, gleam@string:inspect(Err)} end
).