text:
Home | Science | * Gravitation Equations | * Gravitation Description and Equations 02. Force & Weight 03. Power, Energy & Gravity 04. Ballistics 05. Orbital Mechanics 06. The Pendulum Share This Page
Power, Energy & Gravity

Dynamics of gravitational equations

Power | Energy | Lifting a Mass
Potential and Kinetic Energy | Climbing a Hill | Power versus Energy

(double-click any word to see its definition)

Note: This is a multi-page article.
To navigate, use the drop-down lists and arrow keys at the top and bottom of each page.

Power

Power, with units of watts, is the rate at which work is performed or energy is expended. In a mechanical problem, power is the time derivative of energy or work. In the present context, power is the instantaneous product of force times distance divided by time:

(1) $\displaystyle p = \frac{f\ d}{t}$
Where:
• p = Power, watts.
• f = Force, newtons
• d = Displacement, meters
• t = Time, seconds

Because $\rm{\frac{displacement}{time}}$ equals velocity, there is a more useful, alternate form of the above equation:

(2) $\displaystyle p = f\ v$

Where v = velocity, with units of meters per second.

I ask my readers to notice about equation (2) that, for power to be expended, there must be both force and velocity, and the force must be applied in the direction of the velocity. A static force without velocity does not require power to maintain itself, and velocity (including rotational velocity) without force also does not require power.

Again, for power to be expended:
• Force and velocity must both be present, and
• The force must be applied in the direction of the velocity.

This definition may seem excessively strict and detailed, but consider a spinning top — it has both force (a centripetal force at right angles to its velocity) and rotational velocity, but no power is required to maintain the velocity (the top will only slow down if friction is present). The reason is that the force's direction is at right angles to the velocity's direction.

Energy

Energy, with units of joules (or watt-seconds), is power multiplied by time. This means the expenditure of 100 watts for 30 minutes represents an energy expenditure of (100 x 30 x 60) 180,000 joules.

Remember that energy is always conserved. If energy is expended lifting a weight, the energy must have had some prior form (even sometimes as mass). The act of lifting the mass represents an example of energy conversion (potential to kinetic energy), and after the mass has been lifted, the energy expended in lifting it remains as (a) gravitational potential energy and (b) a small amount of additional mass.

Lifting a Mass

Remember the discussion above — lifting a mass against gravity requires power, and power applied over time requires energy. The difference between power and energy is a Calculus idea (energy is the time integral of power), but it's not necessary to understand much Calculus to grasp this idea. (For those who want to learn Calculus, click here.)

We have decided to lift the previous example's ten-kilogram mass from the table to a high shelf, a distance of three meters. We will compute the required energy first, then discuss power later, for a reason that will become obvious.

For a given mass m, a height h over which the mass is to be raised, and a gravitational acceleration g, we compute an energy quantity called work, or force times distance, with units of joules:

(3) $W = mgh$

Where:

• W = Mechanical work, or force times distance, energy with units of joules
• m = Mass, kilograms
• g = Little-g, discussed earlier
• h = height over which the mass is lifted, meters

To lift a ten-kilogram mass three meters against the force of gravity, we must expend this much energy:

(4) $W = mgh = 10 \times 9.80665 \times 3 = 294.1995\ \text{joules}$

Let's round this off to 294 joules for the next discussion. Okay, we have the total energy required, but how much power is needed? Remember that power is equal to force times velocity (equation (2) above), and energy is power multiplied by time. This means all the solutions below will work:

• A power of 294 watts for one second.
• A power of 29.4 watts for ten seconds.
• A power of 2.94 watts for 100 seconds.

Remember this relationship between power and energy — a small amount of power expended over a long time can accomplish the same task as a large amount of power over a short time. But in all cases, the energy required is the same.

Potential and Kinetic Energy

In simple mechanical systems, energy has two primary forms — potential and kinetic. Potential energy is the energy of position or state — a coiled spring, gas under pressure, a book on a high shelf. Kinetic energy is energy of motion — an arrow in flight, a weight being lifted, a satellite in orbit. Many mechanical problems turn out to be descriptions of energy conversions — potential to kinetic and back again. And remember — regardless of the details, energy is always conserved — it may be changed in form, but it is never created or destroyed.

Here is a detailed description of the above mass-lift problem expressed in terms of energy:

• Let's say there is a coiled spring beneath the ten-kilogram mass, and the spring stores mechanical energy equal to 294 joules.
• Before time zero, the spring is kept from releasing its tension, its energy. The spring's stored potential energy causes the spring's mass to increase slightly, according to this Special Relativity principle:
(5) $m = \frac{E}{c^2}$

Where:

• m = mass, kilograms
• E = Energy, joules
• c = The speed of light, defined as 299,792,458 m/s.

Because of its potential energy of 294 joules, the coiled spring gains this much mass:

(6) $m = \frac{E}{c^2} = \frac{294}{299792458^2} = 3.27 \times 10^{-15}\ \text{kilograms}$

This calculation shows that the spring has an energy equivalent mass increase of just over three femtograms, about the mass of five E. coli bacteria.

• At time zero, the spring is released and expends its 294 joules of energy raising the ten-kilogram mass three meters.

The spring's potential energy becomes kinetic energy, the energy of motion.

The time this motion takes doesn't matter — as explained above it could be a fraction of a second, or it could be many minutes. The only issue is whether a total mechanical energy of 294 joules is expended.

• The spring reaches its full height and the mass is deposited on a high shelf.

What had been mechanical tension in the spring became kinetic energy during the lift, and what had been kinetic energy is now gravitational potential energy in the mass sitting on the shelf. The mass now has the energy equivalent mass increase — 3.27 femtograms — that was lost by the spring.

Climbing a Hill

A car must climb a grade at a certain speed. We will use the methods described above to determine how much horsepower is needed to achieve a specified climb rate. Here are the details:

Figure 2: Slope Diagram
• Vehicle mass m: 3000 kilograms
• Hill slope (rise/run): 0.12.
• Hill angle φ = $\displaystyle tan^{-1}(rise/run)$ = 6.84°
• Desired velocity v: 15 m/s (54 kph).

A nomenclature notice. Hill slopes are sometimes expressed as grade percentages, where the value is equal to:

(7) $\displaystyle grade\ \% = 100 \frac{rise}{run}$

Using this nomenclature, our hill slope of 0.12 would be described as a 12% grade. To convert such a grade into an angle, compute:

(8) $\displaystyle \phi = tan^{-1} \left(\frac{grade}{100} \right)$.

When climbing a grade, we experience less gravitational force than in a vertical lift. The force equation for this case is therefore the force equation $f = m\ g$ modified by a slope angle of φ:

(9) $\displaystyle f = m\ g\ sin(\phi)$

Where:

• f = Force, newtons
• m = Mass, kilograms
• g = Little-g, described earlier
• φ = Slope angle

Now that we know how to compute the force, we can find the power required to meet the desired velocity goal of 15 m/s. In a previous page we learned that power is equal to force times velocity, therefore:

(10) $\displaystyle p = f\ v = m\ g\ sin(\phi)\ v = 3000 \times 9.80665 \times sin(6.84°) \times 15 = 52,578.69\ \text{watts}$
To convert from watts to horsepower we divide by 746 (although there are many other possible values):
52,578.69 watts / 746 = 70.48 horsepower.
Power versus Energy

We have established the power required to sustain a particular velocity while climbing a grade, now we will compute the energy needed to climb a certain distance. Let's say the slope continues for two kilometers — how much energy is required to climb it?

Remember that energy is power multiplied by time. Also remember that the same energy can result from a low power level applied over a long time, or a high power level applied over a short time. But perhaps more important, the energy required doesn't depend on the details of the hill, the slope or the time duration, only the vertical height over which the mass is lifted. All we need to know is the vertical lift distance and the mass.

We know that the car traveled a distance d of two kilometers up a grade having an angle φ of 6.84° (refer to Figure 2 above). Let's convert this distance into a vertical height h:
(11) $\displaystyle h = d\ sin(\phi) = 2000 \times sin(6.84°) = 238.19\ \text{meters}$
Now we can apply equation (11) to compute the work (force times distance) energy required. Remember that the car has a mass m of 3000 kilograms:
(12) $\displaystyle W = mgh = 3000 \times 9.80665 \times 238.19$ = 7.01 megajoules
Note: This is a multi-page article.
To navigate, use the drop-down lists and arrow keys at the top and bottom of each page.

 Home | Science | * Gravitation Equations | * Gravitation Description and Equations 02. Force & Weight 03. Power, Energy & Gravity 04. Ballistics 05. Orbital Mechanics 06. The Pendulum Share This Page


text:
# Demonstration of k-means assumptions¶

This example is meant to illustrate situations where k-means produces unintuitive and possibly undesirable clusters.

# Author: Phil Roth <mr.phil.roth@gmail.com>
#         Arturo Amor <david-arturo.amor-quiroz@inria.fr>

## Data generation¶

The function make_blobs generates isotropic (spherical) gaussian blobs. To obtain anisotropic (elliptical) gaussian blobs one has to define a linear transformation.

import numpy as np
from sklearn.datasets import make_blobs

n_samples = 1500
random_state = 170
transformation = [[0.60834549, -0.63667341], [-0.40887718, 0.85253229]]

X, y = make_blobs(n_samples=n_samples, random_state=random_state)
X_aniso = np.dot(X, transformation)  # Anisotropic blobs
X_varied, y_varied = make_blobs(
n_samples=n_samples, cluster_std=[1.0, 2.5, 0.5], random_state=random_state
)  # Unequal variance
X_filtered = np.vstack(
(X[y == 0][:500], X[y == 1][:100], X[y == 2][:10])
)  # Unevenly sized blobs
y_filtered = [0] * 500 + [1] * 100 + [2] * 10

We can visualize the resulting data:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 12))

axs[0, 0].scatter(X[:, 0], X[:, 1], c=y)
axs[0, 0].set_title("Mixture of Gaussian Blobs")

axs[0, 1].scatter(X_aniso[:, 0], X_aniso[:, 1], c=y)
axs[0, 1].set_title("Anisotropically Distributed Blobs")

