Gremlin python coalesce for edges - python

I have written a script to avoid creating duplicates for vertex and edges but I have some troubles with edges.
This is the script:
g.V().has('dog', 'name', 'pluto').fold().\
coalesce(__.unfold(), __.addV('dog').property('name', 'pluto')).store('dog').\
V().has('person', 'name', 'sam').fold().\
coalesce(__.unfold(), __.addV('person').property('name', 'sam')).store('person').\
select('person').unfold().\
coalesce(__.outE('has_dog').where(__.inV().has(T.id, __.select('dog').unfold().id())),
__.addE('has_dog').to(__.select('person').unfold())).toList()
With this, I create the new two vertex and the new edge. If I execute it again, no new vertex and edge is created. For now all it's ok.
If I change 'pluto' with 'charlie' to create a new 'dog' vertex, this script creates the new vertex but return the previous edge created with 'pluto'. So if that 'person' already has that relationship, the script not creates a new one.
The thing I don't understand, it's that the code
__.select('dog').unfold().id()
should return the id of the new/old 'dog' vertex and check if the edge with 'person' exists.
If I execute that script to get the id and I replace that script with the id, for example
__.outE('has_dog').where(__.inV().has(T.id, 42))
the script works correctly and creates the edge with the new 'dog' vertex.
Why with the script to get id doesn't work but with the integer works? It's no sense because I should have the same result.
Thanks.

The issue you have run into is that a has step cannot take an arbitrary traversal but a where step can. You just need to reformulate the has step as a where step. An arbitrary traversal inside a has step is treated as "true" if it returns any result. This is one of those Gremlin things that looks as if it should work but actually does not.
Here is a contrived example that shows the where...by pattern that should be helpful in your case.
gremlin> g.V(3).as('x').V().where(eq('x')).by(id)
==>v[3]
As you are using store the where step becomes something like this
gremlin> g.V(3).store('x').V().where(eq('x')).by(id).by(unfold().id())
==>v[3]
So the line in your code will end up something like this
filter(__.inV().where(eq('dog')).by(T.id).by(unfold().id()))

Related

How to create a multi-dimensional vector with an unknown number of dimensions using Pyhton?

FULLY EDITED (to keep it simple):
I'm trying to automate a task to put a list of links inside a dictionary, than run through each of the links and fetch its respective links and so on thus creating a huge tree-like structure at the end.
I haven't tested this code yet, so tell me if something goes wrong
yourlinks={'a':'google.com','b':'google.com'}
def yourawesomefunction(link):
#does some thing
return dictionary of more links!
def loop(dictionary):
if dictionary=={}:
pass
#when your tree finally ends, go on to the next branch
for x in dictionary:
dictionary[x]=yourawesomefunction(dictionary[x])
#create branches
for x in dictionary:
loop(dictionary[x])
#go into next layer
loop(yourlinks)
Oh and by the way, I hope your tree ends at some point because if it doesn't it will probably follow the first link forever
Also, a function calling itself can cause many problems, so try to avoid this if possible.

KeyError on Generator

So close, and yet so far.
I'm not sure what happened but a generator script that was working for me has suddenly started throwing KeyErrors. I am assigning properties to networkx nodes according to a category I am giving each node.
Each node looks like this...
539943797.0: {'category': 'perimeter'}
and I define the sizes with a variable like this...
node_sizes = {'core':500, 'perimeter':50}
and the actual node draw code looks like this, with the generator in place...
nx.draw_networkx_nodes(G, graph_pos,
node_size=[node_sizes[G.node[node]['category']]for node in G],
alpha=node_alpha,
node_color=[node_colors[G.node[node]['category']]for node in G])
The problem is, the above generator code (which was working not so long ago) gives me a KeyError:'category' error when I run it.
However, calling this...
node_sizes[G.node[539943797.0]['category']]
gets me the value of 50, as I would expect; pulling the category from 539943797.0 as 'perimeter' and the size for that is 50. So far so good. I'm not sure what I'm doing wrong here. I was hoping another few sets of eyes on this could give me a better idea.
I suspect I'm doing something wrong in how I calling the category, or setting it
(I set it here...)
for node in graph[1]:
G.add_node(node)
G.node[node]['category'] = 'perimeter'
If I need to put up more of the code to be better understood I'll try and trim things up and put it out here. Hopefully I've supplied enough.
thanks,
In your comment you say this started happening after you started adding edges. I think that's where the problem is. You'll get this error if even one node doesn't have the 'category' defined. I think adding edges is resulting in the addition of a few nodes that don't have category defined. The first test is to just go through
for node in G.nodes():
if not G.node[node].has_key('category'):
print node
right before where you get your error.
I bet that you'll see that most of your nodes are okay, but a few aren't.
If I have the code
import networkx as nx
G=nx.Graph()
G.add_node(1)
G.node[1]['category'] = 'perimeter'
for node in G.nodes():
if not G.node[node].has_key('category'):
print node
I get no output. All (1) of the nodes has 'category' defined.
However, if I then try
G.add_edge(1,2)
for node in G.nodes():
if not G.node[node].has_key('category'):
print node
It outputs
2
This is because it when I added the edge, networkx saw a node that wasn't in G yet. It assumes you want to add the node too. So now you've added a new node 2, but it doesn't know that it should be defining 'category' as well. So it doesn't.
From what you've described this is almost certainly what is happening. To check this, before adding any edge you can check whether the two nodes are in the graph already. Or, if your code adds a huge number of edges at once, just check whether G.order() is the same before and after. Once you figure out why it's doing this, presumably you can decide what you want to do with those nodes.
If this doesn't find something that's an issue, then you'll need to post more code, so that we have something that reproduces your error.

