Is there any way to access fields of 'super' class (not inherited) - c#

Title may be misleading as I had some trouble searching and even creating a proper question, so let me give a real problem I'm struggling with:
I have a Graph class. Since graphs need nodes and edges I created two additional classes Node (vertex) and Edge. My structure looks like this:
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
}
class Node { ... }
class Edge { ... }
I wrote some methods for Node class, one is particularly problematic for me. Signature:
public List<Node> GetNeighbours(List<Edge> edges) { ... }
Pretty standard. Given a graph I ask a node: how many neighbours do you have? I need list of edges to resolve it.
How can I refactor this code so that I can use Graph properties/fields inside instead of passing a list of edges every time? Is something like this possible:
public List<Node> GetNeighbours()
{
// ...
foreach(edge in *BASE*.edges) { ... }
}
I know that I can't use the base keyword because I don't want any inheritance here (why would a node have to inherit from graph?!) and nested classes seem not to help me as well (no access to "parent's" fields).
This code is working right now, but I feel it's not elegant and I'd like to experience a proper solution.

Pass a reference to the parent class in the Graph constructor.
Something like:
class Graph
{
private ParentType parent;
public void Graph(ref ParentType parent)
{
this.parent = parent;
}
}
Then, in the GetNeighbours method (assuming the ParentType has an Edges collection property):
public List<Node> GetNeighbours()
{
// ...
foreach(var edge in parent.Edges) { ... }
}

From this description of what you're trying to do:
Given a graph I ask a node: how many neighbours do you have?
Are you sure that this should be a method of a Node? Since Graph contains the Nodes and Edges perhaps this method is better off in Graph.
public List<Node> GetNeighbours(Node node)
{
if(!nodes.Contains(node)
{
return new List<Node>(); //No neighbors. Return an empty list.
}
// Find and return the neighbors. This method is in Graph so it
// has access to all of Graph's internals.
}
My reasoning is that since in a sense Graph is a parent and it contains Nodes, Node does not need to know about Graph. Its purpose (Single Responsibility) is complete without any references to Graph.

I would have a method like Graph.AddNodes() or Graph.AddEdges() on Graph so that this is a central place to make sure that all Nodes (and/or Edges) have the reference that it needs. I'm thinking something like this, depending on the model of Node and Edge for you.
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
public void AddEdges(params Edge[] edges) {
foreach (var edge in edges) {
edge.Node1.Parent = this;
edge.Node2.Parent = this;
}
}
}
class Node {
public Graph Parent { get; set; }
public List<Node> GetNeighbours()
{
var neighbors = new List<Node>();
foreach(var edge in parent.Edges) {
if (edge.Node1 == this && !neighbors.Contains(edge.Node2)) {
neighbors.Add(edge.Node2);
}
else if (edge.Node2 == this && !neighbors.Contains(edge.Node1)) {
neighbors.Add(edge.Node1);
}
}
}
}
class Edge {
public Node Node1 { get; set; }
public Node Node2 { get; set; }
}

