<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://www.mindfiretechnology.com/blog/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Mindfire Technology</title>
    <link>https://www.mindfiretechnology.com/blog/</link>
    <description>Welcome to our blog, where we share technical and business knowledge based on real life experiences.</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <item>
      <guid isPermaLink="false">2437</guid>
      <link>https://www.mindfiretechnology.com/blog/archive/a-short-explanation-of-hierarchal-navigable-small-worlds-hnsw-index-for-pgvector/</link>
      <category>System.String[]</category>
      <title>A Short Explanation of Hierarchal Navigable Small Worlds (HNSW) Index for pgvector</title>
      <description>&lt;p&gt;In our last two posts (&lt;a href="https://www.mindfiretechnology.com/blog/archive/installing-pgvector-in-preparation-for-retrieval-augmented-generation/"&gt;here&lt;/a&gt; and &lt;a href="https://www.mindfiretechnology.com/blog/archive/installing-postgresql-in-preparation-for-retrieval-augmented-generation/"&gt;here&lt;/a&gt;) we first &lt;a href="https://www.mindfiretechnology.com/blog/archive/installing-postgresql-in-preparation-for-retrieval-augmented-generation/"&gt;installed PostgreSQL&lt;/a&gt; and then &lt;a href="https://www.mindfiretechnology.com/blog/archive/installing-pgvector-in-preparation-for-retrieval-augmented-generation/"&gt;installed the pgvector extension&lt;/a&gt; to add functionality to PostgreSQL to allow us to use PostgreSQL as our document store for use with Retrieval Augmented Generation (RAG) for use with Large Language Models (LLMs).&lt;/p&gt;