axs[1, 0].scatter(X_varied[:, 0], X_varied[:, 1], c=y_varied)
axs[1, 0].set_title("Unequal Variance")

axs[1, 1].scatter(X_filtered[:, 0], X_filtered[:, 1], c=y_filtered)
axs[1, 1].set_title("Unevenly Sized Blobs")

plt.suptitle("Ground truth clusters").set_y(0.95)
plt.show()

## Fit models and plot results¶

The previously generated data is now used to show how KMeans behaves in the following scenarios:

• Non-optimal number of clusters: in a real setting there is no uniquely defined true number of clusters. An appropriate number of clusters has to be decided from data-based criteria and knowledge of the intended goal.

• Anisotropically distributed blobs: k-means consists of minimizing sample’s euclidean distances to the centroid of the cluster they are assigned to. As a consequence, k-means is more appropriate for clusters that are isotropic and normally distributed (i.e. spherical gaussians).

• Unequal variance: k-means is equivalent to taking the maximum likelihood estimator for a “mixture” of k gaussian distributions with the same variances but with possibly different means.

• Unevenly sized blobs: there is no theoretical result about k-means that states that it requires similar cluster sizes to perform well, yet minimizing euclidean distances does mean that the more sparse and high-dimensional the problem is, the higher is the need to run the algorithm with different centroid seeds to ensure a global minimal inertia.

from sklearn.cluster import KMeans

common_params = {
"n_init": "auto",
"random_state": random_state,
}

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 12))

y_pred = KMeans(n_clusters=2, **common_params).fit_predict(X)
axs[0, 0].scatter(X[:, 0], X[:, 1], c=y_pred)
axs[0, 0].set_title("Non-optimal Number of Clusters")

y_pred = KMeans(n_clusters=3, **common_params).fit_predict(X_aniso)
axs[0, 1].scatter(X_aniso[:, 0], X_aniso[:, 1], c=y_pred)
axs[0, 1].set_title("Anisotropically Distributed Blobs")

y_pred = KMeans(n_clusters=3, **common_params).fit_predict(X_varied)
axs[1, 0].scatter(X_varied[:, 0], X_varied[:, 1], c=y_pred)
axs[1, 0].set_title("Unequal Variance")

y_pred = KMeans(n_clusters=3, **common_params).fit_predict(X_filtered)
axs[1, 1].scatter(X_filtered[:, 0], X_filtered[:, 1], c=y_pred)
axs[1, 1].set_title("Unevenly Sized Blobs")

plt.suptitle("Unexpected KMeans clusters").set_y(0.95)
plt.show()

## Possible solutions¶

For an example on how to find a correct number of blobs, see Selecting the number of clusters with silhouette analysis on KMeans clustering. In this case it suffices to set n_clusters=3.

y_pred = KMeans(n_clusters=3, **common_params).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.title("Optimal Number of Clusters")
plt.show()

To deal with unevenly sized blobs one can increase the number of random initializations. In this case we set n_init=10 to avoid finding a sub-optimal local minimum. For more details see Clustering sparse data with k-means.

y_pred = KMeans(n_clusters=3, n_init=10, random_state=random_state).fit_predict(
X_filtered
)
plt.scatter(X_filtered[:, 0], X_filtered[:, 1], c=y_pred)
plt.title("Unevenly Sized Blobs \nwith several initializations")
plt.show()

As anisotropic and unequal variances are real limitations of the k-means algorithm, here we propose instead the use of GaussianMixture, which also assumes gaussian clusters but does not impose any constraints on their variances. Notice that one still has to find the correct number of blobs (see Gaussian Mixture Model Selection).

For an example on how other clustering methods deal with anisotropic or unequal variance blobs, see the example Comparing different clustering algorithms on toy datasets.

from sklearn.mixture import GaussianMixture

fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(12, 6))

y_pred = GaussianMixture(n_components=3).fit_predict(X_aniso)
ax1.scatter(X_aniso[:, 0], X_aniso[:, 1], c=y_pred)
ax1.set_title("Anisotropically Distributed Blobs")

y_pred = GaussianMixture(n_components=3).fit_predict(X_varied)
ax2.scatter(X_varied[:, 0], X_varied[:, 1], c=y_pred)
ax2.set_title("Unequal Variance")

plt.suptitle("Gaussian mixture clusters").set_y(0.95)
plt.show()

## Final remarks¶

In high-dimensional spaces, Euclidean distances tend to become inflated (not shown in this example). Running a dimensionality reduction algorithm prior to k-means clustering can alleviate this problem and speed up the computations (see the example Clustering text documents using k-means).

In the case where clusters are known to be isotropic, have similar variance and are not too sparse, the k-means algorithm is quite effective and is one of the fastest clustering algorithms available. This advantage is lost if one has to restart it several times to avoid convergence to a local minimum.

Total running time of the script: ( 0 minutes 1.223 seconds)

Gallery generated by Sphinx-Gallery

format: plain


text:
Yazılım Açık Erişim

# Simulation-Optimization of a Patented Baffle Design with Parallel Computing on TRUBA

Kuzay, Mustafa; Nasyrlayev, Nazhmiddin; Demirel, Ender

### JSON-LD (schema.org)

{
"@context": "https://schema.org/",
"@id": 227987,
"@type": "SoftwareSourceCode",
"creator": [
{
"@id": "https://orcid.org/0000-0002-6205-4819",
"@type": "Person",
"affiliation": "Design and Simulation Tech. Inc.",
"name": "Kuzay, Mustafa"
},
{
"@id": "https://orcid.org/0000-0002-0918-5860",
"@type": "Person",
"affiliation": "Eskisehir Osmangazi University",
"name": "Nasyrlayev, Nazhmiddin"
},
{
"@id": "https://orcid.org/0000-0002-0440-7866",
"@type": "Person",
"affiliation": "Design and Simulation Tech. Inc.",
"name": "Demirel, Ender"
}
],
"datePublished": "2021-09-18",
"description": "
Treatment of surface waters in large cities has been emerged as a critical point of interest for governments in recent years with increasing clean water demand and development of energy intensive water treatment technologies such as ozone and ultraviolet. Raw water is mixed with the disinfectant in a multi-chambered contact tank at the last stage of the treatment procedure to remove viruses and pathogens from the water. An efficient contact system is required for the effective disinfection of water with less disinfectant and energy usage, whereas conventional mixing systems suffer from low mixing and high energy requirements due to the recirculation effects and short-circuiting occurred inside the chambers. A slot-baffle design has been invented in order to improve mixing efficiency of the system by increasing interaction between two neighboring chambers. Although slot-baffle design has been proved to significantly increase the efficiency of a multi-chambered contact system in the literature, geometrical properties of the design have not been optimized yet since an intense computational resource and time are required for the optimization of the design. In this case study, we aim at optimizing the patented design with an augmented simulation-optimization framework developed by DSTECH team.
",
"identifier": 227987,
"inLanguage": {
"@type": "Language",
"alternateName": "eng",
"name": "English"
},
"keywords": [
"contact tank",
"simulation-optimization",
"CFD",
"water treatment"
],
"name": "Simulation-Optimization of a Patented Baffle Design with Parallel Computing on TRUBA",
"url": "https://aperta.ulakbim.gov.tr//record/227987",
"version": "1.0.0"
}
512
59
görüntülenme
indirilme
Tüm sürümler Bu sürüm
Görüntülenme 512512
İndirme 5959
Veri hacmi 465.3 MB465.3 MB
Tekil görüntülenme 397397
Tekil indirme 4747


text:
• Algebraic resolution of the Burgers equation with a forcing term

• # Fulltext

https://www.ias.ac.in/article/fulltext/pram/088/05/0074

• # Keywords

Lie algebra; Burgers equation; symmetry reduction

• # Abstract

We introduce an inhomogeneous term, $f (t,x)$, into the right-hand side of the usual Burgers equation and examine the resulting equation for those functions which admit at least one Lie point symmetry. For those functions $f (t,x)$ which depend nontrivially on both $t$ and $x$, we find that there is just one symmetry. If $f$ is a function of only $x$, there are three symmetries with the algebra $sl(2,R)$. When $f$ is a function of only $t$ , there are five symmetries with the algebra $sl(2,R)\oplus_{s} 2A_1$. In all the cases, the Burgers equation is reduced to the equation for a linear oscillator with nonconstant coefficient.

• # Author Affiliations

1. Department of Mathematics, Pondicherry University, Kalapet 605 014, India
2. School of Mathematics, Statistics and Computer Science, University of KwaZulu-Natal, Private Bag X54001, Durban 4000, Republic of South Africa and Institute for Systems Science, Department of Mathematics, Durban University of Technology, P.O. Box 1334, Durban 4000, Republic of South Africa

• # Pramana – Journal of Physics

Volume 94, 2020
All articles
Continuous Article Publishing mode

• # Editorial Note on Continuous Article Publication

Posted on July 25, 2019


text:
The header_n() functions can be used to automatically pull the header_n derivations from the table or change them for future use.

header_n(table)

set_header_n(table, value)

## Arguments

table A tplyr_table object A tplyr_table object A data.frame with columns with the treatment variable, column variabes, and a variable with counts named 'n'. A data.frame with columns with the treatment variable, column variabes, and a variable with counts named 'n'.

## Value

For tplyr_header_n the header_n binding of the tplyr_table object. For tplyr_header_n<- and set_tplyr_header_n the modified object.

## Details

The header_n object is created by Tplyr when a table is built and intended to be used by the add_column_headers() function when displaying table level population totals. These methods are intended to be used for calling the population totals calculated by Tplyr, and to overwrite them if a user chooses to.

If you have a need to change the header Ns that appear in your table headers, say you know you are working with a subset of the data that doesn't represent the totals, you can replace the data used with set_header_n().

## Examples

tab <- tplyr_table(mtcars, gear)


text:
# Introduction to Instrumental Variables

### Unmeasured confounding

• Suppose there are unobserved variables $$U$$ that affect both $$A$$ and $$Y$$, then $$U$$ is an unmeasured confounding

