0% found this document useful (0 votes)
7 views

Lab2

Uploaded by

Alex Siryani
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

Lab2

Uploaded by

Alex Siryani
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

🤖

Lab2:Graphs
Graph Data Structure
Definition
A graph is a non-linear data structure consisting of nodes (vertices) and
edges.

Applications: Representing networks like social networks, transport routes,


etc.

Types:

1. Undirected Graph: Edges have no direction.

2. Directed Graph (Digraph): Edges have a direction.

Graph Representation

1. Adjacency Matrix
A 2D array where adj[i][j] = 1 indicates an edge exists between nodes i and
j.

import numpy as np

Lab2:Graphs 1
# [A, B, C, D, E, F]
graph = np.array([[0,1,0,1,1,0],
[1,0,1,0,0,0],
[0,1,0,0,1,1],
[1,0,0,0,1,0],
[1,0,1,1,0,1],
[0,0,1,0,1,0]])

print (" A B C D E F")


l = 0
for r in graph:
print (chr(65+l)+ " ", end='')
line = ""
for c in r:
line= line + str(c) + " "
print (line)
l+=1

2. Adjacency List
A dictionary where each key is a node, and its value is a list of adjacent nodes.

# Adjacency List
graph = { 'A': ['B','D','E'],
'B': ['A','C'],
'C': ['B','E','F'],
'D': ['A','E'],
'E': ['A','C','D','F'],
'F': ['C','E']
}

NetworkX Package

Lab2:Graphs 2
import networkx as nx

# Define an object from the Graph class


graph = nx.Graph()

graph.add_node("A")
graph.add_node("B")
graph.add_node("C")
graph.add_node("D")
graph.add_node("E")
graph.add_node("F")

#{ 'A': ['B','D','E'],
# 'B': ['A','C'],
# 'C': ['B','E','F'],
# 'D': ['A','E'],
# 'E': ['A','C','D','F'],
# 'F': ['C','E']
# }

graph.add_edge("A","B")
graph.add_edge("A","D")
graph.add_edge("A","E")
graph.add_edge("B","A")
graph.add_edge("B","C")
graph.add_edge("C","B")
graph.add_edge("C","E")
graph.add_edge("C","F")
graph.add_edge("D","A")
graph.add_edge("D","E")
graph.add_edge("E","A")
graph.add_edge("E","C")
graph.add_edge("E","D")

Lab2:Graphs 3
graph.add_edge("E","F")
graph.add_edge("F","C")
graph.add_edge("F","E")

print("Number of nodes: ", graph.number_of_nodes())


