Expand description
Reading and writing graphs in foreign file formats (igraph_foreign.h).
igraph can exchange graphs with other software through a number of textual (and one binary) file formats. This module wraps all of them, in two flavours:
- file based:
Graph::read_graph_*(path, ...)andgraph.write_graph_*(path, ...)take anything implementingAsRef<Path>and open/close the file themselves (the file handle is always closed, also on errors, and write errors detected when flushing are reported); - in memory:
Graph::read_graph_*_from_str(&str, ...)parses a string andgraph.write_graph_*_to_string(...)returns the serialization as aString, without touching the file system (they use the POSIXfmemopen/open_memstreamstreams under the hood). igraph copies string attributes byte by byte, so a_to_stringwriter replaces invalid UTF-8 (only possible in attributes read from files in another encoding, e.g. Latin-1 Pajek labels) with U+FFFD: use the file based writer to keep the exact bytes.
On top of that, GraphFormat together with Graph::read_graph and
Graph::write_graph offers a format-agnostic entry point, able to guess
the format from the file extension (GraphFormat::from_path).
§Example
use igraph::{foreign::GmlWriteOptions, prelude::*};
// A directed triangle with a pendant vertex, serialized as an edge list...
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, true).unwrap();
let text = g.write_graph_edgelist_to_string().unwrap();
assert_eq!(text, "0 1\n1 2\n2 0\n2 3\n");
// ...and parsed back: exactly the same graph.
let h = Graph::read_graph_edgelist_from_str(&text, 0, true).unwrap();
assert!(g.is_same_graph(&h).unwrap());
// GML round trip of Zachary's karate club, through a temporary file.
let karate = Graph::famous("Zachary").unwrap();
let path = std::env::temp_dir().join(format!("igraph-doc-foreign-{}.gml", std::process::id()));
karate.write_graph_gml(&path, &GmlWriteOptions::default()).unwrap();
let k = Graph::read_graph_gml(&path).unwrap();
std::fs::remove_file(&path).unwrap();
assert!(!k.is_directed());
assert_eq!(k, karate); // GML keeps vertex ids and edges§Supported formats
| Format | Read | Write | Attributes (after attributes::enable) | Notes |
|---|---|---|---|---|
| Edge list | read_graph_edgelist | write_graph_edgelist | none | whitespace separated 0-based vertex ids |
| NCOL | read_graph_ncol | write_graph_ncol | vertex name, edge weight | symbolic (named) weighted edge list of the LGL software |
| LGL | read_graph_lgl | write_graph_lgl | vertex name, edge weight | adjacency-list-like, # vertex headers |
| Pajek | read_graph_pajek | write_graph_pajek | name, x/y/z, color, bipartite type, edge weight, … | .net files, 1-based ids |
| GraphML | read_graph_graphml | write_graph_graphml, write_graph_graphml_with | all (typed, with defaults), node ids as vertex id | XML; reading requires igraph built with libxml2; the in-memory writer write_graph_graphml_to_string takes the prefixattr flag of write_graph_graphml_with |
| GML | read_graph_gml | write_graph_gml | numeric and string ones, numeric vertex id | see GmlWriteOptions |
| DIMACS flow | read_graph_dimacs_flow | write_graph_dimacs_flow | none (capacities in DimacsFlow) | max-flow / edge problems |
| graph database | read_graph_graphdb | — | none | binary, see read_graph_graphdb_from_bytes |
| UCINET DL | read_graph_dl | — | vertex name, edge weight | full matrix, edge list and node list forms |
| Graphviz DOT | — | write_graph_dot | all | output only |
| LEDA | — | write_graph_leda | one vertex and one edge attribute | output only |
Every file based function has an in-memory twin with the _from_str /
_to_string suffix (_from_bytes for the binary graph database format),
taking the same arguments but the path. The one exception is GraphML:
write_graph_graphml_to_string(prefixattr)
is the twin of
write_graph_graphml_with(path, prefixattr)
(pass false for the behaviour of
write_graph_graphml(path)).
SafeLocale and with_safe_locale bind igraph’s locale helpers.
§Attributes
Many formats carry vertex names, edge weights and other attributes. igraph
stores them through a pluggable, process-wide attribute handler,
which is off by default and is turned on by
attributes::enable (see the
attributes module for the typed accessors):
- without the handler, readers parse attributes (validating their
syntax) but discard them: only the structure of the graph is returned.
The options asking to store names or weights (
NcolLglOptions) are accepted and harmless. Writers see no attribute: those asked to export a named one (e.g. thenames/weightsarguments ofwrite_graph_ncol) emit an igraph warning (seetake_warnings) and fall back to plain vertex ids, and GraphML/GML/DOT files are written without attributes; - with the handler (call
enablebefore reading), readers store what they find in the file as graph, vertex and edge attributes (the “Attributes” column above), and the writers export the attributes of the graph. Values missing for some elements are NaN for numeric attributes,""for strings andfalsefor booleans, unless the format has its own defaults (a missing NCOL/LGL weight is 1, a GraphML<key>may declare a<default>, Pajek parameters take Pajek’s defaults, see the readers); GraphML and GML omit NaN values when writing.
What each format keeps, with the handler on (the individual readers and writers have the details):
| Format | Read into attributes | Written from attributes |
|---|---|---|
| Edge list, DIMACS, graph database | nothing | nothing |
| NCOL, LGL | vertex name (string), edge weight (numeric), as selected by NcolLglOptions | the vertex / edge attributes named by the names / weights arguments |
| UCINET DL | vertex name (labels), edge weight (values) | — |
| Pajek | vertex name, x/y/z, color, shapes and the other Pajek parameters, bipartite type (boolean), edge weight and edge parameters | the attributes named after Pajek parameters; other attributes are ignored |
| GraphML | every <key>, typed (boolean, numeric, string), for graph, vertices and edges; the node ids as the string vertex attribute id | every graph, vertex and edge attribute (typed keys), optionally prefixed (g_/v_/e_, write_graph_graphml_with) |
| GML | every numeric or string field of the graph, node and edge records, including the numeric node id | numeric and string attributes (booleans as 0/1); a numeric vertex id supplies the node ids |
| DOT | — | every graph, vertex and edge attribute |
| LEDA | — | one vertex and one edge attribute, named in the call |
Two GraphML caveats: the node ids land in a string id vertex
attribute (unless a vertex key already defines an attribute named id), which the
GraphML writer then exports as an ordinary <key>, so a second round
trip carries it as data; and when the <edge> elements have ids, igraph
1.0.0 and 1.0.1 also create a string id edge attribute but, because
of a bug in src/io/graphml.c, fill it with the node ids (in node
order, padded with "" if there are more edges than nodes): don’t rely on it
(delete it with
remove_edge_attr if it gets in the way). See
read_graph_graphml.
use igraph::{attributes, foreign::NcolLglOptions, prelude::*};
attributes::enable().unwrap(); // before reading!
let text = "rome paris 1420\nparis london 460\nlondon rome\n";
let g = Graph::read_graph_ncol_from_str(text, &[], &NcolLglOptions::default()).unwrap();
assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), ["rome", "paris", "london"]);
// A missing weight defaults to 1 when at least one edge has an explicit weight.
assert_eq!(g.edge_attr_numeric_values("weight", ..).unwrap(), [1420.0, 460.0, 1.0]);
// Writers use the attributes they are asked for.
let out = g.write_graph_ncol_to_string(Some("name"), Some("weight")).unwrap();
assert_eq!(out, "rome paris 1420\nparis london 460\nrome london 1\n");Data that igraph returns through explicit output arguments does not need
the handler, e.g. the capacities and source/target vertices of a DIMACS
file, returned in DimacsFlow. When only the vertex naming of an NCOL
file matters, the predefnames argument of
read_graph_ncol fixes it without attributes:
vertex i then is the i-th predefined name.
§Locale and threads
The parsers and writers assume that the C locale uses a decimal point.
Rust programs start in the "C" locale, so nothing needs to be done unless
some library called setlocale; in that case wrap the I/O with
SafeLocale or with_safe_locale.
All the functions can be called from several threads at once (but see
SafeLocale for platforms without per-thread locales). The GML
reader of igraph 1.0.0 and 1.0.1 is not reentrant (it uses static
buffers), and so is the default GML Creator line (it uses ctime): these
wrappers serialize them internally with a process-wide lock.
§See also
Graph::from_edgesandGraph::famous(inconstructors) to build graphs in code;Graph::get_adjacencyand the rest of theconversionmodule to export graphs as matrices;Graph::is_same_graphto check that a round trip kept the vertex ids,Graph::isomorphicwhen a format (NCOL, LGL, bipartite Pajek) relabels the vertices;Graph::maxflowandGraph::st_mincutto solve the problems read from DIMACS files.
The C documentation of the whole chapter is at https://igraph.org/c/html/latest/igraph-Foreign.html.
Structs§
- Dimacs
Flow - The content of a DIMACS flow file, as returned by
Graph::read_graph_dimacs_flow. - GmlWrite
Options - Options of the GML writer (
Graph::write_graph_gml). - Ncol
LglOptions - Options of the NCOL and LGL readers (
Graph::read_graph_ncol,Graph::read_graph_lgl). - Safe
Locale - RAII guard temporarily switching the numeric locale to
"C"(igraph_enter_safelocale/igraph_exit_safelocale).
Enums§
- Dimacs
Problem - The problem described by a DIMACS file, see
DimacsFlow. - Graph
Format - Graph file formats known to igraph, for the format-agnostic
Graph::read_graphandGraph::write_graph.
Functions§
- with_
safe_ locale - Runs
fwith the"C"numeric locale in effect, seeSafeLocale.