• This violates ignorability assumption

• Since we cannot control for the unobserved confounders $$U$$ and average over its distribution, if using matching or IPTW methods, the estimates of causal effects is biased

• Solution: instrumental variables

### Instrumental variables

• Instrumental variables (IV): an alternative causal inference method that does not rely on the ignorability assumption

• $$Z$$ is an IV

• It affects treatment $$A$$, but does not directly affect the outcome $$Y$$
• We can think of $$Z$$ as encouragement (of treatement)

### Example of an encouragement design

• $$A$$: smoking during pregnancy (yes/no)
• $$Y$$: birth weight
• $$X$$: mother’s age, weight, etc

• Concern: there could be unmeasured confounders
• Challenge: it is not ethical to randomly assign smoking
• $$Z$$: randomized to either received encouragement to stop smoking ($$Z=1$$) or receive usual care ($$Z=0$$)

• Causal effect of encouragement, also called intent-to-treat (ITT) effect, may be of some interest $E\left(Y^{Z=1}\right)-E\left(Y^{Z=0}\right)$
• Focus of IV methods is still causal effect of the treatment $E\left(Y^{A=1}\right)-E\left(Y^{A=0}\right)$

### IV is randomized

• Like the previous smoking example, sometimes IV is randomly assigned as part of the study

• Other times IV is believed to be randomized in nature (natural experiment). For example,

• Mendelian randomization (?)
• Quarter of birth
• Geographic distance to specialty care provider

## Randomized trials with noncompliance

### Randomized trials with noncompliance

• Setup
• $$Z$$: randomization to treatment (1 treatment, 0 control)
• $$A$$: treatment received, binary (1 treatment, 0 control)
• $$Y$$: outcome
• Due to noncompliance, not everyone assigned treatment will actually receive the treatment, and vice verse ($$A \neq Z$$)
• There can be confounding $$X$$, like common causes affecting both treatment received $$A$$ and the outcome $$Y$$
• It may be reasonable to assume that $$Z$$ does not directly affect $$Y$$

### Causal effect of assignment on receipt

• Observed data: $$(Z, A, Y)$$

• Each subject has two potential values of treatment

• $$A^{Z=1} = A^1$$: value of treatment if randomized to treatment
• $$A^{Z=0} = A^0$$: value of treatment if randomized to control
• Average causal effect of treatment assignment on treatment received $E\left(A^1 - A^0\right)$
• If perfect compliance, this would be $$1$$
• By randomization and consistency, this is estimable from the observed data $E\left(A^1\right) = E(A \mid Z=1), \quad E\left(A^0\right) = E(A \mid Z=0)$

### Causal effect of assignment on outcome

• Average causal effect of treatment assignment on the outcome $E\left(Y^{Z=1} - Y^{Z=0}\right)$

• This is intention-to-treat effect
• If perfect compliance, this would be equal to the causal effect of treatment received
• By randomization and consistency, this is estimable from the observed data $E\left(Y^{Z=1}\right) = E(Y \mid Z=1), \quad E\left(Y^{Z=0}\right) = E(Y \mid Z=0)$

## Compliance classes

### Subpopulations based on potential treatment

$$A^0$$ $$A^1$$ Label
0 0 Never-takers
0 1 Compliers
1 0 Defiers
0 0 Always-takers
• For never-takers and always-takers,
• Encouragement does not work
• Due to no variation in treatment received, we cannot learn anything about the effect of treatment in these two subpopulations
• For compliers, treatment received is randomized
• For defiers, treatment received is also randomized, but in the opposite way

### Local average treatment effect

• We will focus on a local average treatment effect, i.e., the complier average causal effect (CACE)

\begin{align*} & E\left(Y^{Z=1} \mid A^0=0, A^1=1 \right) - E\left(Y^{Z=0} \mid A^0=0, A^1=1 \right)\\ = & E\left(Y^{Z=1} - Y^{Z=0} \mid \text{compliers} \right)\\ = & E\left(Y^{a=1} - Y^{a=0} \mid \text{compliers} \right) \end{align*}

• “Local”: this is a causal effect in a subpopulation
• No inference about defiers, always-takers, or never-takers

## Instrumental variable assumptions

### IV assumption 1: exclusion restriction

1. $$Z$$ is associated with the treatment $$A$$

1. $$Z$$ affects the outcome only through its effect on treatment

• $$Z$$ cannot directly, or indirectly though its effect on $$U$$, affect $$Y$$

### Is the exclusion restriction assumption realistic?

• If $$Z$$ is a random treatment assignment, then the exclusion restriction assumption is met

• It should affect treatment received
• It should not affect the outcome or unmeasured confounders
• However, it the subjects or clinicians are not blinded, knowledge of what they are assigned to could affect $$Y$$ or $$U$$

• We need to examine the exclusion restriction assumption carefully for any given study

### IV assumption 2: monotonicity

• Monotonicity assumption: there are no defiers

• No one consistently does the opposite of what they are told
• Probability of treatment should increase with more encouragement
• With monotonicity,

$$Z$$ $$A$$ $$A^0$$ $$A^1$$ Class
0 0 0 ? Never-takers or compliers
0 1 1 1 Always-takers or defiers
1 0 0 0 Never-takers or defiers
1 1 ? 1 Always-takers or compliers

# Estimate Causal Effects with Instrumental Variables

### Estimate CACE: 1. rewrite the ITT effect

• Due to randomization, we can identify the ITT effect $E\left( Y^{z=1} - Y^{z=0} \right) = E(Y\mid Z=1) - E(Y\mid Z=0)$

• Expand the first term in the above ITT effect \begin{align*} E(Y\mid Z=1) = & E(Y\mid Z=1, \text{always takers})P(\text{always takers}\mid Z=1)\\ & + E(Y\mid Z=1, \text{never takers})P(\text{never takers}\mid Z=1)\\ & + E(Y\mid Z=1, \text{compliers})P(\text{compliers}\mid Z=1) \end{align*}

• Note 1: among always takers and never takes, $$Z$$ does nothing
• $$E(Y\mid Z=1, \text{always takers}) = E(Y\mid \text{always takers}), \quad \text{etc.}$$
• Note 2: by randomization,
• $$P(\text{always takers}\mid Z=1) = P(\text{always takers}), \quad \text{etc.}$$

### Estimate CACE: 1. rewrite the ITT effect, cont.

• Therefore, the first term in the ITT effect is \begin{align*} E(Y\mid Z=1)=& E(Y\mid\text{always takers})P(\text{always takers})\\ & + E(Y\mid \text{never takers})P(\text{never takers})\\ & + E(Y\mid Z=1, \text{compliers})P(\text{compliers}) \end{align*}

• Similarly, the second term is \begin{align*} E(Y\mid Z=0)=& E(Y\mid\text{always takers})P(\text{always takers})\\ & + E(Y\mid \text{never takers})P(\text{never takers})\\ & + E(Y\mid Z=0, \text{compliers})P(\text{compliers}) \end{align*}

• Their difference is \begin{align*} & E(Y\mid Z=1) - E(Y\mid Z=0)\\ = & \left[E(Y\mid Z=1, \text{compliers})- E(Y\mid Z=0, \text{compliers})\right]P(\text{compliers}) \end{align*}

### Estimate CACE: 2. compute proportion of compliers

• Thus, the relationship between CACE and ITT effect is $\text{CACE} = \frac{E(Y\mid Z=1) - E(Y\mid Z=0)}{P(\text{compliers})}$

• To compute $$P(\text{compliers})$$, note that

• $$E(A\mid Z=1)$$: proportion of always takers plus compliers
• $$E(A\mid Z=0)$$: proportion of always takers
• Thus the difference is $P(\text{compliers}) = E(A\mid Z=1) - E(A\mid Z=0)$

### Estimate CACE: final formula

$\text{CACE} = \frac{E(Y\mid Z=1) - E(Y\mid Z=0)} {E(A\mid Z=1) - E(A\mid Z=0)}$

• Numerator: ITT, causal effect of treatment assignment on the outcome

• Denominator: causal effect of treatment assignment on the treatment received
• Denominator is between 0 and 1. Thus, CACE $$\geq$$ ITT
• ITT is underestimate of CACE, because some people assigned to treatment did not take it
• If perfect compliance, CACE $$=$$ ITT

## IVs in observational studies

### IVs in observational studies

• IVs can also be used in observational (non-randomized) studies

• $$Z$$: instrument
• $$A$$: treatment
• $$Y$$: outcome
• $$X$$: covariates
• $$Z$$ can be thought of as encouragement
• If binary, just encouragement yes or no
• If continuous, a ‘dose’ of encouragement
• $$Z$$ can be thought of as randomizers in natural experiments

• The key challenge: think of a variable that affects $$Y$$ only through $$A$$
• Only the assumption $$Z$$ affecting $$A$$ can be checked with data
• The validity of the exclusion restriction assumption rely on subject matter knowledge

### Natural experiment example 1: calendar time as IV

• Rationale: sometimes treatment preferences change over a short period of time

• $$A$$: drug A vs drug B

• $$Z$$: early time period (drug A is encouraged) vs late time period (drug B is encouraged)

• $$Y$$: BMI

### Natural experiment example 2: distance as IV

• Rationale: shorter distance to NICU is an encouragement

• $$A$$: delivery at high level NICU vs regular hospital

• $$Z$$: differential travel time from nearest high level NICU to nearest regular hospital

• $$Y$$: mortality

### More examples of natural experiments

• Mendelian randomization: some genetic variant is associate with some behavior (e.g., alcohol use) but is assumed to not be associated with outcome of interest

• Provider preference: use treatment prescribed to previous patients as an IV for current patient

• Quarter of birth: to study causal effect of years in school on income

## Two stage least squares

### Ordinary least squares (OLS) fails if there is confounding

• In OLS, one important assumption is that the covariate $$A$$ is independent with residuals $$\epsilon$$

$Y_i = \beta_0 + A_i \beta_1 + \epsilon_i$

• However, if there is confounding, $$A$$ and $$\epsilon$$ are correlated. So OLS fails.

