Wednesday, March 30, 2022

Program for first order Logic

from logic import *

# Sentence: "If it's rains, it's wet".
def rainWet():
    Rain = Atom('Rain') # whether it's raining
    Wet = Atom('Wet') # whether it's wet
    return Implies(Rain, Wet)

# Sentence: "There is a light that shines."
def lightShines():
    def Light(x): return Atom('Light', x)    # whether x is lit
    def Shines(x): return Atom('Shines', x)  # whether x is shining
    return Exists('$x', And(Light('$x'), Shines('$x')))

# Defining Parent in terms of Child.
def parentChild():
    def Parent(x, y): return Atom('Parent', x, y)  # whether x has a parent y
    def Child(x, y): return Atom('Child', x, y)    # whether x has a child y 

return Forall('$x', Forall('$y', Equiv(Parent('$x', '$y'), Child('$y', '$x')))) 

Tuesday, March 29, 2022

Program on Game playing algorithms.

 

The Classic Tic-Tac-Toe Game in Python:


DashBoard:




Code in Python:

''' We will make the board using dictionary 
    in which keys will be the location(i.e : top-left,mid-right,etc.)
    and initialliy it's values will be empty space and then after every move 
    we will change the value according to player's choice of move. '''

theBoard = {'7': ' ' , '8': ' ' , '9': ' ' ,
            '4': ' ' , '5': ' ' , '6': ' ' ,
            '1': ' ' , '2': ' ' , '3': ' ' }

board_keys = []

for key in theBoard:
    board_keys.append(key)

''' We will have to print the updated board after every move in the game and 
    thus we will make a function in which we'll define the printBoard function
    so that we can easily print the board everytime by calling this function. '''

def printBoard(board):
    print(board['7'] + '|' + board['8'] + '|' + board['9'])
    print('-+-+-')
    print(board['4'] + '|' + board['5'] + '|' + board['6'])
    print('-+-+-')
    print(board['1'] + '|' + board['2'] + '|' + board['3'])

# Now we'll write the main function which has all the gameplay functionality.
def game():

    turn = 'X'
    count = 0


    for i in range(10):
        printBoard(theBoard)
        print("It's your turn," + turn + ".Move to which place?")

        move = input()        

        if theBoard[move] == ' ':
            theBoard[move] = turn
            count += 1
        else:
            print("That place is already filled.\nMove to which place?")
            continue

        # Now we will check if player X or O has won,for every move after 5 moves. 
        if count >= 5:
            if theBoard['7'] == theBoard['8'] == theBoard['9'] != ' ': # across the top
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")                
                break
            elif theBoard['4'] == theBoard['5'] == theBoard['6'] != ' ': # across the middle
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break
            elif theBoard['1'] == theBoard['2'] == theBoard['3'] != ' ': # across the bottom
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break
            elif theBoard['1'] == theBoard['4'] == theBoard['7'] != ' ': # down the left side
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break
            elif theBoard['2'] == theBoard['5'] == theBoard['8'] != ' ': # down the middle
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break
            elif theBoard['3'] == theBoard['6'] == theBoard['9'] != ' ': # down the right side
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break 
            elif theBoard['7'] == theBoard['5'] == theBoard['3'] != ' ': # diagonal
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break
            elif theBoard['1'] == theBoard['5'] == theBoard['9'] != ' ': # diagonal
                printBoard(theBoard)
                print("\nGame Over.\n")                
                print(" **** " +turn + " won. ****")
                break 

        # If neither X nor O wins and the board is full, we'll declare the result as 'tie'.
        if count == 9:
            print("\nGame Over.\n")                
            print("It's a Tie!!")

        # Now we have to change the player after every move.
        if turn =='X':
            turn = 'O'
        else:
            turn = 'X'        
    
    # Now we will ask if player wants to restart the game or not.
    restart = input("Do want to play Again?(y/n)")
    if restart == "y" or restart == "Y":  
        for key in board_keys:
            theBoard[key] = " "

        game()

if __name__ == "__main__":
    game()

Output:



Program on informed search method.

Implementation of A Star Search Algorithm in python – Artificial Intelligence

In this tutorial, we will understand the A Star Search Algorithm with a solved numerical example and implementation in python.

A Star Solved Numerical Example:





Numbers written on edges represent the distance between nodes. Numbers written on nodes represent the heuristic value.

Given the graph, find the cost-effective path from A to G. That is A is the source node and G is the goal node.

Now from A, we can go to point B or E, so we compute f(x) for each of them,

