Hello Matt (others),

Perhaps you have some thoughts on this issue:
        https://issues.apache.org/jira/browse/TINKERPOP3-825

Finally, here is the new sack-merge operator in action. As you can see, we can 
now do energy diffusions. If you make your sack value a function of sin/cos, 
then you can simulate wave dynamics and all the neat things that come with that 
(e.g. quantum probabilities due to superposition of traverser state).

// starting a marko, the traverser has a sack of 1.0.
gremlin> g.withSack(1.0d,sum).V(1).sack()
==>1.0

// taking the outgoing knows-edges from marko, the sacks of the split 
traversers are normalized.
gremlin> g.withSack(1.0d,sum).V(1).local(out('knows').barrier(normSack)).sack()
==>0.5
==>0.5

// because the merge operator is sum, sacks collapse via sum
gremlin> 
g.withSack(1.0d,sum).V(1).local(out('knows').barrier(normSack)).in('knows').barrier().sack()
==>1.0
==>1.0

Why two 1.0s?!?! Well, that is what the ticket above is all about. The "right" 
solution is:

gremlin> 
g.withSack(1.0d,sum).V(1).local(out('knows').barrier(normSack)).in('knows').barrier().sideEffect{it.setBulk(1)}.sack()
==>1.0

Please provide any feedback you may have,
Marko.

http://markorodriguez.com

On Sep 1, 2015, at 9:57 AM, Marko Rodriguez <[email protected]> wrote:

> Hello,
> 
>> You say "the merge operator can easily just be: g.withSack(1.0, sum)", but
>> that is the syntax for the split operator, so do you need a third parameter
>> there?
> 
> The are multiple overloads. Merge is a BiFunction and split is a 
> UnaryOperator -- see new_merge/ branch.
> 
>> Also, is normalizeSack a user-defined traversal?  In this case, it would be
>> doing math on the floating point sack values?
> 
> No, its a Consumer<TraverserSet<?>> and used by CollectingBarrierStep.
> 
>> One of the interesting (and relevant for my application) possibilities is
>> to direct the priority of the traverser execution based on what you might
>> call "propagation energy".  That is, with each split or merge (or based on
>> other "amplification" criteria), the traversers lose momentum, and the
>> limited compute resources are applied to the traversers with the highest
>> momentum.  I suppose this is a vendor-specific feature, but would there be
>> a story for directing the priority of execution of the set of traversers?
> 
> You can always where(sack().gt(0.01)).
> 
> HTH,
> Marko.
> 
> http://markorodriguez.com
> 
> 
>> 
>> On Mon, Aug 31, 2015 at 11:06 AM, Marko Rodriguez <[email protected]>
>> wrote:
>> 
>>> Hello,
>>> 
>>> In TinkerPop 3.0.0, at the last minute, I got rid of the sack() merge
>>> operator as I was lost in how we would do merge traverser sacks. Why did I
>>> get gun shy? Watch:
>>> 
>>> gremlin> g = TinkerFactory.createModern().traversal()
>>> ==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
>>> gremlin> g.withSack(1.0).V(1).outE().inV().sack()
>>> ==>1.0
>>> ==>1.0
>>> ==>1.0
>>> 
>>> So v[1] has 3 outgoing edges and so it generates three traversers and the
>>> sack of the parent traverser is simply copied to the children. What if you
>>> want to preserve a total sack value in the traversal. That is, there can
>>> never be more than 1.0 "energy" in the graph. Well, if your edges have
>>> weights (lets say) and they always are fraction of 1.0, well that would
>>> work. *** NOTE  that the TinkerGraph modern graph is not 1.0 normalized. ***
>>> 
>>> gremlin> g.withSack(1.0d).V(1).outE().sack(mult).by('weight').inV().sack()
>>> ==>0.4
>>> ==>0.5
>>> ==>1.0
>>> 
>>> Hmm.. but if they are not 1.0 normalized well, that sucks. And its hard to
>>> keep such data consistent with large graphs constantly adding and removing
>>> edges…And what if you don't have weights!!?
>>> 
>>> For the last week I was going down this rabbit hole of "split operators"
>>> for the withSack() source method. It was all nasty and convoluted. However,
>>> last night and into this morning, I think we can solve this simply with a
>>> "barrier consumer." Watch.
>>> 
>>> gremlin>
>>> g.withSack(1.0).V(1).local(outE().barrier(normalizeSack)).inV().sack()
>>> ==>0.3333333333333333
>>> ==>0.3333333333333333
>>> ==>0.3333333333333333
>>> 
>>> Cool. What about if you want to have it normalized by edge weights?
>>> 
>>> gremlin>
>>> g.withSack(1.0).V(1).local(outE().sack(mult).by('weight').barrier(normalizeSack)).inV().sack()
>>> ==>0.2105263157894737
>>> ==>0.2631578947368421
>>> ==>0.5263157894736842
>>> 
>>> Notice how local(barrier()) gathers all the traversers generated by outE()
>>> for the current object and then allows you to do some mutation on them.
>>> NormalizeSack simply recompute sacks based on the aggregate.
>>> 
>>> With this, the merge operator can easily just be: g.withSack(1.0,sum). And
>>> then, we can support furcating and converging "energy" in the graph.
>>> 
>>> This will lead into some very very trippy (theoretical) work I've been
>>> doing with constructive and destructive wave interference models with
>>> Gremlin. We will be able to support "optical algorithms" -- refraction,
>>> diffusion, interference, etc.
>>> 
>>> Any thoughts/concerns/recommendations?
>>> 
>>> Marko.
>>> 
>>> http://markorodriguez.com
>>> 
>>> 
> 

Reply via email to