• Two stage least squares can estimate causal effect in the instrumental variables (IV) setting

### Two stage least squares (2SLS)

• Stage 1: regress $$A$$ on $$Z$$ $A_i = \alpha_0 + Z_i \alpha_1 + e_i$
• By randomization, $$Z$$ and $$e$$ are independent
• Obtain the predicted value of $$A$$ given $$Z$$ for each subject $\hat{A}_i = \hat{\alpha}_0 + Z_i \hat{\alpha}_1$
• $$\hat{A}$$ is projection of $$A$$ onto the space spanned by $$Z$$
• Stage 2: regress $$Y$$ on $$\hat{A}$$ $Y_i = \beta_0 + \hat{A}_i \beta_1 + \epsilon_i$
• By exclusion restriction, $$Z$$ is independent of $$Y$$ given $$A$$

### Interpretation of $$\beta_1$$ in 2SLS: the causal effect

• Consider the case where both $$Z$$ and $$A$$ are binary $\beta_1 = E\left(Y \mid \hat{A}=1 \right) - E\left(Y \mid \hat{A}=0 \right)$

• There are two values of $$\hat{A}$$ in the 2nd stage model, $$\hat{\alpha}_0$$ and $$\hat{\alpha}_0 + \hat{\alpha}_1$$

• When we go from $$Z=0$$ to $$Z=1$$, what we observe is going from $$\hat{\alpha}_0$$ to $$\hat{\alpha}_0 + \hat{\alpha}_1$$
• We observe a mean difference of $$\hat{E}(Y\mid Z=1) - \hat{E}(Y\mid Z=0)$$ with a $$\hat{\alpha}_1$$ unit change in $$\hat{A}$$
• Thus, we should observe a mean difference of $$\frac{\hat{E}(Y\mid Z=1) - \hat{E}(Y\mid Z=0)}{\hat{\alpha}_1}$$ with $$1$$ unit change in $$\hat{A}$$

• The 2SLS estimator is a consistent estimator of the CACE $\beta_1 = \text{CACE} = \frac{\hat{E}(Y\mid Z=1) - \hat{E}(Y\mid Z=0)}{\hat{E}(A\mid Z=1) - \hat{E}(A\mid Z=0)}$

### More general 2SLS

• 2SLS can be used

• with covariates $$X$$, and
• for non-binary data (e.g, a continuous instrument)
• Stage 1: regression $$A$$ on $$Z$$ and covariates $$X$$

• and obtain the fitted values $$\hat{A}$$
• Stage 2: regress $$Y$$ on $$\hat{A}$$ and $$X$$

• Coefficient of $$\hat{A}$$ is the causal effect

## Sensitivity analysis and weak instruments

### Sensitivity analysis

• Sensitivity analysis method studies when each of the IV assumption (partly) fails

• Exclusion restriction: if $$Z$$ does affect $$Y$$ by an amount $$p$$, would my conclusion change? Vary $$p$$
• Monotonically: if the proportion of defiers was $$\pi$$, would my conclusion change?

### Strength of IVs

• Depend on how well an IV predicts treatment received, we can class it as a strong instrument or a weak instrument

• For a weak instrument, encouragement barely increases the probability of treatment

• Measure the strength of an instrument: estimate the proportion of compliers $E(A \mid Z=1) - E(A \mid Z=0)$

• Alternatively, we can just use the observed proportions of treated subjects for $$Z=1$$ and for $$Z=0$$

### Problems of weak instruments

• Suppose only 1% of the population are compliers

• Then only 1% of the samples have useful information about the treatment effect

• This leads to large variance estimates, i.e., estimate of causal effect is unstable
• The confidence intervals can be too wide to be useful

### References

• Coursera class: “A Crash Course on Causality: Inferring Causal Effects from Observational Data”, by Jason A. Roy (University of Pennsylvania)


text:
# mertonByTimeSeries

Estimate default probability using time-series version of Merton model

## Description

example

[PD,DD,A,Sa] = mertonByTimeSeries(Equity,Liability,Rate) estimates the default probability of a firm by using the Merton model.

example

[PD,DD,A,Sa] = mertonByTimeSeries(___,Name,Value) adds optional name-value pair arguments.

## Examples

collapse all

Dates     = MertonDataTS.Dates;
Equity    = MertonDataTS.Equity;
Liability = MertonDataTS.Liability;
Rate      = MertonDataTS.Rate;

Compute the default probability by using the time-series approach of Merton's model.

[PD,DD,A,Sa] = mertonByTimeSeries(Equity,Liability,Rate);
plot(Dates,PD)

Dates     = MertonDataTS.Dates;
Equity    = MertonDataTS.Equity;
Liability = MertonDataTS.Liability;
Rate      = MertonDataTS.Rate;

Compute the plot for the default probability values by using the time-series approach of Merton's model. You compute the PD0 (blue line) by using the default values. You compute the PD1 (red line) by specifying an optional Drift value.

PD0 = mertonByTimeSeries(Equity,Liability,Rate);
PD1 = mertonByTimeSeries(Equity,Liability,Rate,'Drift',0.10);
plot(Dates, PD0, Dates, PD1)

## Input Arguments

collapse all

Market value of the firm’s equity, specified as a positive value.

Data Types: double

Liability threshold of the firm, specified as a positive value. The liability threshold is often referred to as the default point.

Data Types: double

Annualized risk-free interest rate, specified as a numeric value.

Data Types: double

### Name-Value Pair Arguments

Specify optional comma-separated pairs of Name,Value arguments. Name is the argument name and Value is the corresponding value. Name must appear inside quotes. You can specify several name and value pair arguments in any order as Name1,Value1,...,NameN,ValueN.

Example: [PD,DD,A,Sa] = mertonByTimeSeries(Equity,Liability,Rate,'Maturity',4,'Drift',0.22,'Tolerance',1e-5,'NumPeriods',12)

Time to maturity corresponding to the liability threshold, specified as the comma-separated pair consisting of 'Maturity' and a positive value.

Data Types: double

Annualized drift rate, expected rate of return of the firm's assets, specified as the comma-separated pair consisting of 'Drift' and a numeric value.

Data Types: double

Number of periods per year, specified as the comma-separated pair consisting of 'NumPeriods' and a positive integer. Typical values are 250 (yearly), 12 (monthly), or 4 (quarterly).

Data Types: double

Tolerance for convergence of the solver, specified as the comma-separated pair consisting of 'Tolerance' and a positive scalar value.

Data Types: double

Maximum number of iterations allowed, specified as the comma-separated pair consisting of 'MaxIterations' and a positive integer.

Data Types: double

## Output Arguments

collapse all

Probability of default of the firm at maturity, returned as a numeric.

Distance-to-default, defined as the number of standard deviations between the mean of the asset distribution at maturity and the liability threshold (default point), returned as a numeric.

Value of firm's assets, returned as a numeric value.

Annualized firm's asset volatility, returned as a numeric value.

collapse all

### Merton Model for Time Series

In the Merton model, the value of a company's equity is treated as a call option on its assets, and the liability is taken as a strike price.

Given a time series of observed equity values and liability thresholds for a company, mertonByTimeSeries calibrates corresponding asset values, the volatility of the assets in the sample's time span, and computes the probability of default for each observation. Unlike mertonmodel, no equity volatility input is required for the time-series version of the Merton model. You compute the probability of default and distance-to-default by using the formulae in Algorithms.

## Algorithms

Given the time series for equity (E), liability (L), risk-free interest rate (r), asset drift (μA), and maturity (T), mertonByTimeSeries sets up the following system of nonlinear equations and solves for a time series asset values (A), and a single asset volatility (σA). At each time period t, where t = 1...n:

$\begin{array}{l}{A}_{1}=\left(\frac{{E}_{1}+{L}_{1}{e}^{-{r}_{1}{T}_{1}}N\left({d}_{2}\right)}{N\left({d}_{1}\right)}\right)\\ {A}_{t}=\left(\frac{{E}_{t}+{L}_{t}{e}^{-{r}_{t}{T}_{t}}N\left({d}_{2}\right)}{N\left({d}_{1}\right)}\right)\\ ...\\ {A}_{n}=\left(\frac{{E}_{n}+{L}_{n}{e}^{-{r}_{n}{T}_{n}}N\left({d}_{2}\right)}{N\left({d}_{1}\right)}\right)\end{array}$

where N is the cumulative normal distribution. To simplify the notation, the time subscript is omitted for d1 and d2. At each time period, d1, and d2 are defined as:

${d}_{1}=\frac{\mathrm{ln}\left(\frac{A}{L}\right)+\left(r+0.5{\sigma }_{A}^{2}\right)T}{{\sigma }_{A}\sqrt{T}}$

${d}_{2}={d}_{1}-{\sigma }_{A}\sqrt{T}$

The formulae for the distance-to-default (DD) and default probability (PD) at each time period are:

$DD=\frac{\mathrm{ln}\left(\frac{A}{L}\right)+\left({\mu }_{A}-0.5{\sigma }_{A}^{2}\right)T}{{\sigma }_{A}\sqrt{T}}$

$PD=1-N\left(DD\right)$

## References

[1] Zielinski, T. Merton's and KMV Models In Credit Risk Management.

[2] Loeffler, G. and Posch, P.N. Credit Risk Modeling Using Excel and VBA. Wiley Finance, 2011.

[3] Kim, I.J., Byun, S.J, Hwang, S.Y. An Iterative Method for Implementing Merton.

[4] Merton, R. C. “On the Pricing of Corporate Debt: The Risk Structure of Interest Rates.” Journal of Finance. Vol. 29. pp. 449–470.


text:
## Calculus: Early Transcendentals 8th Edition

$1/a\gt 1/b$
Use the "rules for inequalities" Prove rule 5 Multiply equation by 1/ab Final result is $1/a\gt 1/b$


text:
### [WHC] Nullsecdive: Small Ships 2019-05-01TZ21:00

At the end of a NNitH class, I offered to lead a frigate roam into a Null sec connection we had off Bacon that landed just two jumps from our Null Sec Campus. This was a chance for folks to drop a clone and fly around. Plan was to fly fast, no logi and pick our fights carefully.

