A Quantum Engineering Focused Linux Distro From Scratch — Day 8

Marcus Edwards
6 min readJan 21, 2023

--

The test suite for qeelibrs needed improving - it was bare-bones.

I refactored the test suite so that unit tests were in src/test.rs and integration tests were under tests/ per the Rust documentation.

src/tests.rs

fn create_ket() -> Ket {
let imaginary_coeff = super::coefficient::create_coefficient(1.0, true);
let real_coeff = super::coefficient::create_coefficient(1.0, false);
let complex_coeff = super::coefficient::create_complex_coefficient(real_coeff, imaginary_coeff);
let init_state = BitVec::from_elem(3, false);
let ket = super::ket::create_ket(complex_coeff, init_state.clone(), vec![]);
ket
}

#[test]
fn test_create_coefficient() {
let magnitude:f64 = 1.00;
let imaginary:bool = true;
let coeff = super::coefficient::create_coefficient(magnitude, imaginary);
assert_eq!(coeff.get_magnitude(), magnitude);
assert_eq!(coeff.get_imaginary(), imaginary);
}

...

#[test]
fn test_equals_coefficient() {
let coeff = super::coefficient::create_coefficient(1.0, true);
let other = super::coefficient::create_coefficient(1.0, true);
assert!(coeff.equals_coefficient(other));

let different = super::coefficient::create_coefficient(2.0, false);
assert!(!coeff.equals_coefficient(different));
}

tests/integration.rs

extern crate qasm;

use std::collections::BTreeMap;
use rustsimulationservice::parser::execute_qasm;

#[test]
fn test_lexer() {
let source = r#"
OPENQASM 2.0;
qreg a[3];
CX a[0], a[1];
"#;

let tokens = qasm::lex(source);
assert_eq!(
vec![
qasm::Token::OpenQASM,

...

qasm::Token::NNInteger(1),
qasm::Token::RSParen,
qasm::Token::Semicolon
],
tokens
)
}

The package also needed a better README.

README.md

# qeelibrs
An original (albeit simple) state vector simulator implementation in Rust.

## Library Usage

Quantum `States` are made up of `Kets`. A `Ket` takes a complex coefficient,
an initial value and an entanglement vector.

```
fn create_ket() -> Ket {
let imaginary_coeff = super::coefficient::create_coefficient(1.0, true);
let real_coeff = super::coefficient::create_coefficient(1.0, false);
let complex_coeff = super::coefficient::create_complex_coefficient(real_coeff, imaginary_coeff);

let init_val = BitVec::from_elem(3, false);
let ket = super::ket::create_ket(complex_coeff, init_val.clone(), vec![]);
ket
}
```

...

I shifted my attention back to LFS. I created an LFS user in a new LFS user group. The -m option here means we create a home for the new user. I learned that the -k option specifies an existing directory containing the files we want to initialize the user’s home with. Here, /dev/null is an empty directory stand-in. The -s and -g options’ meanings are obvious.

sudo groupadd lfs
sudo useradd -s /bin/bash -g lfs -m -k /dev/null lfs
passwd lfs

I recommend using a password keeper whenever generating or setting passwords. NordPass is a high-quality option in terms of features and security since they use XChaCha20 encryption which is a longer code than 256-bit AES. 1Password is a nice online option and Bitwarden is an open source alternative.

Since I didn’t follow the LFS book’s recommendations and restarted my computer (again) I had to get used to running these commands before interacting with the LFS filesystem each time, after identifying the LFS volume.

export LFS=/mnt/lfs
sudo mount -v -t ext4 /dev/sda2 $LFS
cd $LFS

I carried on with the directory setup.