A → B = g(B) + h(B) = 2 + 6 = 8

A → E = g(E) + h(E) = 3 + 7 = 10

Since the cost for A → B is less, we move forward with this path and compute the f(x) for the children nodes of B.

Now from B, we can go to point C or G, so we compute f(x) for each of them,

A → B → C = (2 + 1) + 99= 102

A → B → G = (2 + 9 ) + 0 = 11

Here the path A → B → G has the least cost but it is still more than the cost of A → E, thus we explore this path further.

Now from E, we can go to point D, so we compute f(x),

A → E → D = (3 + 6) + 1 = 10

Comparing the cost of A → E → D with all the paths we got so far and as this cost is least of all we move forward with this path.

Now compute the f(x) for the children of D

A → E → D → G = (3 + 6 + 1) +0 = 10

Now comparing all the paths that lead us to the goal, we conclude that A → E → D → G is the most cost-effective path to get from A to G.

Implementation of A Star Search Algorithm in python:

def aStarAlgo(start_node, stop_node):
    open_set = set(start_node)
    closed_set = set()
    g = {}               #store distance from starting node
    parents = {}         # parents contains an adjacency map of all nodes
    #distance of starting node from itself is zero
    g[start_node] = 0
    #start_node is root node i.e it has no parent nodes
    #so start_node is set to its own parent node
    parents[start_node] = start_node
    while len(open_set) > 0:
        n = None
        #node with lowest f() is found
        for v in open_set:
            if n == None or g[v] + heuristic(v) < g[n] + heuristic(n):
                n = v
        if n == stop_node or Graph_nodes[n] == None:
            pass
        else:
            for (m, weight) in get_neighbors(n):
                #nodes 'm' not in first and last set are added to first
                #n is set its parent
                if m not in open_set and m not in closed_set:
                    open_set.add(m)
                    parents[m] = n
                    g[m] = g[n] + weight
                #for each node m,compare its distance from start i.e g(m) to the
                #from start through n node
                else:
                    if g[m] > g[n] + weight:
                        #update g(m)
                        g[m] = g[n] + weight
                        #change parent of m to n
                        parents[m] = n
                        #if m in closed set,remove and add to open
                        if m in closed_set:
                            closed_set.remove(m)
                            open_set.add(m)
        if n == None:
            print('Path does not exist!')
            return None
        
        # if the current node is the stop_node
        # then we begin reconstructin the path from it to the start_node
        if n == stop_node:
            path = []
            while parents[n] != n:
                path.append(n)
                n = parents[n]
            path.append(start_node)
            path.reverse()
            print('Path found: {}'.format(path))
            return path
        # remove n from the open_list, and add it to closed_list
        # because all of his neighbors were inspected
        open_set.remove(n)
        closed_set.add(n)
    print('Path does not exist!')
    return None

#define fuction to return neighbor and its distance
#from the passed node
def get_neighbors(v):
    if v in Graph_nodes:
        return Graph_nodes[v]
    else:
        return None
#A star example 1
#for simplicity we ll consider heuristic distances given
#and this function returns heuristic distance for all nodes
def heuristic(n):
    H_dist = {
        'A': 11,
        'B': 6,
        'C': 5,
        'D': 7,
        'E': 3,
        'F': 6,
        'G': 5,
        'H': 3,
        'I': 1,
        'J': 0
    }
    return H_dist[n]

#Describe your graph here
Graph_nodes = {
    'A': [('B', 6), ('F', 3)],
    'B': [('A', 6), ('C', 3), ('D', 2)],
    'C': [('B', 3), ('D', 1), ('E', 5)],
    'D': [('B', 2), ('C', 1), ('E', 8)],
    'E': [('C', 5), ('D', 8), ('I', 5), ('J', 5)],
    'F': [('A', 3), ('G', 1), ('H', 7)],
    'G': [('F', 1), ('I', 3)],
    'H': [('F', 7), ('I', 2)],
    'I': [('E', 5), ('G', 3), ('H', 2), ('J', 3)],
}

aStarAlgo('A', 'J')
#Output:
#Path found: ['A', 'F', 'G', 'I', 'J']

#for simplicity we ll consider heuristic distances given
#and this function returns heuristic distance for all nodes
def heuristic(n):
    H_dist = {
        'A': 11,
        'B': 6,
        'C': 99,
        'D': 1,
        'E': 7,
        'G': 0,
    }
    return H_dist[n]