SPOILER WARNING!
Mhzentul Lafarius - FCChe Galtor Enigmatic Online Macilles Mikus Walker

Early on we had a small gate camp that dispersed as we came through but a corax pilot suspected to be part of the gang began trailing us. After 4-5 gate jumps, I finally decided to orbit the next gate at 2500m and wait for the corax to come through, figuring that with pirate and T2 frigates we would burn down the corax real fast. Grabbed his pod while at it. Rest of the roam was quiet and we headed back to WH after of course visiting Poitot.

Killed: 13m
Lost: 0

Thank you for joining in a fleet and letting me practice FCing! This was my second PVP roam. Please submit feedback if you can!

Mhzentul


text:
# Research problems in communication complexity

There have been many open challenges questions in this forum.

For instance,

What are some of research avenues, areas available within communication complexity? What are some of most interesting open problems?

Some famous open problems AFAIK are status of log-rank conjecture, status of $\mathsf{IP}^{}\notin\mathsf{PH}^{cc}$, status of examples of natural problems in $\mathsf{NP}_k^{cc}$,$\mathsf{coNP}_k^{cc}$.

What are some of lesser known open problems or areas that are not yet mature within communication complexity or areas that have made new progress but much more needs to be done?

Are there any problems relating other areas such as circuit complexity, machine learning etc?

AFAIK communication complexity is more about finding bounds that about algorithms. Are there any problems with number theoretic, algebraic, geometric flavor or more generally in an algorithmic flavor?

More generally what would be some areas a newly entered person should focus energy to select a problem to work on (comm complexity field does not have many hearsay research topics as other traditional areas in mainstream theory CS with direct real world applications)?

• corrected should be just $\mathsf{IP}$. Mar 11, 2015 at 9:40
• So is this the inner product function then? Mar 11, 2015 at 10:32
• Correct. $\mathsf{IP}$ is function while $\mathsf{IP}_n$ is problem instance. Mar 11, 2015 at 11:51

It's hard to say what areas a new communication complexity researcher should delve into, since easy problems have likely been solved already, and harder problems are hard ;) One suggestion is to take known communication lower bounds (such as for Disjointness or Equality) and apply these to get new lower bounds in other areas of computer science. New applications of communication complexity pop up all the time, and if you can find a niche no one's considered before, you might be able to leverage the existing bounds you're learning now rather than having to develop new lower bounds. This will help you get some papers out while you're learning all about communication complexity ;)

Another suggestion is to work hard to ask new questions people haven't yet considered, or to think about new settings that are fresh and less well-studied. Multiparty communication might be a good area to look at---it is generally less understood than two-party communication, and (IMO at least) there's lots of fresh motivation for studying it (from e.g. multicore processors to wireless sensor networks)

For a concrete open problem, there's a nice one from multiparty communication in the number-on-the-forehead (NOF) model. In the NOF communication model, there are $k$ players who wish to compute some function $f(x_1,\ldots, x_k)$ on $k$ inputs. However, each player $PLR_i$ sees every input except $x_i$. (You can imagine $PLR_i$ having $x_i$ written on his forehead.) Because lots of input is shared between players, it should be easier to compute functions of their input. Conversely, proving lower bounds on the communication complexity in the NOF model is harder.

Here's a $\textbf{big open problem}$ in this area: come up with some explicit function f(x_1,\ldots, x_k) for k=polylog(n) such that $\omega(polylog(n))$ communication is required in the NOF model.

Through a chain of results from the early nineties [Hastad-Goldman91, Beigel-Tarui94], it's known that such a result would imply that $f \not\in ACC_0$. For this connection to circuit complexity, you're actually free to assume that each player sends a single message to some "referee" who compiles the messages to produce an output. Lower bounds even in this restricted setting are far from what we'd like. The current state-of-the-art lower bound techniques for the NOF model don't give lower bounds better than $\Omega(n/2^k)$ or thereabouts. These become trivial once $k > \log(n)$.

• That is a hard conundrum. Is Grolmusz's attempt best lower bound? Mar 11, 2015 at 11:53
• Alexander Sherstov has several recent papers which AFAIK give the strongest NOF lower bounds. There is also this paper by Amir Yehudayoff and Anup Rao: eccc.hpi-web.de/report/2014/060 Mar 12, 2015 at 3:22
• The $\Omega(n/4^k)$ lower bound is for the inner product function. Babai/Nisan/Szegedy noted that a Boolean function chosen uniformly at random with high probability has $\epsilon$-error communication complexity of at least $n/2-(\log k)/2+\log(1-\epsilon)-1$, which for $k=\log n$ is simply $\Omega(n)$. So, as usual, counting arguments tell us that hard functions exist, but we have made limited progress proving lower bounds for explicit functions. Note the deterministic telescope protocol yields an upper bound of $(2k-1)\lceil n/(2^{k-1}-1)\rceil$ for IP, which is $2k-1$ for $k \ge 2+\log n$. Mar 14, 2015 at 21:13

Several long-standing key open problems are in the Kushilevitz and Nisan textbook (see also the list of errata which mentions that Open Problem 8.6 was solved by Dietzfelbinger).

Razborov's 2011 introductory survey lists four open problems, two of which are also in the KN textbook:

1. (KN 2.10) Is it true that $D(f) \le O(\log \chi(f))$? Here $\chi(f)$ is the minimal number of $f$-monochromatic rectangles into which the input space $X \times Y$ can be partitioned. $D(f) \ge \log \chi(f)$ was shown by Yao in 1979, and $D(f) \le O((\log \chi(f))^2)$ was shown by Aho/Ullman/Yannakakis in 1983. See also KN Theorem 2.11 and the historical discussion in the bibliographic notes to Chapter 2.
2. (Log-rank conjecture, KN 2.20) Is it true that $\log\chi(f) \le (\log rk(M_f))^{O(1)}$ for every Boolean function $f$? Here $M_f$ is the communication matrix of $f$, and $rk(M)$ denotes the rank of matrix $M$.
3. Is it true that $R(f) \le poly(Q(f))$? Here $R(f)$ denotes the bounded-error communication complexity of $f$ and $Q(f)$ the quantum communication complexity. Note that $Q(f) \le R(f)$ by definition.
4. Prove that $D(IP^k_n) \ge n^\epsilon$ for, say, $k = \lceil (\log n)^2\rceil$ and some fixed constant $\epsilon > 0$. Here $IP^k_n$ denotes the inner product function with respect to $k$-party NOF protocols.

As is hinted at by Razborov's survey, there are open questions about the quantum communication setting; this area is not covered by the 1997 textbook.

Edit 2015-03-14: After some thought, I don't understand problem 4, since the telescope protocol of Grolmusz uses $2k-1$ bits of communication to compute $IP^k_n$ per block of at most $2^{k-1}-1$ rows in the communication matrix. For $k \ge 2+\log n$, there is precisely one such block and the cost of the protocol is then just $2k-1$ bits. In particular, for $k=(\log n)^2$ this is $2(\log n)^2 - 1 = o(n^\epsilon)$ bits for every $\epsilon > 0$. In summary, IP seems to have structure that can be exploited in the number-on-forehead setting when there are a large number of parties. To reach the $\Omega(n)$ lower bound in the region $2+\log n \le k \le n/2$ therefore seems to require a different function.

• Hasn't $1.$ been shown false by Mika Goos in "Lower Bounds for Clique Vs Independent Set" paper? Mar 19, 2015 at 9:19
• @Turbo: the paper of Göös (eccc.hpi-web.de/report/2015/012) shows that the co-nondeterministic communication complexity of CIS is at least $\Omega((\log n)^{1.128})$. This is a breakthrough, but it doesn't resolve the relationship between the deterministic communication complexity and the $\log\chi(f)$ measure. Mar 24, 2015 at 8:35


text: dc.contributor.author Hocking, Laird Robert dc.date.accessioned 2018-09-27T11:54:09Z dc.date.available 2018-09-27T11:54:09Z dc.date.issued 2018-10-20 dc.date.submitted 2017-08-29 dc.identifier.uri https://www.repository.cam.ac.uk/handle/1810/281805 dc.description.abstract The subject of this thesis is a class of fast inpainting methods (image or video) based on the idea of filling the inpainting domain in successive shells from its boundary inwards. Image pixels (or video voxels) are filled by assigning them a color equal to a weighted average of either their already filled neighbors (the direct'' form of the method) or those neighbors plus additional neighbors within the current shell (the semi-implicit'' form). In the direct form, pixels (voxels) in the current shell may be filled independently, but in the semi-implicit form they are filled simultaneously by solving a linear system. We focus in this thesis mainly on the image inpainting case, where the literature contains several methods corresponding to the {\em direct} form of the method - the semi-implicit form is introduced for the first time here. These methods effectively differ only in the order in which pixels (voxels) are filled, the weights used for averaging, and the neighborhood that is averaged over. All of them are very fast, but at the same time all of them leave undesirable artifacts such as kinking'' (bending) or blurring of extrapolated isophotes. This thesis has two main goals. First, we introduce new algorithms within this class, which are aimed at reducing or eliminating these artifacts, and also target a specific application - the 3D conversion of images and film. The first part of this thesis will be concerned with introducing 3D conversion as well as Guidefill, a method in the above class adapted to the inpainting problems arising in 3D conversion. However, the second and more significant goal of this thesis is to study these algorithms as a class. In particular, we develop a mathematical theory aimed at understanding the origins of artifacts mentioned. Through this, we seek is to understand which artifacts can be eliminated (and how), and which artifacts are inevitable (and why). Most of the thesis is occupied with this second goal. Our theory is based on two separate limits - the first is a {\em continuum} limit, in which the pixel width $h \rightarrow 0$, and in which the algorithm converges to a partial differential equation. The second is an asymptotic limit in which $h$ is very small but non-zero. This latter limit, which is based on a connection to random walks, relates the inpainted solution to a type of discrete convolution. The former is useful for studying kinking artifacts, while the latter is useful for studying blur. Although all the theoretical work has been done in the context of image inpainting, experimental evidence is presented suggesting a simple generalization to video. Finally, in the last part of the thesis we explore shell-based video inpainting. In particular, we introduce spacetime transport, which is a natural generalization of the ideas of Guidefill and its predecessor, coherence transport, to three dimensions (two spatial dimensions plus one time dimension). Spacetime transport is shown to have much in common with shell-based image inpainting methods. In particular, kinking and blur artifacts persist, and the former of these may be alleviated in exactly the same way as in two dimensions. At the same time, spacetime transport is shown to be related to optical flow based video inpainting. In particular, a connection is derived between spacetime transport and a generalized Lucas–Kanade optical flow that does not distinguish between time and space. dc.description.sponsorship Cambridge Overseas Scholarship dc.language.iso en dc.rights All rights reserved dc.rights All Rights Reserved en dc.rights.uri https://www.rioxx.net/licenses/all-rights-reserved/ en dc.subject image processing dc.subject image inpainting dc.subject partial differential equations dc.subject stopped random walks dc.subject numerical analysis dc.subject convergence rates dc.subject 3D conversion dc.subject parallel algorithms dc.title Shell-Based Geometric Image and Video Inpainting dc.type Thesis dc.type.qualificationlevel Doctoral dc.type.qualificationname Doctor of Philosophy (PhD) dc.publisher.institution University of Cambridge dc.publisher.department Applied Mathematics and Theoretical Physics dc.date.updated 2018-09-25T17:54:31Z dc.identifier.doi 10.17863/CAM.29168 dc.publisher.college Sidney Sussex College dc.type.qualificationtitle Doctor of Philosophy (PhD) Applied Mathematics cam.supervisor Schoenlieb, Carola-Bibiane cam.thesis.funding false


