﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>Duracellko.NET</title>
		<link>http://duracellko.net/</link>
		<description>Welcome to Duracellko.NET</description>
		<copyright>Copyright Duracellko.NET © 2013</copyright>
		<pubDate>Tue, 07 Jul 2026 09:34:50 GMT</pubDate>
		<lastBuildDate>Tue, 07 Jul 2026 09:34:50 GMT</lastBuildDate>
		<item>
			<title>How to optimize your calendar using quantum computing</title>
			<link>http://duracellko.net/posts/2026/07/qcalendar</link>
			<description>&lt;p&gt;This has probably happened to everyone, and to some people quite often. You receive a meeting invitation from your manager, but it conflicts with a meeting you already have with a colleague. So you try to move that meeting, but the colleague finds that your new proposal now conflicts with another meeting, meaning one of those meetings would need to move too. Finding an optimal schedule for meetings so that no conflicts remain is a very hard problem. Can a quantum computer help?&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2026/07/qcalendar</guid>
			<pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;This has probably happened to everyone, and to some people quite often. You receive a meeting invitation from your manager, but it conflicts with a meeting you already have with a colleague. So you try to move that meeting, but the colleague finds that your new proposal now conflicts with another meeting, meaning one of those meetings would need to move too. Finding an optimal schedule for meetings so that no conflicts remain is a very hard problem. Can a quantum computer help?&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;A few years ago, I started looking into the field of quantum computing. Everywhere I heard that quantum computing was more powerful at solving hard problems, but no one explained what kind of problems. That is what I wanted to learn, so I picked up what some people call the bible of quantum computing, the &lt;a href="https://www.cambridge.org/highereducation/books/quantum-computation-and-quantum-information/01E10196D0A682A6AEFFEA52D53BE9AE"&gt;Quantum Computation and Quantum Information&lt;/a&gt; book. The book presents two interesting results in quantum computing. The first is &lt;a href="https://en.wikipedia.org/wiki/Shor%27s_algorithm"&gt;Shor's algorithm&lt;/a&gt;, or more generally, the &lt;a href="https://en.wikipedia.org/wiki/Quantum_Fourier_transform"&gt;Quantum Fourier Transform&lt;/a&gt;. This approach can solve the &lt;a href="https://en.wikipedia.org/wiki/Hidden_subgroup_problem"&gt;Hidden Subgroup Problem&lt;/a&gt; in polynomial time — in other words, problems where the solution space is periodic. The other highlighted achievement was &lt;a href="https://en.wikipedia.org/wiki/Grover%27s_algorithm"&gt;Grover's algorithm&lt;/a&gt;, which provides a quadratic speedup for searching through any NP-hard problem's solution space.&lt;/p&gt;
