Sign in

    moongraphml

    Typed, bounded GraphML graph interchange for MoonBit

    graphml
    graph
    interchange
    xml
    data
    Download zip
    Version
    0.1.0
    License
    MIT
    Last updated
    yesterday
    Downloads
    10

    #MoonGraphML

    MoonBit-native GraphML graph-data interchange: read, validate, build, transform, and write graph documents with typed attributes and explicit resource limits. It connects MoonBit applications to existing graph-data workflows without reimplementing graph algorithms or visual layout.

    Module: FikaRomantic/moongraphml, version 0.1.0. Source: FikaRomantic/MoonGraphML. Repository owner, maintenance commit identity and publishing account: FikaRomantic.

    moon add FikaRomantic/moongraphml@0.1.0

    #Where it fits

    • Software and data engineering: exchange service dependencies, build graphs, and data-pipeline relationships with external analysis tools.
    • Network and asset management: represent device links, named ports, nested topology, and multi-party connections using hyperedges.
    • Research and data analysis: move typed relationship datasets between MoonBit applications and Python/R graph-analysis workflows, retaining parallel edges, self-loops, and explicit direction.

    #Features and boundaries

    The library supports GraphML key declarations, six scalar types, scoped defaults, directed/undirected/mixed binary edges, parallel edges, self-loops, hierarchical graphs, hierarchical ports, and hyperedge endpoints. It validates identifiers, key scopes/types, references, child ordering, and configured resource budgets. Owned documents, address/ID indexes, typed table views, batch construction, reference-aware renaming, graph replacement/selection, node-induced subgraph extraction, document merging, and edge-mapped CSR adjacency exports are available through the API guide.

    Owned readonly wrappers (Document, FlatGraph, GraphIndex, Attributes, CsrAdjacency and builders) keep their representation private. Use their public methods rather than accessing fields; returned graph/array views are detached. The explicit pub(all) raw construction records remain editable.

    Custom XML attribute data preserves expanded names (namespace URI plus local name), attributes, text, payload-element prefixes, and in-scope namespace bindings. GraphML containers also retain their namespace contexts and extra attributes. This retains context for opaque QName values such as xsi:type="p:Rectangle", including inherited bindings and default-namespace resets. Key defaults separately retain their wrapper attributes and namespace context. The library does not interpret or schema-validate these QName values. Structural GraphML prefixes and attribute-prefix spelling may change; comments, processing instructions, CDATA spelling, and source bytes are not preserved. Serialization is deterministic for a given document, not XML canonicalization or graph isomorphism. Nodes are emitted before edges; an existing parse.order hint is updated to nodesfirst. Other parse hints are preserved, not automatically recomputed after every transformation.

    XML parsing buffers the document in memory. This is not a constant-memory streaming parser, complete XML Schema validator, layout engine, or graph algorithm framework. DTD declarations are rejected. External GraphML locators are retained as metadata and never fetched; application-defined XML is not interpreted or executed. XML local names/prefixes must be NCNames, GraphML identifier/name tokens must satisfy their NMTOKEN rules, and GraphML desc elements with attributes or child elements are rejected. See compatibility and security limits.

    #Build and run locally

    Install the MoonBit toolchain, then run these commands from the project directory:

    moon update moon check --target all --deny-warn moon build --target all --deny-warn moon test --target all --deny-warn

    This revision is tested with moonc v0.10.16+b1a0e846e and the 2026-10-16 moon/moonrun. On Windows, that particular driver/compiler pair can fail on generated \\?\ output paths before checking project code. A project-local adapter normalizes only extended drive-path arguments and still invokes the installed 0.10.16 compiler; it does not modify the toolchain or dependencies:

    pwsh -NoProfile -File scripts/Invoke-Moon016.ps1 check --target all --deny-warn --frozen pwsh -NoProfile -File scripts/Invoke-Moon016.ps1 build --target all --deny-warn --frozen pwsh -NoProfile -File scripts/Invoke-Moon016.ps1 test --target all --deny-warn --frozen pwsh -NoProfile -File scripts/Invoke-Moon016.ps1 fmt

    The same normalization is applied to moonfmt when formatting. The adapter requires Windows .NET Framework's C# compiler and is restricted to 0.10.16. Use normal commands on unaffected toolchains; --frozen additionally requires dependencies already resolved by moon update. The adapter and all generated files remain development-only and are excluded from the Moon package.

    Four executable examples exercise different workflows:

    moon run --target native --deny-warn examples/dependencies moon run --target native --deny-warn examples/topology moon run --target native --deny-warn examples/datasets moon run --target native --deny-warn examples/analytics

    dependencies builds a service graph, checks import/export equivalence, and looks up an incident edge. topology renames a port while updating binary-edge and hyperedge references. datasets resolves typed defaults into rows and merges separately scoped graph documents. analytics extracts a service subset, exports outgoing/incoming CSR, and joins edge ordinals to exact Int64 weights and defaults without merging parallel relationships. Each example has assertions.

    #Library usage

    Import the module in your consuming package's moon.pkg:

    import {
    "FikaRomantic/moongraphml" @graphml,
    }

    A minimal executable can use the same API as the dependency example:

    Set pkgtype(kind: "executable") in that executable package's moon.pkg and place the following entry point in main.mbt:

    fn main raise {
    let builder = try! @graphml.FlatBuilder::new([], @graphml.Directed)
    try! builder.add_node("api")
    try! builder.add_node("database")
    try! builder.add_edge("api", "database", id="uses")
    let document = try! builder.finish()
    let xml = try! @graphml.serialize(document)
    assert_true(document.equivalent(try! @graphml.parse(xml)))
    println(xml)
    }

    try! keeps examples compact. Production callers should catch GraphError and use its variants or message() to report invalid data, unsupported projections, and exceeded budgets. Defaults remain declarations until requested through Attributes or a typed row view; they are not silently materialized.

    #Selecting graph data and exporting adjacency

    let flat = try! document.flat_graph(@graphml.GraphAddress::root(0))
    let selected = try! flat.induced_subgraph(["api", "database"])
    let selected_flat = try! selected.flat_graph(@graphml.GraphAddress::root(0))
    let csr = try! selected_flat.to_csr(orientation=@graphml.Incoming)
    let database = csr.node_index("database").unwrap()
    let dependencies = try! csr.row(database)

    Induced extraction keeps source order and every edge with both endpoints selected, including loops and parallel edges. It preserves keys/defaults, graph/node/edge attributes and XML contexts; unknown or duplicate selections are errors. Stale unqualified parse.* hints are removed from the graph and selected nodes. This selected-graph operation does not include document-root metadata or rewrite application references embedded in opaque XML.

    CSR uses indptr/indices plus node_ids, edge_indices and reversed mapping arrays. Parallel entries are retained, not summed; rows follow original edge order, so columns are not necessarily sorted. Undirected non-loops emit two arcs, undirected loops one. Incoming reverses directed arcs. Join original edge attributes by edge_indices, rather than coercing all weights to floating point. max_arcs defaults to 2,000,000 and is checked before arc-buffer allocation. Both features require an existing lossless FlatGraph view and do not silently flatten advanced structures. See precise contracts and costs.

    #Command-line tool

    The CLI reads UTF-8 GraphML files. roundtrip, inspect, and verify use the strict read profile by default; there is no separate strict subcommand.

    moon run --target native --deny-warn cmd/graphml -- inspect tests/fixtures/flat.graphml moon run --target native --deny-warn cmd/graphml -- verify tests/fixtures/flat.graphml moon run --target native --deny-warn cmd/graphml -- roundtrip tests/fixtures/flat.graphml moon run --target native --deny-warn cmd/graphml -- roundtrip-compatible tests/fixtures/flat.graphml moon run --target native --deny-warn cmd/graphml -- generate 7 moon run --target native --deny-warn cmd/graphml -- subgraph tests/fixtures/flat.graphml a b moon run --target native --deny-warn cmd/graphml -- csr tests/fixtures/flat.graphml moon run --target native --deny-warn cmd/graphml -- csr-incoming tests/fixtures/flat.graphml

    inspect prints stored counts and structural flags. verify checks model-equivalent serialization/reparsing. roundtrip emits normalized GraphML. roundtrip-compatible explicitly tolerates selected reference-library scalar spellings (True/False, inf/-inf/nan), without weakening reference or structure checks. generate emits a deterministic test graph for an integer seed. Usage errors exit with code 2; file, validation, and conversion errors exit with code 1. Errors currently print to standard output, so check the exit code before treating captured output as XML.

    subgraph INPUT [NODE_ID ...] emits GraphML (no IDs means an empty graph). csr/csr-incoming emit JSON mapping arrays. These commands require exactly one root graph and a supported flat structure; they refuse ambiguous/lossy input.

    #Interoperability

    The reproducible differential suite has passed 500 NetworkX 3.6.1 cases and 300 python-igraph 1.0.0 cases, two directions per case, with zero failures. These 1,600 directional checks cover a declared flat scalar multigraph subset, not every GraphML feature or every version of either tool. NetworkX defaults are explicitly expanded before re-export; the igraph subset uses finite doubles and avoids precision claims for arbitrary 64-bit integers. Generated IDs and reference-library metadata are treated according to the stated comparison profile. Details and reproduction commands are in COMPATIBILITY.md.

    The additional adapter suite passed 100 induced-subgraph comparisons with NetworkX 3.6.1, 400 outgoing/incoming CSR checks against an independent XML endpoint scan (including mixed direction), and 9 CLI rejection checks: python scripts/adapters_interop.py after a native CLI build. It uses the same development dependencies as the main differential suite, not SciPy at runtime.

    #Development and licensing

    Formatting and public-interface consistency can be checked with moon fmt and moon info, followed by git diff --exit-code. The included CI installs the current stable toolchain, checks/builds/tests all targets with warnings denied, checks formatting/interfaces, and runs the four examples and CLI smoke tests. Release gates also verify report-free history and consistent maintainer identity. python scripts/package_qa.py packages with moon package --frozen, checks the archive's exclusions, and runs an isolated build/test/examples check without publishing. python scripts/release_guard.py checks source scale, nonempty history, and report/cache exclusions. Its --release mode additionally checks the real namespace, repository and all author/committer identities. Do not use moon publish --dry-run as an offline package test: publication is an explicit later operation.

    The original GraphML implementation is MIT licensed. Milky2018/xml supplies XML syntax and namespace parsing; moonbitlang/x supplies CLI filesystem/process helpers. Both dependencies are Apache-2.0. NetworkX, igraph, lxml, xmlschema, elementpath, and texttable are independent development tools or their dependencies, not copied implementations or MoonBit runtime dependencies. See third-party notices and changes.

    GraphError

    pub(all) suberror GraphError {
    InvalidXml(String)
    InvalidGraph(path~ : String, reason~ : String)
    LimitExceeded(resource~ : String, limit~ : Int)
    Unsupported(path~ : String, feature~ : String)
    } derive(Eq,
    Debug
    )

    GraphError::equal

    fn GraphError::equal(GraphError, GraphError) -> Bool

    GraphError::message

    fn GraphError::message(self : GraphError) -> String

    Human-readable diagnostics for command-line/UI presentation; variants remain machine-readable.

    GraphError::not_equal

    fn GraphError::not_equal(x : GraphError, y : GraphError) -> Bool

    Attributes

    pub struct Attributes {
    // private fields
    }

    A scoped, owned view of explicit attributes and key defaults.

    Attributes::contains_explicit

    fn Attributes::contains_explicit(self : Attributes, id : String) -> Bool raise GraphError

    Attributes::get

    fn Attributes::get(self : Attributes, id : String, use_default? : Bool) -> DataValue? raise GraphError

    Omitted values are resolved from defaults only when requested; explicit empties remain empty.

    Attributes::key_by_name

    fn Attributes::key_by_name(self : Attributes, name : String) -> String? raise GraphError

    Attribute names are not necessarily unique. Ambiguity is an error, never an arbitrary choice.

    Attributes::new

    fn Attributes::new(doc : Document, scope : Scope, data : Array[Datum], limits? : Limits) -> Attributes raise GraphError

    Attributes::resolved

    fn Attributes::resolved(self : Attributes) -> Array[Datum]

    Resolve in declaration order, keeping explicit data metadata and adding applicable defaults.

    Attributes::scalar

    fn Attributes::scalar(self : Attributes, id : String, use_default? : Bool) -> Scalar? raise GraphError

    CountHint

    pub struct CountHint {
    // private fields
    }

    XML Schema nonNegativeInteger has no machine-word upper bound.

    CountHint::decimal

    fn CountHint::decimal(self : CountHint) -> String

    CountHint::parse

    fn CountHint::parse(input : String) -> CountHint raise GraphError

    CountHint::to_int64

    fn CountHint::to_int64(self : CountHint) -> Int64?

    Return None on machine-word overflow without invalidating a legal XML Schema value.

    CsrAdjacency

    pub struct CsrAdjacency {
    // private fields
    }

    Owned compressed sparse rows, not an aggregated numerical matrix. Parallel entries are retained and rows follow original edge order, not sorted columns. All array accessors return copies; caller mutation cannot corrupt row offsets.

    CsrAdjacency::arc_count

    fn CsrAdjacency::arc_count(self : CsrAdjacency) -> Int

    CsrAdjacency::edge_indices

    fn CsrAdjacency::edge_indices(self : CsrAdjacency) -> Array[Int]

    CsrAdjacency::indices

    fn CsrAdjacency::indices(self : CsrAdjacency) -> Array[Int]

    CsrAdjacency::indptr

    fn CsrAdjacency::indptr(self : CsrAdjacency) -> Array[Int]

    CsrAdjacency::node_count

    fn CsrAdjacency::node_count(self : CsrAdjacency) -> Int

    CsrAdjacency::node_ids

    fn CsrAdjacency::node_ids(self : CsrAdjacency) -> Array[String]

    CsrAdjacency::node_index

    fn CsrAdjacency::node_index(self : CsrAdjacency, id : String) -> Int?

    CsrAdjacency::reversed

    fn CsrAdjacency::reversed(self : CsrAdjacency) -> Array[Bool]

    CsrAdjacency::row

    fn CsrAdjacency::row(self : CsrAdjacency, node_index : Int) -> Array[CsrEntry] raise GraphError

    Copy one row in O(degree) time without copying all other rows or metadata.

    CsrEntry

    pub(all) struct CsrEntry {
    target : Int
    edge_index : Int
    reversed : Bool
    } derive(Eq,
    Debug
    )

    One stored adjacency entry. edge_index refers to the original FlatGraph edge order. reversed marks an arc emitted opposite to stored endpoint order; incoming directed self-loops are marked reversed even though endpoints agree.

    CsrEntry::equal

    fn CsrEntry::equal(CsrEntry, CsrEntry) -> Bool

    CsrEntry::not_equal

    fn CsrEntry::not_equal(x : CsrEntry, y : CsrEntry) -> Bool

    CsrEntry::to_repr

    CsrOrientation

    pub(all) enum CsrOrientation {
    Outgoing
    Incoming
    } derive(Eq,
    Debug
    )

    Directed arcs follow either source-to-target or target-to-source. Undirected edges always contribute both orientations, except a self-loop contributes one.

    CsrOrientation::equal

    CsrOrientation::not_equal

    fn CsrOrientation::not_equal(x : CsrOrientation, y : CsrOrientation) -> Bool

    DataValue

    pub(all) enum DataValue {
    ScalarValue(Scalar)
    XmlPayload(Array[XmlValue])
    }

    Datum

    pub(all) struct Datum {
    key : String
    id : String?
    value : DataValue
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Datum::new

    fn Datum::new(key : String, value : Scalar, id? : String) -> Datum

    DegreeHints

    pub(all) struct DegreeHints {
    in_degree : CountHint?
    out_degree : CountHint?
    }

    Direction

    pub(all) enum Direction {
    Directed
    Undirected
    } derive(Eq,
    Debug
    )

    Direction::equal

    fn Direction::equal(Direction, Direction) -> Bool

    Direction::from_name

    fn Direction::from_name(name : String) -> Direction raise GraphError

    Direction::name

    fn Direction::name(self : Direction) -> String

    Direction::not_equal

    fn Direction::not_equal(x : Direction, y : Direction) -> Bool

    Document

    pub struct Document {
    // private fields
    }

    Document::append_graph

    fn Document::append_graph(self : Document, graph : Graph, limits? : Limits) -> Document raise GraphError

    Add a top-level graph without merging ID scopes or changing existing addresses.

    Document::data

    fn Document::data(self : Document) -> Array[Datum]

    Document::description

    fn Document::description(self : Document) -> String?

    Document::edge_at

    fn Document::edge_at(self : Document, address : ElementAddress) -> Edge raise GraphError

    Document::equivalent

    fn Document::equivalent(self : Document, other : Document) -> Bool

    Model equivalence ignores key/data declaration ordering, attribute order and XML prefixes. Node/edge/port/endpoint and top-level graph ordinals remain significant for stable addressing. This is not graph isomorphism or XML C14N. Explicit data and omitted defaults remain distinct.

    Document::extra_attributes

    fn Document::extra_attributes(self : Document) -> Array[XmlAttributeValue]

    Document::flat_graph

    fn Document::flat_graph(self : Document, address : GraphAddress) -> FlatGraph raise GraphError

    Reject advanced structures rather than silently translating them into a different graph.

    Document::graph_at

    fn Document::graph_at(self : Document, address : GraphAddress) -> Graph raise GraphError

    Get a detached graph using a structural address, including graphs without IDs.

    Document::graph_count

    fn Document::graph_count(self : Document) -> Int

    Document::graphs

    fn Document::graphs(self : Document) -> Array[Graph]

    Return detached graphs. Changes to the returned arrays never mutate the document.

    Document::key_count

    fn Document::key_count(self : Document) -> Int

    Document::keys

    fn Document::keys(self : Document) -> Array[Key]

    Document::merge

    fn Document::merge(self : Document, other : Document, prefix : String, limits? : Limits) -> MergeResult raise GraphError

    Append independently scoped graphs. Imported key IDs and global graph IDs get a prefix. Different root descriptions or conflicting root extension attributes are errors, not lost data.

    Document::namespace_bindings

    fn Document::namespace_bindings(self : Document) -> Array[NamespaceBinding]

    Return a detached root namespace context, including bindings used in extension values.

    Document::new

    fn Document::new(keys : Array[Key], graphs : Array[Graph], data? : Array[Datum], description? : String, extra? : Array[XmlAttributeValue], namespaces? : Array[NamespaceBinding], limits? : Limits) -> Document raise GraphError

    Construct an owned document. Validation is performed before detaching caller arrays.

    Document::node_at

    fn Document::node_at(self : Document, address : ElementAddress) -> Node raise GraphError

    Document::remove_key

    fn Document::remove_key(self : Document, id : String, limits? : Limits) -> Document raise GraphError

    Removing an in-use key is intentionally forbidden, rather than silently discarding attributes.

    Document::rename_key

    fn Document::rename_key(self : Document, old_id : String, new_id : String, limits? : Limits) -> Document raise GraphError

    Rename a declaration and every corresponding data reference atomically.

    Document::rename_node

    fn Document::rename_node(self : Document, root : Int, old_id : String, new_id : String, limits? : Limits) -> Document raise GraphError

    Rename one node in a top-level ID scope and update all edge/hyperedge references.

    Document::rename_port

    fn Document::rename_port(self : Document, root : Int, node_id : String, old_name : String, new_name : String, limits? : Limits) -> Document raise GraphError

    Rename a hierarchical port and update binary edges and hyperedge endpoints atomically.

    Document::replace_edge

    fn Document::replace_edge(self : Document, address : ElementAddress, edge : Edge, limits? : Limits) -> Document raise GraphError

    Replace edge properties, preserving hierarchy and enforcing reference validity.

    Document::replace_graph

    fn Document::replace_graph(self : Document, address : GraphAddress, replacement : Graph, limits? : Limits) -> Document raise GraphError

    Replace a graph atomically. Ancestor references and global IDs are revalidated.

    Document::replace_key

    fn Document::replace_key(self : Document, key : Key, limits? : Limits) -> Document raise GraphError

    Replace a declaration, retaining its ID. Existing data must still satisfy its type and scope.

    Document::replace_node

    fn Document::replace_node(self : Document, address : ElementAddress, node : Node, limits? : Limits) -> Document raise GraphError

    Replace a node without deleting any incident references implicitly.

    Document::select_graphs

    fn Document::select_graphs(self : Document, roots : Array[Int], limits? : Limits) -> Document raise GraphError

    Keep selected top-level graphs, retaining the shared key catalog and document attributes.

    Document::statistics

    fn Document::statistics(self : Document) -> Statistics

    Linear scan of structure plus one edge-pair map per graph, without layout or graph algorithms.

    Edge

    pub(all) struct Edge {
    id : String?
    source : String
    target : String
    source_port : String?
    target_port : String?
    directed : Bool?
    description : String?
    data : Array[Datum]
    graph : Graph?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Edge::effective_direction

    fn Edge::effective_direction(self : Edge, graph : Graph) -> Direction

    Edge::new

    fn Edge::new(source : String, target : String, id? : String, directed? : Bool, source_port? : String, target_port? : String) -> Edge

    EdgeRow

    pub(all) struct EdgeRow {
    id : String?
    source : String
    target : String
    direction : Direction
    attributes : Map[String, Scalar]
    }

    ElementAddress

    pub(all) struct ElementAddress {
    graph : GraphAddress
    index : Int
    } derive(Eq,
    Debug
    )

    ElementAddress::equal

    ElementAddress::not_equal

    fn ElementAddress::not_equal(x : ElementAddress, y : ElementAddress) -> Bool

    Endpoint

    pub(all) struct Endpoint {
    id : String?
    node : String
    port : String?
    kind : EndpointKind
    description : String?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Endpoint::new

    fn Endpoint::new(node : String, kind? : EndpointKind, port? : String, id? : String) -> Endpoint

    EndpointKind

    pub(all) enum EndpointKind {
    In
    Out
    Undir
    } derive(Eq,
    Debug
    )

    EndpointKind::equal

    EndpointKind::from_name

    fn EndpointKind::from_name(name : String) -> EndpointKind raise GraphError

    EndpointKind::name

    fn EndpointKind::name(self : EndpointKind) -> String

    EndpointKind::not_equal

    fn EndpointKind::not_equal(x : EndpointKind, y : EndpointKind) -> Bool

    FlatBuilder

    pub struct FlatBuilder {
    // private fields
    }

    Batch construction avoids repeatedly revalidating the entire graph for each addition. Forward node references are permitted; finish() validates them before producing a document.

    FlatBuilder::add_edge

    fn FlatBuilder::add_edge(self : FlatBuilder, source : String, target : String, id? : String, directed? : Bool, attributes? : Array[(String, Scalar)], description? : String) -> Unit raise GraphError

    FlatBuilder::add_node

    fn FlatBuilder::add_node(self : FlatBuilder, id : String, attributes? : Array[(String, Scalar)], description? : String) -> Unit raise GraphError

    On failure, no node or ID is inserted. Scalar-only inputs cannot create cyclic XML trees.

    FlatBuilder::edge_count

    fn FlatBuilder::edge_count(self : FlatBuilder) -> Int

    FlatBuilder::finish

    fn FlatBuilder::finish(self : FlatBuilder) -> Document raise GraphError

    Finish is repeatable; subsequent builder changes cannot mutate already returned documents.

    FlatBuilder::new

    fn FlatBuilder::new(keys : Array[Key], direction : Direction, graph_id? : String, description? : String, limits? : Limits) -> FlatBuilder raise GraphError

    FlatBuilder::node_count

    fn FlatBuilder::node_count(self : FlatBuilder) -> Int

    FlatBuilder::set_graph_data

    fn FlatBuilder::set_graph_data(self : FlatBuilder, attributes : Array[(String, Scalar)]) -> Unit raise GraphError

    Set graph data as a complete replacement, with duplicate/scope/type checks before mutation.

    FlatGraph

    pub struct FlatGraph {
    // private fields
    }

    A lossless view of a single non-hierarchical binary graph.

    FlatGraph::edge_rows

    fn FlatGraph::edge_rows(self : FlatGraph, use_defaults? : Bool) -> Array[EdgeRow] raise GraphError

    FlatGraph::graph

    fn FlatGraph::graph(self : FlatGraph) -> Graph

    FlatGraph::indexed_edges

    fn FlatGraph::indexed_edges(self : FlatGraph) -> Array[IndexedEdge]

    Stable positional endpoints allow direct use by existing graph algorithm libraries.

    FlatGraph::induced_subgraph

    fn FlatGraph::induced_subgraph(self : FlatGraph, node_ids : Array[String], limits? : Limits) -> Document raise GraphError

    Copy a node-induced subgraph, retaining source order and all edges whose two endpoints are selected. Unknown or repeated node IDs are errors, not typos silently ignored. Keys, defaults and graph/element XML metadata are retained; topology-derived parse hints are removed. Document-level metadata is outside a FlatGraph view. The resulting Document is independently owned and validated.

    FlatGraph::node_rows

    fn FlatGraph::node_rows(self : FlatGraph, use_defaults? : Bool) -> Array[NodeRow] raise GraphError

    Tables are an explicit projection: descriptions and XML metadata remain available in graph().

    FlatGraph::to_csr

    fn FlatGraph::to_csr(self : FlatGraph, orientation? : CsrOrientation, max_arcs? : Int) -> CsrAdjacency raise GraphError

    Export a topology adapter in O(V+E) expected time and O(V+A) space, A <= 2E. Preflight the configurable arc ceiling before allocating arc buffers. Node ordinals follow source node order, edge ordinals follow source edge order. Weights/XML/IDs stay in the original FlatGraph and can be joined by ordinal; they are neither coerced to Double nor silently aggregated here.

    FlatGraph::to_document

    fn FlatGraph::to_document(self : FlatGraph, limits? : Limits) -> Document raise GraphError

    Produce a one-graph document. Document-level metadata is outside this selected-graph view.

    Graph

    pub(all) struct Graph {
    id : String?
    edge_default : Direction
    description : String?
    data : Array[Datum]
    nodes : Array[Node]
    edges : Array[Edge]
    hyperedges : Array[Hyperedge]
    locator : Locator?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Graph::new

    fn Graph::new(edge_default : Direction, id? : String, description? : String) -> Graph

    Graph::parse_info

    fn Graph::parse_info(self : Graph) -> ParseInfo raise GraphError

    Parse hints are advisory metadata; lexical validity does not prove content consistency.

    GraphAddress

    pub(all) struct GraphAddress {
    root : Int
    steps : Array[GraphStep]
    } derive(Eq,
    Debug
    )

    GraphAddress::child

    fn GraphAddress::child(self : GraphAddress, step : GraphStep) -> GraphAddress

    GraphAddress::equal

    GraphAddress::not_equal

    fn GraphAddress::not_equal(x : GraphAddress, y : GraphAddress) -> Bool

    GraphAddress::root

    fn GraphAddress::root(index : Int) -> GraphAddress

    GraphIndex

    pub struct GraphIndex {
    // private fields
    }

    IDs for nodes/edges are scoped to the top-level graph; graph IDs are document-global.

    GraphIndex::edge

    fn GraphIndex::edge(self : GraphIndex, root : Int, id : String) -> Edge?

    GraphIndex::edge_address

    fn GraphIndex::edge_address(self : GraphIndex, root : Int, id : String) -> ElementAddress?

    GraphIndex::graph_address

    fn GraphIndex::graph_address(self : GraphIndex, id : String) -> GraphAddress?

    GraphIndex::hyperedge_address

    fn GraphIndex::hyperedge_address(self : GraphIndex, root : Int, id : String) -> ElementAddress?

    GraphIndex::incident_edges

    fn GraphIndex::incident_edges(self : GraphIndex, root : Int, node : String) -> Array[ElementAddress]

    GraphIndex::incident_hyperedges

    fn GraphIndex::incident_hyperedges(self : GraphIndex, root : Int, node : String) -> Array[ElementAddress]

    GraphIndex::new

    fn GraphIndex::new(doc : Document) -> GraphIndex

    GraphIndex::node

    fn GraphIndex::node(self : GraphIndex, root : Int, id : String) -> Node?

    GraphIndex::node_address

    fn GraphIndex::node_address(self : GraphIndex, root : Int, id : String) -> ElementAddress?

    GraphStep

    pub(all) enum GraphStep {
    NodeGraph(Int)
    EdgeGraph(Int)
    HyperedgeGraph(Int)
    } derive(Eq,
    Debug
    )

    Each step names the owning element of a nested graph, by its ordinal in that graph.

    GraphStep::equal

    fn GraphStep::equal(GraphStep, GraphStep) -> Bool

    GraphStep::not_equal

    fn GraphStep::not_equal(x : GraphStep, y : GraphStep) -> Bool

    Hyperedge

    pub(all) struct Hyperedge {
    id : String?
    description : String?
    data : Array[Datum]
    endpoints : Array[Endpoint]
    graph : Graph?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Hyperedge::new

    fn Hyperedge::new(id? : String, description? : String) -> Hyperedge

    IdHint

    pub(all) enum IdHint {
    CanonicalIds
    FreeIds
    } derive(Eq,
    Debug
    )

    IdHint::equal

    fn IdHint::equal(IdHint, IdHint) -> Bool

    IdHint::not_equal

    fn IdHint::not_equal(x : IdHint, y : IdHint) -> Bool

    IdHint::to_repr

    IndexedEdge

    pub(all) struct IndexedEdge {
    id : String?
    source : Int
    target : Int
    direction : Direction
    }

    Key

    pub(all) struct Key {
    id : String
    scope : Scope
    name : String?
    value_type : ValueType?
    default_value : DataValue?
    default_extra : Array[XmlAttributeValue]
    default_namespaces : Array[NamespaceBinding]
    description : String?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Key::new

    fn Key::new(id : String, scope : Scope, value_type : ValueType, name? : String, default_value? : Scalar, description? : String) -> Key raise GraphError

    Limits

    pub(all) struct Limits {
    input_bytes : Int
    xml_depth : Int
    xml_elements : Int
    attributes_per_element : Int
    text_bytes : Int
    keys : Int
    graphs : Int
    nodes : Int
    edges : Int
    ports : Int
    endpoints : Int
    output_bytes : Int
    } derive(Eq,
    Debug
    )

    Limits::default

    fn Limits::default() -> Limits

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::to_repr

    Limits::validate

    fn Limits::validate(self : Limits) -> Unit raise GraphError

    Locator

    pub(all) struct Locator {
    href : String
    link_type : String?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    MergeResult

    pub(all) struct MergeResult {
    document : Document
    imported_roots : Array[Int]
    key_mapping : Map[String, String]
    graph_mapping : Map[String, String]
    }

    The mappings make import renaming explicit to applications.

    NamespaceBinding

    pub(all) struct NamespaceBinding {
    prefix : String?
    namespace_uri : String
    }

    A prefix binding in scope. None denotes the default namespace; an empty URI resets it.

    Node

    pub(all) struct Node {
    id : String
    description : String?
    data : Array[Datum]
    ports : Array[Port]
    graph : Graph?
    locator : Locator?
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Node::degree_hints

    fn Node::degree_hints(self : Node) -> DegreeHints raise GraphError

    Node::new

    fn Node::new(id : String, description? : String) -> Node

    NodeRow

    pub(all) struct NodeRow {
    id : String
    attributes : Map[String, Scalar]
    }

    OrderHint

    pub(all) enum OrderHint {
    FreeOrder
    NodesFirst
    AdjacencyList
    } derive(Eq,
    Debug
    )

    OrderHint::equal

    fn OrderHint::equal(OrderHint, OrderHint) -> Bool

    OrderHint::not_equal

    fn OrderHint::not_equal(x : OrderHint, y : OrderHint) -> Bool

    ParseInfo

    pub(all) struct ParseInfo {
    nodes : CountHint?
    edges : CountHint?
    max_in_degree : CountHint?
    max_out_degree : CountHint?
    node_ids : IdHint?
    edge_ids : IdHint?
    order : OrderHint?
    }

    ParseInfo::check_flat_counts

    fn ParseInfo::check_flat_counts(self : ParseInfo, graph : Graph) -> Unit raise GraphError

    Explicit flat-profile check. Nested graphs are intentionally not assigned invented counting rules.

    Port

    pub(all) struct Port {
    name : String
    description : String?
    data : Array[Datum]
    ports : Array[Port]
    extra : Array[XmlAttributeValue]
    namespaces : Array[NamespaceBinding]
    }

    Port::new

    fn Port::new(name : String, description? : String) -> Port

    ReadProfile

    pub(all) enum ReadProfile {
    Strict
    ReferenceLibraries
    } derive(Eq,
    Debug
    )

    Reference tools emit some non-XSD scalar spellings; tolerance must be explicitly requested.

    ReadProfile::equal

    fn ReadProfile::equal(ReadProfile, ReadProfile) -> Bool

    ReadProfile::not_equal

    fn ReadProfile::not_equal(x : ReadProfile, y : ReadProfile) -> Bool

    Scalar

    pub(all) enum Scalar {
    BooleanValue(Bool)
    IntegerValue(Int)
    LongValue(Int64)
    FloatValue(Float)
    DoubleValue(Double)
    TextValue(String)
    } derive(
    Debug
    )

    Scalar::equivalent

    fn Scalar::equivalent(self : Scalar, other : Scalar) -> Bool

    Scalar::lexical

    fn Scalar::lexical(self : Scalar) -> String

    Scalar::parse

    fn Scalar::parse(kind : ValueType, authored : String) -> Scalar raise GraphError

    Scalar::to_repr

    Scalar::value_type

    fn Scalar::value_type(self : Scalar) -> ValueType

    Scope

    pub(all) enum Scope {
    All
    DocumentScope
    GraphScope
    NodeScope
    EdgeScope
    PortScope
    HyperedgeScope
    EndpointScope
    } derive(Eq,
    Debug
    )

    Scope::applies_to

    fn Scope::applies_to(self : Scope, domain : Scope) -> Bool

    Scope::equal

    fn Scope::equal(Scope, Scope) -> Bool

    Scope::from_name

    fn Scope::from_name(name : String) -> Scope raise GraphError

    Scope::name

    fn Scope::name(self : Scope) -> String

    Scope::not_equal

    fn Scope::not_equal(x : Scope, y : Scope) -> Bool

    Scope::to_repr

    Statistics

    pub(all) struct Statistics {
    keys : Int
    graphs : Int
    nodes : Int
    edges : Int
    hyperedges : Int
    endpoints : Int
    ports : Int
    data_entries : Int
    max_graph_depth : Int
    has_mixed_direction : Bool
    has_parallel_edges : Bool
    has_self_loops : Bool
    has_locators : Bool
    has_xml_payloads : Bool
    } derive(Eq,
    Debug
    )

    Counts describe stored structure, not inferred expansion of hyperedges or locators.

    Statistics::equal

    fn Statistics::equal(Statistics, Statistics) -> Bool

    Statistics::not_equal

    fn Statistics::not_equal(x : Statistics, y : Statistics) -> Bool

    ValueType

    pub(all) enum ValueType {
    Boolean
    Int32
    Int64
    Float32
    Float64
    StringValue
    } derive(Eq,
    Debug
    )

    ValueType::equal

    fn ValueType::equal(ValueType, ValueType) -> Bool

    ValueType::from_name

    fn ValueType::from_name(name : String) -> ValueType raise GraphError

    ValueType::name

    fn ValueType::name(self : ValueType) -> String

    ValueType::not_equal

    fn ValueType::not_equal(x : ValueType, y : ValueType) -> Bool

    XmlAttributeValue

    pub(all) struct XmlAttributeValue {
    namespace_uri : String?
    name : String
    value : String
    }

    XmlElementValue

    pub(all) struct XmlElementValue {
    namespace_uri : String?
    name : String
    prefix : String?
    namespaces : Array[NamespaceBinding]
    attributes : Array[XmlAttributeValue]
    content : Array[XmlValue]
    }

    XmlValue

    pub(all) enum XmlValue {
    Element(XmlElementValue)
    Text(String)
    }

    from_rows

    fn from_rows(keys : Array[Key], nodes : Array[NodeRow], edges : Array[EdgeRow], edge_default? : Direction, graph_id? : String, limits? : Limits) -> Document raise GraphError

    Build a flat graph from typed table rows with explicit declarations, not inferred coercions.

    inspect_keys

    fn inspect_keys(input : String, limits? : Limits) -> Array[Key] raise GraphError

    parse

    fn parse(input : String, limits? : Limits, profile? : ReadProfile) -> Document raise GraphError

    serialize

    fn serialize(doc : Document, limits? : Limits) -> String raise GraphError

    Serialize the supported GraphML information model, not its original XML spelling.