text:
# 3. Create a Pipeline¶

In this tutorial, we will create a simple pipeline and execute it. Later on, we’ll create our own version of a block and use it in the pipeline. All of the code can be found in one block at the bottom of this page.

With Bifrost, there is one main module you will be calling as a user: bifrost.pipeline. This handles all the behind-the-scenes pipeline construction, giving you a high-level view at arranging a series of blocks.

We would like to construct the following pipeline, which will serve to calculate the beats per minute of a song. As we will soon see, some intermediate operations will be required to get the bpm, and we can then write our own block.

1. Read in a .wav file to a ring buffer.
2. Channelize it with a GPU FFT.
3. Write it back to disk as a filterbank file.

## 3.1. Blocks in this Example¶

This setup will require bifrost blocks which:

1. Read in the .wav file.
2. Copy the raw data to the GPU.
3. Split the time axis into chunks which we can FFT over.
4. FFT this new axis.
5. Take the modulus squared of these FFTs.
6. Transpose this data into a format compatible with the sigproc writer.
7. Copy the data back to the CPU.
8. Convert the data into integer data types.
9. Write this data to a filterbank file.

This file could then be used to do things like calculating the beats per minute of the song at different points of time, or could be used to just view the frequency components of the song with time.

First, ensure you have a working Bifrost installation. You should also have some CUDA-compatible GPUs to run this example.

## 3.2. Code Walkthrough¶

Now, let’s create the pipeline.

The first thing to do is to actually load in Bifrost. Load in the base library as bf:

import bifrost as bf

Next, let’s load in some function libraries. We want blocks, which is the block module in Bifrost, which is a collection of previously-written blocks for various functionality,and views, which is a library for manipulations of ring headers.

import bifrost.blocks as blocks
import bifrost.views as views

Now, let’s create our data “source,” our source block. This is the block that feeds our pipeline with data. In this example, we work with a .wav file. I assume that you have own some sort of audio file and can convert it, using, e.g., online-convert.

Now, I want to load this into Bifrost. The syntax for this instance is:

raw_data = blocks.read_wav(['heyjude.wav'], gulp_nframe=4096)

Where ['heyjude.wav'] is a list of .wav files, which is in this case, a sample of Hey Jude. gulp_nframe is an argument passed to this block which tells it how many frames of data to gulp at once.

Some terminology:

• frame: One chunk of data. In this case, it is a single sample of the audio file. By setting gulp_nframe=4096, we tell the block to read in 4096 samples at a time, and put these into the ring buffer at once.
• gulp: One read or write of the ring buffer. Imagine the block taking a gulp of data. Then gulp_nframe is how many frames are in that gulp.

Now, raw_data is now a reference to a block object, which implicitly points at the ring buffer which will hold the raw .wav data.

Next, we want to put this data onto the GPU. Bifrost makes this simple. Insert a copy block as follows:

gpu_raw_data = blocks.copy(raw_data, space='cuda')

In this line we are telling Bifrost to create a new block, a copy block, and set its input to be the raw_data variable which is the source block for our audio file. Then, by setting space='cuda', we tell Bifrost to create a ring in GPU memory, and copy all of the contents of raw_data into this new ring. With this GPU ring, we can connect more blocks and do GPU processing.

Now, since we only want to do a Fourier transform at different parts of the song, not the entirety of the song, we want to chunk up this audio file into segments over which we can Fourier transform. This lets us get a frequency view at various points of the song. Since our data comes as one long time stream, we want to break it up into parts. Bifrost lets you do this without extra processing. You simply manipulate the header of the ring, which stores all of the descriptions for the ring. These manipulations are accomplished with views:

chunked_data = views.split_axis(gpu_raw_data, 'time', 256, label='fine_time')

What have we done here? We took gpu_raw_data, which is a block on the GPU, and which implicitly points to its output ring buffer which sits on the GPU, and put it into the split_axis view. We said take the 'time' axis of this ring, and break it up into 256-size chunks. Create a new axis for this data, and call that axis 'fine_time'.

Note that views are special in that they do not actually modify the data. They just modify the metadata, which lets blocks interpret the data differently.

Now, let’s actually do the processing. We want to do an FFT along this 256-size axis, and we want to do it on the GPU. Bifrost knows which ring is on the GPU and CPU, so this is implicitly passed to the FFT block:

fft_output = blocks.fft(chunked_data, axes='fine_time', axis_labels='freq')

So, in this line, we create a new block, an FFT block, and send in the chunked data. We tell the FFT block to perform the Fourier transform along the 'fine_time' axis, which is the one of 256-size chunks, and then after Fourier transforming, to name the output axis 'freq'.

The Bifrost FFT block wraps cuFFT, the CUDA FFT package, which is heavily optimized. Inside the block, bifrost.ndarray’s are being created from the output ring of chunked_data, and this is being passed with ctypes into a C++ function.

Next, we want to take the square of these FFTs. In Bifrost, this can be done with the detect block:

squared = blocks.detect(fft_output, mode='scalar')

This block takes in the output of the FFT (we are still on the GPU!), squares each element (the 'scalar' mode), and then puts this in a new ring, implicitly stored in the squared block object.

Now, after carefully reading the documentation for the transpose block, we decide that we want to arrange the axes such that it goes in as ['time', 'pol', 'freq']. To do this, we need to transpose it. Bifrost has the transpose block to do this. We simply type the desired output axis layout as the only argument, and it is all performed on the GPU for us:

transposed = blocks.transpose(squared, ['time', 'pol', 'freq'])

This transposes the axes for us. Now we can ship it into sigproc writer. But first, we have to offload from the GPU:

host_transposed = blocks.copy(transposed, space='cuda_host')

Then, convert to an 8-bit integer data type for storage, with the quantize block ('i8' means an 8-bit i nteger).

quantized = bf.blocks.quantize(host_transposed, 'i8')

Finally, we pass the data stream into a sink block, which ends the pipeline and stores the data in a filterbank file:

blocks.write_sigproc(quantized)

In this case, the filename will be determined from the header information, which contains the name of the original .wav file. The output file is of type .fil, and is the channelized version of the original music file. It is the frequency decomposition of the audio.

So, what have we done? We:

1. Read in the .wav file.
2. Copied the raw data to the GPU.
3. Split the time axis into chunks which we could FFT over.
4. FFT’d along this new axis.
5. Took the modulus squared of these FFTs.
6. Transposed this data into a format compatible with the sigproc writer.
7. Copied the data back to the CPU.
8. Converted the data into integer data types.
9. Wrote this data to a filterbank file.

## 3.3. All the Code¶

For ease of reference, here is all the code at once:

import bifrost as bf
import bifrost.blocks as blocks
import bifrost.views as views


text:
This is an old version, view current version.

9.4 Argument types and qualifiers

Stan’s functions all have declared types for both arguments and returned value. As with built-in functions, user-defined functions are only declared for base argument type and dimensionality. This requires a different syntax than for declaring other variables. The choice of language was made so that return types and argument types could use the same declaration syntax.

The type void may not be used as an argument type, only a return type for a function with side effects.

Base variable type declaration

The base variable types are integer, real, vector, row_vector, and matrix. No lower-bound or upper-bound constraints are allowed (e.g., real<lower=0> is illegal). Specialized types are also not allowed (e.g., simplex is illegal) .

Dimensionality declaration

Arguments and return types may be arrays, and these are indicated with optional brackets and commas as would be used for indexing. For example, int denotes a single integer argument or return, whereas real[ ] indicates a one-dimensional array of reals, real[ , ] a two-dimensional array and real[ , , ] a three-dimensional array; whitespace is optional, as usual.

The dimensions for vectors and matrices are not included, so that matrix is the type of a single matrix argument or return type. Thus if a variable is declared as matrix a, then a has two indexing dimensions, so that a[1] is a row vector and a[1, 1] a real value. Matrices implicitly have two indexing dimensions. The type declaration matrix[ , ] b specifies that b is a two-dimensional array of matrices, for a total of four indexing dimensions, with b[1, 1, 1, 1] picking out a real value.

Dimensionality checks and exceptions

Function argument and return types are not themselves checked for dimensionality. A matrix of any size may be passed in as a matrix argument. Nevertheless, a user-defined function might call a function (such as a multivariate normal density) that itself does dimensionality checks.

Dimensions of function return values will be checked if they’re assigned to a previously declared variable. They may also be checked if they are used as the argument to a function.