print("Number of edges: ", graph.number_of_edges())
print("Nodes: ", list(graph.nodes))
print("Edges: ", list(graph.edges))
print("Neighbors of node A: ", list(graph.adj["A"]))
print("Number of edges connented to node A (degree): ", graph.de

Output:
Number of nodes: 6
Number of edges: 8
Nodes: ['A', 'B', 'C', 'D', 'E', 'F']
Edges: [('A', 'B'), ('A', 'D'), ('A', 'E'), ('B', 'C'), ('C',
Neighbors of node A: ['B', 'D', 'E']
Number of edges connented to node A (degree): 3

graph.remove_node("C")
print("Nodes: ", list(graph.nodes))

Output:
Nodes: ['A', 'B', 'D', 'E', 'F']

print("Edges: ", list(graph.edges))

Lab2:Graphs 4
Output:
Edges: [('A', 'B'), ('A', 'D'), ('A', 'E'), ('D', 'E'), ('E',

graph.remove_edge("A","E")
print("Neighbors of node A: ", list(graph.adj["A"]))

Output:
Neighbors of node A: ['B', 'D']

# Define a weighted directed graph

digraph = nx.DiGraph()

digraph.add_edge("A", "D", weight=60)


digraph.add_edge("A", "C", weight=12)
digraph.add_edge("B", "A", weight=10)
digraph.add_edge("C", "B", weight=20)
digraph.add_edge("C", "D", weight=32)
digraph.add_edge("E", "A", weight=7)

print("Number of nodes: ", digraph.number_of_nodes())


print("Number of edges: ", digraph.number_of_edges())
print("Nodes: ", list(digraph.nodes))
print("Edges: ", list(digraph.edges))
print("Neighbors of node A: ", list(digraph.adj["A"]))
print("Number of edges connented to node A (degree): ", digraph

Output:
Number of nodes: 5

Lab2:Graphs 5
Number of edges: 6
Nodes: ['A', 'D', 'C', 'B', 'E']
Edges: [('A', 'D'), ('A', 'C'), ('C', 'B'), ('C', 'D'), ('B',
Neighbors of node A: ['D', 'C']
Number of edges connented to node A (degree): 4

Tree Data Structure


Definition
A hierarchical structure with a root node and child nodes.

Applications: File systems, organizational charts, etc.

Components:

Root: Topmost node.

Leaf: Node without children.

!pip install treelib==1.6.1


from treelib import Node, Tree

tree = Tree()
tree.create_node("A", "a")
tree.create_node("B", "b", parent="a")
tree.create_node("C", "c", parent="a")
tree.create_node("D", "d", parent="b")
tree.create_node("E", "e", parent="b")
tree.create_node("H", "h", parent="d")
tree.create_node("I", "i", parent="d")
tree.create_node("F", "f", parent="c")
tree.create_node("G", "g", parent="c")

Lab2:Graphs 6
print(tree)

Output:

print("Tree depth: ", tree.depth())


print("Number of nodes: ", tree.size())
print("Tree Root: ", tree.root)
print("Nodes: ", tree.all_nodes())
node = tree.get_node("d")
print("Depth of node D: ", tree.depth(node))
print("Parent of node D: ", tree.parent('d'))
print("Leaves of node D: ", tree.leaves('d'))
print("Children of node D: ", tree.is_branch('d'))
print("Siblings of node H: ", tree.siblings('h'))

Output:
Tree depth: 3
Number of nodes: 9
Tree Root: a
Nodes: [Node(tag=A, identifier=a, data=None), Node(tag=B, ident
Depth of node D: 2
Parent of node D: Node(tag=B, identifier=b, data=None)
Leaves of node D: [Node(tag=H, identifier=h, data=None), Node(t

Lab2:Graphs 7
Children of node D: ['h', 'i']
Siblings of node H: [Node(tag=I, identifier=i, data=None)]

# Show subtree
sub_t = tree.subtree('b')
sub_t.show()

Output:
b'B\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 D\n\xe2\x94\x82 \xe2

# Remove a node
tree.remove_node("b")
tree.show()

# Move a node
tree.move_node('f', 'h')
tree.show()

Lab Solution:
Network Topology and Graph Representation

Exercise 1: Graph Representation of a Computer Network

Problem Description
We need to represent a computer network with 7 devices as a graph. The devices
and their connections with bandwidths are as follows:

Devices: Router, Switch1, Switch2, Server1, Server2, PC1, PC2.

Connections:

Lab2:Graphs 8
Router ↔ Switch1: 100 Mbps

Router ↔ Switch2: 50 Mbps

Switch1 ↔ Server1: 200 Mbps

Switch1 ↔ PC1: 150 Mbps

Switch2 ↔ Server2: 300 Mbps

Switch2 ↔ PC2: 250 Mbps

Server1 ↔ Server2: 400 Mbps

PC1 ↔ PC2: 100 Mbps

Task 1: Draw an Undirected Graph


Represent each device as a node.

Represent each connection as an edge labeled with its bandwidth.

Task 2: Code to Represent the Graph Using an Adjacency Matrix

import numpy as np

# Adjacency matrix
graph = np.array([
[0, 100, 50, 0, 0, 0, 0], # Router connections
[100, 0, 0, 200, 0, 150, 0], # Switch1 connections
[50, 0, 0, 0, 300, 0, 250], # Switch2 connections
[0, 200, 0, 0, 400, 0, 0], # Server1 connections
[0, 0, 300, 400, 0, 0, 0], # Server2 connections
[0, 150, 0, 0, 0, 0, 100], # PC1 connections
[0, 0, 250, 0, 0, 100, 0], # PC2 connections
])

# Device names
devices = ['Router', 'Switch1', 'Switch2', 'Server1', 'Server2',

Lab2:Graphs 9
# Print header row with formatted spacing
header = f"{'':<10}" + ''.join(f"{device:<10}" for device in dev
print(header)
print('-' * len(header)) # Separator line for clarity

# Print each row with device name and matrix values, formatted f
for i, row in enumerate(graph):
line = f"{devices[i]:<10}" + ''.join(f"{value:<10}" for valu
print(line)

Exercise 2: ComputerNetwork Class

Task 1: Define the Class


Define a class called ComputerNetwork with the following:

An attribute deviceNames (list of device names in order).

A constructor to initialize networkMap (2D array representing latencies).

A function fillDeviceNames() to read unique device names from the user.

Task 2: Write Pseudocode for fillDeviceNames()


Write the pseudocode for the fillDeviceNames() function.

Ensure the user cannot enter duplicate device names.

Task 3: Implement the Class Functions


Write the following functions in the class:

connectionExists() : Check if there’s a direct connection between two


devices.

updateLatency() : Update the latency between two connected devices.

Task 4: Test the Class


Create an object network1 using the adjacency matrix from Exercise 1.

Call the fillDeviceNames() function and print the network map.

Lab2:Graphs 10
Modify the latency between "Router" and "Switch1" to 15 ms.

Modify the latency between "Router" and "Server1" to 50 ms.

Print the updated network map.

class ComputerNetwork:
def __init__(self, network_map):
self.networkMap = network_map # 2D array representing l
self.deviceNames = [] # List of device names

def fillDeviceNames(self):
print("Enter device names (no duplicates):")
for i in range(len(self.networkMap)):
while True:
device = input(f"Enter device name for row {i +
if device not in self.deviceNames:
self.deviceNames.append(device)
break
else:
print("Device name already exists. Try again

def connectionExists(self, a, b):


if a not in self.deviceNames or b not in self.deviceName
return False
index1 = self.deviceNames.index(a)
index2 = self.deviceNames.index(b)
return self.networkMap[index1][index2] > 0

def updateLatency(self, a, b, c):


if not self.connectionExists(a, b):
print(f"No direct connection exists between {a} and
else:
index1 = self.deviceNames.index(a)
index2 = self.deviceNames.index(b)
self.networkMap[index1][index2] = c
self.networkMap[index2][index1] = c # Symmetric upd

Lab2:Graphs 11
print(f"Latency between {a} and {b} updated to {c} m

def printNetworkMap(self):
print("\nNetwork Map:")
header = f"{'':<10}" + ''.join(f"{name:<10}" for name in
print(header)
print('-' * len(header))
for i, row in enumerate(self.networkMap):
line = f"{self.deviceNames[i]:<10}" + ''.join(f"{val
print(line)

# Using the class


network_map = [
[0, 100, 50, 0, 0, 0, 0],
[100, 0, 0, 200, 0, 150, 0],
[50, 0, 0, 0, 300, 0, 250],
[0, 200, 0, 0, 400, 0, 0],
[0, 0, 300, 400, 0, 0, 0],
[0, 150, 0, 0, 0, 0, 100],
[0, 0, 250, 0, 0, 100, 0],
]

# Define the network


network1 = ComputerNetwork(network_map)

# Call the functions


network1.fillDeviceNames()
network1.printNetworkMap()

# Update latencies
network1.updateLatency("Router", "Switch1", 15)
network1.updateLatency("Router", "Server1", 50)

Lab2:Graphs 12
# Print updated network map
network1.printNetworkMap()

FUNCTION fillDeviceNames():
DISPLAY "Enter device names, ensuring no duplicates:"
FOR each row in networkMap:
WHILE True:
PROMPT "Enter device name for this row:"
IF device name is not already in deviceNames:
ADD device name to deviceNames
BREAK
ELSE:
DISPLAY "Device name already exists. Try again."

Exercise 3: Tree Representation of Network Topology

Task 1: Draw the Network Tree


Draw a tree representing the hierarchical network topology:

Core Router (Router C)

Distribution Routers (Router D1 and Router D2)

Departmental Switches (Switch S1, Switch S2, Switch S3)

Workstation (W1 under Switch S2)

Use the treelib package to represent the network topology as a tree.

Print the tree structure in a readable format

!pip install treelib==1.6.1


from treelib import Node, Tree
tree = Tree()
tree.create_node("Router C", "C")
tree.create_node("Router D1", "D1", parent="C")
tree.create_node("Router D2", "D2", parent="C")
tree.create_node("Switch1", "S1", parent="D2")

Lab2:Graphs 13
tree.create_node("Switch2", "S2", parent="D2")
tree.create_node("Switch3", "S3", parent="D2")
tree.create_node("Workstation", "W1", parent="S1")

print(tree)

Lab2:Graphs 14

You might also like