Solving a graph issue with Python

I have one situation and I would like to approach this problem with Python, but unfortunately I don't have enough knowledge about the graphs. I found one library which seems very suitable for this relatively simple task, networkx, but I am having issues doing exact things I want, which should be fairly simple.
I have a list of nodes, which can have different types, and two "classes" of neighbors, upwards and downwards. The task is to find paths between two target nodes, with some constraints in mind:
only nodes of specific type can be traversed, i.e. if starting nodes are of type x, any node in the path has to be from another set of paths, y or z
if a node has a type y, it can be passed through only once
if a node has type z, it can be passed through twice
in case a node of type z is visited, the exit has to be from the different class of neighbor, i.e. if its visited from upwards, the exit has to be from downwards
So, I tried some experimentation but I, as said, have struggled. First, I am unsure what type of graph this actually represents? Its not directional, since it doesn't matter if you go from node 1 to node 2, or from node 2 to node 1 (except in that last scenario, so that complicates things a bit...). This means I can't just create a graph which is simply multidirectional, since I have to have that constraint in mind. Second, I have to traverse through those nodes, but specify that only nodes of specific type have to be available for path. Also, in case the last scenario happens, I have to have in mind the entry and exit class/direction, which puts it in somewhat directed state.
Here is some sample mockup code:
import networkx as nx
G=nx.DiGraph()
G.add_node(1, type=1)
G.add_node(2, type=2)
G.add_node(3, type=3)
G.add_edge(1,2, side="up")
G.add_edge(1,3, side="up")
G.add_edge(2,1, side="down")
G.add_edge(2,3, side="down")
for path in nx.all_simple_paths(G,1,3):
print path
The output is fairly nice, but I need these constraints. So, do you have some suggestions how can I implement these, or give me some more guidance regarding understanding this type of problem, or suggest a different approach or library for this problem? Maybe a simple dictionary based algorithm would fit this need?
Thanks!
You might be able to use the all_simple_paths() function for your problem if you construct your graph differently. Simple paths are those with no repeated nodes. So for your constraints here are some suggestions to build the graph so you can run that algorithm unmodified.
only nodes of specific type can be traversed, i.e. if starting nodes are of type x, any node in the path has to be from another set of paths, y or z
Given a starting node n, remove all other nodes with that type before you find paths.
if a node has a type y, it can be passed through only once
This is the definition of simple paths so it is automatically satisfied.
if a node has type z, it can be passed through twice
For every node n of type z add a new node n2 with the same edges as those pointing to and from n.
in case a node of type z is visited, the exit has to be from the different class of neighbor, i.e. if its visited from upwards, the exit has to be from downwards
If the edges are directed as you propose then this could be satisfied if you make sure the edges to z are all the same direction - e.g. in for up and out for down...
The best way to do this I think is by calculating all valid paths of length at most k between the source S and every other node, then using that information to calculate all valid paths of length at most k+1. You then just repeat this until you get a fixed point where no paths are modified.
In practice, this means you should set up a list of paths at each node. At each step, you take each node U in turn and look at the paths that, in the previous step, terminated at some neighbour V of U. If any of those paths can be extended to be a new, distinct path to U, extend it and add it to U's list.
If when you execute a step you find no new paths, that's your termination state. You can then check the list of paths at the target node T.
Pseudocode (in a very loose C# formalism):
var paths = graph.nodes.ToDictionary(node => node, node => new List<List<node>>())
paths[S].Add(new List<node> {S}) // The trivial path that'll start us off.
bool notAFixedPoint = true;
while (notAFixedPoint)
{
notAFixedPoint = false // Assume we're not gonna find any new paths.
foreach (var node in graph)
{
var pathsToNode = paths[node]
foreach (var neighbour in node.Neighbours)
{
var pathsToNeighbour = paths[neighbour]
// ExtendPaths is where all the logic about how to recognise a valid path goes.
var newPathsToNode = ExtendPaths(pathsToNeighbour, node)
// The use of "Except" here is for expository purposes. It wouldn't actually work,
// because collections in most languages are compared by reference rather than by value.
if (newPathsToNode.Except(pathsToNode).IsNotEmpty())
{
// We've found some new paths, so we can't terminate yet.
notAFixedPoint = true
pathsToNode.AddMany(newPathsToNode)
}
}
}
}
return paths[T]
This looks like an optimization problem to me -- look up "Traveling Salesman" for a classic example that is somewhat close to what you want to do.
I've had good luck using "simulated annealing" for optimization problems, but you might also take a look at "genetic algorithms".

Python - using a string in for in statement?

so i know this is a bit of a workaround and theres probably a better way to do this, but heres the deal. Ive simplified the code from where tis gathering this info from and just given solid values.
curSel = nuke.selectedNodes()
knobToChange = "label"
codeIn = "[value in]"
kcPrefix = "x"
kcStart = "['"
kcEnd = "']"
changerString = kcPrefix+kcStart+knobToChange+kcEnd
for x in curSel:
changerString.setValue(codeIn)
But i get the error i figured i would - which is that a string has no attribute "setValue"
its because if i just type x['label'] instead of changerString, it works, but even though changer string says the exact same thing, its being read as a string instead of code.
Any ideas?
It looks like you're looking for something to evaluate the string into a python object based on your current namespace. One way to do that would be to use the globals dictionary:
globals()['x']['label'].setValue(...)
In other words, globals()['x']['label'] is the same thing as x['label'].
Or to spell it out explicitly for your case:
globals()[kcPrefix][knobToChange].setValue(codeIn)
Others might suggest eval:
eval('x["label"]').setValue(...) #insecure and inefficient
but globals is definitely a better idea here.
Finally, usually when you want to do something like this, you're better off using a dictionary or some other sort of data structure in the first place to keep your data more organized
Righto, there's two things you're falling afoul of. Firstly, in your original code where you are trying to do the setValue() call on a string you're right in that it won't work. Ideally use one of the two calls (x.knob('name_of_the_knob') or x['name_of_the_knob'], whichever is consistent with your project/facility/personal style) to get and set the value of the knob object.
From the comments, your code would look like this (my comments added for other people who aren't quite as au fait with Nuke):
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].getValue()
codeIn = nuke.thisNode()['codeinput'].getValue()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
Using this sample UI with the values in the two fields as shown and the button firing off the script...
...this code is going to give you an error message of 'Nothing is named "foo"' when you execute it because the .getValue() call is actually returning you the evaluated result of the knob - which is the error message as it tries to execute the TCL [value foo], and finds that there isn't any object named foo.
What you should ideally do is instead invoke .toScript() which returns the raw text.
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].toScript()
codeIn = nuke.thisNode()['codeinput'].toScript()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
You can sidestep this problem as you've noted by building up a string, adding in square brackets etc etc as per your original code, but yes, it's a pain, a maintenance nightmare, and starting to go down that route of building objects up from strings (which #mgilson explains how to do in both a globals() or eval() method)
For those who haven't had the joy of working with Nuke, here's a small screencap that may (or may not..) provide more context:

How to apply a modifier in Python, creating a new mesh?

Let's say I have a bpy.types.Object containing a bpy.types.Mesh data field; how can I apply one of the modifiers associated with the object, in order to obtain a NEW bpy.types.Mesh, possibly contained within a NEW bpy.types.Object, thus leaving the original scene unchaged?
I'm interested in applying the EdgeSplit modifier right before exporting vertex data to my custom format; the reason why I want to do this is to have Blender automatically and transparently duplicate the vertices shared by two faces with very different orientations.
I suppose you're using the 2.6 API.
bpy.ops.object.modifier_apply (modifier='EdgeSplit')
...applies to the currently active object its Edge Split modifier. Note that it's object.modifier_apply (...)
You can use
bpy.context.scene.objects.active = my_object
to set the active object. Note that it's objects.active.
Also, check the modifier_apply docs. Lot's of stuff you can only do with bpy.ops.*.
EDIT: Just saw you need a new (presumably temporary) mesh object. Just do
bpy.ops.object.duplicate()
after you set the active object and the new active object then becomes the duplicate (it retains any added modifier; if it was an object named 'Cube', it duplicates it, makes it active and names it 'Cube.001') to which you can then apply the modifier. Hope this was clear enough :)
EDIT: Note, that bpy.ops.object.duplicate() uses not active object, but selected. To ensure the correct object is selected and duplicated do this
bpy.ops.object.select_all(action = 'DESELECT')
object.select = True
There is another way, which seems better suited for custom exporters: Call the to_mesh method on the object you want to export. It gives you a copy of the object's mesh with all the modifiers applied. Use it like this:
mesh = your_object.to_mesh(scene = bpy.context.scene, apply_modifiers = True, settings = 'PREVIEW')
Then use the returned mesh to write any data you need into your custom format. The original object (including it's data) will stay unchanged and the returned mesh can be discarded after the export is finished.
Check the Blender Python API Docs for more info.
There is one possible issue with this method. I'm not sure you can use it to apply only one specific modifier, if you have more than one defined. It seems to apply all of them, so it might not be useful in your case.

Categories