Any errors raised by calls to functions inside user functions or return type mismatches are simply passed on; this typically results in a warning message and rejection of a proposal during sampling or optimization.

Data-only qualifiers

Some of Stan’s built-in functions, like the differential equation solvers, have arguments that must be data. Such data-only arguments must be expressions involving only data, transformed data, and generated quantity variables.

In user-defined functions, the qualifier data may be placed before an argument type declaration to indicate that the argument must be data only. For example,

real foo(data real x) {
return x^2;
}

requires the argument x to be data only.

Declaring an argument data only allows type inference to proceed in the body of the function so that, for example, the variable may be used as a data-only argument to a built-in function.


text:
?
Free Version
Moderate

# Triangles with Congruent Angles

SATMAT-YINDLY

If $PQ=6$, $QR=4$, $PR=7$, and $RT=6.4$ as shown in the figure above, and if $\angle Q\cong \angle T$; then, what is the perimeter of $\triangle RTW$?

A

$27.2$

B

$24.4$

C

$19.4$

D

$17$


text:
Finished Iris Split Mechanical Keyboard build with a brushed copper plate, Zealios v2 67g, Iris Rev2 PCBs, and green LEDs to give a Copper Oxide effect without the rust.

I’ve been experimenting with ergonomic set-ups and the wonders of mechanical keyboards, and so I decided to build my own!

In this blog I’ll talk about my experience building my first mechanical keyboard. I’ll cover my aims (and constraints) for the build, followed by a build log, before finishing with some comments and further work.

### Aims, Constraints, & Components

#### Aims

I’m at that stage of my career where I’m starting to optimise my work-flow so I can focus on the tasks that need the real attention. This ranges from using vim macros to handy scripts, however standard keyboards are incompatible with these kinds of tricks as it still requires as much work to invoke them. I’ve been using a Vortex Race 3 as my daily driver which partially bridges the gap with programmable macros, bindable keys, and stock layouts that make typing more efficient; however it is not fully reprogrammable to the n-th degree. That is, It doesn’t have arbitrary macros that can invoke arbitrary programs, with arbitrary symbols bound to keys, and an arbitrary number of layers I can switch out as needed. I’d like a keyboard that can do this without hindering my flow.

My vortex race 3, rocking cherry MX clears and a granite keyset as standard.

I spend a lot of time at my desk typing on a fixed board and so I’d like to mitigate the long-term effects of RSI. I’ve been looking into stand-up desks, better ways to work, and the ergonomic things I can do to mitigate negative effects of, in essence, cramping up over a desk due to extended use. Standard keyboards force your hands to a small fixed-point of the table when working, and this is not a natural position for your hands. I’ve seen the effects of long term cramping over a career and I aim to avoid the same result. I have used several non-standard boards with different mechanical switches including my Vortex (MX Clears), a Roccat Ryos MK Pro (MX Black), a Filco Ninja Majestouch 2 (MX Brown), Apple Magic keyboard (Butterfly switches of doom), a Das model S (MX blues), and others. All of these models are fixed, and so the next logical step is to consider split keyboards. My colleague Alastair wrote a great blog about his split build: he designed his split board to sit on top of his MacBook Pro. This is a good build, however split builds like these are still bounded by the fixed-point problem and fiddly to get right as you must find the right angle of split before you make the board. Instead I aim to build a board which is essentially cut in half so I can adjust the angle of use as much as I need. This seems like a general solution to the fixed-point problem and there are several builds out there that do this well.

The Ninja and the Apple

#### Constraints

I only have a week to build this board and any related components, such as firmware. This means the build should be well planned out, and more importantly actionable within the time I have. So some of the components such as the PCB will likely come pre-made to make this build possible. Fortunately we have a maker lab at work stocked with the latest and greatest in technology from laser cutters to 3D printers and so resourcing the tooling shouldn’t be an issue. This does however mean I need to ensure all the components arrive before I start the build, which may take some time if they are custom components.

Second, I need to consider the factors of an ergonomic build that will improve my work-flow as these will likely inform the kind of build I make. The first consideration is the kind of switch and the actuation force required to press them. I tend to prefer tactile switches with heavier actuation forces such as the Cherry MX clears. Tactile switches have a noticeable activation point for good touch typing whilst being office friendly (not loud). The choice of switch will likely have an effect on the switch plates I can mount them to, the PCBs that support them, and an onset of other side effects for the build (such as build height, usable keycaps, and more).

Tactile clear switches are tactile and have a bump. Clickly blue switches have a bump and a moving jacket that makes the click. Linear red switches have neither and simply slide. Gifs sourced here

Another important consideration is how many keys I use on a daily basis. As an engineer I make use of many special characters and short-cuts, however none of these are supported by larger full-size (100%) boards by default. Further I don’t use most of the keys on standard size boards, and this adds travel time for the rare occassion I do. Tenkeyless (87%) boards improve this with less keys, however I find there is still unused keys and time wasted moving my hands to or from the home row. My vortex at 75% hits a sweet spot of key use-density, quality, and size; however it is not split and has limited programmability of macros and key-bindings. I am yet to try a smaller (40-50%) board, such as the planck, but my suspicion is that this won’t be good for my large hands. This suggests I may prefer I mid-sized board with a higher number of modifiers and layers within range of the home row.

At this point in the build process I did some research to get an idea of how mechanical keyboards work, and consider which components would be best for me. Here is a quick list of useful links you may find useful:

• Special mentions go to LinusTechTips, TAEKeyboards, and RhinoFeed which are a good start as any for somebody to get into mechanical keyboards.
• The Chyrosran77 Youtube channel had some particularly interesting insights into the history of key switches and keyboards which made for some good binge watching.
• Mechanical Keyboards Subreddit: It almost goes without saying that this is one of the best resources for finding information about mechanical keyboards. The guides go into a lot of detail and Google almost always bought me here.
• The anatomy of a board: This guide by a Microsoft engineer is a particularly detailed account of how each of the components of a board work.
• WASD keyboard guide: This guide was useful in describing the differences between the popular brands of mechanical switches and the peripherals (like O-rings). This was further complimented by Reddit.

#### Components

Now the research phase is over and the components have arrived, we can look at the components in detail. As is self-evident by the title I settled on an Iris split keyboard with a brushed copper switch plate, Zealios v2 67g tactile switches, and DSA profile PBT plastic keycaps in an off-white/grey. As for the style, I am going for an industrial look with a copper main and metallic accents. Combined with green LEDs the goal is to give a copper patina look but without the patina. For firmware I went with the Quantum Mechanical Keyboard (QMK) firmware due to its ubiquity, ease of use, and time constraints on this project. Let’s look into the iris PCBs first to set the stage on why I went with this form factor.

Iris Rev2 PCBs B-stock in black and white.

When choosing a split I had plenty of choice including the Ergodox, Iris, Let’s Split, Helix and more. The Ergo family seemed to be slight too big / too much going on for me; this would increase finger travel time when working. Conversely Let’s Split and Helix families of boards were slightly too small and I wouldn’t make full use of my thumbs. An alternative was the Helidox design which solved this problem, however I was not keen on the exposed boards on top. Instead I went for the Iris which struck a sweet spot between utility and size as well as having fashionable designs around. Additionally there are plenty of Iris build guides, which is valuable given the time constraints of this build (here, here, here).

Keeb offer multiple kinds of PCB but their most recent incarnation, the Rev 3, was the only one in circulation. I wasn’t a fan of this design as it already had many of the components pre-soldered on. I wanted to learn about soldering and working with these components myself so I had to find some old stock. The result of this search is a pair of B-stock boards of different colours; whilst this isn’t symmetric, it is still pretty. The choice of PCB informed the rest of the design and in particular the switch plate.

Next I had to consider the aesthetics of the board, notably by choosing a switch plate. On one hand I wanted something practical with no flex, but on the other hand it had to look nice (as all custom boards must!). I settled on a metal plate as this would stabilise the typing surface and the mount onto which the PCB was fixed. I went with copper after settling on an industrial (but not quite cyberpunk) look. I went with an open-side design and an acrylic base for the board so I could show off the circuitry underneath.

Next up was the choice of switches. I wanted something tactile but I’m not a fan of Cherry MX browns (it feels like standing on slugs) and I use MX clears in the vortex. Popular switches at the time included Zealios v2, Holy Pandas, Kailh Box Royals, or variations of Alps. I went with Zealios v2 67g as they are known for the tactile bump at the top of the switch, the solid design, and MX compatibility with the other components. Realistically switch choice should have governed the choice of switch plate and PCB, and in the next build it may, but for the first build I wanted something familiar.

Layout of the components at the start of the build. You may notice here some resistors are not used in the build, this is because I ordered the wrong ones!

The last main component was the key caps, I chose to go with DSA profile dye-sublimated keycaps from signature plastics. I wanted a profile that could help me navigate to the home row, whilst having a quality sound and feel that wouldn’t distract me at work. I played it safe and emulated the vortex colour scheme of off-white and grey caps (GEK and GKK). The rest of the components include: a silver TRRS cable with silver sheaf, Pro Micro (and now an elite C) micro-controllers, green 1.8mm LEDs, resistors for the LEDs and switches, diodes, nickel plated M2 stand-offs, and a strip of green LEDs for under glow.

### Build Log

With the components ready to go, it was time to build the board. The process followed roughly the following steps:

1. Test the components to ensure they work.
2. Lacquer the unfinished copper on the switch plate.
3. Solder the resistors, TRRS jack, and diodes onto the PCBs.
4. Mount the switches onto the plate and PCB.
5. Solder the LEDs and micro controllers onto the PCBs.

Each of the following subsections cover these.

#### Test the components to ensure they work

First I tested that the LEDs, the micro-controller, and the switches operated as expected. To test the LEDs I rigged together a simple circuit using a 5V rail from an Arduino, a resistor, and an LED. I simply tested that the LEDs turned on.

This simple test caught a few dud LEDs, and gave me an idea of how bright these diodes were.

Next I tested that each of the switches worked as expected, I used a multi-meter to measure the current across them. No surprises here.