&lt;p&gt;Before we continue on to use pgvector and PostgreSQL for RAG (&lt;a href="https://www.mindfiretechnology.com/blog/archive/haystack-google-and-gemma-a-tutorial/"&gt;similar to how we did in this post&lt;/a&gt;) we should first briefly explain how pgvector handles similarity comparisons. Moreover, how does it differ from our simple cosine similarity that we previously used as the basis for a semantic search (&lt;a href="https://www.mindfiretechnology.com/blog/archive/semantic-search-and-cosine-similarity/"&gt;back in this post&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Recall that a cosine similarity (&lt;a href="https://www.mindfiretechnology.com/blog/archive/cosine-similarity/"&gt;as explained in this post&lt;/a&gt;) requires us to first embed every single passage then embed our query. Then we do a comparison of the embedded query to every single embedded passage. The result is a slow process. It can take minutes to do each query. This isn’t very useful for real life cases where we need a quick response to avoid losing our user’s interest. So how can we speed up the process?&lt;/p&gt;
&lt;p&gt;One very clever answer is to use an algorithm called the Hierarchal Navigable Small World (HNSW) algorithm. The HNSW algorithm was first published in a 2016 paper called “Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs” by Malkov and Yashunin (&lt;a href="https://arxiv.org/abs/1603.09320"&gt;found here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This short post isn’t going to go into all the details of how the HNSW algorithm works in detail, but I’m going to give a high level ‘gist’ of what it does and why it significantly speeds up cosine similarity comparisons. I’ll also include some great links to posts that explain it in more detail.&lt;/p&gt;
&lt;p&gt;Consider the way we did our &lt;a href="https://www.mindfiretechnology.com/blog/archive/semantic-search-and-cosine-similarity/"&gt;semantic search back in this post&lt;/a&gt;. We loaded a copy of the Federalist Papers, split it up into chunks of text, and then embedded each chuck. When we did a query we had to compare to every single chuck of text using a straight linear search, i.e. starting with the first one and trying out every single one until we get to the end of the list. So, if we have 10,000 chunks of text to search through we must perform 10,000 cosine similarity calculations.&lt;/p&gt;
&lt;p&gt;How might we utilize Artificial Intelligence algorithms to speed this search up? &lt;/p&gt;
&lt;p&gt;To visualize this, imagine all the chunks of text existing on a graph. Except this graph is likely a 768-dimensional graph instead of a 2-D graph. But since we can’t imagine what 768 dimensions looks like, we’ll just imagine this in 2 dimensions instead, though the concept is identical.&lt;/p&gt;
&lt;p&gt;Suppose the embedding for our query sits somewhere within this graph next to chunks of text. What we want is the chunk (or chunks) of text closest to the query.&lt;/p&gt;
&lt;p&gt;There is an Artificial Intelligence (AI) algorithm that works something like that called the &lt;a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm"&gt;k-nearest neighbor (KNN) algorithm&lt;/a&gt;. In this example (taken from Wikipedia) the green circle could be thought of as our query and blue boxes and red triangles represent the chunks of text we’re comparing to.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.mindfiretechnology.com/blog/media/hnsw_picture1.png" alt="A diagram showcasing a green filled in circle at the center, two red filled in triangles orbiting it, a blue filled in square orbiting it, and then a solid circle encircling all the beforementioned. Then, you have two more filled in blue squares orbiting that, then, a dotted line encircling all the beforementioned. Then, you have three blue filled in squares orbiting all that, and three red filled in triangles orbiting all that as well." /&gt;&lt;/p&gt;
&lt;p&gt;So, in this example, we’d probably want to return either the top 3 closest chunks of text (in the solid circle) or the top 5 (in the dotted circle).&lt;/p&gt;
&lt;p&gt;A lot of research has gone into how to speed up a KNN search. HNSW utilizes some of these clever speed ups to KNN so that it can quickly find the best documents without having to do a comparison with every single chunk of text.&lt;/p&gt;
&lt;p&gt;Imagine that the chunks of text are connected to each other in a graph that likes something like this (taken from the 2016 HNSW paper):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.mindfiretechnology.com/blog/media/hnsw_picture2.png" alt="A diagram showing various light blue circles connected to each other with lines, some are connected to more than one. There is also a green circle in which several of the blue circles also are connected to. One of the light blue circles has a red arrow pointed at it, and it then has a second red arrow pointing from it to the green circle (despite not being connected to the green circle by a line)." /&gt;&lt;/p&gt;
&lt;p&gt;You enter the graph where the red dotted line enters and then you traverse the graph until you find a node (i.e. the chunk of text) that has no connected nodes that are closer to your query. Because you don’t have to search every single node the process is quite a bit faster. The downside is that you may not find the closest match this way, but you should find something pretty close.&lt;/p&gt;
&lt;p&gt;However, even with this speed up, you will quickly overwhelm the algorithm as the number of documents grows and the process will become intractable again.&lt;/p&gt;
&lt;p&gt;So, imagine that you have multiple layers of graphs at different levels of abstraction. Something like this (taken from the 2016 paper)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.mindfiretechnology.com/blog/media/hnsw_picture3.png" alt="A diagram with three levels, labled Layer=2, Layer=1, and Layer=0. On Layer=0, the top layer, there is a red dot and a light blue dot, a red arrow points from the red dot to the light blue dot. On Layer=1, which is the middle layer, there are four light blue dots arranged in a parallelogram, three of them are connected to each other forming the shape of a triangle, and a fourth is connected to just one corner dot, but has a red arrow pointing from it to another light blue dot, but not the one it is connected to. A red arrow descends from Layer=2 from the light blue dot to the aforementioned dot that is only connected once. A black dotted line descends from Layer=2's red dot to a light blue dot direclty under it which is connected to the previous two light blue dots, but is not connected in any way to the singular dot that is connected only once. On Layer=0, the bottom layer, you see a rendition of the previous diagram with the many light blue dots and single green dot. All four of the dots found on Layer=1 have lines dropping to connect to the dots in Layer=0." /&gt;&lt;/p&gt;
&lt;p&gt;You’d be able to search the top layer first (which is a very sparse graph, so there aren’t many nodes to search!), find the closest node, then descend a level to find a closer node, descend again, etc. The result would be a much faster search even if the number of documents got quite large.&lt;/p&gt;
&lt;p&gt;This is in essence what the HNSW algorithm does. It creates layers of such graphs and keeps them properly connected to allow for a fast search. The downside is that the HNSW algorithm isn’t guaranteed to return the absolute closest matches.&lt;/p&gt;
&lt;p&gt;pgvector creates a new kind of field (a vector) with a new kind of index that allows you to do a cosine similarity search using an HNSW index.&lt;/p&gt;
&lt;p&gt;For now, that’s really all you need to know to understand how we’re going to utilize PostgreSQL and pgvector to do a faster cosine similarity search.&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An excellent post explaining HNSW in detail: &lt;a href="https://www.datastax.com/guides/hierarchical-navigable-small-worlds"&gt;https://www.datastax.com/guides/hierarchical-navigable-small-worlds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An even more detailed explanation: &lt;a href="https://towardsdatascience.com/similarity-search-part-4-hierarchical-navigable-small-world-hnsw-2aad4fe87d37"&gt;https://towardsdatascience.com/similarity-search-part-4-hierarchical-navigable-small-world-hnsw-2aad4fe87d37&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Interesting post about HNSW and pgvector: &lt;a href="https://www.crunchydata.com/blog/hnsw-indexes-with-postgres-and-pgvector"&gt;https://www.crunchydata.com/blog/hnsw-indexes-with-postgres-and-pgvector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The pgvector repo: &lt;a href="https://github.com/pgvector/pgvector"&gt;https://github.com/pgvector/pgvector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Great post on HNSW and pgvector: &lt;a href="https://neon.tech/blog/understanding-vector-search-and-hnsw-index-with-pgvector"&gt;https://neon.tech/blog/understanding-vector-search-and-hnsw-index-with-pgvector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      <pubDate>Tue, 18 Jun 2024 09:00:00 -0600</pubDate>
      <a10:updated>2024-06-18T09:00:00-06:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2305</guid>
      <link>https://www.mindfiretechnology.com/blog/archive/native-isnt-really-better/</link>
      <category>System.String[]</category>
      <title>Native Isn't Really Better</title>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Old Rivalries&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mobile Application development is one of the most popular software specializations in the world, with an estimated 224,000 developers located in the United States alone. Nearly every major app or program has some mobile variant. You could probably think of a dozen mobile apps that you use regularly that are also present on your computer or tablet. Moreover, these apps are usually not exclusive to one type of mobile device, whether that means tablets and smartphones, or iOS and Android.&lt;/p&gt;
&lt;p&gt;For those of us who are not overly familiar with the software development process for mobile apps, the lines between iOS and Android or even Windows and MacOS may seem blurred or unimportant. Yet, as those who have worked in the industry for many years know very well, the differences between these operating systems are drastic when it comes to software development and can completely reshape the way an engineer must design their code.&lt;/p&gt;
&lt;p&gt;Operating systems, such as Android and iOS, run on entirely different languages known as native languages. This is similar to different countries with different cultures and languages, each with entirely different ways of accomplishing more-or-less the same tasks or end-goals. These coding languages can also be the defining aspect of a software engineer and their career, especially if they choose to specialize in a particular language rather than spread their talent across multiple languages.&lt;/p&gt;
&lt;p&gt;Because of this, there has grown a divide between software engineers, each deciding for themselves which languages and tools are best to accomplish different tasks, or which languages and tools they are more comfortable using. For the last couple decades, since the rivalry between Microsoft and Apple began with their unique and yet similarly popular operating systems, the argument has mostly been between which native languages or tools to use, that of Microsoft’s .NET with C#, Google’s Java and Kotlin, or Apple’s Objective-C (or more recently, Swift). Moreover, there is also the argument of which preferred IDE (integrated development environment) to use, that of either Microsoft’s Visual Studio or Apple’s Xcode.&lt;/p&gt;
&lt;p&gt;However, in the last decade another debate has enflamed: cross-platform tools, which build a bridge between different operating systems. The debate being less about which native language is best, and more about whether to go native and write unique code for each mobile operating system , or to use cross-platform tools which apply the written code to both iOS and Android simultaneously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The Future: Cross-platform Frameworks&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The idea is relatively simple in concept: a framework or tool that can translate code written from a single language to various other languages or platforms.&lt;/p&gt;
&lt;p&gt;The upsides to something like this are easily understood, as now a software engineer who is specialized in only one type of language can now develop applications for multiple operating systems without having to learn an entirely new language. In opposition, however, there used to be many valid arguments against cross-platform such as issues with compatibility, latency, and build size- yet, in recent years that is no longer the case. In fact, it’s gotten to the point that cross-platform frameworks have started to make developing across multiple operating systems individually look downright wasteful and short sighted.&lt;/p&gt;
&lt;p&gt;“.NET, that is Xamarin and MAUI, now compile down to pure native code, and they’ve been optimized to the point that once you’ve compiled your app and built it for the final target platform of Android or iOS, it’s going to run as fast- and sometimes faster- than native code would.” Says Tony Pitman, a software engineer with over 40 years of experience, who has watched this progression closely and has become an expert in the use of Xamarin and .NET MAUI. “Because, when you write native code, you’re the one doing all the work figuring out how to optimize it, and the .NET team has done a great job optimizing their code to produce extremely performant native code on Android and iOS.”&lt;/p&gt;
&lt;p&gt;It would seem, then, that all the old arguments against cross-platform frameworks may no longer be relevant. It might, in rare cases, be more efficient for software developers to still use native code to write their apps, but only if they intend to release it for just the one OS and if their engineer is already specialized or trained in that native language. Otherwise, if you need to run your code/app on multiple operating systems, it may genuinely be a waste of money and time to write native code for each individual use case when the tools used for cross-platform development are near perfect replacements.&lt;/p&gt;
&lt;p&gt;Consider the amount of time and resources wasted, all being spent on developing applications for different devices and operating systems. Not only are you doubling the time it takes to write the code, as you have to write it entirely from scratch per native language, but you are also, most likely, are paying an entirely different team of software developers trained on that particular language.&lt;/p&gt;
&lt;p&gt;To make it even more difficult to justify, having code written in two different languages means you’ll end up with entirely different and separate codebases that will need to be maintained simultaneously. This means that if you have an app which is both on iOS and Android, and you need to make an update to the app, you’ll have to make the needed changes individually. The complication with this is not just twice the time and effort needed, but also the potential of complications with one OS/device causing a delay for both deployments. You might have that update ready to go on Android but be forced to delay its launch all because of an issue which is unique to iOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Just Like Mother Used to Make It&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, to further elaborate on the point regarding how close cross platform frameworks get their code to native: these tools have been meticulously refined over the years to grapple into every part of a native application. For example, native components such as entry boxes (i.e., a fillable text form) can be very unique between operating systems, having their own special style that is reflective of that OS’ theme. In light of this, Xamarin and MAUI have been designed to cross-compile into those native components, meaning the same entry box will look as if it was written in native code on each device.&lt;/p&gt;
&lt;p&gt;The value in this is that developers don’t have to spend extra effort ensuring that their apps fit the unique feeling of each operating system. The downside is that if you wanted the theme to be identical between devices and operating systems, then you have some additional work to do. The upside in turn, however, is that these cross-platform frameworks have tools and functionality to allow just that, including the ability to overwrite and/or adjust how those native components will appear after cross-compiling.&lt;/p&gt;
&lt;p&gt;For example, on Android many text entry boxes (especially on websites) contain a magenta or purple underline rather than a box outline, while iOS usually has a recessed box with an outline. With Xamarin, you can choose to have your text entry element displayed how Android or iOS would natively display such a box as if you had written said app in their native language. Or, if you wanted both to simply show as an empty outlined box, you could adjust the custom properties to force it to display identically as such. Either way it is a one-time effort that can be executed using a language the developer knows, and the cross-platform framework will do the heavy lifting of compiling to both operating systems, seamlessly creating the desired component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Options for every Developer&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The majority of the arguments made to this point are specific to Xamarin and MAUI. Meaning, the ability to cross-compile into native components is (mostly) a unique feature to their platforms. Other solutions such as Flutter or ReactNative get around this by having their own unique components, which can be customized or adjusted according to the desires and needs of the developer.&lt;/p&gt;
&lt;p&gt;This presents another interesting selling point of getting into the cross-platform framework arena, that there are multiple frameworks a developer can choose from. So, every developer has the opportunity to choose a platform that best suits their coding preferences.&lt;/p&gt;
&lt;p&gt;“When it comes to choosing a cross-platform development tool, a lot of that decision is going to depend on the developer’s experience and what they’re used to. If you have a developer who is used to using Microsoft tools and writing Windows desktop apps and similar mobile apps, they’re more likely to use Xamarin or MAUI just because they’re used to .NET and they have a lot of experience with C#.” Says Tony, thinking back on his own experience with .NET and C#, which led him to Xamarin. “On the other hand, if you have a developer who is more web based, so they do websites and web pages, and a lot of JavaScript, they’ll probably gravitate more towards ReactNative or Flutter.”&lt;/p&gt;
&lt;p&gt;He continues, elaborating on the flexibility of these tools, and how all that really matters is what you are comfortable with and what you are willing to learn, stating “Now that doesn’t mean that either one of those people couldn’t learn the other tool- in fact, I know a lot of developers who are Microsoft .NET people and yet use Flutter, and also people who are more web-based who really like .NET and C#.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Conclusion&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ten years ago there could’ve been arguments made about the superiority of writing in native code with native tools due to the rather primitive cross-platform tools of the time. However, that is no longer the case, not even in the slightest. Not only can these tools compile applications to near identical replications of a natively written app but can even supersede them in terms of quality and performance. Most importantly, however, they can cut down development time significantly, reducing costs and allowing your developers to spend more time innovating rather than duplicating work and maintaining multiple codebases. It’s just plain better&lt;/p&gt;
&lt;p&gt;What are your thoughts on the state and use of cross-platform development systems? Do you still prefer to use native languages and tools? Let us know your thoughts in the comment section below.&lt;/p&gt;
</description>
      <pubDate>Fri, 25 Aug 2023 12:00:00 -0600</pubDate>
      <a10:updated>2023-08-25T12:00:00-06:00</a10:updated>
    </item>
  </channel>
</rss>