Skip to content

Commit 8a5dbcd

Browse files
committed
Improve GCN infer override and weighted payload handling
1 parent 493831e commit 8a5dbcd

22 files changed

Lines changed: 1030 additions & 141 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.geaflow.common.config;
21+
22+
import static org.apache.geaflow.common.config.keys.FrameworkConfigKeys.INFER_ENV_USER_TRANSFORM_CLASSNAME;
23+
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
27+
public final class InferConfigHelper {
28+
29+
private InferConfigHelper() {
30+
}
31+
32+
public static boolean hasTransformClassName(Configuration configuration) {
33+
return getTransformClassName(configuration) != null;
34+
}
35+
36+
public static String getTransformClassName(Configuration configuration) {
37+
if (configuration == null || !configuration.contains(INFER_ENV_USER_TRANSFORM_CLASSNAME)) {
38+
return null;
39+
}
40+
String transformClassName = configuration.getString(
41+
INFER_ENV_USER_TRANSFORM_CLASSNAME.getKey(), null);
42+
if (transformClassName == null) {
43+
return null;
44+
}
45+
String normalizedClassName = transformClassName.trim();
46+
return normalizedClassName.isEmpty() ? null : normalizedClassName;
47+
}
48+
49+
public static Configuration buildInferConfiguration(Configuration baseConfig,
50+
String pythonTransformClassName) {
51+
if (baseConfig == null || pythonTransformClassName == null) {
52+
return baseConfig;
53+
}
54+
String normalizedClassName = pythonTransformClassName.trim();
55+
if (normalizedClassName.isEmpty()) {
56+
return baseConfig;
57+
}
58+
if (normalizedClassName.equals(getTransformClassName(baseConfig))) {
59+
return baseConfig;
60+
}
61+
Map<String, String> configMap = new HashMap<>(baseConfig.getConfigMap());
62+
configMap.put(INFER_ENV_USER_TRANSFORM_CLASSNAME.getKey(), normalizedClassName);
63+
Configuration configuration = new Configuration(configMap);
64+
configuration.setMasterId(baseConfig.getMasterId());
65+
return configuration;
66+
}
67+
}

geaflow/geaflow-core/geaflow-runtime/geaflow-operator/src/main/java/org/apache/geaflow/operator/impl/graph/compute/dynamic/DynamicGraphVertexCentricComputeOp.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
package org.apache.geaflow.operator.impl.graph.compute.dynamic;
2121

2222
import java.io.IOException;
23-
import java.util.HashMap;
2423
import java.util.HashSet;
2524
import java.util.List;
26-
import java.util.Map;
2725
import java.util.Set;
2826
import org.apache.geaflow.api.function.iterator.RichIteratorFunction;
2927
import org.apache.geaflow.api.graph.base.algo.AbstractIncVertexCentricComputeAlgo;
@@ -32,6 +30,7 @@
3230
import org.apache.geaflow.api.graph.function.vc.base.IncGraphInferContext;
3331
import org.apache.geaflow.collector.ICollector;
3432
import org.apache.geaflow.common.config.Configuration;
33+
import org.apache.geaflow.common.config.InferConfigHelper;
3534
import org.apache.geaflow.common.config.keys.FrameworkConfigKeys;
3635
import org.apache.geaflow.common.exception.GeaflowRuntimeException;
3736
import org.apache.geaflow.infer.InferContext;
@@ -196,14 +195,6 @@ public void close() throws IOException {
196195
}
197196

198197
static Configuration buildInferConfiguration(Configuration baseConfig, String pythonTransformClassName) {
199-
if (pythonTransformClassName == null || pythonTransformClassName.trim().isEmpty()) {
200-
return baseConfig;
201-
}
202-
Map<String, String> configMap = new HashMap<>(baseConfig.getConfigMap());
203-
configMap.put(FrameworkConfigKeys.INFER_ENV_USER_TRANSFORM_CLASSNAME.getKey(),
204-
pythonTransformClassName);
205-
Configuration configuration = new Configuration(configMap);
206-
configuration.setMasterId(baseConfig.getMasterId());
207-
return configuration;
198+
return InferConfigHelper.buildInferConfiguration(baseConfig, pythonTransformClassName);
208199
}
209200
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.geaflow.dsl.common.algo;
21+
22+
import org.apache.geaflow.dsl.common.data.RowVertex;
23+
24+
public interface DynamicVertexRuntimeContext<K, M> extends AlgorithmRuntimeContext<K, M> {
25+
26+
void setVertexId(K vertexId);
27+
28+
RowVertex loadVertex();
29+
}

geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/graph/GCN.java

Lines changed: 80 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,59 @@
1919

2020
package org.apache.geaflow.dsl.udf.graph;
2121