Luckily no switches were broken which is a relief as I only have 2 to spare.

Next I tested that the micro-controllers were not dead on arrival and installed the firmware. I couldn’t test the actual functionality at this point until everything else was done, which is annoying but unavoidable. This involved cloning the firmware from GitHub, and later QMK toolbox. I had some trouble with this due to some avrdude related issues using an incompatible version of gcc to compile the key-mappings. I also could not run make keebio/iris/rev2:default:avrdude to flash the default key-mappings onto the micro-controllers as this required a two-stage reset and fiddling with the dodgy Caterina bootloader. In the end I installed qmk toolbox and let that do the work.

Fiddly and annoying.

#### Lacquer the unfinished copper on the switch plate.

The copper plates I received were not finished. I wanted to preserve the beautiful gleam of the copper for as long as can and so I lacquered the plates. This was perhaps the most unpleasant part of the build, but I’ll get into those later for the sake of narrative. To lacquer untreated Copper you need the following:

• A clean dust free area with plenty of ventilation.
• knowledge of the materials you are working with including metal lacquer.
• Plenty of time and patience.

I had one of these to begin with. To lacquer Copper you must first clean off any dust or dirt using something that doesn’t leave a residue like Isapropyl Alcohol. Next you must apply the lacquer consistently across the plates, making sure the plates are flat so the lacquer doesn’t travel to one side. Then the waiting game begins as you must keep an eye on this to ensure no dirt settles on the sticky surface, and reapply a new coating every 15 minutes or so. Do not leave it 24 hours to re-apply in hot conditions as this will cause the lacquer to flake off, and you do not want to have to clean it off!

This will take some time and patience.

#### Solder the resistors, TRRS jack, and diodes onto the PCBs.

Next I had to prepare the through-hole diodes and resistors to solder to the board. To get a consistent fold, I used the acrylic base as an edge against which to fold the components over.

Folding the diodes

Followed by careful placement.

Placing the diodes in the correct spots.

And finally soldering.

Soldering diodes for one side done.

This process was repeated for the resistors too.

Placing the resistors in the correct spots.

It’s worth checking at this point that you haven’t got any dry joints which may cause your board not to work later if the contacts aren’t connected.

I'm checking the solder job on a reset switch that came with the board and notice it's a little dry. In these cases it's good to add more solder if you can.

Now is a good place to note that as these boards were B-stock one came pre-soldered with a few reset switches, MOSFETs and other components (probably for testing). In particular the other board has SMD diodes and a MOSFET on which meant I doubled up on my order of diodes!

This process was repeated on the other board (flipped over).

Soldering the section board.

So that I had the following:

Solder pair.

I also soldered on the micro-controller headers at this point.

#### Mount the switches onto the plate and PCB.

Time to solder these together.

Now that the small components were soldered on it was time to mount the switches. It’s important to solder switches into the main corners of the plates and mount the PCB first so that you have a stable surface to solder the rest on.

Solder the corners first

It was at this point that I realised not only that I had soldered the headers on the wrong way round, but also that I didn’t have enough clearance between the micro-controller and the acrylic base. At this point I used a heat gun to remove the headers and fit some smaller ones which ruined the finish in the process.

Before fitting the full set, I quickly assembled the other switch plate to see if it had the same problem, luckily it didn’t.

Checking the set-up of the other plate.

Lastly I soldered on the rest of the switches

The last of the switches, it's looking good!

#### Solder the LEDs and micro controllers onto the PCBs

At this point it’s day 5 of the build and the end is in sight! At this point the remaining tasks were to solder on the last components and test that the keyboard worked. The switches supported through-hole LEDs which needed to be mounted and soldered in the same way as the resistors.

The LEDs need to be mounted in the same way as the resistors.

And they sit comfortably in each switch

The LEDs need to be mounted in the same way as the resistors.

Finally the micro-controllers were soldered onto the new headers. Luckily the burn isn’t too visible.

Soldering on the micro-controllers

After assembly and screwing on the acrylic base the build was done! Luckily the firmware worked as expected too, however there were a few caveats which I cover in the Comments below.

The finished build without the cables.

Overall the build was a success with caveats. The board (sort of) works and is pleasant to type on however there were several problems during the build and plenty of room for improvement. I am largely happy with the result however as there is a beauty in uniqueness of the build and the memories that come with it!

Board at the end of build week

The build itself is very solid with the copper and acrylic base; there is no flex in the boards. The open design means the switch mounts are exposed however, so the board has a noticable clack when used. This isn’t a deep clack either, but rather rounded with accentuated highs. This was muted to a degree by rubber O-rings under the keycaps, however the sound is still prominent and I’m not convinced I’m a fan just yet. It measures in volume somewhere between the muted thunk of cherry MX clears and the thinner click of MX blues.

As for my lacquering skills there is plenty of room for improvement. I had never lacquered anything before, let alone a copper surface that is very sensitive to patina. Additionally the plates dried outside during one of the hottest summers on record and so the plastic layers cracked several times. The result was a bubbly and inconsistent finish. This took multiple attempts to get right, and multiple experiments to find the best way to prepare the copper. According to the internet white vinegar and salt is meant to be quite effective at removing patina however I found it was far too aggressive at removing the top layers of copper. This had long lasting effects as I couldn’t restore the copper to a patina-free point. Next I tried 99% strength Isopropyl alcohol which was much better at removing bad lacquer, cleaning the copper, and leaving no residue. I also ruined a Pyrex bowl when lacquering the plates. The result was barely worth the effort but I have received compliments about the professional quality of the finish, despite the patina on the undersides of the plates. If I were to work with untreated metals again, I would ensure they are treated professionally before use.

The lacquering process took several attempts with noticeable patina on the underside of the plates.

As with most custom builds the costs rack up quickly and so heavy emphasis was placed on testing components before fitting them. This caught 2 dud LEDs and no dead switches however 2 LEDs broke later in the build sometime between soldering and testing the boards. The modular nature of the build meant it was largely impossible to ensure all components worked together as expected until the last micro-controller was soldered on. Faith in my soldering is not ideal but necessary and falls into that uncomfortable space of problems that are fiddly to get right and outright unpleasant to adjust in small increments. This was noticeable when mounting the micro-controllers above LED solder points as I had to solder this whilst keeping the mount straight at a fixed height.

The working right side with one dead diode, I was able to replace the other one. Can you figure out why this one is so hard to replace?

With the finishing posts in sight it was only fitting that more problems would come my way. Firstly, my custom keycaps were shipped from America and consequently got stuck in customs for 2 weeks. After arrival and installation I discovered an unfortunate feature of the Pro micro: the weak soldering on the USB header. Within 30 minutes of use the header (and traces) had snapped off! After futile efforts to recover the micro-controller, I ordered an Elite-C from SpaceCat design. The silver lining is that I get to try out the DFU bootloader, and future proof half of my keyboard with a USB C-type port; the obvious downsides are cost and the need to have 2 types of cable should I ever choose to swap the master micro-controller over.

When fitting the keycaps the USB header snapped off. Pictured is the final look of the board, and the USB cable with a broken header on it.

With all things said and done this project took quite a lot longer than I had planned. I gave myself a week to build the board, and write the software. Only one of these tasks was completed due to the delay on the keycap delivery, and micro-controller-based problems. It is possible to have worked on the software without caps however the week was up and I was going on holiday :-) This time didn’t account for months of research required and months to deliver the components but I consider that to be a constant factor in most builds of this scale. Despite this, I believe the build was a success by virtue of the fact the board is at work and ready to go; I just need to spend a lunchtime replacing the broken controller. As for user testing and firmware, I may write another post in the future.

For my first build this board represents a success. It is a high quality piece of kit and I suspect it will help my productivity substantially, if not minimising RSI. Admittedly I could have spent more time considering the ergonomic, stylistic, and practical matters but I’m leaving that to further work.

#### Further work

The base is finished but there is more work to be done! These odd jobs, some of which are not necessary, will improve the keyboard and (hopefully) my skills.

I’m not sold on the idea of using green LEDs to give an ‘oxidation effect’ as it contrasts too heavily with the copper, silver, and brass parts. Ideally the PCB would support 3-pin LEDs to vary the colour as needed but that wasn’t supported in the rev 2. I may replace the green LEDS with yellow ones to be consistent.

Additionally I never installed the LED under glow strip and I’m debating whether the build really needs it.

In order to maximise the ergonomic utility of this board I would like to add copper tents (like laser ninja’s) to the board so I can vary the angle of the board; ideally to an arbitrary degree but more realistically at fixed heights by screwing into the M2 fittings. If you’re reading this and you know of copper tents like these then please let me know!

At the time of writing, I have just received my replacement micro-controller but have not soldered it onto the PCB yet. As a result I haven’t used the board, or written my own firmware for it. The plan is to design and write my on Space Cadet QMK driver with Greek, logic, and LISP-inspired layers. This will help with accessing these symbols as unicode, for which support is increasing.

Once all is said and done this build was largely component based. If I make another keyboard I may build it from the ground up. That is, using spare Arm powered micro-controllers, custom wiring, custom cut cases, and bespoke firmware. I would learn more about the fundamental components of firmware and driver get to design a board from the ground up. This board would be portable (at 75% in size like the race 3) to work with my laptop and would fit into a mahogany case with a removable lid so I can use it on the go.

Vortex in a glamour shot. Also featuring possible micro-controllers for the next build.

#### Acknowledgements

I’d like to thank Alastair Reid for his tips and discussions about keyboard builds over lunch in the early phases, and the many RSI jokes made in the office!

I’d like to thank Yiangos Yiangou for his help in sourcing the Iris PCB B-stock which is now out of circulation, and for his advice to look at laserboost for switch plates!

I’d like to thank Vasileios Laganakos for his recommendations to go with Signature Plastics for the keycaps. They top a great build!

I’d like to thank the folks at laserboost and Signature Plastics for their advice and help choosing switch plates and keycaps.

Last but not least I’d like to thank the folks in the hardware lab at work who helped me solder and debug my board.

Packing away the board to use next week