/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.tinkerpop.gremlin.process.computer; import org.apache.tinkerpop.gremlin.process.computer.util.VertexProgramHelper; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Supplier; /** * A {@link MessageScope} represents the range of a message. A message can have multiple receivers and message scope * allows the underlying {@link GraphComputer} to apply the message passing algorithm in whichever manner is most * efficient. It is best to use {@link MessageScope.Local} if possible as that provides more room for optimization by * providers than {@link MessageScope.Global}. * * @author Marko A. Rodriguez (http://markorodriguez.com) * @author Matthias Broecheler (me@matthiasb.com) */ public abstract class MessageScope { /** * A Global message is directed at an arbitrary vertex in the graph. * The recipient vertex need not be adjacent to the sending vertex. * This message scope should be avoided if a {@link Local} can be used. */ public final static class Global extends MessageScope { private static final Global INSTANCE = Global.of(); private final Iterable<Vertex> vertices; private Global(final Iterable<Vertex> vertices) { this.vertices = vertices; } public static Global of(final Iterable<Vertex> vertices) { return new Global(vertices); } public static Global of(final Vertex... vertices) { return new Global(Arrays.asList(vertices)); } public Iterable<Vertex> vertices() { return this.vertices; } public static Global instance() { return INSTANCE; } @Override public int hashCode() { return 4676576; } @Override public boolean equals(final Object other) { return other instanceof Global; } } /** * A Local message is directed to an adjacent (or "memory adjacent") vertex. * The adjacent vertex set is defined by the provided {@link Traversal} that dictates how to go from the sending vertex to the receiving vertex. * This is the preferred message scope as it can potentially be optimized by the underlying {@link Messenger} implementation. * The preferred optimization is to not distribute a message object to all adjacent vertices. * Instead, allow the recipients to read a single message object stored at the "sending" vertex. * This is possible via `Traversal.reverse()`. This optimizations greatly reduces the amount of data created in the computation. * * @param <M> The {@link VertexProgram} message class */ public final static class Local<M> extends MessageScope { public final Supplier<? extends Traversal<Vertex, Edge>> incidentTraversal; public final BiFunction<M, Edge, M> edgeFunction; private Local(final Supplier<? extends Traversal<Vertex, Edge>> incidentTraversal) { this(incidentTraversal, (final M m, final Edge e) -> m); // the default is an identity function } private Local(final Supplier<? extends Traversal<Vertex, Edge>> incidentTraversal, final BiFunction<M, Edge, M> edgeFunction) { this.incidentTraversal = incidentTraversal; this.edgeFunction = edgeFunction; } public static <M> Local<M> of(final Supplier<? extends Traversal<Vertex, Edge>> incidentTraversal) { return new Local<>(incidentTraversal); } public static <M> Local<M> of(final Supplier<? extends Traversal<Vertex, Edge>> incidentTraversal, final BiFunction<M, Edge, M> edgeFunction) { return new Local<>(incidentTraversal, edgeFunction); } public BiFunction<M, Edge, M> getEdgeFunction() { return this.edgeFunction; } public Supplier<? extends Traversal<Vertex, Edge>> getIncidentTraversal() { return this.incidentTraversal; } @Override public int hashCode() { return this.edgeFunction.hashCode() ^ this.incidentTraversal.get().hashCode(); } @Override public boolean equals(final Object other) { return other instanceof Local && ((Local<?>) other).incidentTraversal.get().equals(this.incidentTraversal.get()) && ((Local<?>) other).edgeFunction == this.edgeFunction; } /** * A helper class that can be used to generate the reverse traversal of the traversal within a {@link MessageScope.Local}. */ public static class ReverseTraversalSupplier implements Supplier<Traversal<Vertex, Edge>> { private final MessageScope.Local<?> localMessageScope; public ReverseTraversalSupplier(final MessageScope.Local<?> localMessageScope) { this.localMessageScope = localMessageScope; } public Traversal<Vertex, Edge> get() { return VertexProgramHelper.reverse(this.localMessageScope.getIncidentTraversal().get().asAdmin()); } } } }