22-
import java.lang.reflect.InvocationTargetException;
23-
import java.lang.reflect.Method;
24-
import java.util.ArrayList;
2522
import java.util.Collections;
2623
import java.util.Iterator;
2724
import java.util.List;
25+
import java.util.Objects;
2826
import java.util.Optional;
27+
import org.apache.geaflow.common.config.ConfigHelper;
28+
import org.apache.geaflow.common.config.Configuration;
29+
import org.apache.geaflow.common.config.InferConfigHelper;
30+
import org.apache.geaflow.common.config.keys.FrameworkConfigKeys;
31+
import org.apache.geaflow.common.exception.GeaflowRuntimeException;
2932
import org.apache.geaflow.common.type.Types;
3033
import org.apache.geaflow.dsl.common.algo.AlgorithmRuntimeContext;
3134
import org.apache.geaflow.dsl.common.algo.AlgorithmUserFunction;
35+
import org.apache.geaflow.dsl.common.algo.DynamicVertexRuntimeContext;
3236
import org.apache.geaflow.dsl.common.algo.IncrementalAlgorithmUserFunction;
3337
import org.apache.geaflow.dsl.common.data.Row;
34-
import org.apache.geaflow.dsl.common.data.RowEdge;
3538
import org.apache.geaflow.dsl.common.data.RowVertex;
3639
import org.apache.geaflow.dsl.common.data.impl.ObjectRow;
3740
import org.apache.geaflow.dsl.common.function.Description;
3841
import org.apache.geaflow.dsl.common.types.ArrayType;
3942
import org.apache.geaflow.dsl.common.types.GraphSchema;
4043
import org.apache.geaflow.dsl.common.types.StructType;
4144
import org.apache.geaflow.dsl.common.types.TableField;
45+
import org.apache.geaflow.dsl.udf.graph.gcn.AbstractGCNSubgraphAdapter;
4246
import org.apache.geaflow.dsl.udf.graph.gcn.GCNConfig;
4347
import org.apache.geaflow.dsl.udf.graph.gcn.GCNFeatureCollector;
4448
import org.apache.geaflow.dsl.udf.graph.gcn.GCNInferPayload;
4549
import org.apache.geaflow.dsl.udf.graph.gcn.GCNInferResult;
4650
import org.apache.geaflow.dsl.udf.graph.gcn.GCNResultParser;
4751
import org.apache.geaflow.dsl.udf.graph.gcn.GCNSubgraphBuilder;
52+
import org.apache.geaflow.infer.InferContext;
4853
import org.apache.geaflow.model.graph.edge.EdgeDirection;
54+
import org.apache.geaflow.model.graph.edge.IEdge;
4955

5056
@Description(name = "gcn", description = "built-in udga for GCN inference")
5157
public class GCN implements AlgorithmUserFunction<Object, Object>, IncrementalAlgorithmUserFunction {
5258

5359
private AlgorithmRuntimeContext<Object, Object> context;
60+
private DynamicVertexRuntimeContext<Object, Object> dynamicContext;
5461
private GCNConfig config;
5562
private GCNSubgraphBuilder subgraphBuilder;
63+
private InferContext<Object> localInferContext;
5664
private final GCNFeatureCollector featureCollector = new GCNFeatureCollector();
5765
private final GCNResultParser resultParser = new GCNResultParser();
5866

5967
@Override
6068
public void init(AlgorithmRuntimeContext<Object, Object> context, Object[] params) {
6169
this.context = context;
70+
this.dynamicContext = context instanceof DynamicVertexRuntimeContext
71+
? (DynamicVertexRuntimeContext<Object, Object>) context : null;
6272
this.config = parseConfig(params);
6373
this.subgraphBuilder = new GCNSubgraphBuilder(config);
74+
this.localInferContext = createLocalInferContext(context.getConfig());
6475
}
6576

6677
@Override
@@ -69,13 +80,17 @@ public void process(RowVertex vertex, Optional<Row> updatedValues, Iterator<Obje
6980
return;
7081
}
7182
GCNInferPayload payload = subgraphBuilder.build(vertex.getId(), new DynamicGraphAdapter(vertex));
72-
Object rawResult = context.infer(payload);
83+
Object rawResult = infer(payload);
7384
GCNInferResult result = resultParser.parse(vertex.getId(), rawResult);
7485
context.take(ObjectRow.create(result.toRowValues()));
7586
}
7687

7788
@Override
7889
public void finish(RowVertex graphVertex, Optional<Row> updatedValues) {
90+
if (localInferContext != null) {
91+
localInferContext.close();
92+
localInferContext = null;
93+
}
7994
}
8095

8196
@Override
@@ -102,65 +117,88 @@ private GCNConfig parseConfig(Object[] params) {
102117
return new GCNConfig(numHops, numSamplesPerHop, className);
103118
}
104119