&lt;p&gt;After reading the book, I became interested in more than just the simple question of "What kind of problems can quantum computing solve efficiently?" I wanted to get some hands-on experience and try some quantum computing frameworks, so I started thinking about a good problem to practice on. Most of the NP-hard problems I could think of were optimization problems.&lt;/p&gt;
&lt;p&gt;An optimization problem does not search for just any solution, but for the &lt;em&gt;best&lt;/em&gt; solution — for example, "What is the shortest path from A to B?" Formally, an optimization problem defines a function &lt;span class="math"&gt;\(f: X \rightarrow R\)&lt;/span&gt; that assigns a number to every solution; this function is usually called the cost function. The problem is then to find the &lt;span class="math"&gt;\(x\)&lt;/span&gt; with the minimum value of &lt;span class="math"&gt;\(f(x)\)&lt;/span&gt;. It is also possible to search for the maximum value by simply defining the cost function as &lt;span class="math"&gt;\(g(x) = -f(x)\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Then I realized that the book did not mention optimization problems at all, so I looked into whether there were any quantum computing achievements for optimization problems. I found the breakthrough paper &lt;a href="https://arxiv.org/abs/1411.4028"&gt;A Quantum Approximate Optimization Algorithm&lt;/a&gt;. This paper was published in 2014, so its results could not have been included in a book published in 2010.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;
&lt;p&gt;Let's formalize the meeting scheduling problem. The input is a set of unscheduled meetings. Each meeting has a list of required attendees. For simplicity, let's say each meeting requires one hour. The output is a schedule — a time slot assigned to each meeting — such that any two meetings with at least one common attendee are assigned different time slots. The goal is to minimize the number of time slots occupied by all meetings.&lt;/p&gt;
&lt;p&gt;For example, consider the following meetings:&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Meeting&lt;/th&gt;
&lt;th&gt;Attendees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quantum Mechanics symposium&lt;/td&gt;
&lt;td&gt;Albert Einstein, Boris Podolsky, Claude Shannon, John von Neumann&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calculus and Differential Equations lecture&lt;/td&gt;
&lt;td&gt;Isaac Newton, Gottfried Wilhelm Leibniz, Leonhard Euler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graph Theory and Combinatorics colloquium&lt;/td&gt;
&lt;td&gt;Paul Erdős, Leonhard Euler, Frank Ramsey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Information Theory symposium&lt;/td&gt;
&lt;td&gt;Claude Shannon, Alan Turing, John von Neumann&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;em&gt;Quantum Mechanics symposium&lt;/em&gt; and the &lt;em&gt;Information Theory symposium&lt;/em&gt; cannot be scheduled at the same time because &lt;em&gt;Claude Shannon&lt;/em&gt; and &lt;em&gt;John von Neumann&lt;/em&gt; attend both. The &lt;em&gt;Calculus and Differential Equations lecture&lt;/em&gt; and the &lt;em&gt;Graph Theory and Combinatorics colloquium&lt;/em&gt; cannot overlap because &lt;em&gt;Leonhard Euler&lt;/em&gt; attends both.&lt;/p&gt;
&lt;p&gt;One optimal schedule is:&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Meeting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8:00&lt;/td&gt;
&lt;td&gt;Quantum Mechanics symposium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8:00&lt;/td&gt;
&lt;td&gt;Calculus and Differential Equations lecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9:00&lt;/td&gt;
&lt;td&gt;Graph Theory and Combinatorics colloquium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9:00&lt;/td&gt;
&lt;td&gt;Information Theory symposium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="minimum-graph-coloring"&gt;Minimum Graph Coloring&lt;/h2&gt;
&lt;p&gt;The scheduling problem can be transformed into the Minimum Graph Coloring problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Graph coloring&lt;/strong&gt; is the task of assigning a color to each vertex such that no two vertices connected by an edge share the same color. The &lt;strong&gt;minimum graph coloring&lt;/strong&gt; problem asks: what is the smallest number of colors needed to accomplish this? That number is called the &lt;strong&gt;chromatic number&lt;/strong&gt; of the graph.&lt;/p&gt;
&lt;h3 id="transforming-the-schedule-into-a-graph"&gt;Transforming the schedule into a graph&lt;/h3&gt;
&lt;p&gt;The scheduling problem maps to a graph in two steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each meeting becomes a &lt;strong&gt;vertex&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Two vertices are connected by an &lt;strong&gt;edge&lt;/strong&gt; if and only if the two meetings share at least one attendee.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Applying this to our example produces the following graph:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/07/meetings-graph.png" class="img-fluid" alt="Meetings graph"&gt;&lt;/p&gt;
&lt;p&gt;We then find a coloring of this graph using the minimum number of colors. Each color is assigned a unique time slot. It is straightforward to see why this works: if two conflicting meetings were assigned the same time slot, they would be connected by an edge yet share the same color — which violates the coloring constraint. Therefore, a valid coloring guarantees a conflict-free schedule, and minimizing the number of colors minimizes the number of time slots.&lt;/p&gt;
&lt;h2 id="quantum-approximate-optimization-algorithm-for-minimum-graph-coloring"&gt;Quantum Approximate Optimization Algorithm for Minimum Graph Coloring&lt;/h2&gt;
&lt;p&gt;Now we arrive at the quantum part. The Quantum Approximate Optimization Algorithm (QAOA) is a hybrid quantum-classical algorithm designed to solve combinatorial optimization problems. "Hybrid" means it uses a quantum computer and a classical computer together, playing to the strengths of each.&lt;/p&gt;
&lt;p&gt;Before QAOA can run on a quantum computer, the solution space of the optimization problem must be encoded in quantum states. The usual form of encoding is a binary encoding into multiple qubits in basis &lt;span class="math"&gt;\(|0 \rangle\)&lt;/span&gt; and &lt;span class="math"&gt;\(|1 \rangle\)&lt;/span&gt;, similar to how the solution would be encoded in a classical binary array. In context of the &lt;em&gt;Minimum Graph Coloring&lt;/em&gt; problem, this means that every vertex coloring must be assigned to a unique quantum state (or classical binary string). Then the optimization &lt;em&gt;cost function&lt;/em&gt; is defined as a &lt;strong&gt;cost Hamiltonian&lt;/strong&gt; &lt;span class="math"&gt;\(H_c\)&lt;/span&gt;. A "Hamiltonian" is an operator or function that calculates the total energy of the quantum state. In this way, an optimization problem of finding the best solution can be transformed into the problem of finding a quantum state with the lowest energy.&lt;/p&gt;
&lt;p&gt;There are many ways to look at how QAOA works, and I found an interesting one in &lt;a href="https://arxiv.org/abs/2511.18377"&gt;An Introduction to the Quantum Approximate Optimization Algorithm&lt;/a&gt;. The paper mentions the &lt;a href="https://en.wikipedia.org/wiki/Adiabatic_theorem"&gt;Adiabatic Theorem&lt;/a&gt; and its implication, stating:&lt;/p&gt;
&lt;blockquote class="blockquote"&gt;
&lt;p&gt;If a quantum system is in the lowest energy state of the initial Hamiltonian &lt;span class="math"&gt;\(H_i\)&lt;/span&gt; and it is slowly transformed to the final Hamiltonian &lt;span class="math"&gt;\(H_f\)&lt;/span&gt;, then the system stays in the lowest energy state through the transformation, ending in the lowest energy state of Hamiltonian &lt;span class="math"&gt;\(H_f\)&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So a quantum optimization algorithm would start with a well-known initial Hamiltonian and its well-known lowest energy state. For an &lt;span class="math"&gt;\(n\)&lt;/span&gt;-qubit system, a good starting point is the Hamiltonian&lt;/p&gt;
&lt;div class="math"&gt;
\[
H_i = - \sum_{i=1}^{n}X_i
\]&lt;/div&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(X_i\)&lt;/span&gt; is the Pauli-X matrix applied to qubit &lt;span class="math"&gt;\(i\)&lt;/span&gt;. It is easy to check that the lowest energy state of &lt;span class="math"&gt;\(H_i\)&lt;/span&gt; is the superposition of all basis states.&lt;/p&gt;
&lt;div class="math"&gt;
\[
\psi_0 = | + \rangle ^{\otimes n} = H^{\otimes n}|0,\ldots,0\rangle
\]&lt;/div&gt;
&lt;p&gt;Then we define a time-evolving Hamiltonian for the system that would slowly transform from &lt;span class="math"&gt;\(H_i\)&lt;/span&gt; to &lt;span class="math"&gt;\(H_f\)&lt;/span&gt;. So the Hamiltonian at time &lt;span class="math"&gt;\(t\)&lt;/span&gt; would be&lt;/p&gt;
&lt;div class="math"&gt;
\[
H(t) = (1-t)H_i + tH_f, \quad t \in [0, 1]
\]&lt;/div&gt;
&lt;p&gt;This means that the quantum circuit for the full time evolution would need to implement the unitary operation&lt;/p&gt;
&lt;div class="math"&gt;
\[
U = e^{-i \int_{0}^{1} H(t)dt}
\]&lt;/div&gt;
&lt;p&gt;Unfortunately, in general this quantum circuit would need an infinite number of gates to implement this operation precisely. However, it is possible to approximate it by splitting the continuous time interval &lt;span class="math"&gt;\(t \in [0, 1]\)&lt;/span&gt; into a finite number of steps. The steps do not have to be the same size, so the circuit would be the product of the following unitary operations.&lt;/p&gt;
&lt;div class="math"&gt;
\[
U \approx \prod_{k=1}^{p} e^{-i (1-t_k) H_i} e^{-i t_k H_f}
\]&lt;/div&gt;
&lt;p&gt;where &lt;span class="math"&gt;\(t_k\)&lt;/span&gt; are timestamps on the interval &lt;span class="math"&gt;\([0, 1]\)&lt;/span&gt;. We can generalize this operation by replacing &lt;span class="math"&gt;\((1-t_k)\)&lt;/span&gt; with &lt;span class="math"&gt;\(\beta_k / 2\)&lt;/span&gt; and &lt;span class="math"&gt;\(t_k\)&lt;/span&gt; with &lt;span class="math"&gt;\(\gamma_k / 2\)&lt;/span&gt;. Then the operation looks like&lt;/p&gt;
&lt;div class="math"&gt;
\[
U \approx U_p = \left( e^{-i \frac{\beta_p}{2} H_i} e^{-i \frac{\gamma_p}{2} H_f} \right) \ldots \left( e^{-i \frac{\beta_1}{2} H_i} e^{-i \frac{\gamma_1}{2} H_f} \right)
\]&lt;/div&gt;
&lt;p&gt;A nice property of this operation is that, for a well-chosen Hamiltonian, the operation &lt;span class="math"&gt;\(e^{-i \frac{\alpha}{2} H}\)&lt;/span&gt; is easy to implement in a quantum circuit. Usually it just needs to entangle specific qubits and then simply rotate a specific qubit by angle &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt;. Also, increasing the number of steps in the circuit improves the precision of the calculation, as&lt;/p&gt;
&lt;div class="math"&gt;
\[
\lim_{p \to \infty} U_p | \psi_0 \rangle = U | \psi_0 \rangle
\]&lt;/div&gt;
&lt;p&gt;Another way to look at it is that the algorithm starts with a superposition of all possible states and then iteratively alternates between the final and initial (also known as the mixer) Hamiltonians, with different weights / rotation angles. The final Hamiltonian shifts the phase toward optimal solutions, thus amplifying optimal solutions and suppressing bad ones. The mixer Hamiltonian then mixes the state so that the algorithm does not get stuck in a local minimum, but instead searches for the global one.&lt;/p&gt;
&lt;h3 id="final-hamiltonian"&gt;Final Hamiltonian&lt;/h3&gt;
&lt;p&gt;Now that we know how the quantum optimization algorithm works, we need to find the final Hamiltonian and the angles &lt;span class="math"&gt;\(\beta_i\)&lt;/span&gt; and &lt;span class="math"&gt;\(\gamma_i\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Recently, a new study proposed a qubit-efficient cost Hamiltonian for the Minimum Graph Coloring problem. It was published in &lt;a href="https://arxiv.org/abs/2604.21123"&gt;"Qubit-efficient and gate-efficient encodings of graph partitioning problems for quantum optimization"&lt;/a&gt;. It suggests encoding the coloring in the following way.&lt;/p&gt;
&lt;p&gt;Each vertex (meeting) is assigned a block of &lt;span class="math"&gt;\(L = \lceil \log_2 C \rceil\)&lt;/span&gt; qubits, where &lt;span class="math"&gt;\(C\)&lt;/span&gt; is an upper bound on the chromatic number derived from &lt;a href="https://en.wikipedia.org/wiki/Brooks%27_theorem"&gt;Brooks' theorem&lt;/a&gt;. These &lt;span class="math"&gt;\(L\)&lt;/span&gt; qubits encode the vertex's color as a binary integer. With &lt;span class="math"&gt;\(n\)&lt;/span&gt; vertices, the coloring register uses &lt;span class="math"&gt;\(n \cdot L\)&lt;/span&gt; qubits total.&lt;/p&gt;
&lt;p&gt;For example, with 4 meetings and a chromatic number bound of 3, the number of bits per meeting is &lt;span class="math"&gt;\(L = \lceil \log_2 3 \rceil = 2\)&lt;/span&gt;, which leads to 8 qubits total in the coloring register.&lt;/p&gt;
&lt;p&gt;The paper phrases the cost function directly in terms of classical bits, before any quantum encoding enters the picture. Let &lt;span class="math"&gt;\(x_{v,k} \in \{0, 1\}\)&lt;/span&gt; denote bit &lt;span class="math"&gt;\(k\)&lt;/span&gt; of vertex &lt;span class="math"&gt;\(v\)&lt;/span&gt;'s color, so that vertex &lt;span class="math"&gt;\(v\)&lt;/span&gt;'s color is the integer &lt;span class="math"&gt;\(\sum_{k} x_{v,k} 2^k\)&lt;/span&gt;. The cost function — called a Hamiltonian by analogy with its quantum counterpart — is&lt;/p&gt;
&lt;div class="math"&gt;
\[
H = A \sum_{(u,v) \in E} \prod_{k=0}^{L-1} \left( 2x_{u,k}x_{v,k} - x_{u,k} - x_{v,k} + 1 \right) + \sum_{k=0}^{L-1} P_k \sum_{v \in V} x_{v,k}
\]&lt;/div&gt;
&lt;p&gt;The first term is the &lt;strong&gt;adjacency penalty&lt;/strong&gt;. The factor &lt;span class="math"&gt;\(2x_ux_v - x_u - x_v + 1\)&lt;/span&gt; is just the XNOR of the two bits written as a polynomial: it equals &lt;span class="math"&gt;\(1\)&lt;/span&gt; when &lt;span class="math"&gt;\(x_u = x_v\)&lt;/span&gt; and &lt;span class="math"&gt;\(0\)&lt;/span&gt; otherwise. So the product over all &lt;span class="math"&gt;\(L\)&lt;/span&gt; bits of an edge &lt;span class="math"&gt;\((u, v)\)&lt;/span&gt; evaluates to &lt;span class="math"&gt;\(1\)&lt;/span&gt; exactly when every bit matches — that is, when the two meetings have been assigned the same color — and to &lt;span class="math"&gt;\(0\)&lt;/span&gt; the moment a single bit differs. Summed over all edges and scaled by a constant &lt;span class="math"&gt;\(A\)&lt;/span&gt;, this term adds energy &lt;span class="math"&gt;\(A\)&lt;/span&gt; for every conflicting pair of meetings and zero energy for every properly colored edge.&lt;/p&gt;
&lt;p&gt;The second term is the &lt;strong&gt;lexicographic penalty&lt;/strong&gt;. Taken alone, the adjacency term is satisfied by any proper coloring, including wasteful ones — nothing stops the optimizer from giving every meeting its own color. The weights &lt;span class="math"&gt;\(P_k\)&lt;/span&gt; grow with &lt;span class="math"&gt;\(k\)&lt;/span&gt; fast enough that setting a higher-order color bit always costs more than a lower-order one, which nudges the optimizer toward colorings that only use the lower-order bits, i.e. the smallest color indices. This is exactly the minimum coloring we are after. In the paper, &lt;span class="math"&gt;\(A = (n+1)^L\)&lt;/span&gt; and &lt;span class="math"&gt;\(P_k = (n+1)^k\)&lt;/span&gt;, where &lt;span class="math"&gt;\(n\)&lt;/span&gt; is the number of vertices; both are chosen large enough that satisfying adjacency is always prioritized over lexicographic minimization, but small enough that lexicographic terms converge sufficently for approximation.&lt;/p&gt;
&lt;p&gt;To run this Hamiltonian on a quantum computer, it needs to be expressed as an operator acting on qubits, and the natural choice for encoding a single bit is the Pauli-&lt;span class="math"&gt;\(Z\)&lt;/span&gt; operator, whose eigenvalues are &lt;span class="math"&gt;\(+1\)&lt;/span&gt; and &lt;span class="math"&gt;\(-1\)&lt;/span&gt; rather than &lt;span class="math"&gt;\(0\)&lt;/span&gt; and &lt;span class="math"&gt;\(1\)&lt;/span&gt;. The standard substitution is&lt;/p&gt;
&lt;div class="math"&gt;
\[
x_{v,k} = \frac{1 - Z_{v,k}}{2}
\]&lt;/div&gt;
&lt;p&gt;so &lt;span class="math"&gt;\(Z_{v,k} = +1\)&lt;/span&gt; when bit &lt;span class="math"&gt;\(k\)&lt;/span&gt; of vertex &lt;span class="math"&gt;\(v\)&lt;/span&gt;'s color is &lt;span class="math"&gt;\(0\)&lt;/span&gt;, and &lt;span class="math"&gt;\(Z_{v,k} = -1\)&lt;/span&gt; when it is &lt;span class="math"&gt;\(1\)&lt;/span&gt;. Plugging this into the adjacency factor collapses it to&lt;/p&gt;
&lt;div class="math"&gt;
\[
2x_{u,k}x_{v,k} - x_{u,k} - x_{v,k} + 1 = \frac{1 + Z_{u,k}Z_{v,k}}{2}
\]&lt;/div&gt;
&lt;p&gt;Taking the product of this over all &lt;span class="math"&gt;\(L\)&lt;/span&gt; bits and expanding it turns the single XNOR factor into a sum over every non-empty subset &lt;span class="math"&gt;\(S\)&lt;/span&gt; of bit positions, since &lt;span class="math"&gt;\(\prod_k (1 + Z_{u,k}Z_{v,k})/2 = 2^{-L}\sum_{S \subseteq \{ 0, \ldots, L-1 \}} \prod_{k \in S} Z_{u,k}Z_{v,k}\)&lt;/span&gt; (the &lt;span class="math"&gt;\(S = \emptyset\)&lt;/span&gt; term contributes only a constant). That factor of &lt;span class="math"&gt;\(2^{-L}\)&lt;/span&gt; combines with the classical-level coefficient &lt;span class="math"&gt;\(A = (n+1)^L\)&lt;/span&gt; to give a smaller effective weight, &lt;span class="math"&gt;\((n+1)^L \cdot 2^{-L} = \left(\frac{n+1}{2}\right)^L\)&lt;/span&gt;, on each qubit-level term. After dropping the remaining constant terms — they shift every state's energy equally and therefore never change which state has the lowest energy — the cost Hamiltonian becomes&lt;/p&gt;
&lt;div class="math"&gt;
\[
H_f = A' \sum_{(u,v) \in E} \sum_{\substack{S \subseteq \{0,\ldots,L-1\} \\ S \neq \emptyset}} \prod_{k \in S} Z_{u,k} Z_{v,k} \; - \; \frac{1}{2} \sum_{k=0}^{L-1} (n+1)^k \sum_{v \in V} Z_{v,k}
\]&lt;/div&gt;
&lt;p&gt;with &lt;span class="math"&gt;\(A' = \left(\frac{n+1}{2}\right)^L\)&lt;/span&gt; — the rescaled, qubit-level version of the classical-level &lt;span class="math"&gt;\(A\)&lt;/span&gt; from before.&lt;/p&gt;
&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Now that we know theoretically how the quantum circuit should look, let's implement it. The first step for me was to look at which quantum computing framework I would like to use. My goal was to learn, not necessarily to run the program on an actual quantum computer, so my preference was a vendor-independent framework. Therefore, I tried to avoid &lt;a href="https://www.ibm.com/quantum/qiskit"&gt;Qiskit&lt;/a&gt; or &lt;a href="https://microsoft.github.io/qdk/"&gt;Q#&lt;/a&gt;, even though there were a lot of references to Qiskit. I found a possibly interesting project, &lt;a href="https://projectq.ch/"&gt;ProjectQ&lt;/a&gt;. Unfortunately, just installing it produced a warning that it was not compatible with the new version of Python 3.14, and even running a simple example didn't work on this version. Then I noticed that the latest release was from 2022, so I decided to try something else.&lt;/p&gt;
&lt;p&gt;The next candidate was &lt;a href="https://pennylane.ai/"&gt;PennyLane&lt;/a&gt;, which worked very well, so the implementation in this post uses this framework.&lt;/p&gt;
&lt;h3 id="initial-hamiltonian-implementation"&gt;Initial Hamiltonian implementation&lt;/h3&gt;
&lt;p&gt;Let's start with the function that implements the initial Hamiltonian.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def _get_initial_hamiltonian(self, qubit_count: int) -&amp;gt; qpl.Hamiltonian:
    coeffs = [1.0] * qubit_count
    ops = [1.0 * qpl.PauliX(i) for i in range(qubit_count)]
    return qpl.Hamiltonian(coeffs, ops)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PennyLane represents a Hamiltonian as a weighted sum of operators: a list of coefficients paired with a matching list of operators. Here every coefficient is &lt;span class="math"&gt;\(1.0\)&lt;/span&gt; and every operator is &lt;span class="math"&gt;\(X_i\)&lt;/span&gt; for qubit &lt;span class="math"&gt;\(i\)&lt;/span&gt;, which gives &lt;span class="math"&gt;\(H_i = \sum_i X_i\)&lt;/span&gt; — the transverse-field mixer from the theory section, applied across all &lt;code&gt;qubit_count&lt;/code&gt; qubits.&lt;/p&gt;
&lt;p&gt;One detail worth flagging: the sign here is the opposite of the &lt;span class="math"&gt;\(-\sum_i X_i\)&lt;/span&gt; used earlier. This does not matter in practice. The mixer only ever appears inside &lt;code&gt;qpl.qaoa.mixer_layer&lt;/code&gt; as &lt;span class="math"&gt;\(e^{-i\beta H_i}\)&lt;/span&gt;, so flipping the sign of &lt;span class="math"&gt;\(H_i\)&lt;/span&gt; is equivalent to negating &lt;span class="math"&gt;\(\beta\)&lt;/span&gt;, and the classical optimizer is free to pick whichever sign works best within its bounds.&lt;/p&gt;
&lt;h3 id="final-hamiltonian-implementation"&gt;Final Hamiltonian implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def _get_final_hamiltonian(self) -&amp;gt; qpl.Hamiltonian:
    n = self.vertex_count
    num_bits = self.color_bits_count

    def qubit(vertex_idx: int, bit: int) -&amp;gt; int:
        return vertex_idx * num_bits + bit

    coeffs: list[float] = []
    ops: list[qpl.operation.Operator] = []

    # --- Adjacency term ---
    adj_coeff = ((n + 1) / 2) ** num_bits
    for u, v in self.graph.edges():
        ui = self.vertices.index(u)
        vi = self.vertices.index(v)
        for mask in range(1 &amp;lt;&amp;lt; num_bits):
            subset_qubits: list[int] = []
            for k in range(num_bits):
                if mask &amp;amp; (1 &amp;lt;&amp;lt; k):
                    subset_qubits.append(qubit(ui, k))
                    subset_qubits.append(qubit(vi, k))

            if subset_qubits:
                op: qpl.operation.Operator = qpl.PauliZ(subset_qubits[0])
                for q in subset_qubits[1:]:
                    op = op @ qpl.PauliZ(q)

                coeffs.append(adj_coeff)
                ops.append(op)

    # --- Lexicographic term ---
    for k in range(num_bits):
        coeff = -0.5 * (n + 1) ** k
        for i in range(n):
            coeffs.append(coeff)
            ops.append(qpl.PauliZ(qubit(i, k)))

    return qpl.Hamiltonian(coeffs, ops)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a direct transcription of the Hamiltonian derived above. Four attributes of the solver instance have impact on the Hamiltonian:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;self.graph&lt;/code&gt; — the conflict meeting graph itself. Each edge represents a conflict between meetings. The function builds an adjacency term for each edge.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.vertex_count&lt;/code&gt; (&lt;span class="math"&gt;\(n\)&lt;/span&gt;) — the number of meetings, computed once as &lt;code&gt;len(graph.nodes())&lt;/code&gt;. It sets the base of the exponential weights &lt;span class="math"&gt;\(A\)&lt;/span&gt; and &lt;span class="math"&gt;\(P_k\)&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.color_bits_count&lt;/code&gt; (&lt;span class="math"&gt;\(L\)&lt;/span&gt;) — the number of qubits used per vertex, computed from the logarithm of the Brooks'-theorem bound described earlier.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.vertices&lt;/code&gt; — a fixed-order list of the graph's nodes, used to translate a vertex (which might be an arbitrary Python object, like a meeting name) into an integer index. That index is what &lt;code&gt;qubit()&lt;/code&gt; uses to compute the qubit offset for a given vertex and bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The small helper function &lt;code&gt;qubit&lt;/code&gt; calculates the index of the qubit that represents a specific bit of the color of a specific vertex. Notice that the color encoding is &lt;strong&gt;little-endian&lt;/strong&gt;: the more significant bit has the higher qubit index.&lt;/p&gt;
&lt;p&gt;The nested loop over &lt;code&gt;mask in range(1 &amp;lt;&amp;lt; num_bits)&lt;/code&gt; is the code's way of iterating over every non-empty subset &lt;span class="math"&gt;\(S\)&lt;/span&gt; of bit positions: each bit of &lt;code&gt;mask&lt;/code&gt; says whether bit position &lt;span class="math"&gt;\(k\)&lt;/span&gt; belongs to &lt;span class="math"&gt;\(S\)&lt;/span&gt;, and whenever it does, both &lt;span class="math"&gt;\(Z_{u,k}\)&lt;/span&gt; and &lt;span class="math"&gt;\(Z_{v,k}\)&lt;/span&gt; are folded into the running tensor product via PennyLane's &lt;code&gt;@&lt;/code&gt; operator (which composes operators into a multi-qubit Pauli string). The lexicographic term above is a much simpler double loop — one term per bit position &lt;span class="math"&gt;\(k\)&lt;/span&gt; per vertex &lt;span class="math"&gt;\(v\)&lt;/span&gt;, with coefficient &lt;span class="math"&gt;\(-\frac{1}{2}(n+1)^k\)&lt;/span&gt; exactly as derived above.&lt;/p&gt;
&lt;h3 id="quantum-circuit-implementation"&gt;Quantum circuit implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def get_colors(self, angles: list[float]) -&amp;gt; tuple[list[int], float]:
    @qpl.qnode(self._dev)
    def circuit() -&amp;gt; npt.NDArray[np.int_]:
        self._execute_layers(pnp.array(angles))
        return qpl.probs(wires=range(self.all_qubits_count))

    probabilities = circuit()

    state = np.argmax(probabilities)
    probability = probabilities[state]

    return (self._decode_colors(state), probability)

def _execute_layers(self, angles: npt.NDArray[np.float64]) -&amp;gt; None:
    for i in range(self.all_qubits_count):
        qpl.Hadamard(wires=i)

    half = len(angles) // 2
    for beta, gamma in zip(angles[:half], angles[half:]):
        qpl.qaoa.cost_layer(gamma, self.final_hamiltonian)
        qpl.qaoa.mixer_layer(beta, self.initial_hamiltonian)

def _decode_colors(self, state: np.int_) -&amp;gt; list[int]:
    colors: list[int] = []

    for i in range(self.vertex_count):
        color = 0
        bit_value = 1
        for j in range(self.color_bits_count):
            bit_index = i * self.color_bits_count + j
            # Reflect: qubit 0 is the highest bit of the integer.
            bit_index = self.coloring_qubits_count - 1 - bit_index
            if state &amp;amp; (1 &amp;lt;&amp;lt; bit_index):
                color += bit_value
            bit_value &amp;lt;&amp;lt;= 1

        colors.append(color)

    return colors
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;get_colors&lt;/code&gt; function calculates the coloring with the highest probability. The function uses the following attributes of the solver instance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;self.coloring_qubits_count&lt;/code&gt; (&lt;span class="math"&gt;\(n \cdot L\)&lt;/span&gt;) — the total number of qubits. It equals the number of vertices times the number of coloring bits per vertex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.all_qubits_count&lt;/code&gt; — same as &lt;code&gt;coloring_qubits_count&lt;/code&gt; here. However, there is a version with some ancilla qubits that would be counted in this attribute too.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the angles are provided, running the circuit is just a matter of executing it and reading out &lt;code&gt;qpl.probs()&lt;/code&gt;, which returns the full probability distribution over all &lt;span class="math"&gt;\(2^n\)&lt;/span&gt; basis states — the quantum-simulator equivalent of running the real circuit many times and building a histogram of measurement outcomes. &lt;code&gt;np.argmax&lt;/code&gt; picks the single most likely basis state, and &lt;code&gt;_decode_colors&lt;/code&gt; turns that state back into a coloring. For color decoding, keep in mind that the encoding is little-endian.&lt;/p&gt;
&lt;p&gt;The interesting part is &lt;code&gt;_execute_layers&lt;/code&gt;, since it is what turns the abstract &lt;span class="math"&gt;\(U_p\)&lt;/span&gt; formula from the theory section into an actual sequence of gates:&lt;/p&gt;
&lt;p&gt;The first loop applies a Hadamard gate to every qubit, which is what actually prepares the uniform superposition &lt;span class="math"&gt;\(\psi_0 = |+\rangle^{\otimes n}\)&lt;/span&gt;. The &lt;code&gt;angles&lt;/code&gt; list is the flat &lt;span class="math"&gt;\((\beta_0,\ldots, \beta_{p-1}, \gamma_0,\ldots, \gamma_{p-1})\)&lt;/span&gt; array provided as parameter, split in half and zipped back into &lt;span class="math"&gt;\(p\)&lt;/span&gt; &lt;code&gt;(beta, gamma)&lt;/code&gt; pairs. Each pair becomes one layer: &lt;code&gt;qpl.qaoa.cost_layer&lt;/code&gt; compiles &lt;span class="math"&gt;\(e^{-i\gamma H_f}\)&lt;/span&gt; into gates directly from the &lt;code&gt;final_hamiltonian&lt;/code&gt; object built earlier, and &lt;code&gt;qpl.qaoa.mixer_layer&lt;/code&gt; does the same for &lt;span class="math"&gt;\(e^{-i\beta H_i}\)&lt;/span&gt; using &lt;code&gt;initial_hamiltonian&lt;/code&gt;. Stacking &lt;span class="math"&gt;\(p\)&lt;/span&gt; of these pairs is exactly the product &lt;span class="math"&gt;\(U_p\)&lt;/span&gt; from the theory section.&lt;/p&gt;
&lt;h3 id="finding-angles-optimizing"&gt;Finding angles — optimizing&lt;/h3&gt;
&lt;p&gt;Now we have the quantum circuit with angles as parameters, so the only question is how to set up those angles. Unfortunately, this is the hardest part of QAOA. The first thing we need is to implement a function that returns the expectation value of the quantum circuit measured using the final Hamiltonian. This means we would not measure all qubits individually, but only the total energy. In other words, if we start with the initial state as a superposition of all basis states&lt;/p&gt;
&lt;div class="math"&gt;
\[
\psi_0 = | + \rangle ^{\otimes n} = H^{\otimes n}|0,\ldots 0 \rangle
\]&lt;/div&gt;
&lt;p&gt;and run the circuit that implements the unitary operation&lt;/p&gt;
&lt;div class="math"&gt;
\[
U_p = \left( e^{-i \frac{\beta_p}{2} H_i} e^{-i \frac{\gamma_p}{2} H_f} \right) \ldots \left( e^{-i \frac{\beta_1}{2} H_i} e^{-i \frac{\gamma_1}{2} H_f} \right)
\]&lt;/div&gt;
&lt;p&gt;and we get the final state&lt;/p&gt;
&lt;div class="math"&gt;
\[
\psi_f = U\psi_0
\]&lt;/div&gt;
&lt;p&gt;then the expectation value of the final Hamiltonian (or mean total energy) is&lt;/p&gt;
&lt;div class="math"&gt;
\[
\langle \psi_f | H_f | \psi_f \rangle
\]&lt;/div&gt;
&lt;p&gt;The following function creates such circuit.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def _build_cost_circuit(self) -&amp;gt; qpl.QNode:
    @qpl.qnode(self._dev)
    def circuit(angles: npt.NDArray[np.float64]) -&amp;gt; float:
        self._execute_layers(angles)
        return qpl.expval(self.final_hamiltonian)

    return circuit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks almost identical to the circuit inside &lt;code&gt;get_colors&lt;/code&gt;, and it is — same device, same &lt;code&gt;_execute_layers&lt;/code&gt; call — but it returns &lt;code&gt;qpl.expval(self.final_hamiltonian)&lt;/code&gt; instead of &lt;code&gt;qpl.probs()&lt;/code&gt;. Instead of the full probability distribution, &lt;code&gt;qpl.expval&lt;/code&gt; returns a single number: &lt;span class="math"&gt;\(\langle \psi_f | H_f | \psi_f \rangle\)&lt;/span&gt;, the expectation value we derived above.&lt;/p&gt;
&lt;p&gt;The quantum simulator calculates the exact expectation value. Using a real quantum computer, the function would return the mean measured value over multiple measured samples.&lt;/p&gt;
&lt;p&gt;Next we will use classical optimization algorithms to find optimal parameters that minimize the output of the &lt;code&gt;_cost_circuit&lt;/code&gt; function. There are many algorithms, and Claude recommended &lt;a href="https://docs.scipy.org/doc/scipy/reference/optimize.minimize-cobyla.html"&gt;COBYLA&lt;/a&gt; (Constrained Optimization BY Linear Approximation) that is implemented in &lt;a href="https://docs.scipy.org/doc/scipy/index.html"&gt;SciPy&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def find_optimal_angles(self) -&amp;gt; list[float]:
    angles: list[float] = []
    angles += [self.starting_beta] * self.evolution_count
    angles += [self.starting_gamma] * self.evolution_count

    bounds: list[tuple[float, float]] = []
    bounds += [(0, math.pi / 2)] * self.evolution_count
    bounds += [(0, math.pi)] * self.evolution_count

    options: dict[str, Any] = { "maxiter": self.optimization_steps }

    def cost_fun(angles: npt.NDArray[np.float64]) -&amp;gt; float:
        return self._cost_circuit(angles)

    result = opt.minimize(
        cost_fun,
        pnp.array(angles),
        method="COBYLA",
        bounds=bounds,
        options=options)

    return [float(x) for x in result.x]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;evolution_count&lt;/code&gt; is the class variable that sets &lt;span class="math"&gt;\(p\)&lt;/span&gt;, the number of iterations (evolutions) in the QAOA circuit. It also defines the number of &lt;span class="math"&gt;\(\beta\)&lt;/span&gt; and &lt;span class="math"&gt;\(\gamma\)&lt;/span&gt; angles. The function creates a list of angles &lt;span class="math"&gt;\((\beta_0,\ldots, \beta_{p-1}, \gamma_0,\ldots, \gamma_{p-1})\)&lt;/span&gt; and sets each &lt;span class="math"&gt;\(\beta_i\)&lt;/span&gt; to &lt;code&gt;starting_beta&lt;/code&gt; and each &lt;span class="math"&gt;\(\gamma_i\)&lt;/span&gt; to &lt;code&gt;starting_gamma&lt;/code&gt; — the array with the layout that is expected by &lt;code&gt;_execute_layers&lt;/code&gt; function. The bounds keep every &lt;span class="math"&gt;\(\beta\)&lt;/span&gt; in &lt;span class="math"&gt;\([0, \pi/2]\)&lt;/span&gt; and every &lt;span class="math"&gt;\(\gamma\)&lt;/span&gt; in &lt;span class="math"&gt;\([0, \pi]\)&lt;/span&gt;, which follows from the periodicity of the rotations each angle controls — searching outside these ranges would just retrace angles already covered inside them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cost_fun&lt;/code&gt; is a thin wrapper around the &lt;code&gt;_cost_circuit&lt;/code&gt; QNode built above, and &lt;code&gt;opt.minimize(..., method="COBYLA")&lt;/code&gt; hands it to SciPy's COBYLA optimizer, which repeatedly proposes new angle values, evaluates the quantum circuit at each one (via &lt;code&gt;cost_fun&lt;/code&gt;), and uses those samples to find the optimal angles. &lt;code&gt;optimization_steps&lt;/code&gt; caps how many such evaluations COBYLA is allowed to make. The angles it converges to (&lt;code&gt;result.x&lt;/code&gt;) are what gets passed into &lt;code&gt;get_colors&lt;/code&gt; afterward.&lt;/p&gt;
&lt;h3 id="putting-it-all-together"&gt;Putting it all together&lt;/h3&gt;
&lt;p&gt;All the pieces — Hamiltonians, circuit, optimizer — come together in one function that takes a &lt;code&gt;networkx&lt;/code&gt; graph and returns a colored copy of it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def min_graph_coloring(graph: Graph[Any]) -&amp;gt; GraphColoringResult:
    result = graph.copy()

    # A graph with no vertices or edges needs no quantum computation at all.
    if not graph.nodes():
        return GraphColoringResult(result, 1.0)

    if not graph.edges():
        for v in result.nodes():
            result.nodes[v]["color"] = 0
        return GraphColoringResult(result, 1.0)

    solver = GraphColoringSolver(result)
    optimal_angles = solver.find_optimal_angles()

    probability = 0.0

    if len(optimal_angles) &amp;gt; 0:
        colors, probability = solver.get_colors(optimal_angles)

        # The most probable measurement is not guaranteed to be a valid
        # coloring — verify before accepting it.
        if _verify_coloring(result, solver.vertices, colors):
            for i, v in enumerate(solver.vertices):
                result.nodes[v]["color"] = colors[i]
            return GraphColoringResult(result, probability)

    # Fallback: unique color per vertex (always a valid n-coloring).
    for i, v in enumerate(solver.vertices):
        result.nodes[v]["color"] = i
    return GraphColoringResult(result, probability)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two edge cases are handled before any quantum computation happens: a graph with no vertices needs nothing, and a graph with vertices but no edges has no conflicts, so every meeting can share color 0. Otherwise a &lt;code&gt;GraphColoringSolver&lt;/code&gt; is built for the graph — this is the constructor that computes the Brooks'-theorem bound, lays out the qubits, and builds &lt;code&gt;initial_hamiltonian&lt;/code&gt; and &lt;code&gt;final_hamiltonian&lt;/code&gt; — angles are optimized with &lt;code&gt;find_optimal_angles&lt;/code&gt;, and the resulting state is decoded with &lt;code&gt;get_colors&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That last step is a heuristic, not a guarantee: QAOA can converge on a state that is &lt;em&gt;not&lt;/em&gt; a valid coloring at all, so &lt;code&gt;_verify_coloring&lt;/code&gt; double-checks the decoded colors against the actual graph before accepting them. If verification fails — or if the optimizer returned no angles at all — the function falls back to assigning every vertex its own color, which is trivially a valid coloring, but likely far from minimal.&lt;/p&gt;
&lt;p&gt;Full source code can be found at &lt;a href="https://github.com/duracellko/qcalendar"&gt;https://github.com/duracellko/qcalendar&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="tuning"&gt;Tuning&lt;/h2&gt;
&lt;p&gt;The code above has three variables whose setup I have not yet mentioned: &lt;code&gt;starting_beta&lt;/code&gt;, &lt;code&gt;starting_gamma&lt;/code&gt;, and &lt;code&gt;evolution_count&lt;/code&gt;. These need to be tuned by experimentation.&lt;/p&gt;
&lt;p&gt;I ran some configurations on a quantum simulator. First, I started with a graph with 4 vertices and 2 edges.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/07/graph1.png" class="img-fluid" alt="Graph with 4 vertices and 2 edges"&gt;&lt;/p&gt;
&lt;p&gt;The upper bound on the chromatic number of this graph is 2, so the algorithm needs only 4 qubits to run.&lt;/p&gt;
&lt;p&gt;The following variables were set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;evolution_count&lt;/code&gt;: 2&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxiter&lt;/code&gt; (COBYLA - maximum number of evaluations): 100&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following table presents the probability results for different values of &lt;code&gt;starting_beta&lt;/code&gt; and &lt;code&gt;starting_gamma&lt;/code&gt;.&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;&lt;span class="math"&gt;\(\beta\)&lt;/span&gt;&lt;/th&gt;
&lt;th style="text-align: left;"&gt;&lt;span class="math"&gt;\(\gamma\)&lt;/span&gt;&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Probability&lt;/th&gt;
&lt;th style="text-align: right;"&gt;All solutions probability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.001&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.001&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.227535758&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.910143032&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.227534724&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.910138897&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.1&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.227536269&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.910145077&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.249993679&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.999974715&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 8\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.249981939&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.999927754&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 8 + 0.01\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4 + 0.01\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.235672315&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.94268926&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Probability - probability of the resulting coloring with the highest probability.&lt;/li&gt;
&lt;li&gt;All solutions probability - sum of probabilities of all colorings with the minimum number of colors. This is the probability of the optimal outcome. Keep in mind that there is more than 1 optimal solution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I ran the program on the graph with 4 vertices and 4 edges.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/07/graph2.png" class="img-fluid" alt="Graph with 4 vertices and 4 edges"&gt;&lt;/p&gt;
&lt;p&gt;The upper bound on the chromatic number of this graph is 3, so the algorithm needs 8 qubits to run. The same values were used for &lt;code&gt;evolution_count&lt;/code&gt; and &lt;code&gt;maxiter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following table presents the probability results for different values of &lt;code&gt;starting_beta&lt;/code&gt; and &lt;code&gt;starting_gamma&lt;/code&gt;.&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;&lt;span class="math"&gt;\(\beta\)&lt;/span&gt;&lt;/th&gt;
&lt;th style="text-align: left;"&gt;&lt;span class="math"&gt;\(\gamma\)&lt;/span&gt;&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Probability&lt;/th&gt;
&lt;th style="text-align: right;"&gt;All solutions probability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.001&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.001&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.019252569&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.054215138&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.013778213&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.100561002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;0.1&lt;/td&gt;
&lt;td style="text-align: left;"&gt;0.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.017372671&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.047633037&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00390625&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.046875&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 8\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.023170804&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.165677839&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 8 + 0.01\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;span class="math"&gt;\(\pi / 4 + 0.01\)&lt;/span&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.026776257&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.125146567&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;It seems that a good starting point would be somewhere around &lt;span class="math"&gt;\(\pi / 8\)&lt;/span&gt; and &lt;span class="math"&gt;\(\pi / 4\)&lt;/span&gt;. However, this is based only on simulation.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;We mapped meeting scheduling to the minimum graph coloring problem and showed how QAOA can be used in a hybrid quantum-classical workflow to search for good schedules. Then we implemented the QAOA algorithm using PennyLane. The implementation includes the initial and final Hamiltonians, the quantum circuit, and the classical optimizer. Finally, we ran some experiments on a quantum simulator to find good starting values for the angles.&lt;/p&gt;
&lt;p&gt;Note: Full source code can be found at &lt;a href="https://github.com/duracellko/qcalendar"&gt;https://github.com/duracellko/qcalendar&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Cooperative vs Preemptive Multitasking</title>
			<link>http://duracellko.net/posts/2026/02/cooperative-preemptive-multitasking</link>
			<description>&lt;p&gt;A few weeks ago I wrote about the ideas behind the origin of &lt;a href="http://duracellko.net/posts/2026/01/async-await"&gt;async/await&lt;/a&gt; constructs in modern programming languages, and it reminded me that async/await revives an old pattern. The main goal of async/await frameworks is to provide a way to split a procedure into small tasks that can be scheduled on available processors. Soon after the first preview, it became clear that a task does not have to be only a piece of code running on a processor; it can be code running in another process, on another device, or even on another machine. Examples include a Remote Procedure Call (RPC), a database query, or a write request to a file system. In practice, these kinds of tasks are often more common than CPU-bound tasks, and async/await frameworks schedule them using a framework-level scheduler. This raises a question: why do we need an extra scheduler when modern operating systems already provide schedulers for threads and processes?&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2026/02/cooperative-preemptive-multitasking</guid>
			<pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;A few weeks ago I wrote about the ideas behind the origin of &lt;a href="http://duracellko.net/posts/2026/01/async-await"&gt;async/await&lt;/a&gt; constructs in modern programming languages, and it reminded me that async/await revives an old pattern. The main goal of async/await frameworks is to provide a way to split a procedure into small tasks that can be scheduled on available processors. Soon after the first preview, it became clear that a task does not have to be only a piece of code running on a processor; it can be code running in another process, on another device, or even on another machine. Examples include a Remote Procedure Call (RPC), a database query, or a write request to a file system. In practice, these kinds of tasks are often more common than CPU-bound tasks, and async/await frameworks schedule them using a framework-level scheduler. This raises a question: why do we need an extra scheduler when modern operating systems already provide schedulers for threads and processes?&lt;/p&gt;
&lt;p&gt;To answer that, let's look at older operating systems. In the early 1990s, before Mac OS was considered favorite choice for developers, the first operating systems supporting multitasking were introduced. That multitasking model was relatively simple and worked like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The operating system started a program.&lt;/li&gt;
&lt;li&gt;The program then told the operating system:
&lt;ol&gt;
&lt;li&gt;Please create a new window for me.&lt;/li&gt;
&lt;li&gt;When anything happens to the window (for example, the user clicks a button or resizes the window), call this callback function.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;The program returned control to the operating system.&lt;/li&gt;
&lt;li&gt;When an event occurred, the operating system invoked the program's callback function.&lt;/li&gt;
&lt;li&gt;The callback handled the event and returned control to the operating system.&lt;/li&gt;
&lt;li&gt;The operating system scheduled the next task.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/02/cooperative-multitasking.svg" class="img-fluid" alt="Cooperative Multitasking"&gt;&lt;/p&gt;
&lt;p&gt;This type of multitasking is called &lt;em&gt;cooperative multitasking&lt;/em&gt;. The operating system relies on the program to return control back to it. It may not look complicated today, but it was a big change in thinking for programmers who were used to having complete control over the computer at that time. The advantage of cooperative multitasking is that it has low overhead and is therefore very efficient. The disadvantage is that if a program does not return control to the operating system, the whole system becomes unresponsive. For example, if a program enters an infinite loop, the operating system will never get control back, and thus cannot handle any other programs. Same as today, programs had bugs that could cause such situations.&lt;/p&gt;
&lt;p&gt;To solve this problem, &lt;em&gt;preemptive multitasking&lt;/em&gt; was introduced. In preemptive multitasking, the operating system has a timer that interrupts running programs after a certain time slice. When interrupted, the operating system saves the state of the program, and then switches to another program. This way, even if a program does not return control back to the operating system, the operating system can still regain control after the time slice expires. This makes the system more robust and responsive. However, preemptive multitasking has higher overhead due to context switching and state saving.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/02/preemptive-multitasking.svg" class="img-fluid" alt="Preemptive Multitasking"&gt;&lt;/p&gt;
&lt;p&gt;Keep in mind that the same programming model with window event handlers is still used on modern operating systems with preemptive multitasking. The difference is that the OS can interrupt the program at any time, not just when the program yields.&lt;/p&gt;
&lt;p&gt;Now let's go back to async/await frameworks. Most applications using async/await frameworks assume that tasks are short-running and do not get stuck running indefinitely. Of course, there can still be bugs in the code and it is not possible to guarantee that a task will not get stuck. However, it is not expected that developers would write routines to handle such situations. In such cases, the usual approach is to restart the whole application. Thus, async/await frameworks can use a cooperative multitasking approach, where tasks are expected to return control back to the scheduler. This makes async/await frameworks more efficient, as they do not have to deal with frequent context switching and state saving.&lt;/p&gt;
&lt;p&gt;It is interesting that a use case (cooperative multitasking) that was considered problematic in early operating systems and replaced by a more robust approach (preemptive multitasking) is now being used in modern async/await frameworks. This shows that sometimes, old ideas can be revived and adapted to new contexts, providing efficient solutions to modern problems.&lt;/p&gt;
&lt;p&gt;Also, similar use cases can be seen in real-life scenarios. Constant meetings make software development less efficient because of the context switching. However, if stakeholders think that developers could get stuck on some tasks, then a more robust development process is worth the cost of context switching.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Quantum coin flipping</title>
			<link>http://duracellko.net/posts/2026/01/quantum-coin-flip</link>
			<description>&lt;p&gt;&lt;a href="http://duracellko.net/quantum-cryptography"&gt;The previous post&lt;/a&gt; showed that quantum networks can be as interesting as quantum computers. The main difference from classical networks is the ability to transmit qubits instead of classical bits. This post explores another interesting problem in network communication: Coin flipping.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2026/01/quantum-coin-flip</guid>
			<pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;a href="http://duracellko.net/quantum-cryptography"&gt;The previous post&lt;/a&gt; showed that quantum networks can be as interesting as quantum computers. The main difference from classical networks is the ability to transmit qubits instead of classical bits. This post explores another interesting problem in network communication: Coin flipping.&lt;/p&gt;
&lt;p&gt;The problem of coin flipping is to make a fair random decision between two choices. For example, Alice and Bob want to see a movie. Alice prefers the latest DC movie and Bob prefers the latest Marvel movie. They agree to flip a coin to decide which movie to watch. The problem is that they are in different parts of the city, so they cannot flip a physical coin together. Instead, they need to communicate over a network to agree on the coin flip result. The issue is that either Alice or Bob could cheat. For example, Alice could flip a coin and Bob announces &lt;em&gt;head&lt;/em&gt; as his guess. Alice could then simply say &lt;em&gt;tail&lt;/em&gt; even if the coin landed on &lt;em&gt;head&lt;/em&gt;. Or, if Alice announces the coin flip result first, Bob could claim that he guessed it correctly no matter what Alice announced.&lt;/p&gt;
&lt;p&gt;A solution could be to involve a trusted third party, Charlie. Alice and Bob can both send their guesses to Charlie, who flips a coin and announces the result. However, a trusted third party is not always available, or may not be trustworthy.&lt;/p&gt;
&lt;h2 id="random-choice"&gt;Random choice&lt;/h2&gt;
&lt;p&gt;Before looking at a coin flipping protocol, let's consider a simpler problem: random choice or a random function. In the real world, it is possible to flip a coin and have it randomly land on head or tail, ideally with a 50% probability for each side. In other words, we want a function that randomly returns either 0 or 1. However, mathematically, all functions are deterministic. This means that for a given input, the function always returns the same output. A solution is to use a secret counter as input for the random function. The counter is incremented for each call, so the input is always different. Since the counter is secret, the output appears random to an outside observer. The function should be complex enough that an observer cannot deduce anything about the counter from previous outputs to predict future outputs. However, as the function has a finite number of instructions, there is a certain number of outputs after which it becomes possible to predict future outputs. Of course, it is desirable for this number to be large enough to be practically unreachable. Therefore, such functions are called pseudorandom functions or pseudorandom number generators.&lt;/p&gt;
&lt;p&gt;Another solution is to introduce some randomness from outside the function. For example, today's CPUs have special hardware instructions that return random numbers based on physical processes inside the CPU. Such functions are called true random functions or true random number generators. These systems use physical noise that comes from outside the computer system, such as thermal or electrical noise.&lt;/p&gt;
&lt;p&gt;Even for this simpler problem, quantum mechanics can help. A qubit can be prepared at a 45° angle (↗️). Then it is &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-measurement"&gt;measured&lt;/a&gt; in the standard basis: x-axis (90°) and y-axis (0°). The measurement returns 0° or 90° with 50% probability each. We can assign bit 0 to 0° and bit 1 to 90°. Quantum measurement then returns a truly random bit.&lt;/p&gt;
&lt;h2 id="classical-coin-flipping"&gt;Classical coin flipping&lt;/h2&gt;
&lt;p&gt;As mentioned, if Alice reveals her coin flip result first, Bob can cheat by claiming that he guessed it correctly. If Bob reveals his guess first, Alice can cheat by announcing the opposite of Bob's guess. Therefore, Alice should reveal &lt;em&gt;something&lt;/em&gt; about the coin flip result. That &lt;em&gt;something&lt;/em&gt; must hide the actual result, so that Bob cannot deduce it and cheat. However, it must also be possible for Bob to verify that Alice did not change her mind when she reveals the actual result. In other words, Alice has to &lt;strong&gt;commit to a value&lt;/strong&gt;. That &lt;em&gt;something&lt;/em&gt; may be, for example, a cryptographic hash function like &lt;a href="https://en.wikipedia.org/wiki/SHA-3"&gt;SHA-3&lt;/a&gt;. So the protocol can look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice generates a large random number &lt;span class="math"&gt;\(x\)&lt;/span&gt; with at least as many bits as the output size of &lt;strong&gt;SHA-3&lt;/strong&gt; (for example, 256 bits).&lt;/li&gt;
&lt;li&gt;Alice sends the hash value &lt;span class="math"&gt;\(h = \mathrm{SHA3}(x)\)&lt;/span&gt; to Bob.&lt;/li&gt;
&lt;li&gt;Bob guesses if &lt;span class="math"&gt;\(x\)&lt;/span&gt; is even or odd and sends his guess to Alice. Notice that he cannot tell anything about &lt;span class="math"&gt;\(x\)&lt;/span&gt;, because it is hard to compute the inverse of the hash function.&lt;/li&gt;
&lt;li&gt;Alice sends &lt;span class="math"&gt;\(x\)&lt;/span&gt; to Bob. Bob computes the hash value and compares it with the value &lt;span class="math"&gt;\(h\)&lt;/span&gt; received in step 2. This way, he can verify that Alice did not change her mind.&lt;/li&gt;
&lt;li&gt;If Bob's guess matches the parity of &lt;span class="math"&gt;\(x\)&lt;/span&gt;, Bob wins; otherwise, Alice wins.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This protocol is secure as long as the hash function is secure. This means that it is hard to compute the inverse of the hash function and hard to find collisions (two different inputs that produce the same output). There may be other commitment functions based on other hard computational problems.&lt;/p&gt;
&lt;h2 id="quantum-coin-flipping"&gt;Quantum coin flipping&lt;/h2&gt;
&lt;p&gt;Is it possible to use the secrecy of quantum states to implement a coin flipping protocol without relying on hard computational problems? The answer is yes, it is possible. The communication should follow this protocol:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice randomly chooses a single basis: either horizontal/vertical (HV) or diagonal/antidiagonal (DA).&lt;/li&gt;
&lt;li&gt;Alice randomly chooses a string of bits and converts each bit to a qubit using the chosen basis.&lt;/li&gt;
&lt;/ol&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret bit&lt;/th&gt;
&lt;th&gt;Basis&lt;/th&gt;
&lt;th&gt;Qubit direction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;Horizontal (0°) ➡️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;Vertical (90°) ⬆️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;Diagonal (45°) ↗️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;Anti-diagonal (135°) ↘️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Alice sends the qubits to Bob.&lt;/li&gt;
&lt;li&gt;Bob randomly chooses a basis (HV or DA) for each qubit and measures them.&lt;/li&gt;
&lt;li&gt;Bob guesses the basis used by Alice (HV or DA) and sends his guess to Alice. If the guess is correct, Bob wins; otherwise, Alice wins.&lt;/li&gt;
&lt;li&gt;Alice reveals the basis she used and the original bits.&lt;/li&gt;
&lt;li&gt;Bob takes the measurements from step 4 where his basis was the same as Alice's basis. He compares the measurement results with the original bits revealed by Alice. If they match, Bob accepts the result; otherwise, he accuses Alice of cheating.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The following example shows how the protocol works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice chooses the horizontal/vertical basis (HV) - ➡️⬆️.&lt;/li&gt;
&lt;li&gt;Alice chooses random bits 1, 0, 1, 1, 0 and encodes them to qubits: ⬆️➡️⬆️⬆️➡️.&lt;/li&gt;
&lt;li&gt;Alice sends the qubits to Bob.&lt;/li&gt;
&lt;li&gt;Bob measures the qubits in random bases:&lt;/li&gt;
&lt;/ol&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Row&lt;/th&gt;
&lt;th&gt;Qubit&lt;/th&gt;
&lt;th&gt;Bob's basis&lt;/th&gt;
&lt;th&gt;Bob's measurement&lt;/th&gt;
&lt;th&gt;Probability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;⬆️&lt;/td&gt;
&lt;td&gt;HV ➡️⬆️&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;➡️&lt;/td&gt;
&lt;td&gt;HV ➡️⬆️&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;⬆️&lt;/td&gt;
&lt;td&gt;DA ↗️↘️&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;⬆️&lt;/td&gt;
&lt;td&gt;HV ➡️⬆️&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;➡️&lt;/td&gt;
&lt;td&gt;DA ↗️↘️&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Bob guesses basis DA.&lt;/li&gt;
&lt;li&gt;Alice announces that Bob guessed incorrectly and sends him the original bits.&lt;/li&gt;
&lt;li&gt;Bob now takes his measurements from rows 1, 2, and 4, where his basis matched Alice's basis. He compares them with the original bits 1, 0, and 1. They match, so Bob accepts the result.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If Bob wants to cheat, he would need to find the actual angle of a qubit. For example, if Bob measures 1, the qubit could be either ⬆️ (0°) or ↗️ (45°). There is no way for Bob to distinguish between the two possibilities with a single measurement. Therefore, Bob cannot reliably guess Alice's basis.&lt;/p&gt;
&lt;p&gt;Alice may have an even harder job cheating. If she wants to change her basis after Bob sends his guess, she would need to know all of Bob's measurements. For example, if Alice sent qubit ➡️ (90°) and wanted to convince Bob that it was diagonal ↗️ or ↘️, Bob would measured it in the DA basis and he could have obtained 0 or 1 with 50% probability. There is no way for Alice to know which one Bob obtained. Therefore, she cannot reliably change her basis and original bits to match Bob's measurements.&lt;/p&gt;
&lt;h2 id="network-routing"&gt;Network routing&lt;/h2&gt;
&lt;p&gt;I presented the problem of sending a secret message and coin flipping over a quantum network. I could present the protocols using a simple angle representation of qubits without needing knowledge about vector spaces or matrices. However, you may notice that both protocols require a direct line to send qubits between the parties. This is rarely the case with classical networks, where messages are routed through multiple intermediate nodes, e.g., through a WiFi router, ISP, Internet backbone, or mail server. It is possible to route qubits through multiple intermediate nodes as well. The intermediate nodes would need to perform &lt;a href="https://en.wikipedia.org/wiki/Quantum_teleportation"&gt;quantum teleportation&lt;/a&gt; to forward the qubits to the next node. This is out of scope for this post, as it requires understanding of &lt;a href="https://en.wikipedia.org/wiki/Quantum_entanglement"&gt;quantum entanglement&lt;/a&gt;. The problem is that quantum teleportation requires transferring classical bits that hold certain information about the qubit being teleported. I suspect that this information could leak some details about the qubit, which could compromise the security of the protocols.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Quantum cryptography</title>
			<link>http://duracellko.net/posts/2026/01/quantum-cryptography</link>
			<description>&lt;p&gt;In &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-problems"&gt;the previous post&lt;/a&gt;, I presented some computational problems that can be solved more efficiently by quantum computers. Today, however, computers are not only used for computations but also for solving various types of problems. In fact, most computer applications today perform minimal computation and primarily store, transfer, and transform data. Are there any non-computational problems that are difficult for classical computers but can be solved efficiently by quantum computers? In following posts, I will present examples of such problems in the field of computer networks.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2026/01/quantum-cryptography</guid>
			<pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;In &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-problems"&gt;the previous post&lt;/a&gt;, I presented some computational problems that can be solved more efficiently by quantum computers. Today, however, computers are not only used for computations but also for solving various types of problems. In fact, most computer applications today perform minimal computation and primarily store, transfer, and transform data. Are there any non-computational problems that are difficult for classical computers but can be solved efficiently by quantum computers? In following posts, I will present examples of such problems in the field of computer networks.&lt;/p&gt;
&lt;h2 id="qubit-secret-properties"&gt;Qubit secret properties&lt;/h2&gt;
&lt;p&gt;Before presenting specific problems, let's examine some properties of qubits. As mentioned, a &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-qubit"&gt;qubit&lt;/a&gt; is a rotation angle, which can also be represented by a unit vector.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/qubit-direction.svg" class="img-fluid" alt="Qubit direction"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-measurement"&gt;Measuring a qubit&lt;/a&gt; requires specifying a basis or direction in which to measure the qubit. For example, in the picture above, the direction is specified by the x-axis and y-axis. The measurement will return either the x-axis or y-axis with certain probabilities. Thus, it is possible to observe one of two possible values. Interestingly, it is physically impossible to obtain the actual angle that represents the qubit's value. This is impossible for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After measurement, the qubit's value collapses to either the x-axis or y-axis direction. The original value is lost, and it is not possible to perform further measurements to recover the original value.&lt;/li&gt;
&lt;li&gt;Even if multiple measurements were possible, the angle is a real number with infinite precision. Therefore, an infinite number of measurements would be required to obtain the exact value.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means a qubit can store a secret value that cannot be retrieved. Can this property be used to transfer secret information securely over a network?&lt;/p&gt;
&lt;h2 id="quantum-cryptography"&gt;Quantum cryptography&lt;/h2&gt;
&lt;p&gt;Consider the following problem: Alice wants to send a secret message to Bob over an insecure network. An eavesdropper, Eve, can read all messages sent over the network. How can Alice and Bob communicate securely so that Eve cannot read the secret message? This problem is typically solved by &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-problems#public-key-cryptography"&gt;public key cryptography&lt;/a&gt;. Classical public key cryptography transforms the secret message using a function that is easy to compute, but whose inverse is hard to compute. A well-known example is the &lt;a href="https://en.wikipedia.org/wiki/RSA_cryptosystem"&gt;RSA&lt;/a&gt; algorithm, which relies on the difficulty of factoring large numbers. Unfortunately, this problem is easy for quantum computers, as explained in the &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-problems"&gt;Problems for Quantum Computing&lt;/a&gt; post. Therefore, the security of such cryptosystems is threatened by quantum computers. Additionally, there is no formal proof that the number factorization problem is truly hard. It is possible that future advancements in computer science may yield an efficient algorithm for classical computers, breaking the security of the cryptosystem.&lt;/p&gt;
&lt;p&gt;Is it possible to send a secret message securely by transmitting qubits instead of classical bits? Before answering, note that public key cryptography is not used directly to send secret messages. Instead, it is used to exchange a secret key, which is then used to encrypt and decrypt actual messages using symmetric key cryptography, such as &lt;a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard"&gt;AES&lt;/a&gt;. The real problem is securely exchanging a secret key over an insecure network. Public key cryptography algorithms are much slower and have limitations on message size (e.g., 125 bytes). Quantum cryptography also has limitations that prevent direct use of qubits for sending secret messages.&lt;/p&gt;
&lt;h2 id="bb84"&gt;BB84&lt;/h2&gt;
&lt;p&gt;The first quantum cryptography protocol was introduced in 1984 by Charles Bennett and Gilles Brassard, and is known as &lt;a href="https://en.wikipedia.org/wiki/BB84"&gt;BB84&lt;/a&gt;. Let's start with a single bit. Alice wants to send a random secret bit (0 or 1) to Bob. She generates another random bit to specify the basis rotation: either horizontal/vertical (HV) or diagonal (DA). She then prepares a qubit according to the following table:&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret bit&lt;/th&gt;
&lt;th&gt;Basis bit&lt;/th&gt;
&lt;th&gt;Qubit direction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;Horizontal (90°) ➡️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;Vertical (0°) ⬆️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;Diagonal (45°) ↗️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;Anti-diagonal (135°) ↘️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;She sends the prepared qubit to Bob over the insecure network. Bob also randomly chooses a basis bit (HV or DA) and measures the received qubit using his chosen basis. If Bob's basis bit matches Alice's, he will obtain the correct secret bit with 100% probability. If the basis bits do not match, Bob will obtain either 0 or 1 with 50% probability. The following table summarizes the possible cases:&lt;/p&gt;
&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret bit&lt;/th&gt;
&lt;th&gt;Alice's Basis&lt;/th&gt;
&lt;th&gt;Qubit&lt;/th&gt;
&lt;th&gt;Bob's Basis&lt;/th&gt;
&lt;th&gt;Measured Bit&lt;/th&gt;
&lt;th&gt;Probability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;➡️&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;➡️&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;td&gt;50% each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;⬆️&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0 (HV)&lt;/td&gt;
&lt;td&gt;⬆️&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;td&gt;50% each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;↗️&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;td&gt;50% each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;↗️&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;↘️&lt;/td&gt;
&lt;td&gt;0 (HV) ➡️⬆️&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;td&gt;50% each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (DA)&lt;/td&gt;
&lt;td&gt;↘️&lt;/td&gt;
&lt;td&gt;1 (DA) ↗️↘️&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;After Bob confirms his measurement, Alice sends her basis bit to Bob over the classical network. Bob compares his basis bit with Alice's. If the basis bits match, both keep the measured bit as part of the secret key. If not, they discard the measured bit. After repeating this process multiple times, Alice and Bob will have a shared secret key consisting of bits where their basis bits matched.&lt;/p&gt;
&lt;p&gt;What if Eve intercepts and measures the qubits sent from Alice to Bob? She faces the same situation as Bob and must randomly choose a basis for measurement. For example, if Alice sends a horizontal qubit ➡️ and Eve measures it in the HV basis, she obtains the correct secret bit 0 and the qubit remains unchanged. However, if she measures it in the DA basis, she obtains either 0 or 1 with 50% probability, and the qubit changes to either diagonal ↗️ or anti-diagonal ↘️. When Eve then sends the qubit to Bob, there is a chance that Bob's measurement will not match Alice's original secret bit, even if his basis matches Alice's. This effect is explained in the experiment with three polarization filters in the &lt;a href="http://duracellko.net/posts/2025/10/quantum-computing-measurement"&gt;Quantum computing measurement&lt;/a&gt; post.&lt;/p&gt;
&lt;p&gt;After exchanging some secret bits via qubits, Alice and Bob should randomly select some bits and compare them over the classical network. If there is no Eve, all compared bits should match. If there is an eavesdropper, some bits will not match due to Eve's interference. If the number of mismatched bits exceeds a certain threshold, Alice and Bob can conclude that there is an eavesdropper and discard the entire key. Otherwise, they can use the remaining bits as their shared secret key.&lt;/p&gt;
&lt;p&gt;Which bits to surrender and compare should be decided randomly after the key exchange to prevent Eve from predicting which bits to measure and which bits to leave untouched.&lt;/p&gt;
&lt;p&gt;Compared to classical public key cryptography, BB84 allows exchanging secret keys of any length. If Alice and Bob want to exchange &lt;span class="math"&gt;\(N\)&lt;/span&gt; secret bits, they must exchange approximately &lt;span class="math"&gt;\(4N\)&lt;/span&gt; qubits: &lt;span class="math"&gt;\(2N\)&lt;/span&gt; qubits because half the time Bob's basis will not match Alice's, and another &lt;span class="math"&gt;\(2N\)&lt;/span&gt; qubits to surrender for eavesdropper detection. Additionally, they need to exchange &lt;span class="math"&gt;\(4N\)&lt;/span&gt; bits over the classical network to share basis bits and another &lt;span class="math"&gt;\(4N\)&lt;/span&gt; bits to compare the surrendered bits. This is a reasonable cost for securely exchanging secret keys of any length.&lt;/p&gt;
&lt;p&gt;This means Alice and Bob can exchange a secret key of the same length as the message they want to send securely. They can then use &lt;a href="https://en.wikipedia.org/wiki/One-time_pad"&gt;one-time pad&lt;/a&gt; symmetric key cryptography, which is very simple and theoretically unbreakable.&lt;/p&gt;
&lt;h2 id="limitations"&gt;Limitations&lt;/h2&gt;
&lt;p&gt;One important limitation is that the BB84 protocol requires online communication between Alice and Bob. Unlike public key cryptography, where Alice can encrypt a message and send it to Bob's mailbox for later decryption, BB84 requires both parties to be online simultaneously to exchange qubits and classical bits.&lt;/p&gt;
&lt;p&gt;Another limitation is the need for authenticity of the classical bits. When Bob receives Alice's basis bits and the surrendered bits for comparison, he must be able to verify they are actually from Alice and not from Eve. Otherwise, Eve could impersonate Alice and send false bases and comparison bits, making it impossible for Bob to detect her presence. This problem can be solved by using classical public key cryptography to sign the classical messages. Digital signatures may be required anyway, so Bob can verify that the secret message is actually from Alice, and vice versa.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;I presented the problem of sending a secret message over an insecure network. This problem is reasonably solved by public key cryptography today. However, public key cryptography is not formally proven to be secure. Using quantum communication, it is possible to exchange a secret message that is formally proven to be theoretically unbreakable.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>async/await question</title>
			<link>http://duracellko.net/posts/2026/01/async-await</link>
			<description>&lt;p&gt;When interviewing a candidate, I usually ask them to tell me something about async/await as we know it in modern languages like C# or TypeScript. What does it do? Why do we use it? Most people talk about technical details and threads. Some of these details are correct, some are not. However, I have never heard anyone mention the problem that led to the inception of the async/await concept.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2026/01/async-await</guid>
			<pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;When interviewing a candidate, I usually ask them to tell me something about async/await as we know it in modern languages like C# or TypeScript. What does it do? Why do we use it? Most people talk about technical details and threads. Some of these details are correct, some are not. However, I have never heard anyone mention the problem that led to the inception of the async/await concept.&lt;/p&gt;
&lt;p&gt;At university, one of the modules I studied was Parallel Algorithms. The module covered questions like, "What kind of problems can be solved more efficiently on multiple processors?" and "How can we design efficient parallel algorithms?" And the first lecture presented question: "Does the number of available processors impact the design of a parallel algorithm?" This question is very important because, when tasked with designing a parallel algorithm, we usually don't know how many processors will be available. The answer is "No." The number of processors does not impact the design of a parallel algorithm. How does that work? When designing a parallel algorithm, we split the computational work into small tasks. Unfortunately, those tasks may have dependencies on one another.&lt;/p&gt;
&lt;p&gt;For example, consider an algorithm to sum the numbers in an array of length &lt;span class="math"&gt;\(N\)&lt;/span&gt;. The algorithm creates &lt;span class="math"&gt;\(N\)&lt;/span&gt; tasks, and each task adds the value at a specific index to the result of the previous task, starting with the value at index 0: &lt;span class="math"&gt;\(T(i) = T(i-1) + A[i]\)&lt;/span&gt; where &lt;span class="math"&gt;\(T(0) = A[0]\)&lt;/span&gt;. The final result is &lt;span class="math"&gt;\(T(N-1)\)&lt;/span&gt;. The problem is that each task depends on the previous one, so there are no two tasks that can run in parallel. The following image presents the tasks for an array of length 5. The arrows represent dependencies between tasks.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/01/sum-algorithm-sequential.png" class="img-fluid" alt="Sequential algorithm to sum up numbers in array"&gt;&lt;/p&gt;
&lt;p&gt;To take advantage of multiple processors, we need to change the dependencies between the tasks. We can do this by making the algorithm work in rounds. In the first round, each task adds two values from the array. In the second round, each task adds two values from the results of the previous round, and so on. The following image presents the tasks for an array of length 10. The arrows represent dependencies between tasks.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2026/01/sum-algorithm-parallel.png" class="img-fluid" alt="Parallel algorithm to sum up numbers in array"&gt;&lt;/p&gt;
&lt;p&gt;How does this work with a different number of processors? To run the algorithm, we need a scheduler that assigns tasks to processors. The scheduler can work quite simply: it takes tasks in order &lt;span class="math"&gt;\(T(0), T(1), \ldots\)&lt;/span&gt; and assigns each task to the next available processor. However, a task is scheduled only after all its dependencies are finished. If there are &lt;span class="math"&gt;\(N\)&lt;/span&gt; processors available, then there is always a processor available for each task. If there are &lt;span class="math"&gt;\(m\)&lt;/span&gt; processors available, where &lt;span class="math"&gt;\(m &amp;lt; N\)&lt;/span&gt;, then the scheduler assigns the first &lt;span class="math"&gt;\(m\)&lt;/span&gt; tasks to processors. As soon as any task is finished, it takes the next task and assigns it to the available processor. This way, we don't have to design a parallel algorithm for a specific number of processors. The scheduler maps any algorithm to the available processors.&lt;/p&gt;
&lt;p&gt;And that is exactly what async/await frameworks do. They provide a programming language construct to split a procedure, function, or method into small tasks. The framework then implements a scheduler that assigns tasks to available processors, or technically, threads. So the framework provides an abstraction for engineers to write parallel procedures without worrying about the number of available processors.&lt;/p&gt;
&lt;p&gt;Historically, processors used to have only a single core. At some point, this changed and multi-core processors became commercially available. There was a need for a framework to bring those multiple cores to software developers without requiring them to know how many cores the target system would have. That was the motivation for creating async/await frameworks.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Problems for Quantum Computing</title>
			<link>http://duracellko.net/posts/2025/10/quantum-computing-problems</link>
			<description>&lt;p&gt;In &lt;a href="http://duracellko.net/quantum-computing-nondeterminism"&gt;the previous post&lt;/a&gt;, I explained nondeterministic Turing machines and how quantum computers can simulate them for certain problems. It was mentioned that simulation is possible using Quantum Fourier Transform when solutions are periodic. In this post, I will present some examples and other use cases of quantum computing.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2025/10/quantum-computing-problems</guid>
			<pubDate>Tue, 28 Oct 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;In &lt;a href="http://duracellko.net/quantum-computing-nondeterminism"&gt;the previous post&lt;/a&gt;, I explained nondeterministic Turing machines and how quantum computers can simulate them for certain problems. It was mentioned that simulation is possible using Quantum Fourier Transform when solutions are periodic. In this post, I will present some examples and other use cases of quantum computing.&lt;/p&gt;
&lt;p&gt;The previous post already mentioned an example of a problem that can be efficiently solved by a quantum computer: &lt;strong&gt;Order finding&lt;/strong&gt;. This is the problem of finding the smallest integer &lt;span class="math"&gt;\(r\)&lt;/span&gt; such that &lt;span class="math"&gt;\(x^r = 1 \mod N\)&lt;/span&gt;, where &lt;span class="math"&gt;\(x\)&lt;/span&gt; and &lt;span class="math"&gt;\(N\)&lt;/span&gt; are given integers. Probably the most famous application of order finding is &lt;a href="https://en.wikipedia.org/wiki/Integer_factorization"&gt;Integer factorization&lt;/a&gt;. This is the problem of finding prime factors for a given integer &lt;span class="math"&gt;\(N\)&lt;/span&gt;, meaning finding prime numbers &lt;span class="math"&gt;\(q_1, q_2, \ldots, q_n\)&lt;/span&gt; such that &lt;span class="math"&gt;\(q_1 q_2 \ldots q_n = N\)&lt;/span&gt;. There is a quantum algorithm called &lt;a href="https://en.wikipedia.org/wiki/Shor%27s_algorithm"&gt;Shor's algorithm&lt;/a&gt; that can solve integer factorization efficiently by reducing it to order finding.&lt;/p&gt;
&lt;p&gt;Another example of a problem that can be efficiently solved by a quantum computer is &lt;strong&gt;Discrete logarithm&lt;/strong&gt;. This is the problem of finding an integer &lt;span class="math"&gt;\(k\)&lt;/span&gt; such that &lt;span class="math"&gt;\(g^k = x \mod p\)&lt;/span&gt;, where &lt;span class="math"&gt;\(g\)&lt;/span&gt;, &lt;span class="math"&gt;\(x\)&lt;/span&gt;, and &lt;span class="math"&gt;\(p\)&lt;/span&gt; are given integers, and &lt;span class="math"&gt;\(p\)&lt;/span&gt; is prime. This problem can be solved by a modification of Shor's algorithm.&lt;/p&gt;
&lt;h2 id="public-key-cryptography"&gt;Public key cryptography&lt;/h2&gt;
&lt;p&gt;Shor's algorithm has a significant impact on information security, specifically on public key cryptography. The most common use of public key cryptography is &lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"&gt;Transport Layer Security&lt;/a&gt; (TLS), which is used to secure communication over the Internet. Public key cryptography solves two main problems: secure key exchange and digital signatures. Secure key exchange provides confidentiality in information security by allowing two parties to agree on a shared secret key over an insecure channel. Digital signatures provide integrity by allowing one party to sign a message in a way that anyone can verify the signature, but only the signer can create it. It means that a sender of a message cannot deny having sent the message.&lt;/p&gt;
&lt;p&gt;Currently, the most common cryptosystems in public key cryptography are &lt;a href="https://en.wikipedia.org/wiki/RSA_cryptosystem"&gt;RSA&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Elliptic-curve_cryptography"&gt;Elliptic-curve cryptography&lt;/a&gt;. The security of RSA is based on the assumption that integer factorization is a hard problem, and elliptic-curve cryptography assumes that the discrete logarithm is a hard problem. While it is believed that these problems are hard to solve on classical computers, Shor's algorithm solves them efficiently using quantum computers. This means that current computer systems secured by public key cryptography are vulnerable to quantum computer attacks. At the moment, quantum computers do not have enough qubits to match the key sizes used by current cryptosystems. However, it is expected that quantum computers will become powerful enough in the future.&lt;/p&gt;
&lt;p&gt;Therefore, there is an ongoing effort to develop and standardize new cryptographic algorithms that are secure against quantum computer attacks. This field is called &lt;a href="https://en.wikipedia.org/wiki/Post-quantum_cryptography"&gt;Post-quantum cryptography&lt;/a&gt;. &lt;a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf"&gt;FIPS 203&lt;/a&gt; standard has been published and it specifies post-quantum cryptographic algorithms for both secure key exchange and digital signatures. These cryptosystems are based on module-lattice based problems. These problems were studied in the field of Machine Learning and are believed to be hard to solve even for quantum computers.&lt;/p&gt;
&lt;h2 id="period-finding"&gt;Period finding&lt;/h2&gt;
&lt;p&gt;Order finding is a special case of a more general problem called &lt;strong&gt;Period finding&lt;/strong&gt;. This is the problem of finding the period &lt;span class="math"&gt;\(r\)&lt;/span&gt; of a periodic function &lt;span class="math"&gt;\(f(x)\)&lt;/span&gt; such that &lt;span class="math"&gt;\(f(x) = f(x + r)\)&lt;/span&gt; for all &lt;span class="math"&gt;\(x\)&lt;/span&gt;. The function is given as a black box, so the only way to get its value is to call the black box with some input. The goal is to find the period &lt;span class="math"&gt;\(r\)&lt;/span&gt; using as few calls to the black box as possible. It should be clear that order finding is a special case of period finding, where the function is defined as &lt;span class="math"&gt;\(f(x) = a^x \mod N\)&lt;/span&gt; for a given &lt;span class="math"&gt;\(a\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Like order finding, period finding can also be solved efficiently using quantum computers. The algorithm using Quantum Fourier Transform can be generalized to achieve this.&lt;/p&gt;
&lt;p&gt;A generalization of the period finding problem is the &lt;a href="https://en.wikipedia.org/wiki/Hidden_subgroup_problem"&gt;Hidden subgroup problem&lt;/a&gt;. Unfortunately, it is not known how to efficiently solve all instances of the hidden subgroup problem using quantum computers, and it is believed that some instances are hard to solve even for quantum computers. For example, &lt;a href="https://en.wikipedia.org/wiki/Learning_with_errors"&gt;Learning with errors&lt;/a&gt; problem can be reduced to some instances of the hidden subgroup problem, and it is believed that learning with errors is hard to solve even for quantum computers. Learning with errors is the basis of some post-quantum cryptographic algorithms, and it likely would not be considered for post-quantum cryptography if it were not believed to be hard for quantum computers.&lt;/p&gt;
&lt;h2 id="quantum-search"&gt;Quantum search&lt;/h2&gt;
&lt;p&gt;In the previous post, I explained that a nondeterministic computing machine can branch its calculation into multiple paths. While classical computers have to evaluate all the paths, quantum computers can find the right path efficiently with high probability in certain cases.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/nondeterministic-computation.png" class="img-fluid" alt="Nondeterministic computation"&gt;&lt;/p&gt;
&lt;p&gt;There is another approach to simulation of nondeterministic computing machines using quantum computers. This approach is called quantum search and it uses &lt;a href="https://en.wikipedia.org/wiki/Grover%27s_algorithm"&gt;Grover's algorithm&lt;/a&gt;. The result is not as impressive as using Quantum Fourier Transform for periodic problems, but it is still better than using classical computers. Quantum Fourier Transform reveals the right path and is equivalent to the computation of a single path. However, Grover's algorithm requires multiple runs and is equivalent to the computation of multiple paths. Specifically, it requires &lt;span class="math"&gt;\(\sqrt{N}\)&lt;/span&gt; computations, where &lt;span class="math"&gt;\(N\)&lt;/span&gt; is the number of all possible paths.&lt;/p&gt;
&lt;p&gt;For example, consider the problem of finding a secret key for &lt;a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard"&gt;Advanced Encryption Standard&lt;/a&gt; (AES) with a key size of 256 bits. There are &lt;span class="math"&gt;\(2^{256}\)&lt;/span&gt; possible keys, and a classical computer would have to try on average half of them, which is &lt;span class="math"&gt;\(2^{255}\)&lt;/span&gt;. A quantum computer using Grover's algorithm would require only &lt;span class="math"&gt;\(\sqrt{2^{256}} = 2^{128}\)&lt;/span&gt; tries. So quantum computers effectively halve the size of the secret key. AES is still considered secure for post-quantum cryptography, but it is recommended to use AES with a key size of 256 bits or more.&lt;/p&gt;
&lt;p&gt;Unfortunately, I did not find a simple illustration or analogy to present quantum search or Grover's algorithm. Books and the Wikipedia article has some geometric explanation, but that requires some knowledge of vector spaces.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;While a smart way has been found to simulate some nondeterministic Turing machines efficiently using quantum computers, it is believed that quantum computers cannot simulate all nondeterministic Turing machines efficiently in general, so there are hard problems that cannot be efficiently solved by quantum computers.&lt;/p&gt;
&lt;p&gt;So far, the biggest success in quantum computing is showing that quantum computers can efficiently find the period of a periodic function, when finding the solution using classical computers would require calculating the function for almost all possible inputs. It is possible that there are other kinds of problems that can be efficiently solved by quantum computers, but they are not known yet. As quantum computations work quite counterintuitively, it requires a lot of ingenuity to find new approaches for quantum algorithms.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Quantum Computing and Nondeterminism</title>
			<link>http://duracellko.net/posts/2025/10/quantum-computing-nondeterminism</link>
			<description>&lt;p&gt;Some time ago, I set out on a journey to understand "What kinds of problems can quantum computation solve efficiently?" I decided to write a series of blog posts to explain this as simply as possible. In previous posts, I discussed the &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;qubit&lt;/a&gt; and &lt;a href="http://duracellko.net/quantum-computing-measurement"&gt;measurement&lt;/a&gt;. In this post, I will finally answer the question. The most common answer is, "It can run computations in parallel." However, as I showed in the previous post about &lt;a href="http://duracellko.net/quantum-computing-measurement"&gt;measurement&lt;/a&gt;, that is not exactly true. A quantum computer cannot simulate a computer with the enormous number of processors. However, it can simulate a different kind of machine in some special cases: the &lt;a href="https://en.wikipedia.org/wiki/Nondeterministic_Turing_machine"&gt;Nondeterministic Turing machine&lt;/a&gt;.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2025/10/quantum-computing-nondeterminism</guid>
			<pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Some time ago, I set out on a journey to understand "What kinds of problems can quantum computation solve efficiently?" I decided to write a series of blog posts to explain this as simply as possible. In previous posts, I discussed the &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;qubit&lt;/a&gt; and &lt;a href="http://duracellko.net/quantum-computing-measurement"&gt;measurement&lt;/a&gt;. In this post, I will finally answer the question. The most common answer is, "It can run computations in parallel." However, as I showed in the previous post about &lt;a href="http://duracellko.net/quantum-computing-measurement"&gt;measurement&lt;/a&gt;, that is not exactly true. A quantum computer cannot simulate a computer with the enormous number of processors. However, it can simulate a different kind of machine in some special cases: the &lt;a href="https://en.wikipedia.org/wiki/Nondeterministic_Turing_machine"&gt;Nondeterministic Turing machine&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="nondeterministic-computing-machine"&gt;Nondeterministic Computing machine&lt;/h2&gt;
&lt;p&gt;I will not go into the details of &lt;a href="https://en.wikipedia.org/wiki/Turing_machine"&gt;Turing machines&lt;/a&gt;, but I need to describe nondeterminism in computing machines. A nondeterministic computing machine (my own term to avoid details of the Turing machine) is a theoretical or hypothetical machine. One perspective is that the machine can solve problems for which it is easy to verify a solution, but hard to find one. For example, it is easy to verify that an integer &lt;span class="math"&gt;\(a\)&lt;/span&gt; divided by integer &lt;span class="math"&gt;\(b\)&lt;/span&gt; has remainder 0, but it is hard to find such number &lt;span class="math"&gt;\(b\)&lt;/span&gt;. By "hard," I mean it takes a lot of computation steps.&lt;/p&gt;
&lt;p&gt;A procedure for a deterministic (classical) computing machine is described as a sequence of steps. After each step, the machine is in a specific state, and the state determines the next step. For example, when iterating through a list of items, the state can be the index of the current item. If the index is less than the length of the list, the next step is to increase the index by one. Otherwise, the next step is to stop the iteration. The following image presents deterministic computation for finding the maximum number in a list. Rectangles represent computation states, and rounded rectangles represent steps. The important point is that there is always exactly one next step.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/deterministic-computation.png" class="img-fluid" alt="Deterministic computation of finding maximum"&gt;&lt;/p&gt;
&lt;p&gt;A procedure for a nondeterministic machine can have multiple next steps after each step. You can think of it as computation splitting and following multiple paths. For example, the following image presents nondeterministic computation for finding a number that divides 15 without remainder.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/nondeterministic-factoring.png" class="img-fluid" alt="Nondeterministic computation of finding dividing number"&gt;&lt;/p&gt;
&lt;p&gt;Problems to be solved by nondeterministic machines are usually presented as decision problems, meaning the output is either &lt;em&gt;yes&lt;/em&gt; or &lt;em&gt;no&lt;/em&gt;. The output is &lt;em&gt;yes&lt;/em&gt; if any of the computation paths outputs &lt;em&gt;yes&lt;/em&gt;. If all paths output &lt;em&gt;no&lt;/em&gt;. then the final output is &lt;em&gt;no&lt;/em&gt;. As mentioned, a nondeterministic computing machine is hypothetical, and there can be different actual realizations of such machine. One option is to try all computation paths one by one in sequence. However, for large inputs, there can be a huge number of computation paths. For example, if the input was a 128-bit number, there would be &lt;span class="math"&gt;\(2^{128}\)&lt;/span&gt; possible computation paths. That is more than the estimated number of atoms in the universe. So that would take too long. Another option is to have multiple processors and try multiple paths in parallel. Parallelism should be easy, because the paths do not have any dependencies. However, it would require an enormous number of processors.&lt;/p&gt;
&lt;h2 id="quantum-computer-and-nondeterminism"&gt;Quantum computer and nondeterminism&lt;/h2&gt;
&lt;p&gt;Can a quantum computer simulate nondeterministic computing machines efficiently? Yes, for some problems. Recall from the &lt;a href="http://duracellko.net/quantum-computing-measurement"&gt;measurement&lt;/a&gt; post that multiple qubits can be in superposition of multiple states or numbers with certain probabilities before measurement. Only after the measurement do the qubits collapse to exactly one of the states. It should be possible to map nondeterministic states to quantum states. Then, a quantum computer can compute on the superposition of all nondeterministic states. The following image presents nondeterministic computation of 5 steps with 2 branches at each step, resulting in 32 possible final states. A quantum computer can follow the same computation paths and end in a superposition of 32 final states.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/nondeterministic-computation.png" class="img-fluid" alt="Nondeterministic computation"&gt;&lt;/p&gt;
&lt;p&gt;If each of the 32 states has approximately the same probability &lt;span class="math"&gt;\(\frac{1}{32}\)&lt;/span&gt;, then measurement will return any of the states with approximately the same probability. In this case, there are 2 &lt;em&gt;yes&lt;/em&gt; states and 30 &lt;em&gt;no&lt;/em&gt; states. Therefore, measurement will return &lt;em&gt;yes&lt;/em&gt; with probability &lt;span class="math"&gt;\(\frac{2}{32}\)&lt;/span&gt; and &lt;em&gt;no&lt;/em&gt; with probability &lt;span class="math"&gt;\(\frac{30}{32}\)&lt;/span&gt;. So it is much more likely to output &lt;em&gt;no&lt;/em&gt; than &lt;em&gt;yes&lt;/em&gt;. But &lt;em&gt;no&lt;/em&gt; is the wrong answer, because there is at least one &lt;em&gt;yes&lt;/em&gt; path.&lt;/p&gt;
&lt;p&gt;Can we manipulate the probabilities so that all &lt;em&gt;no&lt;/em&gt; states have almost zero probability? If we can do that, then the quantum computer could simulate a nondeterministic computing machine efficiently with high probability.&lt;/p&gt;
&lt;h2 id="quantum-fourier-transform"&gt;Quantum Fourier Transform&lt;/h2&gt;
&lt;p&gt;It seems possible to achieve almost zero probability for &lt;em&gt;no&lt;/em&gt; states when each &lt;span class="math"&gt;\(k\)&lt;/span&gt;-th state is &lt;em&gt;yes&lt;/em&gt; state and there are no other &lt;em&gt;yes&lt;/em&gt; states. For example, the following image presents nondeterministic computation where every 5th path outputs &lt;em&gt;yes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/nondeterministic-computation-periodic-states.png" class="img-fluid" alt="Nondeterministic computation with periodic yes states"&gt;&lt;/p&gt;
&lt;p&gt;An example of such a problem is &lt;strong&gt;Order finding&lt;/strong&gt;. This is the problem of finding the smallest integer &lt;span class="math"&gt;\(r\)&lt;/span&gt; such that &lt;span class="math"&gt;\(x^r = 1 \mod N\)&lt;/span&gt;, where &lt;span class="math"&gt;\(x\)&lt;/span&gt; and &lt;span class="math"&gt;\(N\)&lt;/span&gt; are given integers. It is easy to verify the solution, but hard to find it. However, the &lt;em&gt;yes&lt;/em&gt; states are periodic, occurring at positions &lt;span class="math"&gt;\(0, r, 2r, 3r, \ldots\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;If this condition is satisfied, then we can use a method called the &lt;a href="https://en.wikipedia.org/wiki/Quantum_Fourier_transform"&gt;Quantum Fourier Transform&lt;/a&gt;. I will explain how the method works using a simplified example. You may be familiar with &lt;a href="https://en.wikipedia.org/wiki/Alternating_current"&gt;alternating current&lt;/a&gt;, which is the electricity available in wall sockets in every household. The voltage of alternating current changes periodically and can be represented as a sine wave.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/alternating-current.png" class="img-fluid" alt="Alternating current"&gt;&lt;/p&gt;
&lt;p&gt;While there are only 2 wires going to the wall socket, there are 6 wires coming to each household. This is because the electricity provider generates 3 alternating currents, each with the same frequency but shifted by 120° or &lt;span class="math"&gt;\(\frac{2\pi}{3}\)&lt;/span&gt;. This is called 3-phase alternating current. The following image shows 3 sine waves representing the 3 phases of alternating current.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/3-phase-alternating-current.png" class="img-fluid" alt="3-phase alternating current"&gt;&lt;/p&gt;
&lt;p&gt;The phase functions are:&lt;/p&gt;
&lt;div class="math"&gt;
\[
f_1(x) = \sin x
\]&lt;/div&gt;
&lt;div class="math"&gt;
\[
f_2(x) = \sin \left(x - \frac{2\pi}{3}\right)
\]&lt;/div&gt;
&lt;div class="math"&gt;
\[
f_3(x) = \sin \left(x + \frac{2\pi}{3}\right)
\]&lt;/div&gt;
&lt;p&gt;Notice that&lt;/p&gt;
&lt;div class="math"&gt;
\[
f_1(x) + f_2(x) + f_3(x) = 0
\]&lt;/div&gt;
&lt;p&gt;This means that if we combine the 3 alternating currents, the result is 0. That is why it is possible to use only 4 wires for 3-phase alternating current: 3 wires can be combined into one neutral wire. I may be wrong, I am not an electrician.&lt;/p&gt;
&lt;p&gt;Quantum Fourier Transform uses a similar trick. It combines multiple states with different phases in a way that the probability of combined states &lt;span class="math"&gt;\(0, 1, \ldots, k-1\)&lt;/span&gt; is almost zero. That leaves states &lt;span class="math"&gt;\(0, k, 2k, 3k, \ldots\)&lt;/span&gt; with high probability. This is exactly what we want to achieve.&lt;/p&gt;
&lt;p&gt;An explanation of the Quantum Fourier Transform is beyond the scope of this post. As mentioned, let us consider a problem where every &lt;span class="math"&gt;\(k\)&lt;/span&gt;-th solution is the correct one. Of course, the value of &lt;span class="math"&gt;\(k\)&lt;/span&gt; is what we are trying to find. When we find &lt;span class="math"&gt;\(k\)&lt;/span&gt;, we know the solution to our problem. The Quantum Fourier Transform is a procedure to find &lt;span class="math"&gt;\(k\)&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;A quantum computer can simulate a nondeterministic computing machine efficiently when &lt;em&gt;yes&lt;/em&gt; states are periodic. Nondeterministic machines are only a theoretical construct and may be harder to imagine for people outside of computer science. They can be simulated by computers that can run computations in parallel. This is probably the reason why quantum computers are often explained as computers that can run computations in parallel. However, I find the comparison to nondeterministic machines much more insightful. Maybe it is just me, because I have not seen such explanation anywhere else.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Quantum Computing measurement</title>
			<link>http://duracellko.net/posts/2025/10/quantum-computing-measurement</link>
			<description>&lt;p&gt;This is the second post in my journey to discover "What kinds of problems can quantum computation solve efficiently?" In the &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;first post&lt;/a&gt;, I explained what a &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;qubit&lt;/a&gt; is and how it can be represented. In this post, I will cover measurement in (not only) quantum mechanics. You have probably heard that when measuring a qubit, it collapses to one of the measured states and loses its actual value. This led me to some natural questions: What is the difference between measurement and any other operation? How does a qubit know it is being measured?&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2025/10/quantum-computing-measurement</guid>
			<pubDate>Tue, 14 Oct 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;This is the second post in my journey to discover "What kinds of problems can quantum computation solve efficiently?" In the &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;first post&lt;/a&gt;, I explained what a &lt;a href="http://duracellko.net/quantum-computing-qubit"&gt;qubit&lt;/a&gt; is and how it can be represented. In this post, I will cover measurement in (not only) quantum mechanics. You have probably heard that when measuring a qubit, it collapses to one of the measured states and loses its actual value. This led me to some natural questions: What is the difference between measurement and any other operation? How does a qubit know it is being measured?&lt;/p&gt;
&lt;p&gt;I already presented an example of a measurement experiment in the previous post, without explicitly mentioning it. Do you remember the experiment where we rotate two polarization filters 45° relative to each other? Only 50% of the light that passed through the first filter passes through the second filter. So what actually happens? Let us say the first polarization filter sets the polarization direction, and thus our qubit, to the blue arrow. The second filter then "measures" the qubit in the &lt;span class="math"&gt;\(x\)&lt;/span&gt; and &lt;span class="math"&gt;\(y\)&lt;/span&gt; axes.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/qubit-direction.svg" class="img-fluid" alt="Qubit direction"&gt;&lt;/p&gt;
&lt;p&gt;What happens to the photon after passing through the second polarization filter? It changes its polarization to either the direction of the y-axis or the x-axis. The probability it is changed to the y-axis is &lt;span class="math"&gt;\(\cos^2\alpha\)&lt;/span&gt;, and the probability of changing to the x-axis is &lt;span class="math"&gt;\(\sin^2\alpha\)&lt;/span&gt;. In reality, the photon that is changed to the x-axis never passes through the filter and is destroyed. Therefore, the experiment with electron spin is more accurate, as the electron is not destroyed and the outcome is actually one or the other direction. However, photons are easier to observe by eyes.&lt;/p&gt;
&lt;p&gt;Now it is possible to explain the experiment with three polarization filters. As mentioned, the first polarization filter sets polarization to the vertical direction, which is aligned with the y-axis. The second polarization filter measures polarization at a 45° rotation.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/diagonal-measurement.svg" class="img-fluid" alt="Diagonal measurement"&gt;&lt;/p&gt;
&lt;p&gt;This second filter changes the polarization of 50% of the photons to the direction of &lt;span class="math"&gt;\(y^\prime\)&lt;/span&gt; and the other 50% to &lt;span class="math"&gt;\(x^\prime\)&lt;/span&gt;, which does not pass through the filter. Now we have 50% of the light passing through the first filter and 50% of that passing through the second filter. That means 25% of the light passes through both filters, and all of this light has the polarization direction of the &lt;span class="math"&gt;\(y^\prime\)&lt;/span&gt; axis. The third filter then measures again in the horizontal x-axis and vertical y-axis, similar to the first image. The outcome is that 50% of the photons reaching the third filter change polarization to the y-axis and 50% to the x-axis, which is again filtered out. Therefore, the total light passing through all three filters is 12.5%.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/3-filters-measurement.svg" class="img-fluid" alt="3 polarization filters measurement"&gt;&lt;/p&gt;
&lt;h2 id="multiple-qubits"&gt;Multiple qubits&lt;/h2&gt;
&lt;p&gt;With classical bits of values 0 or 1, it is possible to combine them to represent other numbers or values. For example, it is possible to combine 8 bits to represent an integer between 0 and 255. Is it possible to combine multiple qubits in the same way? The answer is "yes." But do we even need it? If a qubit can be any real number between &lt;span class="math"&gt;\(0\)&lt;/span&gt; and &lt;span class="math"&gt;\(2\pi\)&lt;/span&gt;, then it can easily represent a number from a finite set of integers. For example, an integer &lt;span class="math"&gt;\(i \in \{ 0, \ldots, 255 \}\)&lt;/span&gt; can be represented by a qubit as&lt;/p&gt;
&lt;div class="math"&gt;
\[
i \rightarrow \frac{i}{256} \cdot 2\pi
\]&lt;/div&gt;
&lt;p&gt;Interestingly, a similar approach is used in classical computing. Most people say that computers communicate in 0s and 1s. However, that is not exactly true, although it is the best abstraction of the communication signal. While 0s (low voltage) and 1s (high voltage) are commonly used in processors and memory units (likely because it works well with transistors), network communication over cable or air uses more complicated encoding. Just like a qubit value, voltage can also be any real number. So it is possible to use multiple voltage levels to represent more numbers than just 0 and 1. This is especially important in communication. If the communication is at a certain frequency, e.g., 1 kHz, then it would be possible to transmit a maximum of 1000 bits per second with 2 levels. But introducing multiple energy/voltage levels increases the number of bits transmitted per second.&lt;/p&gt;
&lt;p&gt;Why do we not use the same approach with qubits? The first reason is similar to classical computing: there are limited operations we can perform with a single qubit. It is possible to add numbers and perform reflections, but it is not possible to perform multiplications. The second reason is the limitation of measurement. As mentioned above, a measurement of a single qubit can yield one of two possible outcomes, and not an explicit value from a set of multiple integers.&lt;/p&gt;
&lt;h2 id="schrodingers-cat"&gt;Schrödinger's cat&lt;/h2&gt;
&lt;p&gt;We have shown that a qubit measurement aligns the qubit with either the x-axis or y-axis. We can assign the value 0 to the x-axis and 1 to the y-axis. Thus, it is possible to think of a qubit as something between 0 and 1, and measurement reveals exactly one of the values 0 or 1.&lt;/p&gt;
&lt;p&gt;Many people are probably familiar with the &lt;a href="https://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat"&gt;Schrödinger's cat&lt;/a&gt; thought experiment, thanks to The Big Bang Theory sitcom. The thought experiment describes a cat in a box with poison. It is not possible to know if it is dead or alive without opening the box. The idea is to consider the cat both dead and alive at the same time before opening the box.&lt;/p&gt;
&lt;p&gt;The same perception can be applied to a qubit. Instead of considering it as something between 0 and 1, we can consider that a qubit is both 0 and 1, each with a certain probability. This property of a qubit is called superposition of 0 and 1. This is another reason to combine multiple qubits. If we have 2 qubits, each in superposition of 0 and 1, then those 2 qubits are in superposition of values 0, 1, 2, 3. In general, &lt;span class="math"&gt;\(n\)&lt;/span&gt; qubits can be in superposition of numbers &lt;span class="math"&gt;\(0 \ldots 2^n-1\)&lt;/span&gt;. This is the reason why it is often explained that quantum computers can run computations in parallel. The problem, however, is that measurement always reveals only a single number. When running the same computation multiple times, the measurement reveals a different number each time. Therefore, I find the statement "quantum computers can run operations in parallel" a bit misleading.&lt;/p&gt;
&lt;p&gt;Superposition is also the reason that a qubit is usually represented by a two-dimensional vector, instead of just a single angle. There is also a natural way to combine two-dimensional vectors into multi-dimensional vectors, so they can represent the superposition of multiple qubits.&lt;/p&gt;
&lt;h2 id="what-is-measurement"&gt;What is measurement?&lt;/h2&gt;
&lt;p&gt;Quantum mechanics describes measurement of a single qubit as a set of two operations on the qubit, where exactly one operation is chosen with a certain probability and that is applied. The difference from a regular qubit operation is that measurement is not a reversible operation. This partially answers the question, "How does the qubit know it is being measured?" or "What is the difference between measurement and a regular operation?" However, that still does not explain how it works.&lt;/p&gt;
&lt;p&gt;The book &lt;a href="https://www.cambridge.org/highereducation/books/quantum-computation-and-quantum-information/01E10196D0A682A6AEFFEA52D53BE9AE"&gt;Quantum Computation and Quantum Information&lt;/a&gt; provided me with valuable insight into this. I realized it has connections outside of quantum mechanics. There is &lt;a href="https://en.wikipedia.org/wiki/Goodhart%27s_law"&gt;Goodhart's Law&lt;/a&gt;: "When a measure becomes a target, it ceases to be a good measure." For example, when unit test code coverage is being measured, code coverage may become more important than code quality. So a measurement always impacts the system being measured. In some cases, the impact is significant, such as with the unit test code coverage metric. In other cases, the impact is negligible. For example, a car speedometer has almost no impact on the speed of the car, but it is still a non-zero impact. It is always a bit of an art, requiring a lot of experience, to design measurements that have minimal impact on the measured system. It is likely almost impossible in the quantum world, which is so small that even the introduction of a small system change required for measurement probably has a large impact. It is like a variation of &lt;a href="https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion#Third_law"&gt;the 3rd Newton's law&lt;/a&gt;: For every action, there is some reaction.&lt;/p&gt;
&lt;p&gt;Interestingly, quantum mechanics explains this very well. By measuring, we are usually adding something to the quantum system. For example, measuring a photon may require an electron that produces electric current when hit by the photon. So we can consider that a quantum system with measurement is like a system with additional qubit(s). When we combine possible measurement outcomes with changes on the added qubit(s), the combined operation is actually a regular operation that is revertible and does not seem counterintuitive.&lt;/p&gt;
&lt;p&gt;It is extraordinary how something negligible in larger systems can explain the counterintuitive behavior of quantum measurement.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Quantum Computing - qubit</title>
			<link>http://duracellko.net/posts/2025/10/quantum-computing-qubit</link>
			<description>&lt;p&gt;When I asked the question, &amp;quot;What makes quantum computation more powerful?&amp;quot; or &amp;quot;What kinds of problems can quantum computation solve efficiently?&amp;quot;, the usual answer was, &amp;quot;It can run computations in parallel.&amp;quot; While this is perhaps the most accurate single-sentence answer, I find it both inaccurate and misleading. A few years ago, I began my journey studying quantum computing. My background is in computer science, so my main interest was to understand the problem space that can be solved more efficiently by quantum computation. I did not study physics beyond high school, and my goal was not to delve deeply into quantum physics. In the next few blog posts, I will attempt to provide a better explanation to the question, &amp;quot;What kinds of problems can quantum computation solve efficiently?&amp;quot;&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2025/10/quantum-computing-qubit</guid>
			<pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;When I asked the question, "What makes quantum computation more powerful?" or "What kinds of problems can quantum computation solve efficiently?", the usual answer was, "It can run computations in parallel." While this is perhaps the most accurate single-sentence answer, I find it both inaccurate and misleading. A few years ago, I began my journey studying quantum computing. My background is in computer science, so my main interest was to understand the problem space that can be solved more efficiently by quantum computation. I did not study physics beyond high school, and my goal was not to delve deeply into quantum physics. In the next few blog posts, I will attempt to provide a better explanation to the question, "What kinds of problems can quantum computation solve efficiently?"&lt;/p&gt;
&lt;p&gt;I began my self-study of quantum computing with the book &lt;a href="https://direct.mit.edu/books/book/4186/Quantum-Computing-for-Everyone"&gt;Quantum Computing for Everyone&lt;/a&gt; by Chris Bernhardt. The book is a good, light introduction. It covers quantum states and quantum operations. However, it did not provide the insight I was seeking. Then I found another book, &lt;a href="https://www.cambridge.org/highereducation/books/quantum-computation-and-quantum-information/01E10196D0A682A6AEFFEA52D53BE9AE"&gt;Quantum Computation and Quantum Information&lt;/a&gt; by Michael A. Nielsen and Isaac L. Chuang. This book was exactly what I needed to gain the insights I was looking for.&lt;/p&gt;
&lt;h2 id="qubit"&gt;Qubit&lt;/h2&gt;
&lt;p&gt;In this first blog post, I will discuss the qubit. The first book explained it quite well using a physical experiment with electron spin. I will use a different experiment with photons for explanation. Advantage of the photons experiment is that you can try it yourself with some inexpensive polarization filters. When you look through a polarization filter, it filters part of the light (some photons). The actual filtered amount depends on the source of light and the environment, but for simplicity, let's say it filters 50% of the light (50% of the photons). When you place the same polarization filter behind the first one, it does not filter any additional light. However, when you rotate that filter by 90°, the scene becomes black and all light is filtered. When you rotate the filter only 45°, then 50% of the light that passed through the first filter is filtered. The effect is shown in the following images.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/polarization-filter-1.svg" class="img-fluid" alt="2 polarization filters with the same orientation"&gt;
&lt;img src="http://duracellko.net/images/posts/2025/10/polarization-filter-2.svg" class="img-fluid" alt="2 polarization filters with 90° rotation"&gt;
&lt;img src="http://duracellko.net/images/posts/2025/10/polarization-filter-3.svg" class="img-fluid" alt="2 polarization filters with 45° rotation"&gt;&lt;/p&gt;
&lt;p&gt;A very surprising part of the experiment is to rotate the second filter by 90°. That makes the scene black. Then, try inserting another polarization filter between the two and rotate it 45°. The surprising result is that it passes 25% of the light through. This is counterintuitive, because by adding the additional filter, less light is filtered and more photons pass through.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/polarization-filter-4.svg" class="img-fluid" alt="3 polarization filters with 45° rotation"&gt;&lt;/p&gt;
&lt;p&gt;Let us return to the two-filter scenario. In this case, the more you rotate the filters toward 90°, the more photons are filtered and the scene becomes darker. The more you rotate toward 0°, the more photons pass through and the scene becomes lighter. The angle between the filters determines how much light is passed through and how much is filtered. That angle represents the &lt;strong&gt;qubit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;How does this work? The experiment with electron spin would be physically more accurate, but I will continue with photons, even though it is not strictly correct. The first filter allows only photons polarized in a certain direction to pass through. Recall the saying, "There is no up in space." That is what the second filter does: it defines where the "up" direction is. The following image should clarify this.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/qubit-direction.svg" class="img-fluid" alt="Qubit direction"&gt;&lt;/p&gt;
&lt;p&gt;The blue arrow is the direction of polarization set by the first filter. The &lt;span class="math"&gt;\(x\)&lt;/span&gt; and &lt;span class="math"&gt;\(y\)&lt;/span&gt; axes represent the direction of the second filter. It is important to realize that it is possible to rotate the first filter, which means rotating the blue arrow in the &lt;span class="math"&gt;\(x\)&lt;/span&gt;, &lt;span class="math"&gt;\(y\)&lt;/span&gt; coordinates. Alternatively, it is possible to rotate the second filter, which means rotating the &lt;span class="math"&gt;\(x\)&lt;/span&gt; and &lt;span class="math"&gt;\(y\)&lt;/span&gt; axes around the blue arrow. The result is the same, because the final amount of light passed through depends on the angle &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The angle &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt; is significant. It determines how much light is passed through and how much is filtered. The probability that a photon passes through the second filter is &lt;span class="math"&gt;\(\cos^2\alpha\)&lt;/span&gt;. The probability that a photon is filtered out is &lt;span class="math"&gt;\(\sin^2\alpha\)&lt;/span&gt;. Note that &lt;span class="math"&gt;\(\cos^2\alpha + \sin^2\alpha = 1\)&lt;/span&gt; by Pythagorean theorem. Thus, it is certain that a photon is either filtered or passed through. The amount of light passed through is &lt;span class="math"&gt;\(\cos^2\alpha\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the filter is rotated 30°, then &lt;span class="math"&gt;\(\alpha = \frac{\pi}{6}\)&lt;/span&gt; and the probability of a photon passing through is&lt;/p&gt;
&lt;div class="math"&gt;
\[
\cos^2\frac{\pi}{6} = \left(\frac{\sqrt{3}}{2}\right)^2 = \frac{3}{4}
\]&lt;/div&gt;
&lt;p&gt;A qubit can be represented as a real number between &lt;span class="math"&gt;\(0\)&lt;/span&gt; and &lt;span class="math"&gt;\(2\pi\)&lt;/span&gt; (0° to 360°). This means that a qubit can have an infinite number of values, compared to a bit, which can have only two (0 or 1). Notice that the value can be greater than &lt;span class="math"&gt;\(\pi\)&lt;/span&gt;, and thus &lt;span class="math"&gt;\(\sin\alpha\)&lt;/span&gt; and &lt;span class="math"&gt;\(\cos\alpha\)&lt;/span&gt; can be negative. However, the probabilities of passing and filtering (&lt;span class="math"&gt;\(\cos^2\alpha\)&lt;/span&gt; and &lt;span class="math"&gt;\(\sin^2\alpha\)&lt;/span&gt;) are always positive numbers between 0 and 1.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/qubit-direction-2.svg" class="img-fluid" alt="Qubit direction with angle higher than PI"&gt;&lt;/p&gt;
&lt;h2 id="qubit-operations"&gt;Qubit operations&lt;/h2&gt;
&lt;p&gt;Now the question is: what kinds of operations can be performed on a qubit? An important rule in quantum mechanics is that it should be possible to revert the operation. There are two types of operations that allow reverting. The first is &lt;strong&gt;rotation&lt;/strong&gt;. This means adding any constant angle to a qubit. For example, the following image shows a qubit rotated by angle &lt;span class="math"&gt;\(\gamma\)&lt;/span&gt;. In other words, it adds a constant &lt;span class="math"&gt;\(\gamma\)&lt;/span&gt; to qubit &lt;span class="math"&gt;\(\alpha\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/rotation.svg" class="img-fluid" alt="Qubit rotation"&gt;&lt;/p&gt;
&lt;p&gt;It should be clear that the operation can be reverted by subtracting the same angle.&lt;/p&gt;
&lt;p&gt;The second type of operation is &lt;strong&gt;reflection&lt;/strong&gt;. This means reflecting the direction line by another given direction line. For example, the following images present reflection by the y-axis, x-axis, and the diagonal line.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/reflection-y.svg" class="img-fluid" alt="Qubit reflection by y-axis"&gt;
&lt;img src="http://duracellko.net/images/posts/2025/10/reflection-x.svg" class="img-fluid" alt="Qubit reflection by x-axis"&gt;
&lt;img src="http://duracellko.net/images/posts/2025/10/reflection.svg" class="img-fluid" alt="Qubit reflection by diagonal line"&gt;&lt;/p&gt;
&lt;p&gt;The revert operation is even simpler: repeating the same operation returns the qubit to its original state.&lt;/p&gt;
&lt;p&gt;What kinds of operations cannot be reverted? For example, consider the conditional operation:&lt;/p&gt;
&lt;div class="math"&gt;
\[
f(x) =
\begin{cases}
  x+\pi, &amp;amp; \text{if } x&amp;lt;\pi\\
  x, &amp;amp; \text{otherwise}
\end{cases}
\]&lt;/div&gt;
&lt;p&gt;The problem is that it is not possible to revert the value &lt;span class="math"&gt;\(\frac{3}{2}\pi\)&lt;/span&gt;, because&lt;/p&gt;
&lt;div class="math"&gt;
\[
f\left(\frac{3}{2}\pi\right) = f\left(\frac{\pi}{2}\right) = \frac{3}{2}\pi
\]&lt;/div&gt;
&lt;p&gt;So there would be two possible results for the revert operation.&lt;/p&gt;
&lt;p&gt;Even simple multiplication of the angle by a constant is not revertible. For example, multiplication by 2 cannot be reverted either.&lt;/p&gt;
&lt;div class="math"&gt;
\[
2\cdot\frac{\pi}{4} = 2\cdot\frac{5}{2}\pi = \frac{\pi}{2}
\]&lt;/div&gt;
&lt;p&gt;in the space of values between &lt;span class="math"&gt;\(0\)&lt;/span&gt; and &lt;span class="math"&gt;\(2\pi\)&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id="vectors"&gt;Vectors&lt;/h2&gt;
&lt;p&gt;I used the angle representation of a qubit because it is very illustrative. However, the usual representation of a qubit is as a two-dimensional vector. That is, a tuple of two values, specifically &lt;span class="math"&gt;\((\sin\alpha, \cos\alpha)\)&lt;/span&gt;. In the previous image, we presented the qubit as an arrow showing direction. However, the arrow shows only the direction; it does not have any significant length. So we can standardize all arrows to the same length of a single unit. Thus, a qubit can be represented by a point on the unit circle. The coordinates of such point are &lt;span class="math"&gt;\((\sin\alpha, \cos\alpha)\)&lt;/span&gt;. That is the vector.&lt;/p&gt;
&lt;p&gt;The vector representation of a qubit is very useful when combining multiple qubits. I will discuss that in the next post. I will try to avoid vectors as much as possible, as my goal is to explain quantum computing using elementary math.&lt;/p&gt;
&lt;h2 id="dimensional-space"&gt;3-dimensional space&lt;/h2&gt;
&lt;p&gt;The first book, "Quantum Computing for Everyone," explains the qubit as a direction in two-dimensional space. An advantage of this approach is its ease of illustration. However, we live in three-dimensional space, and thus a qubit is actually a direction in three-dimensional space. So we can think of it as a point on the unit sphere, instead of the circle. This point can be represented in different forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Three-dimensional coordinates &lt;span class="math"&gt;\((x, y, z)\)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Two angles known as polar coordinates - you can think of these as GPS coordinates for points on Earth.&lt;/li&gt;
&lt;li&gt;Two-dimensional vector of complex numbers &lt;span class="math"&gt;\(C^2\)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/10/bloch-sphere.svg" class="img-fluid" alt="Bloch sphere"&gt;&lt;/p&gt;
&lt;p&gt;As you might expect, the most common representation of a qubit is the last one: a vector in &lt;span class="math"&gt;\(C^2\)&lt;/span&gt;. Three-dimensional space also means that there are more possibilities for rotations and reflections, and thus more complex qubit operations.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Window icon in WinUI 3</title>
			<link>http://duracellko.net/posts/2025/04/window-icon-in-winui3</link>
			<description>&lt;p&gt;When developing a Windows application using &lt;a href="https://learn.microsoft.com/en-us/windows/apps/winui/winui3/"&gt;WinUI 3&lt;/a&gt;, by default it displays default Windows icon in taskbar: &lt;img src="http://duracellko.net/images/posts/2025/04/idi_application.png" class="img-fluid" alt="Default Windows application icon"&gt; Changing this icon is a little bit more complicated than in other .NET UI frameworks. In &lt;a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/overview/?view=netdesktop-9.0"&gt;WinForms&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview/?view=netdesktop-9.0"&gt;WPF&lt;/a&gt; &lt;strong&gt;Window&lt;/strong&gt; class has &lt;strong&gt;Icon&lt;/strong&gt; property. And these frameworks have classes to work with icon objects. &lt;a href="https://learn.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide"&gt;Universal Windows Platform&lt;/a&gt; application doesn't have window under control and the platform automatically sets the icon from application assets.&lt;/p&gt;</description>
			<enclosure url="http://duracellko.net/images/background.jpg" length="0" type="image" />
			<guid>http://duracellko.net/posts/2025/04/window-icon-in-winui3</guid>
			<pubDate>Sun, 27 Apr 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;When developing a Windows application using &lt;a href="https://learn.microsoft.com/en-us/windows/apps/winui/winui3/"&gt;WinUI 3&lt;/a&gt;, by default it displays default Windows icon in taskbar: &lt;img src="http://duracellko.net/images/posts/2025/04/idi_application.png" class="img-fluid" alt="Default Windows application icon"&gt; Changing this icon is a little bit more complicated than in other .NET UI frameworks. In &lt;a href="https://learn.microsoft.com/en-us/dotnet/desktop/winforms/overview/?view=netdesktop-9.0"&gt;WinForms&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview/?view=netdesktop-9.0"&gt;WPF&lt;/a&gt; &lt;strong&gt;Window&lt;/strong&gt; class has &lt;strong&gt;Icon&lt;/strong&gt; property. And these frameworks have classes to work with icon objects. &lt;a href="https://learn.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide"&gt;Universal Windows Platform&lt;/a&gt; application doesn't have window under control and the platform automatically sets the icon from application assets.&lt;/p&gt;
&lt;h2 id="program-icon"&gt;Program icon&lt;/h2&gt;
&lt;p&gt;In Windows every executable file can have a custom icon. That is the icon that you can see in Windows Explorer, when an executable file is listed. In .NET applications this is set the same way, no matter if the application is WinUI, WinForms, or even console application. Simply add an icon (e.g. &lt;em&gt;MyApp.ico&lt;/em&gt;) to your .NET project and add &lt;code&gt;ApplicationIcon&lt;/code&gt; element to the project file. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;ApplicationIcon&amp;gt;MyApp.ico&amp;lt;/ApplicationIcon&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the first step to setup your application icon. When the application is compiled, then you can see the icon in Windows Explorer. However, running the application would still display the system icon in Windows taskbar. Except in WPF that by default displays the application icon for all windows.&lt;/p&gt;
&lt;h2 id="loading-icon-from-a-file"&gt;Loading icon from a file&lt;/h2&gt;
&lt;p&gt;In WinUI 3 the &lt;a href="https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.window?view=windows-app-sdk-1.7"&gt;Window&lt;/a&gt; class has &lt;a href="https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.window.appwindow?view=windows-app-sdk-1.7"&gt;AppWindow&lt;/a&gt; property that allows system specific customization of the application window. And &lt;a href="https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow?view=windows-app-sdk-1.7"&gt;AppWindow&lt;/a&gt; class has &lt;a href="https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow.seticon?view=windows-app-sdk-1.7"&gt;SetIcon&lt;/a&gt; method that can be used to setup icon of the window. The method has 2 overloads. The first overload accepts a path to &lt;code&gt;.ico&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;So the first option is to include the &lt;code&gt;.ico&lt;/code&gt; file in the published application folder and load it from there. This can be done by adding &lt;code&gt;Content&lt;/code&gt; in the &lt;code&gt;.csproj&lt;/code&gt; file. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;ItemGroup&amp;gt;
  &amp;lt;Content Include="MyApp.ico" /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then setting up the icon in constructor of the window.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        string iconPath = System.IO.Path.Combine(Package.Current.InstalledLocation.Path, "MyApp.ico");
        AppWindow.SetIcon(iconPath);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution has desired effect of displaying correct icon in Windows taskbar. However, it is unusual to include &lt;code&gt;.ico&lt;/code&gt; file in the application folder and having dependency on it. Therefore, the next step is to include the icon directly in the executable.&lt;/p&gt;
&lt;h2 id="loading-icon-from-a-resource"&gt;Loading icon from a resource&lt;/h2&gt;
&lt;p&gt;Including icon in the executable file was done in the first step using &lt;code&gt;ApplicationIcon&lt;/code&gt; element. The problem is, how to load it into the Window object? And for that there is second &lt;code&gt;SetIcon&lt;/code&gt; method overload that accepts &lt;code&gt;IconId&lt;/code&gt; type. We need some Win32 API to get the &lt;code&gt;IconId&lt;/code&gt; value. This is full class with &lt;code&gt;GetApplicationIconId&lt;/code&gt; method to get the &lt;code&gt;IconId&lt;/code&gt; value that can be passed to &lt;code&gt;SetIcon&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static class IconService
{
    public static IconId GetApplicationIconId()
    {
        // Application resource ID assigned by Visual Studio to .NET applications
        // https://devblogs.microsoft.com/oldnewthing/20250423-00/?p=111106
        IntPtr iconResourceId = new(32512);

        IntPtr hModule = NativeMethods.GetModuleHandle(null);
        if (hModule == IntPtr.Zero)
        {
            return default;
        }

        IntPtr hIcon = NativeMethods.LoadIcon(hModule, iconResourceId);
        if (hIcon == IntPtr.Zero)
        {
            return default;
        }

        return Win32Interop.GetIconIdFromIcon(hIcon);
    }

    private static class NativeMethods
    {
        [DllImport("kernel32.dll", EntryPoint = "GetModuleHandle", CharSet = CharSet.Unicode)]
        [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
        public static extern IntPtr GetModuleHandle(string? lpModuleName);

        [DllImport("user32.dll", EntryPoint = "LoadIconW")]
        [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
        public static extern IntPtr LoadIcon(IntPtr hModule, IntPtr lpIconName);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main function used here is &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadiconw"&gt;LoadIcon&lt;/a&gt; that returns Icon handle. The first parameter of the function is handle of the module that the icon should be loaded from. If it is NULL (&lt;code&gt;IntPtr.Zero&lt;/code&gt;) then the function loads Windows system icon and not your custom application icon. Therefore, it is necessary to call function &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew"&gt;GetModuleHandle&lt;/a&gt; to get handle of the module representing the executable file.&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;GetModuleHandle&lt;/code&gt; is simple. It is sufficient to pass &lt;code&gt;null&lt;/code&gt;. That returns handle of the module that started execution of the current process. And that is likely your executable file.&lt;/p&gt;
&lt;p&gt;The second parameter of the &lt;code&gt;LoadIcon&lt;/code&gt; function is more tricky. By definition, it is &lt;code&gt;LPCWSTR&lt;/code&gt; and that is string pointer. Notice, however, that we declared it as &lt;code&gt;IntPtr&lt;/code&gt; and not &lt;code&gt;string&lt;/code&gt;. Resources (e.g. icons, strings, images) in modules (&lt;code&gt;.exe&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt; files) can be accessed by name or by index. And &lt;code&gt;LoadIcon&lt;/code&gt; function accepts both. When the value of second parameter is lower than 65536, then it is considered as a resource index and icon with the specified index is loaded. Otherwise, it is considered as a string pointer and icon with the specified resource name is loaded.&lt;/p&gt;
&lt;p&gt;We want to use the resource index, therefore the function is declared with &lt;code&gt;IntPtr&lt;/code&gt; type and not &lt;code&gt;string&lt;/code&gt;. Specifically, we use fixed index value 32512. According to &lt;a href="https://devblogs.microsoft.com/oldnewthing/20250423-00/?p=111106"&gt;What resource ID should I give my application’s main icon?&lt;/a&gt;, Visual Studio assigns this specific index to the application icon. You can also verify that by opening the compiled executable file in Visual Studio. This displays all resources in the executable file.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://duracellko.net/images/posts/2025/04/module-resources.png" class="img-fluid" alt="Executable file resources"&gt;&lt;/p&gt;
&lt;p&gt;Last part of this method uses &lt;a href="https://learn.microsoft.com/en-us/windows/apps/api-reference/cs-interop-apis/microsoft.ui/microsoft.ui.win32interop.geticonidfromicon"&gt;Win32Interop.GetIconIdFromIcon&lt;/a&gt; to convert the icon handle to &lt;code&gt;IconId&lt;/code&gt;. Notice that after each Win32 API call, the method checks if the result is non-zero. When it is zero, then Win32 function failed, so the method returns default &lt;code&gt;IconId&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Last step is to set the icon in Window class constructor. Btw, the default (or empty) &lt;code&gt;IconId&lt;/code&gt; value, would instruct WinUI to use default system icon.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        AppWindow.SetIcon(IconService.GetApplicationIconId());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This example has only single application window. When the window is closed, the whole application is closed. So the icon is loaded only once and it is never released using &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-destroyicon"&gt;DestroyIcon&lt;/a&gt; function, because it is needed for entire application lifetime.&lt;/p&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
	</channel>
</rss>