#Describe your graph here
Graph_nodes = {
    'A': [('B', 2), ('E', 3)],
    'B': [('A', 2), ('C', 1), ('G', 9)],
    'C': [('B', 1)],
    'D': [('E', 6), ('G', 1)],
    'E': [('A', 3), ('D', 6)],
    'G': [('B', 9), ('D', 1)]
}

aStarAlgo('A', 'G')
Output:




Hill-Climbing in Python– Artificial Intelligence


In hill climbing the basic idea is to always head towards a state which is better than the current one.

So, if you are in town A and you can get to town B and town C (and your target is town D) then you should make a move IF town B or C appear nearer to town D than town A does.

Hill-CLimbing Search Algorithm

1. Evaluate the initial state. 
   If it is also goal state then return it, otherwise continue with the initial state as the current state.

2. Loop until the solution is found or until there are no new operators to be applied in the current state    
   
   a) Select an operator that has not yet been applied to the current state and apply it to produce new state 
  
   b) Evaluate the new state
      i)    If it is a goal state then return it and quit
      ii)   If it is not a goal state but it is better than the current state, then make it as current state
      iii)  If it is not better than the current state, then continue in loop.

Travelling salesman problem

First, let’s code an instantiation of the Travelling salesman problem. If you think about it, such an instantiation should be a list of cities, where each one has information about the distances from there to the other cities. Of course, the distance from each city to itself is zero, and the distance from city A to city B is the same as the distance from city B to city A. That gives us a list, containing n lists of size n (where in this case n equals 4):

tsp = [

[0, 400, 500, 300],

[400, 0, 300, 500],

[500, 300, 0, 400],

[300, 500, 400, 0]

]


Python Code:


import random def randomSolution(tsp): cities = list(range(len(tsp))) solution = [] for i in range(len(tsp)): randomCity = cities[random.randint(0, len(cities) - 1)] solution.append(randomCity) cities.remove(randomCity) return solution def routeLength(tsp, solution): routeLength = 0 for i in range(len(solution)): routeLength += tsp[solution[i - 1]][solution[i]] return routeLength def getNeighbours(solution): neighbours = [] for i in range(len(solution)): for j in range(i + 1, len(solution)): neighbour = solution.copy() neighbour[i] = solution[j] neighbour[j] = solution[i] neighbours.append(neighbour) return neighbours def getBestNeighbour(tsp, neighbours): bestRouteLength = routeLength(tsp, neighbours[0]) bestNeighbour = neighbours[0] for neighbour in neighbours: currentRouteLength = routeLength(tsp, neighbour) if currentRouteLength < bestRouteLength: bestRouteLength = currentRouteLength bestNeighbour = neighbour return bestNeighbour, bestRouteLength def hillClimbing(tsp): currentSolution = randomSolution(tsp) currentRouteLength = routeLength(tsp, currentSolution) neighbours = getNeighbours(currentSolution) bestNeighbour, bestNeighbourRouteLength = getBestNeighbour(tsp, neighbours) while bestNeighbourRouteLength < currentRouteLength: currentSolution = bestNeighbour currentRouteLength = bestNeighbourRouteLength neighbours = getNeighbours(currentSolution) bestNeighbour, bestNeighbourRouteLength = getBestNeighbour(tsp, neighbours) return currentSolution, currentRouteLength def main(): tsp = [ [0, 400, 500, 300], [400, 0, 300, 500], [500, 300, 0, 400], [300, 500, 400, 0] ] print(hillClimbing(tsp)) if __name__ == "__main__": main()

Output:



Thursday, March 17, 2022

Program on Uninformed Search methods.

 The algorithms of a uniformed AI do not consist of any additional data/ info regarding the goal node. It only contains the information provided during the definition of a problem. The plans required to reach from the start state to the goal state differ only on the basis of the length and order of the provided actions.

For example,  Depth-First Search and Breadth-First Search.

Features of Uninformed Search in AI:

  • It does not contain any additional data/ info.
  • The information is provided to the AI during the definition of a problem.
  • It can reach the goal state on the basis of the length and the order of actions performed.
  • The AI doesn’t utilize any knowledge to search for the solution to a problem.
  • A few examples of these include BFS (Breadth-First Search) and DFS (Depth-First Search).
  • This type of AI takes more time to generate a solution for any problem.
  • It is always complete.
  • The total cost incurred is generally more than that of Informed Search in AI.
  • It consumes a fairly moderate time to do the search.
  • The implementation is lengthy.
  • No suggestion is present for finding any solution.

 Depth-first traversal or Depth-first Search is an algorithm to look at all the vertices of a graph or tree data structure. Here we will study what depth-first search in python is, understand how it works with its bfs algorithm, implementation with python code, and the corresponding output to it. 