105-
private class DynamicGraphAdapter implements GCNSubgraphBuilder.GraphAdapter {
120+
private Object infer(GCNInferPayload payload) {
121+
if (localInferContext == null) {
122+
return context.infer(payload);
123+
}
124+
try {
125+
return localInferContext.infer(payload);
126+
} catch (Exception e) {
127+
throw new GeaflowRuntimeException("GCN local infer failed", e);
128+
}
129+
}
130+
131+
private InferContext<Object> createLocalInferContext(Configuration baseConfig) {
132+
if (!ConfigHelper.getBooleanOrDefault(baseConfig.getConfigMap(),
133+
FrameworkConfigKeys.INFER_ENV_ENABLE.getKey(), false)) {
134+
return null;
135+
}
136+
String globalClassName = InferConfigHelper.getTransformClassName(baseConfig);
137+
if (Objects.equals(config.getPythonTransformClassName(), globalClassName)) {
138+
return null;
139+
}
140+
return createInferContext(InferConfigHelper.buildInferConfiguration(baseConfig,
141+
config.getPythonTransformClassName()));
142+
}
143+
144+
protected InferContext<Object> createInferContext(Configuration configuration) {
145+
return new InferContext<>(configuration);
146+
}
147+
148+
private class DynamicGraphAdapter extends AbstractGCNSubgraphAdapter<RowVertex> {
106149

107150
private final GraphSchema graphSchema = context.getGraphSchema();
108-
private final RowVertex rootVertex;
151+
private final Object rootId;
109152

110153
DynamicGraphAdapter(RowVertex rootVertex) {
111-
this.rootVertex = rootVertex;
154+
super(rootVertex.getId(), rootVertex, config.getFeatureDimLimit());
155+
this.rootId = rootVertex.getId();
112156
}
113157

114158
@Override
115-
public List<Object> loadNeighbors(Object nodeId) {
159+
protected List<? extends IEdge<Object, ?>> loadEdges(Object nodeId) {
116160
RowVertex current = switchVertex(nodeId);
117-
if (current == null) {
118-
restoreVertex(rootVertex.getId());
119-
return Collections.emptyList();
120-
}
121-
List<RowEdge> edges = context.loadEdges(EdgeDirection.BOTH);
122-
List<Object> neighbors = new ArrayList<>(edges.size());
123-
for (RowEdge edge : edges) {
124-
Object neighborId = nodeId.equals(edge.getSrcId()) ? edge.getTargetId()
125-
: edge.getSrcId();
126-
neighbors.add(neighborId);
161+
try {
162+
if (current == null) {
163+
return Collections.emptyList();
164+
}
165+
return context.loadEdges(EdgeDirection.BOTH);
166+
} finally {
167+
restoreVertex();
127168
}
128-
restoreVertex(rootVertex.getId());
129-
return neighbors;
130169
}
131170

132171
@Override
133-
public double[] loadFeatures(Object nodeId) {
134-
RowVertex vertex = nodeId.equals(rootVertex.getId()) ? rootVertex : switchVertex(nodeId);
172+
protected RowVertex loadNode(Object nodeId) {
173+
RowVertex vertex = switchVertex(nodeId);
135174
try {
136-
return vertex == null ? new double[config.getFeatureDimLimit()]
137-
: featureCollector.collectFromRowVertex(vertex, graphSchema,
138-
config.getFeatureDimLimit());
175+
return vertex;
139176
} finally {
140-
restoreVertex(rootVertex.getId());
177+
restoreVertex();
141178
}
142179
}
143180

181+
@Override
182+
protected double[] extractFeatures(RowVertex vertex) {
183+
return featureCollector.collectFromRowVertex(vertex, graphSchema,
184+
config.getFeatureDimLimit());
185+
}
186+
144187
private RowVertex switchVertex(Object nodeId) {
145-
try {
146-
Method setVertexId = context.getClass().getMethod("setVertexId", Object.class);
147-
setVertexId.invoke(context, nodeId);
148-
Method loadVertex = context.getClass().getMethod("loadVertex");
149-
return (RowVertex) loadVertex.invoke(context);
150-
} catch (NoSuchMethodException e) {
151-
throw new IllegalStateException("GCN requires dynamic runtime context with loadVertex support", e);
152-
} catch (IllegalAccessException | InvocationTargetException e) {
153-
throw new IllegalStateException("Failed to switch runtime context vertex", e);
188+
if (dynamicContext == null) {
189+
throw new IllegalStateException(
190+
"GCN requires DynamicVertexRuntimeContext to load sampled vertices");
154191
}
192+
dynamicContext.setVertexId(nodeId);
193+
return dynamicContext.loadVertex();
155194
}
156195

157-
private void restoreVertex(Object rootId) {
158-
try {
159-
Method setVertexId = context.getClass().getMethod("setVertexId", Object.class);
160-
setVertexId.invoke(context, rootId);
161-
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
162-
throw new IllegalStateException("Failed to restore runtime context vertex", e);
196+
private void restoreVertex() {
197+
if (dynamicContext == null) {
198+
throw new IllegalStateException(
199+
"GCN requires DynamicVertexRuntimeContext to restore sampled vertices");
163200
}
201+
dynamicContext.setVertexId(rootId);
164202
}
165203
}
166204
}

0 commit comments

Comments
 (0)