5
$\begingroup$

I have a situation where I am adding properties to vertices in my graph. In my case, I am adding a moduleId, which is an integer, and a kind, which is a string. I attach a weight to the edge as well.

correctGraph = Graph[ {
   Property[Property["a", "moduleid" -> 121.4`], "kind" -> "class"], 
   Property[Property["b", "moduleid" -> 25.6`], "kind" -> "table"]}, 
  { Property["a" \[DirectedEdge] "b", EdgeWeight -> 5.34`]}, 
  EdgeLabels -> "EdgeWeight", VertexLabels -> "Name"]

This generates the result, I am looking for:

graph plot

When I look at the type of the moduleId property, I get the correct result

In[64]:= PropertyValue[{correctGraph, "a"}, "moduleid"]
Out[64]= 121.4

In[65]:= NumberQ[PropertyValue[{correctGraph, "a"}, "moduleid"]]
Out[65]= True

In[66]:= Normal@WeightedAdjacencyMatrix[correctGraph]
Out[66]= {{0, 5.34}, {0, 0}}

If I then export the graph and reimport it, the type of the moduleId changes to string. It seems that the type of the original property is not respected. If I try to roundtrip the graph into graphml and back again:

newgraph = ImportString[ExportString[correctGraph, "graphml"], "graphml"];

I do not get the equality that might be expected:

newgraph == correctGraph

It turns out that the types of the moduleId properties has become strings instead of numbers:

In[34]:= { NumberQ[PropertyValue[{newgraph, "a"}, "moduleid"]], 
 NumberQ[PropertyValue[{correctGraph, "a"}, "moduleid"]] }

Out[34]= {False, True}

I have edited the exported string slightly for readability, hoping I did not lose anythng important:

<?xml version='1.0' encoding='UTF-8'?>
<graphml>
 <key id='nodeKey1'
     for='node'
     attr.name='kind'
     attr.type='String' />
 <key id='nodeKey3'
     for='node'
     attr.name='moduleid'
     attr.type='String' />
 <key id='edgeKey2'
     for='edge'
     attr.name='EdgeWeight'
     attr.type='String' />
 <graph id='Graph1'
     edgedefault='directed'>
  <node id='a'>
   <data key='nodeKey1'>class</data>
   <data key='nodeKey3'>121.4`</data>
  </node>
  <node id='b'>
   <data key='nodeKey1'>table</data>
   <data key='nodeKey3'>25.6`</data>
   <data key='nodeKey5'>Name</data>
  </node>
  <edge id='e1'
      source='a'
      target='b'>
   <data key='nodeKey2'>EdgeWeight</data>
   <data key='edgeKey2'>5.34`</data>
  </edge>
 </graph>
</graphml>

As you can see, the exported file stipulates that the type of the moduleId property should be string, not a number:

  <node id='a'>
   ...
   <data key='nodeKey3'>121.4`</data>
  </node>

I would have expected the attr.type attribute to read "double" instead of string. In fact, the type 'String' as specified may not even be legal, since the standard for the graphml format specifies:

The type of the GraphML-Attribute can be either boolean, int, long, float, double, or string. These types are defined like the corresponding types in the Java(TM)-Programming language.

Unfortunately, changing the type to double and importing it does not provide the expected relief.

It would seem that the code for doing export is sloppy. A preliminary investigation shows that the gxl format is no better.

Since I am new at mathematica, I know that a little humility is in order (I may be doing something wrong), but I would like to know what the process is for posting bugs to Wolfram?

BR PV

$\endgroup$
  • $\begingroup$ I don't think any of the formats will preserve everything that Mathematica can store about the graph (except for Mathematica's own formats such as WDX, MX, etc., but those can't be read by other software). If my memory is not cheating me, GML was one of the better behaved formats. Side note: I'm not convinced that ==/=== are reliable for comparing graphs ... I'd avoid them ... I'm saying this because Graph objects are atomic and rather complex. $\endgroup$ – Szabolcs Jan 4 '15 at 23:50
3
$\begingroup$

GraphML export in Mathematica is a bit wonky, and the files that are produced are not standards-compliant. More importantly, many other systems will simply not read these GraphML files.

For this reason, I included an alternative GraphML exporter in IGraph/M. (This is implemented in pure Mathematica code which you can read here.)

For your graph, it produces this:

IGExportString[correctGraph, "GraphML"]

"<?xml version='1.0' encoding='UTF-8'?>
<!-- created by IGraph/M, http://szhorvat.net/mathematica/IGraphM -->
<graphml xmlns='http://graphml.graphdrawing.org/xmlns'
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation='http://graphml.graphdrawing.org/xmlns \http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd'>
 <key for='edge'
     id='e_EdgeLabels'
     attr.name='EdgeLabels'
     attr.type='string' />
 <key for='edge'
     id='e_EdgeWeight'
     attr.name='EdgeWeight'
     attr.type='double' />
 <key for='node'
     id='v_kind'
     attr.name='kind'
     attr.type='string' />
 <key for='node'
     id='v_moduleid'
     attr.name='moduleid'
     attr.type='double' />
 <key for='node'
     id='v_VertexLabels'
     attr.name='VertexLabels'
     attr.type='string' />
 <graph id='Graph'
     edgedefault='directed'>
  <node id='a'>
   <data key='v_kind'>class</data>
   <data key='v_moduleid'>121.4</data>
   <data key='v_VertexLabels'>Name</data>
  </node>
  <node id='b'>
   <data key='v_kind'>table</data>
   <data key='v_moduleid'>25.6</data>
   <data key='v_VertexLabels'>Name</data>
  </node>
  <edge source='a'
      target='b'>
   <data key='e_EdgeLabels'>EdgeWeight</data>
   <data key='e_EdgeWeight'>5.34</data>
  </edge>
 </graph>
</graphml>"

While the result is not perfect (due to flattening of default values), it is now standards compliant and it preserves the types of properties. Both the edge weight and the moduleid are now numeric (of type double). String isn't even a valid type in GraphML (with this capitalization).

Another remaining problem is that Mathematica's built-in GraphML importer is also buggy and will give you moduleid as string despite it being marked as numeric in the file ... The edge weight is fine. This is the input form of the graph:

Graph[{"a", "b"}, {DirectedEdge["a", "b"]}, 
 {Properties -> {"a" -> {"kind" -> "class", "moduleid" -> "121.4"}, 
    "b" -> {"kind" -> "table", "moduleid" -> "25.6"}}, 
  EdgeLabels -> {DirectedEdge["a", "b"] -> EdgeWeight}, 
  VertexLabels -> {"b" -> Name, "a" -> Name}, EdgeWeight -> {5.34}}]

To fix this, you can use

IGVertexMap[ToExpression, "moduleid", g]

Another problem is that for some reason, the values of VertexLabels and EdgeLabels are interpreted as symbols, not strings, so the result looks like this:

enter image description here

I would fix this using

SetProperty[
 RemoveProperty[g, {EdgeLabels, VertexLabels}],
 {EdgeLabels -> "EdgeWeight", VertexLabels -> "Name"}
]

or alternatively using

g // IGVertexMap[ToString, VertexLabels] // IGEdgeMap[ToString, EdgeLabels]

The good news is that this file is at least correctly read by any GraphML-compliant software (which Mathematica sadly isn't...)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.