What is Depth First Search?

What do we do once have to solve a maze? We tend to take a route, keep going until we discover a dead end. When touching the dead end, we again come back and keep coming back till we see a path we didn't attempt before. Take that new route. Once more keep going until we discover a dead end. Take a come back again… This is exactly how Depth-First Search works.

The Depth-First Search is a recursive algorithm that uses the concept of backtracking. It involves thorough searches of all the nodes by going ahead if potential, else by backtracking. Here, the word backtrack means once you are moving forward and there are not any more nodes along the present path, you progress backward on an equivalent path to seek out nodes to traverse. All the nodes are progressing to be visited on the current path until all the unvisited nodes are traversed after which subsequent paths are going to be selected.

DFS Algorithm

Before learning the python code for Depth-First and its output, let us go through the algorithm it follows for the same. The recursive method of the Depth-First Search algorithm is implemented using stack. A standard Depth-First Search implementation puts every vertex of the graph into one in all 2 categories: 1) Visited 2) Not Visited. The only purpose of this algorithm is to visit all the vertex of the graph avoiding cycles.

The DSF algorithm follows as:

  1. We will start by putting any one of the graph's vertex on top of the stack.
  2. After that take the top item of the stack and add it to the visited list of the vertex.
  3. Next, create a list of that adjacent node of the vertex. Add the ones which aren't in the visited list of vertexes to the top of the stack.
  4. Lastly, keep repeating steps 2 and 3 until the stack is empty.

DFS pseudocode

The pseudocode for Depth-First Search in python goes as below: In the init() function, notice that we run the DFS function on every node because many times, a graph may contain two different disconnected part and therefore to make sure that we have visited every vertex, we can also run the DFS algorithm at every node.

DFS(G, u)

    u.visited = true

    for each v ∈ G.Adj[u]

        if v.visited == false

            DFS(G,v)   

init() {

    For each u ∈ G

        u.visited = false

     For each u ∈ G

       DFS(G, u)

}

DFS Implementation in Python (Source Code)

Now, knowing the algorithm to apply the Depth-First Search implementation in python, we will see how the source code of the program works.

Consider the following graph which is implemented in the code below:

example of dfs

graph = {
  '5' : ['3','7'],
  '3' : ['2', '4'],
  '7' : ['8'],
  '2' : [],
  '4' : ['8'],
  '8' : []
}

visited = set() # Set to keep track of visited nodes of graph.

def dfs(visited, graph, node):  #function for dfs 
    if node not in visited:
        print (node)
        visited.add(node)
        for neighbour in graph[node]:
            dfs(visited, graph, neighbour)


print("Following is the Depth-First Search")
dfs(visited, graph, '5')

 

In the above code, first, we will create the graph for which we will use the depth-first search. After creation, we will create a set for storing the value of the visited nodes to keep track of the visited nodes of the graph.

After the above process, we will declare a function with the parameters as visited nodes, the graph itself and the node respectively. And inside the function, we will check whether any node of the graph is visited or not using the “if” condition. If not, then we will print the node and add it to the visited set of nodes.

Then we will go to the neighboring node of the graph and again call the DFS function to use the neighbor parameter.

At last, we will run the driver code which prints the final result of DFS by calling the DFS the first time with the starting vertex of the graph.

Output

The output of the above code is as follow:

 Following is the Depth-First Search
5 3 2 4 8 7

Time Complexity

The time complexity of the Depth-First Search algorithm is represented within the sort of O(V + E), where V is that the number of nodes and E is that the number of edges.

The space complexity of the algorithm is O(V).

Applications

Depth-First Search Algorithm has a wide range of applications for practical purposes. Some of them are as discussed below:

  1. For finding the strongly connected components of the graph
  2. For finding the path
  3. To test if the graph is bipartite
  4. For detecting cycles in a graph
  5. Topological Sorting
  6. Solving the puzzle with only one solution.
  7. Network Analysis
  8. Mapping Routes
  9. Scheduling a problem

Conclusion

Hence, Depth-First Search is used to traverse the graph or tree. By understanding this article, you will be able to implement Depth-First Search in python for traversing connected components and find the path.

They are two of the most important topics that any new python programmer should definitely learn about. Here we will study what breadth-first search in python is, understand how it works with its algorithm, implementation with python code, and the corresponding output to it. Also, we will find out the application and uses of breadth-first search in the real world.

 