marcuse@redblue:/mnt/lfs$ sudo chown -v lfs $LFS/{usr{,/*},lib,var,etc,bin,sbin,tools}
changed ownership of '/mnt/lfs/usr' from root to lfs
changed ownership of '/mnt/lfs/usr/bin' from root to lfs
changed ownership of '/mnt/lfs/usr/lib' from root to lfs
changed ownership of '/mnt/lfs/usr/sbin' from root to lfs
ownership of '/mnt/lfs/lib' retained as lfs
changed ownership of '/mnt/lfs/var' from root to lfs
changed ownership of '/mnt/lfs/etc' from root to lfs
ownership of '/mnt/lfs/bin' retained as lfs
ownership of '/mnt/lfs/sbin' retained as lfs
changed ownership of '/mnt/lfs/tools' from root to lfs
marcuse@redblue:/mnt/lfs$ sudo chown -v lfs $LFS/lib64
changed ownership of '/mnt/lfs/lib64' from root to lfs

Then, I finally logged in as the lfs user for the first time.

marcuse@redblue:/mnt/lfs$ su - lfs
Password:
lfs@redblue:~$

I setup a .bashprofile and .bashrc that separate the LFS system from the host system in terms of where it will find executables and settings.

.bashprofile

exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash

.bashrc

set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$LFS/tools/bin:$PATH
CONFIG_SITE=$LFS/usr/share/config.site
export LFS LC_ALL LFS_TGT PATH CONFIG_SITE
lfs@redblue:~$ ls -la
total 16
drwxr-x--- 2 lfs lfs 4096 Jan 20 16:04 .
drwxr-xr-x 4 root root 4096 Jan 18 19:26 ..
-rw-rw-r-- 1 lfs lfs 59 Jan 20 16:04 .bash_profile
-rw-rw-r-- 1 lfs lfs 242 Jan 20 16:04 .bashrc

Trying to replace the bash.bashrc file in $LFS/etc/ I encountered my first INCIDENT.

lfs@redblue:~$ mv -v $LFS/etc/bash.bashrc $LFS/etc/bash.bashrc.NOUSEmv: cannot move '/etc/bash.bashrc' to '/etc/bash.bashrc.NOUSE': Permission denied
lfs@redblue:~$ sudo mv -v $LFS/etc/bash.bashrc $LFS/etc/bash.bashrc.NOUSE
[sudo] password for lfs:
lfs is not in the sudoers file. This incident will be reported.

I wonder who it was reported to. marcuse, I guess, who was able to help out.

lfs@redblue:~$ su - marcuse
Password:
marcuse@redblue:~$ mv -v $LFS/etc/bash.bashrc $LFS/etc/bash.bashrc.NOUSE
renamed '/etc/bash.bashrc' -> '/etc/bash.bashrc.NOUSE'

Now we were ready to build tools. No, I don’t mean “build tools” like docker compose. I mean in the good old-fashioned English sense that we are going to build some tools.

marcuse@redblue:~$ su - lfs
lfs:~$ cd $LFS
lfs:/mnt/lfs$ source ~/.bash_profile

As it turns out, the #QHack2023 hackathon was in full swing while I was working on this. So, I made a few contributions in the meantime from my host machine.

What do you think the answer to this puzzle is?

Do you have any feedback for me on my pull request to Pennylane, XanaduAI’s package for quantum machine learning? Maybe we can go through it and learn a bit about quantum software.

Here I’m using git to create a beautiful diff of my changes (highlighted in green) to various files in Pennylane.

marcuse@redblue:~/Documents/pennylane$ git diff c05315f93a32c5220cb6bf0ac2aa0456d6a71fa8..HEAD

The first is changelog-dev.md. When you are managing software — not just some grad student’s simulation script that they keep on their harddrive or the research group’s NAS (no offense to academics) — then you keep track of what changed and when. This helps create momentum in the codebase, traceability and accountability in your team. It helps keep your team or company accountable too.

diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index fba961ef..d3a04ecb 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -216,6 +216,9 @@

<h3>Improvements</h3>

+* Added a `measured_wires` property to the `QuantumScript` class.
+ [(#3655)](https://github.com/PennyLaneAI/pennylane/pull/3655)
+
* Most channels in are now fully differentiable in all interfaces.
[(#3612)](https://github.com/PennyLaneAI/pennylane/pull/3612)

@@ -372,3 +375,4 @@ Borja Requena
Matthew Silverman
Antal Száva
David Wierichs
+Marcus Edwards
\ No newline at end of file

changelog-dev.md will become changelog-0.29.0.md when release 0.29.0 is ready. For now, it collects all of the changes that will end up in 0.29.0. I added a description of my change and my name to the changelog, to keep me accountable and to claim some credit.

In this PR I added a new property to a Python class, using Pennylane’s built-in Wires class and Python’s @property decorator.

diff --git a/pennylane/tape/qscript.py b/pennylane/tape/qscript.py
index 12ce56ba..8270a6e8 100644
--- a/pennylane/tape/qscript.py
+++ b/pennylane/tape/qscript.py
@@ -36,6 +36,7 @@ from pennylane.measurements import (
)
from pennylane.operation import Observable, Operator
from pennylane.queuing import AnnotatedQueue, process_queue
+from pennylane.wires import Wires

_empty_wires = qml.wires.Wires([])

@@ -169,6 +170,7 @@ class QuantumScript:
self._prep = [] if prep is None else list(prep)
self._ops = [] if ops is None else list(ops)
self._measurements = [] if measurements is None else list(measurements)
+ self._measured_wires = Wires([])

self._par_info = []
"""list[dict[str, Operator or int]]: Parameter information.
@@ -226,6 +228,12 @@ class QuantumScript:
# QSCRIPT properties
# ========================================================

+ @property
+ def measured_wires(self) -> List[int]:
+ """Returns the measured wires"""
+ self._update_measured_wires()
+ return self._measured_wires
+

When you create a property using the @property decorator in Python, you are setting up a number of things automatically. Normally, you would need to create individual setter and getters for a property — methods that interact with some attribute that is created in the class initialization function __init__(). The decorator does this for you by allowing you to access a private property like QuantumScript()._measured_wires here like a public one at QuantumScript().measured_wires, with whatever changes to the private prop you specify.

The function measured_wires() gave me a place to specify that I want my prop to be “lazy-loaded”. A lazy loaded property is a property that is not available when its class is initialized. You can often specify that relationships or values from database tables be lazy loaded using popular ORMs — this saves time in an initial query.

I made sure that the prop was initialized with an empty list be default, and only calculated when the getter was accessed by a user. If you’re interested in how it was calculated, you should check out the PR — and sign up for QHack! Seriously.

--

--

No responses yet