Index page on unathi.dev
/
Recent content in Index page on unathi.devHugo -- gohugo.ioen-zaSat, 18 Dec 2021 11:10:36 +0800Pipenv + fzf tandem
/posts/pipenv-fzf-tandem/
Sun, 30 Jul 2023 00:00:00 +0000/posts/pipenv-fzf-tandem/Pipenv + fzf tandem More often than not I find myself wanting to reuse a python virtual environment with packages I use frequently already installed. For instance, I might want to do some quick calculations and plotting in a scratch directory with numpy, scipy, pandas, matplotlib, etc. With my current python workflow, it is easy to lie on a Procrustean bed and create a new virtual environment in each and every new scratch directory.<h3 id="pipenv--fzf-tandem">Pipenv + fzf tandem</h3>
<p>More often than not I find myself wanting to reuse a python virtual environment
with packages I use frequently already installed. For instance, I might want to do some quick calculations
and plotting in a scratch directory with <code>numpy</code>, <code>scipy</code>, <code>pandas</code>, <code>matplotlib</code>, etc. With my current python workflow,
it is easy to lie on a Procrustean bed and create a new virtual environment in each and every new scratch directory.
Alternatively, one could create a base virtual environment, install the aforementioned packages and source the virtual
environment from where ever it is located and carry on with one’s calculations and plots.</p>
<p>This latter option is slightly less cumbersome than the former, but it still requires you to
know where the base virtual environment is located. With <code>pipenv</code> and <code>fzf</code> we can lift
the mental overhead of having to knowing where the base virtual environment
is. To this end, I wrote a <a href="https://github.com/Unathi-Skosana/dots/blob/linux/.local/bin/pipenv-activate">script</a> that uses <code>fzf</code> to source a pipenv-managed virtual environment from anywhere.</p>
<div class="collapsable-code">
<input id="391862457" type="checkbox" />
<label for="391862457">
<span class="collapsable-code__language">bash</span>
<span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide"></span>
</label>
<pre class="language-bash" ><code>
#!/bin/sh
# activates a virtualenv managed by pipenv in the current directory
# collect of all pipenv-managed virtual environments
path="$HOME/.local/share/virtualenvs"
venv_paths=("$path"/*)
project_paths=("$path"/*)
# shorten paths to project that created
# the virtual environment, which is stored
# inside the .project file of the corresponding
# virtual environment
for (( i=0; i<${#venv_paths[@]}; i++ )); do
project_path=$(cat "${venv_paths[i]}"/.project)
project_path="${project_path}"
project_path="${project_path##*/}"
project_paths[i]="${project_path}"
done
# select project with fzf
sel_proj=$(printf "%s\n" "${project_paths[@]}" | fzf)
# exit if nothing selected
if [ -z "$sel_proj" ]; then
exit 0
fi
# get index of virtual environment
# that corresponds to selected project
for i in "${!project_paths[@]}"; do
[[ "${project_paths[$i]}" = "${sel_proj}" ]] && break
done
# source virtual environment, and spawn new shell with
# the virtual environment activated
. "${venv_paths[i]}/bin/activate"
$SHELL
</code></pre>
</div>
<div class="my-12 svg-icon" style="content:'';display:block;height:80px;position:relative;
background:url('/img/ornaments/3.svg') no-repeat top center;
background-size: contain;"></div>
TIL: Cholesky decomposition
/posts/til-cholesky-decomposition/
Thu, 24 Nov 2022 00:00:00 +0000/posts/til-cholesky-decomposition/TIL: Cholesky decomposition Not long ago I picked up a Matrix Analysis textbook and came across a matrix factorization with this entry’s namesake. The Cholesky decomposition factorizes a (Hermitian) positive-definite matrix \(\mathbf{A}\) (\( x^{T} \mathbf{A} x > 0 \) for all non-zero \(x \in \mathbb{R}^{d}\)) into a product of a lower triangular matrix \(\mathbf{L}\) and its adjoint:
\[ \mathbf{A} = \mathbf{L}\mathbf{L}^{\dagger}. \]
For a positive-definite matrix such a factorization always exists and is unique!<h3 id="til-cholesky-decomposition">TIL: Cholesky decomposition</h3>
<p>Not long ago I picked up a <a href="https://www.amazon.com/Matrix-Analysis-Graduate-Texts-Mathematics/dp/0387948465">Matrix Analysis textbook</a> and came across a matrix factorization with this entry’s namesake. The Cholesky decomposition factorizes a (Hermitian) positive-definite matrix \(\mathbf{A}\) (\( x^{T} \mathbf{A} x > 0 \) for all non-zero \(x \in \mathbb{R}^{d}\)) into a product of a lower triangular matrix \(\mathbf{L}\) and its adjoint:</p>
<p>\[
\mathbf{A} = \mathbf{L}\mathbf{L}^{\dagger}.
\]</p>
<p>For a positive-definite matrix such a factorization always exists and is unique!</p>
<p>The numerical algorithm for constructing the lower triangular matrix \(\mathbf{L}\) is relatively straightforward. Initially, the matrix \(\mathbf{L}\) begins as a zero matrix with appropriate dimensions (say \(N \times N\)). Additionally, we take the diagonal elements of \(\mathbf{A}\) (\(A_{jj}\)) and arrange them in a non-increasing order then algorithm proceeds as follows:</p>
<ol>
<li>At step \(j\), update the diagonal elements of \(\mathbf{L}\) with those of \(\mathbf{A}\), \(L_{jj} \leftarrow A_{jj}\).</li>
<li>Update the \(L_{ij}\), \(L_{ij} \leftarrow A_{ij} / L_{jj}\) for \(i = j+1, \ldots, N \).</li>
<li>Update the stored diagonal elements of \(A_{ii}\), \(A_{ii} \leftarrow A_{ii} - L_{ij}^2\) for \(i = j+1, \ldots, N\)</li>
<li>Increment \(j\) and go back to step 1 and repeat until \(j\) is equal to index of the last row of the matrix \(\mathbf{A}\).</li>
</ol>
<h4 id="extras">Extras</h4>
<ul>
<li>
<p>The above algorithm can be slightly modified to obtain a low rank approximation of \(\mathbf{A}\): we can stop when all the remaining updated diagonal elements \(A_{jj}\) are below some specified tolerance \(\delta\). For some instances of \(\mathbf{A}\), <em>e.g.</em>, electron repulsion integrals, the rank \(\nu\) of the resulting matrix after this modification can be much smaller than its maximum possible value!</p>
</li>
<li>
<p>A Python implementation of the algorithm can fit in a <a href="https://twitter.com/pervognsen/status/1240943744787968001?t=GG1cDZAbs2enEFkpw6dNcw&s=19">tweet</a>!</p>
</li>
</ul>
<div class="collapsable-code">
<input id="543271968" type="checkbox" />
<label for="543271968">
<span class="collapsable-code__language">python</span>
<span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide"></span>
</label>
<pre class="language-python" ><code>
import numpy as np
def cholesky(A):
m, _ = A.shape
for i in range(m):
A[i,i] = np.sqrt(A[i,i])
A[i+1:, i] /= A[i, i]
A[i, i+1:] = 0
A[i+1:, i+1:] = np.outer(A[i+1:, i], A[i+1:,i])
</code></pre>
</div>
<ul>
<li>Animated GIF of the Cholesky decomposition of a \(20 \times 20\) matrix:</li>
</ul>
<figure class="center" >
<img src="/img/til/cholesky_decomposition/animation.gif" alt="Animation of cholesky decomposition of \\(20\times20\\) matrix" />
</figure>
<div class="my-12 svg-icon" style="content:'';display:block;height:80px;position:relative;
background:url('/img/ornaments/6.svg') no-repeat top center;
background-size: contain;"></div>
Autobiography
/me/
Sat, 18 Dec 2021 11:10:36 +0800/me/About me I am a PhD physics student at Stellenbosch University, South Africa. I’m currently part of the quantum photonics group. My current research interests around quantum simulation applications on cloud-based quantum computers and experimental quantum photonics for quantum communication use cases.
In 2016, I enrolled at Stellenbosch University studying towards a Bachelor of Science degree, which I would obtained in 2018. I completed my Honours degree at the aforementioned university in 2019.<h2 id="about-me">About me</h2>
<p>I am a PhD physics student at Stellenbosch University, South Africa. I’m
currently part of the <a href="http://quantumnanophotonics.org/?page_id=437">quantum photonics
group</a>. My current research
interests around quantum simulation applications on cloud-based quantum
computers and experimental quantum photonics for quantum communication use
cases.</p>
<p>In 2016, I enrolled at Stellenbosch University studying towards a Bachelor of
Science degree, which I would obtained in 2018. I completed my Honours degree at
the aforementioned university in 2019. At the beginning of 2022, I finished my
Masters education at Stellenbosch University, and joined IBM Research Africa as
a research intern in 2022.</p>
<p>Aside from physics research, although not entirely orthogonal, my other
interests include programming, software development, philosophy and history. I
also enjoy acquiring more books than I will ever manage to read (you can see the
ones that I have been read <a href="/reading/">here</a>), and being outdoors
(skateboarding, football, and going on walks).</p>
<p><a href="https://github.com/Unathi-Skosana/resume/releases/download/latest/resume.pdf">CV</a> (Last updated 15/02/2023)</p>
<p><a href="https://github.com/Unathi-Skosana">Github</a>, <a href="/index.xml">RSS</a>, and <a href="mailto:ukskosana@gmail.com">@</a>.</p>
<h2 id="in-the-press">In the press</h2>
<ul>
<li><a href="https://research.ibm.com/blog/south-africa-quantum-ready"><code>IBM Research blog</code></a></li>
<li><a href="https://www.sun.ac.za/english/Lists/news/DispForm.aspx?ID=8584"><code>Stellenbosch University</code></a></li>
<li><a href="http://www.techsmart.co.za/news/South-Africas-Progress-in-becoming-Quantum-Ready-begins-with-empowering-the-youth.html"><code>Techsmart</code></a></li>
<li><a href="https://thenewspaper.co.za/whos-to-say-its-impossible-to-build-your-own-quantum-processor/"><code>The Newspaper</code></a></li>
<li><a href="http://www.quantumnanophotonics.org/media/2021_0312_The%20Star_quantum%20computing.pdf"><code>Star</code></a></li>
<li><a href="https://htxt.co.za/2021/03/17/ibm-working-with-students-to-make-south-africa-quantum-ready/"><code>Hypertext</code></a></li>
</ul>
Epigrams
/epigrams/
Sat, 18 Dec 2021 11:10:36 +0800/epigrams/Humour A book is a mirror: if an ape looks into it an apostle is hardly likely to look out
— Georg Lichtenberg, The Waste Books
Philosophy Each morning you have to break through the dead rubble afresh so as to reach the living warm seed
— Ludwig Wittgestein, Culture and Value
It is perfectly true, as the philosophers say, that life must be understood backwards. But they forget the other proposition, that it must be lived forwards.<h3 id="humour">Humour</h3>
<blockquote>
<p>A book is a mirror: if an ape looks into it an apostle is hardly likely to look out</p>
<p>— Georg Lichtenberg, The Waste Books</p>
</blockquote>
<h3 id="philosophy">Philosophy</h3>
<blockquote>
<p>Each morning you have to break through the dead rubble afresh so as to reach the living warm seed</p>
<p>— Ludwig Wittgestein, Culture and Value</p>
</blockquote>
<blockquote>
<p>It is perfectly true, as the philosophers say, that life must be understood backwards. But they forget the other proposition, that it must be lived forwards.</p>
<p>— Søren Kierkegaard, Kierkegaard’s Notebooks and Journals</p>
</blockquote>
<blockquote>
<p>Philosophy is a battle against the bewitchment of our intelligence by means of language</p>
<p>— Ludwg Wittgestein, Philosophical Investigations</p>
</blockquote>
<h3 id="history">History</h3>
<blockquote>
<p>If education is the transmission of civilization, we are unquestionably progressing. Civilization is not inherited; it has to be learned and earned by each generation anew; if the transmission should be interrupted for one century, civilization would die and we should be savages again…the heritage that we can now more fully transmit is richer than ever before. It is richer than that of Pericles, for it includes all the Greek flowering that followed him; richer than Leonardo’s, for it includes him and the Italian Renaissance; richer than Voltaire’s, for it embraces all the French Enlightenment and its ecumenical dissemination. If progress is real despite our whining, it is not because we are born any healthier, better, or wiser than infants were in the past, but because we are born to a richer heritage, born on a higher level of that pedestal which the accumulation of knowledge and art raises as the ground and support of our being. The heritage rises, and man rises in proportion as he receives it.</p>
<p>History is, above all else, the creation and recording of that heritage; progress is its increasing abundance, preservation, transmission, and use. To those of us who study history not merely as a warning reminder of man’s follies and crimes, but also as an encouraging remembrance of generative souls, the past ceases to be a depressing chamber of horrors; it becomes a celestial city, a spacious country of the mind, wherein a thousand saints, statesmen, inventors, scientists, poets, artists, musicians, lovers, and philosophers still live and speak, teach and carve and sing. The historian will not mourn because he can see no meaning in human existence except that which man puts into it; let it be our pride that we ourselves may put meaning into our lives, and sometimes a significance that transcends death. If a man is fortunate he will, before he dies, gather up as much as he can of his civilized heritage and transmit it to his children. And to his final breath he will be grateful for this inexhaustible legacy, knowing that it is our nourishing mother and our lasting life</p>
<p>— Ariel and Will Durant, The Lessons of History “Is Progress Real?”</p>
</blockquote>
Log
/log/
Sat, 18 Dec 2021 11:10:36 +0800/log/April 2023 Changed sans serif typeface from Apercu to Fira Sans and change the monospaced typeface font from Inconsolata to Fira Mono Added reading time to the metadata block of each blog post. Post covers are now also displayed at the beginning of each blog post. <h3 id="april-2023">April 2023</h3>
<ul>
<li>Changed sans serif typeface from Apercu to Fira Sans and change the monospaced typeface font from Inconsolata to Fira Mono</li>
<li>Added reading time to the metadata block of each blog post.</li>
<li>Post covers are now also displayed at the beginning of each blog post.</li>
</ul>
Tribute to π
/posts/tribute-to-pi/
Sat, 14 Mar 2020 00:00:00 +0000/posts/tribute-to-pi/A visualization of Pi digits Following tradition, as a tribute to pi day, I will attempt to recreate a particular visual representation of the digits of \(\pi\). The original creation is due to Martin Krzywinski, and can be found here. The digits of \(\pi\) were computed using the Chudnovsky algorithm. Due to the CPU-intensive nature of such a computation, I precomputed the digits and saved to them a .dat file for reuse.<h2 id="a-visualization-of-pi-digits">A visualization of Pi digits</h2>
<p>Following tradition, as a tribute to pi day, I will attempt to recreate a particular visual representation of the digits of \(\pi\). The original creation is due to <a href="https://twitter.com/MKrzywinski">Martin Krzywinski</a>, and can be found <a href="http://mkweb.bcgsc.ca/pi/piday">here</a>. The digits of \(\pi\) were computed using the <a href="https://en.wikipedia.org/wiki/Chudnovsky_algorithm">Chudnovsky algorithm</a>. Due to the CPU-intensive nature of such a computation, I precomputed the digits and saved to them a <code>.dat</code> file for reuse. This is less time consuming than computing the digits every time when visualizing them (or if you write buggy programs like I do). Below is the python script that computes the \(n\) first digits of \(\pi\) and saves as them to <code>.dat</code> file:</p>
<div class="collapsable-code">
<input id="817934256" type="checkbox" checked />
<label for="817934256">
<span class="collapsable-code__language">python</span>
<span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide"></span>
</label>
<pre class="language-python" ><code>
def pi_digits(n):
"""
Computes PI using the Chudnovsky algorithm from
http://stackoverflow.com/questions/28284996/python-pi-calculation
"""
# Set precision
getcontext().prec = n
t = Decimal(0)
pi = Decimal(0)
d = Decimal(0)
# Chudnovsky algorithm
for k in range(n):
t = ((-1)**k)*(factorial(6*k))*(13591409+545140134*k)
d = factorial(3*k)*(factorial(k)**3)*(640320**(3*k))
pi += Decimal(t) / Decimal(d)
pi = pi * Decimal(12) / Decimal(640320**(Decimal(1.5)))
pi = 1 / pi
return str(pi)
</code></pre>
</div>
<p>Since the digits will be displayed on a grid, the python script takes as input
the dimensions of the grid, the number of rows and number of columns
respectively.</p>
<p>Now that we have computed the \(n = rc\) digits of \(\pi\), we want to
visualize them. The visualization was done using <code>scatter</code> from <code>matplotlib</code> where the digits are laid out on a \(r \times c\) evenly-spaced meshgrid and the values of the digits are used to fill the colors of the points on the grid, these colors are taken from a discrete color map of size \(10\). The python script used to read the digits from a file and visualize them as explained above is as follows:</p>
<div class="collapsable-code">
<input id="639147528" type="checkbox" checked />
<label for="639147528">
<span class="collapsable-code__language">python</span>
<span class="collapsable-code__toggle" data-label-expand="Show" data-label-collapse="Hide"></span>
</label>
<pre class="language-python" ><code>
def discrete_cmap(N, base_cmap=None):
"""Create an N-bin discrete colormap from the specified input map from:
https://gist.github.com/jakevdp/91077b0cae40f8f8244a
By Jake VanderPlas
License: BSD-style
"""
# Note that if base_cmap is a string or None, you can simply do
# return plt.cm.get_cmap(base_cmap, N)
# The following works for string, None, or a colormap instance:
base = plt.cm.get_cmap(base_cmap)
color_list = base(np.linspace(0, 1, N))
cmap_name = base.name + str(N)
return color_list, base.from_list(cmap_name, color_list, N)
if __name__ == '__main__':
...
...
r, c = np.shape(pi_digits) # pi digits in meshgrid
x, y = np.meshgrid(np.arange(r), np.arange(c))
# initialize figure
fig, axes = plt.subplots(figsize=(8, 4))
color_list, dcmap = discrete_cmap(n, "terrain")
# swap y and x
scat = axes.scatter(
y, x, c=digits[x, y], s=3, cmap=dcmap
)
axes.axis('off')
legend1 = axes.legend(*scat.legend_elements(), loc='upper center',
bbox_to_anchor=(0.5, 1.1), ncol=n, fontsize=4.5,
markerscale=.5, frameon=False)
axes.add_artist(legend1)
# show figure
plt.tight_layout()
plt.show()
</code></pre>
</div>
<p>Above code snippet should be able to reproduce something along these lines</p>
<figure class="center" >
<img src="/img/tribute-to-pi/pi_23_by_23.png" alt="23x23 meshgrid" style="border-radius: 8px;" />
<figcaption class="center" >23x23 meshgrid of the first 529 pi digits</figcaption>
</figure>
<p>What would be even more visually appealing is if we could connect the same-valued
digits that are one point away from each other in either direction with edges as in
the original poster. For example, for a \(3\) by \(3\) grid</p>
<p>\begin{align}
&3,1,4 \newline
&1,5,9 \newline
&2,6,6
\end{align}</p>
<p>In this case the \(1\)s across the diagonal from the first row and second row will be connected with an edge. Similarly the \(6\)s along the third row will be connected with an edge.</p>
<p>To do this, we need to able to find the indices of the connected clusters of numbers across the whole grid and use them to draw the edges between the corresponding grid points. Scouring around the internet, I found that such a computation is related to a concept in graph theory, which is the concept of <a href="https://en.wikipedia.org/wiki/Component_(graph_theory)">connected components</a> of an undirected graph. Finding the connected components of all the digits from \(0-9\) across the grid will give us what we seek. At first sight, this seemed like a daunting task and I was apprehensive of whether a vanilla python implementation would be up to the task. However, it turns out this task is related to labeling components in a pixel array. Consider the following \(3\times3\) pixel array.</p>
<p>\begin{align}
&0,1,0 \newline
&1,0,1 \newline
&0,1,0
\end{align}</p>
<p>The four \(1\)s in the grid would be deemed as a label/component and we would have
only one component in the grid. The <code>scipy</code> library has a high performance C implementation,
<code>scipy.ndimage.label</code>, to label the components and subsequently extract their
indices. The single downfall of this method is that it also considers isolated
groups (\(1\)s surrounded by \(0\)s in all directions) as connected components.
However, one can filter out isolated groups from the original array as done <a href="https://stackoverflow.com/questions/28274091/removing-completely-isolated-cells-from-python-array">here</a>. Putting this all together, we can produce the stunning visuals below. Here are the visualizations for \(23\times23\)</p>
<figure class="center" >
<img src="/img/tribute-to-pi/pi_23_by_23_edges.png" alt="23x23 meshgrid" style="border-radius: 8px;" />
<figcaption class="center" >23x23 meshgrid of the first 529 pi digits with matching neareast-neighbor edges</figcaption>
</figure>
<p>The complete source code and even more higher quality images can be found <a href="https://github.com/Unathi-Skosana/numart">here</a>.</p>
<div class="my-12 svg-icon" style="content:'';display:block;height:80px;position:relative;
background:url('/img/ornaments/6.svg') no-repeat top center;
background-size: contain;"></div>
Bibliography
/tags/bibliography/til-cholesky-decomposition/
Mon, 01 Jan 0001 00:00:00 +0000/tags/bibliography/til-cholesky-decomposition/TIL: Cholesky decomposition links:
Matrix Analysis (Graduate Texts in Mathematics, 169) DOI:10.1002/qua.560120408 <p><a href="/posts/til-cholesky-decomposition">TIL: Cholesky decomposition</a> links:</p>
<ol start="0">
<li><a href="https://www.amazon.com/Matrix-Analysis-Graduate-Texts-Mathematics/dp/0387948465"><code>Matrix Analysis (Graduate Texts in Mathematics, 169)</code></a></li>
<li><a href="https://onlinelibrary.wiley.com/doi/10.1002/qua.560120408"><code>DOI:10.1002/qua.560120408</code></a></li>
</ol>
Bibliography
/tags/bibliography/tribute-to-pi/
Mon, 01 Jan 0001 00:00:00 +0000/tags/bibliography/tribute-to-pi/Tribute to Pi links:
Chudnovsky algorithm Python implementation of Chudnovsky algorithm Connected components (graph theory) Finding connected components in a pixel array Removing completely isolated cells from python array Discrete matplotlib colormap <p><a href="/posts/tribute-to-pi">Tribute to Pi</a> links:</p>
<ol start="0">
<li><a href="https://en.wikipedia.org/wiki/Chudnovsky_algorithm"><code>Chudnovsky algorithm</code></a></li>
<li><a href="https://stackoverflow.com/questions/28284996/python-pi-calculation"><code>Python implementation of Chudnovsky algorithm</code></a></li>
<li><a href="https://en.wikipedia.org/wiki/Component_(graph_theory)"><code>Connected components (graph theory)</code></a></li>
<li><a href="https://stackoverflow.com/questions/46737409/finding-connected-components-in-a-pixel-array"><code>Finding connected components in a pixel array</code></a></li>
<li><a href="https://stackoverflow.com/questions/28274091/removing-completely-isolated-cells-from-python-array"><code>Removing completely isolated cells from python array</code></a></li>
<li><a href="https://gist.github.com/jakevdp/91077b0cae40f8f8244a"><code>Discrete matplotlib colormap</code></a></li>
</ol>
Reading list
/reading/
Mon, 01 Jan 0001 00:00:00 +0000/reading/