What is Breadth-First Search?

As discussed earlier, Breadth-First Search (BFS) is an algorithm used for traversing graphs or trees. Traversing means visiting each node of the graph. Breadth-First Search is a recursive algorithm to search all the vertices of a graph or a tree. BFS in python can be implemented by using data structures like a dictionary and lists. Breadth-First Search in tree and graph is almost the same. The only difference is that the graph may contain cycles, so we may traverse to the same node again.

 

BFS Algorithm

Before learning the python code for Breadth-First and its output, let us go through the algorithm it follows for the same. We can take the example of Rubik’s Cube for the instance. Rubik’s Cube is seen as searching for a path to convert it from a full mess of colors to a single color. So comparing the Rubik’s Cube to the graph, we can say that the possible state of the cube is corresponding to the nodes of the graph and the possible actions of the cube is corresponding to the edges of the graph.

As breadth-first search is the process of traversing each node of the graph, a standard BFS algorithm traverses each vertex of the graph into two parts: 1) Visited 2) Not Visited. So, the purpose of the algorithm is to visit all the vertex while avoiding cycles.

BFS starts from a node, then it checks all the nodes at distance one from the beginning node, then it checks all the nodes at distance two, and so on. So as to recollect the nodes to be visited, BFS uses a queue.

The steps of the algorithm work as follow:

  1.  Start by putting any one of the graph’s vertices at the back of the queue.
  2.  Now take the front item of the queue and add it to the visited list.
  3.  Create a list of that vertex's adjacent nodes. Add those which are not within the visited list to the rear of the queue.
  4.  Keep continuing steps two and three till the queue is empty.

Many times, a graph may contain two different disconnected parts and therefore to make sure that we have visited every vertex, we can also run the BFS algorithm at every node.

BFS pseudocode

The pseudocode for BFS in python goes as below:

create a queue Q 

mark v as visited and put v into Q 

while Q is non-empty 

    remove the head u of Q 

    mark and enqueue all (unvisited) neighbors of u

 

BFS implementation in Python (Source Code)

Now, we will see how the source code of the program for implementing breadth first search in python.

Consider the following graph which is implemented in the code below:

example of bfs

graph = {
  '5' : ['3','7'],
  '3' : ['2', '4'],
  '7' : ['8'],
  '2' : [],
  '4' : ['8'],
  '8' : []
}

visited = [] # List for visited nodes.
queue = []     #Initialize a queue

def bfs(visited, graph, node): #function for BFS
  visited.append(node)
  queue.append(node)

  while queue:          # Creating loop to visit each node
    m = queue.pop(0) 
    print (m, end = " ") 

    for neighbour in graph[m]:
      if neighbour not in visited:
        visited.append(neighbour)
        queue.append(neighbour)


print("Following is the Breadth-First Search")
bfs(visited, graph, '5')    # function calling

 

In the above code, first, we will create the graph for which we will use the breadth-first search. After creation, we will create two lists, one to store the visited node of the graph and another one for storing the nodes in the queue.

After the above process, we will declare a function with the parameters as visited nodes, the graph itself and the node respectively. And inside a function, we will keep appending the visited and queue lists.

Then we will run the while loop for the queue for visiting the nodes and then will remove the same node and print it as it is visited.

At last, we will run the for loop to check the not visited nodes and then append the same from the visited and queue list.

As the driver code, we will call the user to define the bfs function with the first node we wish to visit.

Output

The output of the above code will be as follow:

 Following is the Breadth-First Search
 5 3 7 2 4 8    

Time Complexity

The time complexity of the Breadth first Search algorithm is in the form of O(V+E), where V is the representation of the number of nodes and E is the number of edges.

Also, the space complexity of the BFS algorithm is O(V).

Applications

Breadth-first Search Algorithm has a wide range of applications in the real-world. Some of them are as discussed below:

  1.  In GPS navigation, it helps in finding the shortest path available from one point to another.
  2.  In pathfinding algorithms
  3.  Cycle detection in an undirected graph
  4.  In minimum spanning tree
  5.  To build index by search index
  6.  In Ford-Fulkerson algorithm to find maximum flow in a network.

Conclusion

If you have a good understanding of core python concepts and with the help of what you read today, you can now easily implement breadth first search in python. I hope this article clearly explained how this algorithm works.                                                                                                                        

Bidirectional search

