2
$\begingroup$

This post can be seen as a follow up to Property assignment on a graph vertex using PropertyValue does not work inside a function.

I have defined a function:

Clear[AddStone];
SetAttributes[AddStone, HoldFirst];
AddStone[board_, v_, s : (white | black | empty)] := (PropertyValue[{board, v}, VertexState] = s)

This function should change the VertexState property of vertex v in a graph board to one of white, black, or empty. It works as expected (credits to @Heike), as the execution of:

 board = GridGraph[{5, 5}];

 AddStone[board, 8, white]

 PropertyValue[{board, 8}, VertexState]

demonstrates.

But now suppose that I need to change the property VertexState of vertices of Graphs in a list. For example:

 list = {CycleGraph[5], KaryTree[8]}

The following code doesn't work as expected:

 AddStone[list[[1]], 3, white]

It returns the error:

Set::setraw: "Cannot assign to raw object PropertyValue[{list[[1]],3},VertexState]=white."

From the discussion here, I have an idea of why this happens (though a clear explanation would be appreciated). So I need an alternative way to change the properties of the graphs in a list, when all I have is the list (instead of references to the graphs themselves). I know I could apply SetProperty to the list and obtain a new list of graphs with the updated properties, but SetProperty is slower than PropertyValue because it creates a new copy of the graph with the property changed instead of modifying the original graph.

Summing up, here is my question: Is there a way to use PropertyValue to modify the properties of graphs in a list?

$\endgroup$

2 Answers 2

4
$\begingroup$

You could try using SetProperty instead of PropertyValue:

SetAttributes[AddStone2, HoldFirst];
AddStone2[board_, v_, s : (white | black | empty)] :=
   board = SetProperty[{board, v}, VertexState -> s]

list = {CycleGraph[5], KaryTree[8]};
AddStone2[list[[1]], 1, black];

PropertyValue[{list[[1]], 1}, VertexState]

(* out: black *)
$\endgroup$
7
  • $\begingroup$ As I said in the question, the problem with SetProperty is that it is slower than PropertyValue. I'm working with a large list of huge graphs, and the difference is felt. $\endgroup$
    – a06e
    Jun 9, 2012 at 2:45
  • $\begingroup$ This seems to be the better approach $\endgroup$
    – a06e
    Jun 11, 2012 at 14:49
  • $\begingroup$ @becko: have you made some experiments? Just being curious, I have myself not used Graph for anything serious, but would be interested to learn more about them, they look interesting after all... $\endgroup$ Jun 11, 2012 at 20:03
  • $\begingroup$ @AlbertRetey I have only been using Graph for a couple of weeks. They are pretty handy, but I think it still needs some polishing. On the particular issue in this question, I ended up doing something entirely different (see next comment). My decision to mark this as the answer is based on the argument I gave in the comments (that SetProperty does only one copy). But I haven't run any experiments that demonstrate that this is, in fact, the best approach. $\endgroup$
    – a06e
    Jun 12, 2012 at 0:05
  • $\begingroup$ @AlbertRetey What I am doing is that I am working on the lists of edges directly. Properties are added and modified as wrappers. Only when I am finished and I want to see the result I add the Graph wrapper to the list of edges. So far it has been faster to work like this. $\endgroup$
    – a06e
    Jun 12, 2012 at 0:07
4
$\begingroup$

Honestly it's not quite clear to me to which extent Graph is to be seen as an internal atomic datatype or rather just an expression with head Graph. AtomicQ returns True which indicates the former, but other than that it behaves a lot like the latter. If it really is an internal datatype, my solution might be inefficient and rather a workaround, but it does the job: Just add the following pattern to your AddStone definitions:

AddStone[list_[[idx__]], v_, s : (white | black | empty)] := 
 Module[{g = list[[idx]]},
  AddStone[g, v, s];
  list[[idx]] = g;
  ]

And you can use something like:

AddStone[listofgraphs[[1]], 3, white]

but also:

AddStone[arrayofgraphs[[1,2]], 3, white]

I would consider it at least an oversight that PropertyValue[{g[[1]],1},prop]=val; doesn't work out of the box, especially since PropertyValue[{g[[1]],1},prop] works as expected. I think this could and should be added to PropertyValue.

Edit: I think Heikes answer is the way to go. I'll leave this here since it might be instructive for similar cases where no better alternative is available.

Edit: Some notes about memory efficiency which my remark about whether Graph is really an internal datatype is all about: If Graph is just a wrapper for a normal expression I'd expect exactly one "real" copy to be made, whichever approach you take. The second copy will actually not cause any extra memory to be used due to the "copy on write" that Mathematica uses for general expressions, AFAIK. If it is an internal datatype it might be possible to change it in place, and that would of course be lost with my above way of doing things. But then, I think you are out of luck if only PropertyValue applies changes in place but SetProperty doesn't. You should also note that if Graph is an internal datatype, SetProperty could in principle make changes in place even without having an Hold attribute. From some superficial tests I think that Graph does behave a lot like a normal expression, though. Since it is quite difficult to make precise forecasts how much extra memory each approach will need and how fast they'd be I'd just try that out. My own tests look like what I did suggest could be worth a try, despite the fact that an extra copy is made.

$\endgroup$
4
  • $\begingroup$ For some reason I can't get this to work. $\endgroup$
    – a06e
    Jun 9, 2012 at 18:21
  • $\begingroup$ Strange, it worked for me. I probably should make clear that you will need your definitions and additionally the one I gave. If that's not the problem, can you show what exactly you are trying? $\endgroup$ Jun 10, 2012 at 18:00
  • $\begingroup$ It works now. I don't know what went wrong before. However, this approach still copies the graph to change the property, (when you do g = list[[idx]], the graph is copied to g, and then there is another copy in list[[idx]] = g). So as you say, it is probably better to use SetProperty (as Heike suggests), which involves only one copy. $\endgroup$
    – a06e
    Jun 11, 2012 at 0:43
  • $\begingroup$ I would very much like to see an approach that doesn't need to copy the Graph. I think that PropertyValue, as it stands now, is severely limited if it can't be used as I intend it. $\endgroup$
    – a06e
    Jun 11, 2012 at 0:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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