In this post I will show how to use GeneticSharp and Blazor to solve the TSP (Travelling salesman problem).
According to Wikipedia The travelling salesman problem (TSP) asks the following question:“Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city?”
TSP is a classic sample to test some optimization techniques, as well it’s fairly used to demonstrate how to implement a genetic algorithm. For these reasons I will use it to show you how to implement a basic genetic algorithm in Blazor using GeneticSharp.
This post is a like a mirror of the TSP with GeneticSharp an Unity3D. It’s using the same format to teach TSP and GeneticSharp, but instead of Unity3D, this one is about Blazor.
Note that the performance presented on this demo is not the performance that GeneticSharp presents in other apps kinds, like a ASP .NET Core backend app, a console app or in a Unity 3D game. As WebAssembly do not support create a new thread, we get limited to use a Timer to made this sample interactive. More details about this in next sections of the post.
To better understand this tutorial, you need to have some experiences/knowledges in:
Genetic algorithms (beginner).
We will perform a very basic use of Blazor and everything you need to complete this tutorial will be explained or provided by the code samples, but if you want to find out better what’s happening under the hood, take a look on Blazor Get Started page.
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview6.19307.2
This will install the latest Blazor templates for .NET Core.
This tutorial is based on Blazor preview6. If you are doing this tutorial using a newer Blazor version and have encountered some problem, leave a comment at the end of the post or contact me on Twitter.
Now we’ll create a scaffold Blazor app using the blazor template:
dotnet new blazor -o TspWithGeneticSharp
dotnet watch run
Wait for the message Application started. Press Ctrl+C to shut down show up in terminal, then open the url http://localhost:5000 on your browser, you should see something like this:
I recommend to you use Visual Studio Code to open the project. There are some cool VS Code extensions to work with Blazor.
In the same terminal where you added the GeneticSharp package, type:
This will open the Blazor project with VS Code.
In the root folder of your Blazor project create a new subfolder called Tsp. We’ll add all our C# classes inside this folder.
Defining the TSP chromosome
The chromosome represents a solution of the problem we are trying to solve. In our case the TSP chromosome should represent “the shortest possible route that visits each city and returns to the origin city”.
To represent the cities route each gene of our chromosome will represent an index of a city in the route.
Create a file called TspChromosome.cs:
Representing a city
The next step is define our genetic algorithm fitness function, but first we need to create a simple class to represent a city on a 2D space.
Create a file called TspCity.cs:
The fitness function
Now we need to evaluate the TspChromosome.
Our fitness function will evaluate the chromosome fitness based on the total distance to reach all cities in the route represented by the chromosome. The shorter the distance, the better the chromosome.
Create a file called TspFitness.cs:
Configuring the Genetic Algorithm
In this step we need to configure our genetic algorithm using the TspChromosome, TspFitness and some classic GA operators already built in GeneticSharp.
Create a file called TspGA.cs:
Why use Timer?
GeneticSharp can be used as single threading or multithreading to evaluate chromosomes with the fitness function, but WebAssembly (and Blazor) can use just the UI thread, in this scenario when we call GeneticAlgorithm.Start method it freezes the UI until the GA finish.
To avoid this behavior, the solution is: run each generation of the GA inside a step in a System.Threading.Timer as you can see in the TspGA.Run method.
Inside the folder Pages create a file called Tsp.razor:
Open the file index.html and add the tag below inside the tag head:
Why use IJSRuntime do access DOM?
It’s awesome we can now use C# in the browser with Blazor. But unfortunately we can’t do everything with it, yet. Currently, WebAssembly isn’t able to directly access the DOM API, which means that Blazor isn’t able to either.
Check your terminal window where the command dotnet watch run is running, if there is no error in that window you can access the url http://localhost:5000/tsp.
Hit the Run button and take a look on the browser console window, you will see the distance to reach all cities getting smaller as the generations ran.
This is not a tutorial about Blazor good pratices, so everything here is done in the simplest possible way to introduce how to use GenticSharp with Blazor. I do not talk about things you should use when working with Blazor, such as separate logic from UI and use Blazor components.
Drawing the cities
Now our GA is running inside the browser, but it needs to display the cities route better.
We need to create a visual representation to the cities.
In the Tsp.razor add the method DrawCitiesAsync:
Then call it from OnAfterRenderAsync method, after the clearCanvas call:
In the previous step we drawn the cities and we have the visual of the problem: the cities.
Now we need to draw the solution: the route represented by the best chromosome of each generation.
Add the following method to the Tsp.razor:
Then call it from OnAfterRenderAsync method, after the DrawCitiesAsync call:
Reload the url http://localhost:5000/tsp again, and hit the Run button, now you should see the route been optimizing as the generations are ran:
With only 4 C# classes, 1 JS file and 1 Blazor page we built a pretty nice sample of genetic algorithms using Blazor with GeneticSharp. Now you can improve it with your own ideas or use some of mine ;):
Maybe let user change the genetic algorithm operators (crossover, mutation, selection, etc)?
Move the DrawCitiesAsync and DrawRouteAsync to Blazor components responsible to only draw them?