Bidirectional search is a graph search algorithm that finds the shortest path from a source vertex to a goal vertex.

In implementing bidirectional search in Python, the graph search can either be:

  1. Forward search from the source to the goal vertex.
  2. Backward search from the goal to the source vertex.

The intersection of both forward and backward graphs indicates the end of the search, as displayed below.

Advantages and disadvantages of bidirectional search in Python

AdvantageDisadvantage
1. Less time consuming as the shortest path length is used for the search.The goal node has to be known before the search can be done simultaneously in both directions.

When is it possible to use bidirectional search in Python?

The bidirectional approach can be used:

  1. When the branching factor for the forward and reverse direction is the same.
  2. When both the source and goal vertices are specified and distinct from each other.

Bidirectional search implementation in Python

Below is an implementation of bidirectional search in Python using BFS:  

Python Code:

class adjacent_Node:

def __init__(self, v):

self.vertex = v

self.next = None



class bidirectional_Search:

def __init__(self, vertices):

self.vertices = vertices

self.graph = [None] * self.vertices

self.source_queue = list()

self.last_node_queue = list()

self.source_visited = [False] * self.vertices

self.last_node_visited = [False] * self.vertices

self.source_parent = [None] * self.vertices

self.last_node_parent = [None] * self.vertices

def AddEdge(self, source, last_node):

node = adjacent_Node(last_node)

node.next = self.graph[source]

self.graph[source] = node


node = adjacent_Node(source)

node.next = self.graph[last_node]

self.graph[last_node] = node

def breadth_fs(self, direction = 'forward'):

if direction == 'forward':

current = self.source_queue.pop(0)

connected_node = self.graph[current]

while connected_node:

vertex = connected_node.vertex

if not self.source_visited[vertex]:

self.source_queue.append(vertex)

self.source_visited[vertex] = True

self.source_parent[vertex] = current

connected_node = connected_node.next

else:

current = self.last_node_queue.pop(0)

connected_node = self.graph[current]

while connected_node:

vertex = connected_node.vertex

if not self.last_node_visited[vertex]:

self.last_node_queue.append(vertex)

self.last_node_visited[vertex] = True

self.last_node_parent[vertex] = current

connected_node = connected_node.next

def is_intersecting(self):

#

for i in range(self.vertices):

if (self.source_visited[i] and

self.last_node_visited[i]):

return i

return -1


def path_st(self, intersecting_node,

source, last_node):

path = list()

path.append(intersecting_node)

i = intersecting_node

while i != source:

path.append(self.source_parent[i])

i = self.source_parent[i]

path = path[::-1]

i = intersecting_node

while i != last_node:

path.append(self.last_node_parent[i])

i = self.last_node_parent[i]

print("*****Path*****")

path = list(map(str, path))

print(' '.join(path))

def bidirectional_search(self, source, last_node):

self.source_queue.append(source)

self.source_visited[source] = True

self.source_parent[source] = -1

self.last_node_queue.append(last_node)

self.last_node_visited[last_node] = True

self.last_node_parent[last_node] = -1


while self.source_queue and self.last_node_queue:

self.breadth_fs(direction = 'forward')

self.breadth_fs(direction = 'backward')

intersecting_node = self.is_intersecting()

if intersecting_node != -1:

print("Path exists between {} and {}".format(source, last_node))

print("Intersection at : {}".format(intersecting_node))

self.path_st(intersecting_node,

source, last_node)

exit(0)

return -1



if __name__ == '__main__':

n = 17

source = 1

last_node = 16

my_Graph = bidirectional_Search(n)

my_Graph.AddEdge(1, 4)

my_Graph.AddEdge(2, 4)

my_Graph.AddEdge(3, 6)

my_Graph.AddEdge(5, 6)

my_Graph.AddEdge(4, 8)

my_Graph.AddEdge(6, 8)

my_Graph.AddEdge(8, 9)

my_Graph.AddEdge(9, 10)

my_Graph.AddEdge(10, 11)

my_Graph.AddEdge(11, 13)

my_Graph.AddEdge(11, 14)

my_Graph.AddEdge(10, 12)

my_Graph.AddEdge(12, 15)

my_Graph.AddEdge(12, 16)

out = my_Graph.bidirectional_search(source, last_node)

if out == -1:

print("No path between {} and {}".format(source, last_node))

  

 Output:






Implementation of Bayes Belief Network

 Python Code: import pgmpy.models import pgmpy.inference import networkx as nx import pylab as plt model = pgmpy.models.BayesianModel([('...