Skip to content

Commit

Permalink
图架构增加单独的Dag抽象
Browse files Browse the repository at this point in the history
  • Loading branch information
entropy-cloud committed Nov 10, 2023
1 parent 7ccdf9d commit 48c346e
Show file tree
Hide file tree
Showing 3 changed files with 371 additions and 0 deletions.
105 changes: 105 additions & 0 deletions nop-core/src/main/java/io/nop/core/model/graph/dag/Dag.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.nop.core.model.graph.dag;

import io.nop.api.core.annotations.data.DataBean;
import io.nop.api.core.exceptions.NopException;
import io.nop.core.model.tree.impl.WidthFirstIterator;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static io.nop.core.CoreErrors.ARG_NAME;
import static io.nop.core.CoreErrors.ERR_GRAPH_UNKNOWN_NODE;

@DataBean
public class Dag {
private String rootNodeName;
private Map<String, DagNode> nodes = new HashMap<>();

public Dag() {
}

public Dag(String rootName) {
setRootNodeName(rootName);
nodes.put(rootName, new DagNode(rootName));
}

public Iterator<DagNode> widthFirstIterator() {
return new WidthFirstIterator<>(this::getNextNodes, getRootNode(), true, null);
}

public List<DagNode> getNextNodes(DagNode node) {
Set<String> nextNames = node.getNextNodeNames();
if (nextNames == null || nextNames.isEmpty())
return Collections.emptyList();

return nextNames.stream().map(this::requireNode).collect(Collectors.toList());
}

public DagNode getRootNode() {
return requireNode(rootNodeName);
}

public boolean isEmpty() {
return nodes.isEmpty();
}

public void forEachNode(Consumer<DagNode> action) {
this.nodes.values().forEach(action);
}

public int size() {
return nodes.size();
}

public DagNode getNode(String name) {
return nodes.get(name);
}

public DagNode requireNode(String name) {
DagNode node = getNode(name);
if (node == null)
throw new NopException(ERR_GRAPH_UNKNOWN_NODE)
.param(ARG_NAME, name);
return node;
}

public void analyze() {
new DagAnalyzer(this).analyze();
}

public DagNode addNextNodes(String nodeName, Set<String> next) {
DagNode node = nodes.computeIfAbsent(nodeName, DagNode::new);
if (next != null) {
node.addNextNodes(next);
}
return node;
}

public DagNode addNextNode(String nodeName, String next) {
DagNode node = nodes.computeIfAbsent(nodeName, DagNode::new);
node.addNextNode(next);
return node;
}

public String getRootNodeName() {
return rootNodeName;
}

public void setRootNodeName(String rootNodeName) {
this.rootNodeName = rootNodeName;
}

public Map<String, DagNode> getNodes() {
return nodes;
}

public void setNodes(Map<String, DagNode> nodes) {
this.nodes = nodes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.nop.core.model.graph.dag;

import io.nop.api.core.exceptions.NopException;
import io.nop.commons.collections.bit.IBitSet;
import io.nop.commons.util.CollectionHelper;
import io.nop.commons.util.objects.Pair;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static io.nop.core.CoreErrors.ARG_NODE_NAMES;
import static io.nop.core.CoreErrors.ERR_GRAPH_NODES_NOT_REACHABLE;

/**
* 原始的图结构中有可能包含环,分析之后会删除一些连接使其成为环
*/
public class DagAnalyzer {
private final Dag dag;

private final DagNode[] nodes;

// 删除哪些next连接才能得到一个无循环的DAG结构。
private List<Pair<String, String>> removedLinks = new ArrayList<>();

private final IBitSet analyzedFlags;

public DagAnalyzer(Dag dag) {
this.dag = dag;
this.nodes = new DagNode[dag.size()];
this.analyzedFlags = CollectionHelper.newFixedBitSet(dag.size());
}

public void analyze() {
checkAllReachable();
checkLoop();
}

private void checkAllReachable() {
int index = 0;
Iterator<DagNode> it = dag.widthFirstIterator();
while (it.hasNext()) {
DagNode node = it.next();
this.nodes[index] = node;
node.setNodeIndex(index++);
}

if (index != dag.size()) {
// 通过宽度优先遍历无法达到所有节点
throw new NopException(ERR_GRAPH_NODES_NOT_REACHABLE)
.param(ARG_NODE_NAMES, getUnreachableNodes());
}
}

private void checkLoop() {
DagNode root = nodes[0];

}

private List<String> getUnreachableNodes() {
List<String> ret = new ArrayList<>();
dag.forEachNode(node -> {
if (node.getNodeIndex() <= 0)
ret.add(node.getName());
});
return ret;
}
}
198 changes: 198 additions & 0 deletions nop-core/src/main/java/io/nop/core/model/graph/dag/DagNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package io.nop.core.model.graph.dag;

import io.nop.api.core.annotations.data.DataBean;

import java.util.LinkedHashSet;
import java.util.Set;

@DataBean
public class DagNode implements Comparable<DagNode> {
private String name;

/**
* 如果是从根节点可达的节点,则它的nodeIndex会被设置为宽度遍历的序号
*/
private int nodeIndex = -1;

private boolean internal;

private String controlNodeName;

private Set<String> nextNodeNames;
private Set<String> prevNodeNames;

private Set<String> nextNormalNodeNames;

private Set<String> prevNormalNodeNames;

private boolean nextToEnd;

private boolean eventuallyToEnd;

private boolean nextToEmpty;

private boolean eventuallyToEmpty;

private boolean nextToAssigned;

private boolean eventuallyToAssigned;

public DagNode() {
}

public DagNode(String name) {
setName(name);
}

@Override
public int compareTo(DagNode o) {
return Integer.compare(this.nodeIndex, o.nodeIndex);
}

public void addNextNodes(Set<String> next) {
if (next == null)
return;

if (this.nextNodeNames == null)
this.nextNodeNames = new LinkedHashSet<>();
this.nextNodeNames.addAll(next);
}

public void addNextNode(String next) {
if (next == null)
return;
if (this.nextNodeNames == null)
this.nextNodeNames = new LinkedHashSet<>();
this.nextNodeNames.add(next);
}

public void addPrevNodes(Set<String> prev) {
if (prev == null)
return;

if (this.prevNodeNames == null)
this.prevNodeNames = new LinkedHashSet<>();
this.prevNodeNames.addAll(prev);
}

public void addPrevNode(String prev) {
if (prev == null)
return;

if (this.prevNodeNames == null)
this.prevNodeNames = new LinkedHashSet<>();
this.prevNodeNames.add(prev);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getNodeIndex() {
return nodeIndex;
}

public void setNodeIndex(int nodeIndex) {
this.nodeIndex = nodeIndex;
}

public boolean isInternal() {
return internal;
}

public void setInternal(boolean internal) {
this.internal = internal;
}

public String getControlNodeName() {
return controlNodeName;
}

public void setControlNodeName(String controlNodeName) {
this.controlNodeName = controlNodeName;
}

public Set<String> getNextNodeNames() {
return nextNodeNames;
}

public void setNextNodeNames(Set<String> nextNodeNames) {
this.nextNodeNames = nextNodeNames;
}

public Set<String> getPrevNodeNames() {
return prevNodeNames;
}

public void setPrevNodeNames(Set<String> prevNodeNames) {
this.prevNodeNames = prevNodeNames;
}

public Set<String> getNextNormalNodeNames() {
return nextNormalNodeNames;
}

public void setNextNormalNodeNames(Set<String> nextNormalNodeNames) {
this.nextNormalNodeNames = nextNormalNodeNames;
}

public Set<String> getPrevNormalNodeNames() {
return prevNormalNodeNames;
}

public void setPrevNormalNodeNames(Set<String> prevNormalNodeNames) {
this.prevNormalNodeNames = prevNormalNodeNames;
}

public boolean isNextToEnd() {
return nextToEnd;
}

public void setNextToEnd(boolean nextToEnd) {
this.nextToEnd = nextToEnd;
}

public boolean isEventuallyToEnd() {
return eventuallyToEnd;
}

public void setEventuallyToEnd(boolean eventuallyToEnd) {
this.eventuallyToEnd = eventuallyToEnd;
}

public boolean isNextToEmpty() {
return nextToEmpty;
}

public void setNextToEmpty(boolean nextToEmpty) {
this.nextToEmpty = nextToEmpty;
}

public boolean isEventuallyToEmpty() {
return eventuallyToEmpty;
}

public void setEventuallyToEmpty(boolean eventuallyToEmpty) {
this.eventuallyToEmpty = eventuallyToEmpty;
}

public boolean isNextToAssigned() {
return nextToAssigned;
}

public void setNextToAssigned(boolean nextToAssigned) {
this.nextToAssigned = nextToAssigned;
}

public boolean isEventuallyToAssigned() {
return eventuallyToAssigned;
}

public void setEventuallyToAssigned(boolean eventuallyToAssigned) {
this.eventuallyToAssigned = eventuallyToAssigned;
}
}

0 comments on commit 48c346e

Please sign in to comment.