Here is an alternative approach. Instead of passing the parent reference, you could make each edge aware of the nodes on each end. And make each node aware of the edges connected to them.
A massive advantage of this is that you do not need enumerate possibly massive amounts of nodes/edges to find what you need. You already have what you need so it is much faster.
Here is quick sample of the approach I described along with some tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace GraphModelTest
{
class Program
{
static void Main(string[] args)
{
TestA();
TestB();
TestC();
}
private static void TestC()
{
//A <-> B
//| |
//D <-> C
Node a = new Node("a");
Node b = new Node("b");
Node c = new Node("c");
Node d = new Node("d");
Edge ab = a.ConnectTo(b);
Edge bc = b.ConnectTo(c);
Edge cd = c.ConnectTo(d);
Edge da = d.ConnectTo(a);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Nodes.Add(c);
g.Nodes.Add(d);
g.Edges.Add(ab);
g.Edges.Add(bc);
g.Edges.Add(cd);
g.Edges.Add(da);
Console.WriteLine(g.ToString());
Console.WriteLine("Neighbours of B");
foreach (Node n in b.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
Console.WriteLine("Neighbours of D");
foreach (Node n in d.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
}
private static void TestB()
{
//A <-> B <-> C
Node a = new Node("a");
Node b = new Node("b");
Edge ab = a.ConnectTo(b);
Node c = new Node("c");
Edge bc = b.ConnectTo(c);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Nodes.Add(c);
g.Edges.Add(ab);
g.Edges.Add(bc);
Console.WriteLine(g.ToString());
Console.WriteLine("Neighbours of B");
foreach (Node n in b.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
}
private static void TestA()
{
//A <-> B
Node a = new Node("a");
Node b = new Node("b");
Edge ab = a.ConnectTo(b);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Edges.Add(ab);
Console.WriteLine(g.ToString());
}
}
class Edge
{
public Edge(string name, Node a, Node b)
{
Name = name;
A = a;
B = b;
}
public Node A { get; private set; }
public Node B { get; private set; }
public string Name { get; private set; }
public override string ToString() => $"{Name}";
}
class Node
{
public Node(string name)
{
Name = name;
connectedEdges = new List<Edge>();
}
public string Name { get; private set; }
private ICollection<Edge> connectedEdges;
public IEnumerable<Edge> ConnectedEdges
{
get
{
return connectedEdges.AsEnumerable();
}
}
public void AddConnectedEdge(Edge e)
{
connectedEdges.Add(e);
}
public Edge ConnectTo(Node n)
{
//Create the edge with references to nodes
Edge e = new Edge($"{Name} <-> {n.Name}", this, n);
//Add edge reference to this node
AddConnectedEdge(e);
//Add edge reference to the other node
n.AddConnectedEdge(e);
return e;
}
public IEnumerable<Node> GetNeighbours()
{
foreach (Edge e in ConnectedEdges)
{
//Have to figure which one is not this node
Node node = e.A != this ? e.A : e.B;
yield return node;
}
}
public override string ToString() => $"{Name}";
}
class Graph
{
public Graph()
{
Nodes = new List<Node>();
Edges = new List<Edge>();
}
public ICollection<Node> Nodes { get; set; }
public ICollection<Edge> Edges { get; set; }
public override string ToString()
{
StringBuilder str = new StringBuilder();
str.AppendLine("Graph:");
str.AppendLine("Nodes:");
foreach (Node n in Nodes)
{
str.AppendLine(n.ToString());
}
str.AppendLine("Edges:");
foreach (Edge e in Edges)
{
str.AppendLine(e.ToString());
}
return str.ToString();
}
}
}

Related

AStar Shortest Path using C#

I have been trying to write a function to find the shortest path, implementing the AStar algorithm. I have gone through many solutions on net and on this forum. But my bad, I am having a tough time understanding where exactly I need to 'remove the node from the path' if the path did not hit the destination. Infact the adding and removing nodes as we go along a path and as we come back after reaching a dead-end in the recursion, seemed a bit challenging to understand. At the end of the recursion, if the path is not found, I am clearing the path and returning it, which I know is not the way to implement. However, I am sharing the code here. Could someone kindly help me understand what I am doing wrong?
Here is the Node class;
public class Node
{
public string Name { get; private set; }
public Coordinate Location { get; private set; }
public double g { get; set; }
public double h { get; set; }
public double cost { get { return this.g + this.h; } }
public List<Node> Neighbours { get; set; }
public Node(string name, Coordinate location)
{
Name = name;
Location = location;
Neighbours= new List<Node>();
}
public void AddNeighbours(List<Node> neighbours)
{
Neighbours.AddRange(neighbours);
}
public double distanceTo(Node node)
{
return Location.Distance(node.Location);
}
}
...and here is the Graph class.
public class Graph
{
List<Node> Nodes = new List<Node>();
public Graph(List<Node> nodes)
{
Nodes = nodes;
}
public List<Node> GetShortestPath(Node source, Node destination, HashSet<Node> visited = null, List<Node> path = null )
{
if ( visited == null ) { visited = new HashSet<Node>(); } // Initialize the visited nodes list
if(path == null ) { path = new List<Node>() {}; } // initialize the shortest path list
if (source == destination){ path.Add(destination); return path;}
visited.Add(source); // Currently visiting this node. So, add to the visited nodes
path.Add(source); // Add the current source to the path
foreach (Node neighbour in source.Neighbours) // for each neighbour node
{
// Update the g and h distances
neighbour.g = source.g + source.distanceTo(neighbour);
neighbour.h = neighbour.distanceTo(destination);
}
// Collect the non-visited neighbours
List<Node> nonVisitedNeighbours = source.Neighbours.Where(n => !visited.Contains(n)).ToList();
if (nonVisitedNeighbours.Count > 0) // if non-visited neighbours not empty
{
// sort the neighbours in ascending order and take the first one.
// that will be the closest neighbour with the lowest cost
Node nextNeighbour = nonVisitedNeighbours.OrderBy(n => n.cost).ToList().First();
return GetShortestPath(nextNeighbour, destination, visited, path);
}
path.Clear(); // I hope this is not the right way, but somewhere path.Remove(source) to be added. But not clear where...
Console.WriteLine("No path found!");
return path; // This should return an empty list
}
}
Ok... I fixed it. Here is the final implementation...
public List<Node> GetPath(
Node startNode,
Node targetNode
)
{
List<Node> openSet = new List<Node>();
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while (openSet.Count > 0)
{
Node currentNode = openSet[0];
List<Node> nodesWithLesserCost = openSet
.Skip(1)
.ToList()
.Where(node => node.Cost < currentNode.Cost || node.Cost == currentNode.Cost && node.H < currentNode.H)
.OrderBy(n => n.Cost)
.ToList();
if (nodesWithLesserCost.Any())
{
currentNode = nodesWithLesserCost.First();
}
openSet.Remove(currentNode);
closedSet.Add(currentNode);
if (currentNode == targetNode)
{
return RetracePath(startNode, targetNode);
}
foreach (Node neighbour in currentNode.Neighbours)
{
if (closedSet.Contains(neighbour))
{
continue;
}
double newMovementCostToNeighbour = currentNode.G + currentNode.DistanceTo(neighbour);
if(newMovementCostToNeighbour < neighbour.G || !openSet.Contains(neighbour))
{
neighbour.G = newMovementCostToNeighbour;
neighbour.H = neighbour.DistanceTo(targetNode);
neighbour.Parent = currentNode;
if (!openSet.Contains(neighbour))
{
openSet.Add(neighbour);
}
}
}
}
Console.WriteLine("No Path Found...!");
return new List<Node>();
}

Trying to add a node to a graph

I am implementing a Graph via adjacency list, and I am trying to add a node to a Graph class but I keep getting an error
System.NullReferenceException has been thrown
this is the bit of my code:
MyNode class
class node
{
public List<object>? Neighbors { get; set; }
public int Data { get; set; }
public node() { }
public node(int val)
{
Data = val;
}
}
This is my graph class:
class Graph
{
// Attributes of the Class
public int NumVertices { get; set; } // Number of vertices
public List<node> vertices { get; set; } // list of nodes in the graph
// Graph constructor
public Graph(int size)
{
NumVertices = size;
vertices = new List<node>();
//// Allocate node objects at each index of the vertice list
//for (int i = 0; i < NumVertices; i++)
//{
// Instantiate(vertices[i]);
//}
}
//Adding ege to vertice
public void addEdge(node a, node b)
{
a.Neighbors.Add(b); //---> it referenced here
b.Neighbors.Add(a);
}
Then testing functions: this Instantiates node objects and
public static void TestGraph()
{
node a = new node(1);
node b = new node(2);
node c = new node(3);
node d = new node(4);
node e = new node(5);
node f = new node(6);
Graph myGraph = new Graph(6) ;
//// Add vertices to the node
//myGraph.vertices.Add(a);
//myGraph.vertices.Add(b);
//myGraph.vertices.Add(c);
//myGraph.vertices.Add(d);
//myGraph.vertices.Add(e);
////myGraph.vertices.Add(f);
myGraph.addEdge(a, b); //---> Error originates here
myGraph.addEdge(a, c);
myGraph.addEdge(a, d);
myGraph.addEdge(b, c);
myGraph.addEdge(c, e);
myGraph.addEdge(d, e);
Console.WriteLine("Cheching if a and b are neighbors " + myGraph.isAdjacent(a, b).ToString());
Console.WriteLine("Cheching if a and c are neighbors " + myGraph.isAdjacent(a, c).ToString());
How would I be able to rectify this issues? Thanks
There is no problem with the node object but it's Neighbors field which is supposed to be a List but the constructor doesn't have any initialization for it.
If you change your constructors to initialize Neighbors, the null reference error will go away. Below is an example to how you can initialize the list.
public node(int val)
{
Data = val;
Neighbors = new List<object>();
}
You have never created a Neighbors list, so it will always be null.
I would formulate those lists as getter-only properties with an initializer. Then the lists get initialized automatically and you can't forget to do it later.
class Node
{
public List<Node> Neighbors { get; } = new();
public int Data { get; set; }
public Node() { }
public Node(int val)
{
Data = val;
}
}
Also, this must be a List<Node>, not a List<object>, since you will always add nodes to it. Alternatively, the Neighbors list could also be a List<int> storing the indices of the neighbors in the Graph.Vertices list instead of the nodes themselves.
Graph has a property public int NumVertices { get; set; }. Isn't NumVertices equivalent to Vertices.Count? Since you pass the graph size to the constructor, it makes sense to add this number of nodes the Vertices list in the constructor. NumVertices then automatically matches this size since it returns Vertices.Count.
class Graph
{
public int NumVertices => Vertices.Count;
public List<Node> Vertices { get; } = new();
public Graph(int size)
{
for (int i = 0; i < size; i++) {
Vertices.Add(new Node());
}
}
public void AddEdge(int i, int j)
{
Node a = Vertices[i];
Node b = Vertices[j];
a.Neighbors.Add(b);
b.Neighbors.Add(a);
}
public bool AreAdjacent(int i, int j)
{
return Vertices[i].Neighbors.Contains(Vertices[j]);
}
}
Since the nodes are stored in the Graph object, it makes sense to pass their index in the AddEdge and AreAdjacent methods instead of keeping another reference of them outsize of Graph object. This is better for consistency, as it is only possible to add edges between nodes that are actually in this graph.
Adding edges:
var graph = new Graph(6);
graph.AddEdge(0, 1);
graph.AddEdge(0, 2);
graph.AddEdge(0, 3);
graph.AddEdge(1, 2);
graph.AddEdge(2, 4);
graph.AddEdge(3, 4);
myGraph.addEdge(a, b); //---> Error originates here
So, what is the value of a? Your posted code leaves it uninitialized.

I'm struggling to get a foreach loop working to loop through each element of a LinkedList

Im trying to do a Depth first traversal on graph node but I cant get my for each loop to loop correctly
Here is the code for doing the search
The foreach line is the problem
public void DepthFirstTraverse(T startID, ref List<T> visited)
{
LinkedList<T> adj;
Stack<T> toVisit = new Stack<T>();
GraphNode<T> current = new GraphNode<T>(startID);
toVisit.Push(startID);
while (toVisit.Count != 0)
{
current.ID = toVisit.Peek();
toVisit.Pop();
visited.Add(current.ID);
Console.WriteLine(current.GetAdjList());
foreach(T n in current.GetAdjList())
{
Console.WriteLine("Loop working");
if(toVisit.Contains(n)==false && visited.Contains(n)==true)
{
toVisit.Push(n);
}
}
}
}
Here is the graphnode code, it calls the getadjlist and should loop through for each element in that list, currently it doesnt loop at all
public class GraphNode
{
private T id; // data stored in the node (“id” of the node).
private LinkedList adjList; // adjacent list of the node
// constructor
public GraphNode(T id)
{
this.id = id;
adjList = new LinkedList<T>();
}
// set and get the data stored in the node
public T ID
{
set { id = value; }
get { return id; }
}
//add a directed edge from “this” node to the node "to”
public void AddEdge(GraphNode<T> to)
{
adjList.AddFirst(to.ID);
}
// returns the adjacent list of the node
public LinkedList<T> GetAdjList()
{
return adjList;
}
}

C# How to pool the objects of a node tree efficiently?

I have a node class that contains only value type properties, and one reference type: it's parent node. When performing tree searches, these nodes are created and destroyed hundreds of thousands of times in a very short time span.
public class Node
{
public Node Parent { get; set; }
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
}
The tree search looks something like this:
public static Node GetDepthFirstBest(this ITree tree, Node root)
{
Node bestNode = root;
float bestScore = tree.Evaluate(root);
var stack = new Stack<Node>();
stack.Push(root);
while(stack.Count > 0)
{
var current = stack.Pop();
float score = tree.Evaluate(current);
if (score > bestScore)
{
bestNode = current;
bestScore = score;
}
var children = tree.GetChildren(current);
foreach(var c in children) { stack.Push(c); }
}
return bestNode;
}
Because this is done in a Mono runtime that has a very old GC, I wanted to try and pool the node objects. However, I am at a loss on how to know when a node object is safe to return to the pool, since other nodes that are still in use might reference it as a parent. At the end of the search, the best node is returned and a list of nodes is formed by walking back through its ancestors. I have full control over how the nodes are created inside the tree, if that's useful.
What options could I try and implement?
So, fortunately, if you're doing a Depth-First-Search, which you appear to be, this is a bit easier. Any time you reach a leaf node, there are two possibilities: that leaf node is part of the current deepest tree, or it's not.
If it's not, that means it's safe to return this node to the pool. If it is, that means we can return any nodes in our old tree back to our pool that are not in our own ancestor chain.
Now, if we're not a leafnode, we don't know if we can be freed until after we've finished checking our children. then, once all our children are checked, we find out if any of our children said they were the current best. if so, we keep ourselves
this does mean we're doing quite a bit more checking.
Here's some sudo code:
List bestNodes;
bool evalNode(node, score)
{
if (childCount == 0)
{
if (score > bestScore)
{
bestScore = score;
bestNode = node;
bestNodes.Add(node);
return true;
}
else
{
freeNode(this);
return false;
}
}
else
{
bool inLongest = false;
foreach (child in children)
{
inLongest = evalNode(child, score + 1) || inLongest;
}
if (!inLongest)
{
freeNode(node);
}
else
{
free(bestNodes[score]);
bestNodes[score] = node;
}
return inLongest;
}
}
Try using the ref keyword if your node is a struct, this avoids copying the node every time you pass it through to a function.
Thus:
struct Node
{
object obj;
Node children;
}
public void DoStuffWithNode(ref Node pNode){...Logic...}

Bug in parallel tree printing method

Classes:
public class Tree
{
public Node RootNode { get; set; }
}
public class Node
{
public int Key { get; set; }
public object Value { get; set; }
public Node ParentNode { get; set; }
public List<Node> Nodes { get; set; }
}
Methods:
This method generates a tree.
private static int totalNodes = 0;
static Tree GenerateTree()
{
Tree t = new Tree();
t.RootNode = new Node();
t.RootNode.Key = 0;
t.RootNode.Nodes = new List<Node>();
Console.WriteLine(t.RootNode.Key);
List<Node> rootNodes = new List<Node>();
rootNodes.Add(t.RootNode);
while (totalNodes <= 100000)
{
List<Node> newRootNodes = new List<Node>();
foreach (var rootNode in rootNodes)
{
for (int j = 0; j < 3; j++)
{
totalNodes++;
Console.Write(string.Format(" {0}({1}) ", totalNodes, rootNode.Key));
Node childNode = new Node() {Key = totalNodes, Nodes = new List<Node>(), ParentNode = t.RootNode};
rootNode.Nodes.Add(childNode);
newRootNodes.Add(childNode);
}
Console.Write(" ");
}
Console.WriteLine();
rootNodes = newRootNodes;
}
return t;
}
This method is supposed to print a tree, but node is null in some case:
static void PrintTreeParallel(Node rootNode)
{
List<Node> rootNodes = new List<Node>();
List<Node> newRootNodes = new List<Node>();
rootNodes.Add(rootNode);
Console.WriteLine(rootNode.Key);
while (rootNodes.Count > 0)
{
newRootNodes = new List<Node>();
Parallel.ForEach(rootNodes, node =>
{
if (node != null)
{
Console.Write(string.Format(" {0} ", node.Key));
if (node.Nodes != null)
Parallel.ForEach(node.Nodes,
newRoot => { newRootNodes.Add(newRoot); });
}
else
{
//HOW CAN WE GET HERE?????
Debugger.Break();
Console.WriteLine(rootNodes.Count);
}
});
Console.WriteLine();
rootNodes = newRootNodes;
}
}
Execute:
static void Main(string[] args)
{
var t = GenerateTree();
Console.WriteLine("Tree generated");
PrintTreeParallel(t.RootNode);
Console.WriteLine("Tree printed paral");
Console.ReadLine();
}
Question:
What's wrong here?
Why node is null in some case?
And it happens only when there are a lot of generated nodes. For ex if there would be only 10 nodes everything is OK.
The problem is that you have this code:
Parallel.ForEach(node.Nodes, newRoot => { newRootNodes.Add(newRoot); });
Which allows multiple threads to add items to the newRootNodes list concurrently. As a commenter pointed out, List<T> is not thread-safe. What's probably happening is that one thread's Add is being interrupted by another thread's call to Add, which causes an internal index in the list to be incremented. That leaves a null value in one of the list's items.
Then, later in the loop you have:
rootNodes = newRootNodes;
Which puts the corrupted list as the list that's going to be iterated by the while.
You have a data race here:
Parallel.ForEach(node.Nodes,
newRoot => { newRootNodes.Add(newRoot); });
Adding to a list with multiple threads is not thread-safe and will cause undetermined behavior.
First try to run this part with a simple foreach and see if the problem goes away. Running two nested Parallel.ForEach statements is definitely a bizarre choice.
List<T> is indeed not thread safe, so rootNode.Nodes.Add(childNode); is dropping data in unpredictable ways.
Instead of using List<> use ConcurrentBag<> and it will all work. Note that ConcurrentBag<T> is unordered, but that is fine because you have no way of predicting the order from the threads anyway.

Categories