Rudolf Adamkovič

Personal Website

[Skip to Contents]​

Welcome

Welcome to my website! ☺

About me

Name Rudolf Adamkovič
Nick Rudy
Web https://adamkovic.org
Mail rudolf@adamkovic.org

Interests

  • thinking through writing
  • improving free software
  • playing table tennis
  • exploring by folding bike
  • cooking and baking simply

Industry experience

2012+ Mobile Developer, US/AU/EU clients 1, freelance
  iOS Swift/Objective-C, Android Kotlin/Java, Fennel Lisp,
  Lua, ISO/ANSI C, GNU Make, SQLite, GNU Bash, Git
   
2010–2012 Application Specialist, Raiffeisen Bank, Slovakia
  GNU/Linux, GNU Bash, SQL
   
2007–2010 GNU/Linux Developer, Septim, Czech Republic, remote
  Debian GNU/Linux, GNU Bash, XML/XSD/XSLT, Python
   
2006–2007 Unix Administrator, Hewlett-Packard, Slovakia
  SUSE GNU/Linux, RedHat GNU/Linux, HP-UX
   
2004–2006 Network Administrator, Trnava University, Slovakia
  Debian GNU/Linux, Microsoft Windows

Academic journey

Master of Science (MSc) in Artificial Intelligence, First Class Honors
2024–2026, EU-accredited degree, remote
Munster Technological University, Cork, Munster, Ireland, EU

Bachelor of Science (BSc) in Computer Science, Summa Cum Laude
2018–2023, US-accredited degree, remote
University of the People, Pasadena, California, USA

Public recognition

Business Freelancer with 100% positive public feedback
  Software developer with long-term client relationships
   
Freedom Regular contributor to various FOSS projects
  Signed FSF developer and GNU contributor
   
Education Life-long student and avid Emacs note-taker
  Acknowledged contributor to several textbooks
   
Community Active participant in public mailing lists and bug trackers
  Top <1% Stack Overflow contributor

Work sample

Add support for LaTeX mathematics to Texinfo exports
Org mode, GNU Emacs, Free Software Foundation
https://list.orgmode.org/m2wngizda3.fsf@me.com/
org-mode.git/commit/?id=c940b460c

Upgrade from MathJax 2 to MathJax 3+
Org mode, GNU Emacs, Free Software Foundation
https://list.orgmode.org/orgmode/m2a667n4ax.fsf@me.com/
org-mode.git/commit/?id=62e1513b5

Advertisement

Here is my ad:

NO CORPORATIONS * NO SURVEILLANCE * NO SLAVERY * NO NATIONS * NO RELIGIONS
FREE SPEECH * FREE TECHNOLOGY * FREE INFORMATION * FREE EDUCATION

Science

Science

The study of the falsifiable. 2

By proof/evidence

By understanding

By reasoning

Sources

Falsifiable

That which is

testable either empirically or formally.

Formal science

A science

based on formal proofs.

Formal

That which

can be proved absolutely

from given axioms.

Empirical science

A science

based on empirical evidence

Empirical

That which

is subject to revision

based on observed evidence.

Scientific method

The standard method of empirical research in which we

propose a hypothesis

that explains some aspect of objective reality and

test the hypothesis experimentally.

Results

If the test results match objective reality,

the hypothesis becomes a scientific fact,

possibly supporting a scientific theory.

Hypothesis

A falsifiable statement that is

a proposed explanation

of some aspect of objective reality.

Scientific fact

A true statement supported by empirical evidence.

Scientific theory

A non-empty set of

interrelated scientific facts.

Theorem

A true statement supported by formal proof.

Objective reality

Everything that is. 3

Induction

Reasoning

from the particular to the general.

Terminology

Not to be confused with mathematical induction.

Opposite

Example

Observing pea plants to discover Mendelian inheritance.

Synonyms

  • Inductive reasoning

Deduction

Reasoning

from the general to the particular.

Example

Predicting phenotypes of pea plants using Mendelian inheritance.

Opposite

Synonyms

  • Deductive reasoning

Experimental variable

The part of an experiment that changes.

Experimental group

The group under the influence of the experimental variable.

Control group

A group identical to the experimental group in all but the experimental variable.

Serendipity

Accidental discovery.

Example

Alexander Fleming found mold on his dish killing bacteria, discovering penicillin, the antibiotic produced by the fungus Penicillium.

Basic science

A kind of science that

pursues knowledge for its own sake.

Result

Often solves real-world problems anyway.

Opposite

Applied science

A science that

pursues knowledge to solve real-world problems.

Opposite

Synonyms

  • Technology

Natural science

An empirical science concerned with

natural phenomena.

Life science

See biology.

Physical science

A natural science that

studies the non-living.

Examples

Social science

Behavioral science

A science that

lacks fundamental understanding

and instead relies on observed behavior.

Validity

Requires rigorous statistical analysis, or it decays to pseudoscience.

Example

  • psychology
  • pre-Galilean physics

Order of magnitude

A logarithmic distance of base-\(b\) numbers, where

\(x\) is within \(n\) orders of magnitude of \(y\)

if and only if

\begin{align*} x & < y \cdot b^{\pm n} \\[1ex] y \cdot b^{-n} < x & < y \cdot b^{+n} \\[1ex] b^{-n} < \frac{x}{y} & < b^{+n}. \end{align*}

Practice implementation

(defun my-within-order-of-magnitude-p (x y &optional n b)
  "Return t if X is within N orders of base-B magnitudes of Y.

By default, check for 1 order of base-10 magnitude."
  (let ((n (or n 1))
        (b (or b 10)))
    (and (> (/ (float x) y) (expt b (- 0 n)))
         (< (/ (float x) y) (expt b (+ 0 n))))))
(and (my-within-order-of-magnitude-p 1.00 1.01)
     (my-within-order-of-magnitude-p 1000 1010))
t
(seq-map (lambda (number)
           (list number
                 (my-within-order-of-magnitude-p 1 number)))
         (number-sequence 1 15))
1 t
2 t
3 t
4 t
5 t
6 t
7 t
8 t
9 t
10 nil
11 nil
12 nil
13 nil
14 nil
15 nil

Metric prefix

Powers of 10:

Name Prefix Factor Note
kilo k 101 ⋅ 3 = 103 thousand
mega M 102 ⋅ 3 = 106 million
giga G 103 ⋅ 3 = 109 billion
tera T 104 ⋅ 3 = 1012 trillion
peta P 105 ⋅ 3 = 1015  
exa E 106 ⋅ 3 = 1018  
zetta Z 107 ⋅ 3 = 1021  
yotta Y 108 ⋅ 3 = 1024  
ronna R 109 ⋅ 3 = 1027  
quetta Q 1010 ⋅ 3 = 1030  

Powers of 2:

Name Prefix Factor Approximation
kibi ki 210 10(1 ⋅ 3) = 103
mebi Mi 220 10(2 ⋅ 3) = 106
gibi Gi 230 10(3 ⋅ 3) = 109
tebi Ti 240 10(4 ⋅ 3) = 1012
pebi Pi 250 10(5 ⋅ 3) = 1015
exbi Ei 260 10(6 ⋅ 3) = 1018
zebi Zi 270 10(7 ⋅ 3) = 1021
yobi Yi 280 10(8 ⋅ 3) = 1024

Nobel prize

A high-profile annual award

“for the greatest benefit to humankind”

in natural sciences, economics, literature, and peace.

Fields

Research paper

The standard format for communicating scientific findings [2].

Structure

Each paper has a title and the sections

  1. Abstract
  2. Introduction (with Research aim and Research questions)
  3. Methodology
  4. Results and Discussion
  5. Conclusion

where each section motivates the next.

Title

The part of a research paper that contains

a precise, term-rich name,

enabling discovery through retrieval systems.

Abstract

A section of a research paper that summarizes

question, method, answer, and meaning,

written last, read first.

Length

Typically 150–300 words, one paragraph.

Introduction

A section of a research paper that

identifies a research gap

via literature review, then

proposes the research aim and research questions.

Research aim

The overall goal of the research.

Abstraction level

High-level, yet technical; not coupled to methodology.

Length

Typically one complex sentence.

Research question (RQ)

A specific, answerable question framing a research paper.

Abstraction level

High-level, yet technical; not coupled to methodology.

Count

Typically 3 to 4 per research paper.

Alternative

Research objective

An actionable alternative to research question.

Methodology

A section of a research paper that

explains setup and procedure,

with narration, justifying each step with “in order to”.

Style

Typically written in simple past tense, either active or passive voice.

Results

A section of a research paper that

documents results.

Order

Documents results from most to least important.

Style

Typically written in

  • past simple for actions performed
  • present simple for standing facts

without interpretation, which is reserved for Discussion.

Discussion

A section4 of a research paper that

interprets results.

Direction

Discusses each result from specific findings to broad implications.

Style

Typically written in present tense.

Coverage

Addresses research questions, covering

  • evidence
  • surprises
  • limitations
  • comparisons
  • speculations

Conclusion

A section5 of a research paper that

summarizes findings and their significance.

Tense

Typically written in present tense.

Poster

A visual summary of a research paper.

Usage

Presented at academic conferences.

Style

Light on text, heavy on figures and whitespace.

Mathematics

Mathematics

A formal science that is both

the study of necessary conclusions [3]

and

the language of the universe [4].

Synonyms

  • Math (colloquial)
  • Maths (UK)

Connect

There is no science without mathematics, hence

“Mathematics is the queen of all sciences.”

— Carl Friedrich Gauss

Sources

Ethically licensed reliable sources, alphabetically:

  • APEX Calculus [5]
    @Book{hartman+2018,
      author       = {Gregory Hartman and Troy Siemers and Brian Heinold and
                      Dimplekumar Chalishajar},
      title        = {{APEX} Calculus},
      publisher    = {Self-published},
      year         = 2018,
      edition      = {Version 4.0},
      URL          = {http://www.apexcalculus.com}
    }
    
  • Book of Proof [6]
    @Book{hammack-2018,
      author       = {Richard H. Hammack},
      title        = {Book of Proof},
      publisher    = {Richard Hammack},
      year         = 2018,
      edition      = {3.4},
      note         = {Creative Commons License},
      url          = {https://richardhammack.github.io/BookOfProof/}
    }
    
  • Discrete Mathematics: An Open Introduction [7]
    @Book{levin-2021,
      author       = {Oscar Levin},
      title        = {Discrete Mathematics},
      publisher    = {Self-published},
      year         = 2021,
      edition      = {3th Edition, 5th Printing},
      subtitle     = {An Open Introduction},
      URL          = {http://discrete.openmathbooks.org},
    }
    
  • Essential Precalculus [8]
    @Book{stitz+2013,
      author       = {Carl Stitz and Jeff Zeager},
      editor       = {Sean Fitzpatrick},
      title        = {Essential Precalculus},
      year         = 2013,
      URL          = {https://opentext.uleth.ca}
    }
    
  • Fundamentals of Matrix Algebra [9]
    @Book{hartman-2011,
      author       = {Gregory Hartman},
      title        = {Fundamentals of Matrix Algebra},
      publisher    = {Self-published},
      year         = 2011,
      edition      = 3,
      note         = {Version 3.1110},
      URL          = {http://www.apexcalculus.com/other-texts},
    }
    
  • Introduction to Statistical Thinking [10]
    @Book{yakir-2011,
      author       = {Benjamin Yakir},
      title        = {Introduction to Statistical Thinking},
      publisher    = {Self-published},
      year         = 2011,
      month        = jun,
      organization = {The Hebrew University of Jerusalem},
      url          = {http://pluto.huji.ac.il/~msby/StatThink}
    }
    
  • Linear Algebra Done Right [11]
    @Book{axler-2024,
      author       = {Sheldon Axler},
      title        = {Linear Algebra Done Right},
      publisher    = {Springer},
      year         = 2024,
      edition      = 4,
      URL          = {https://linear.axler.net/},
    }
    

Continuous

That which is uncountable.

Example

The set of all reals \(\mathbb{R}\).

Opposite

Conceptually, continuous is the opposite of discrete.

Electronics

Conceptually, continuous is synonymous to “analog” in electronics.

Discrete

That which is countable.

Example

The set of all integers \(\mathbb{Z}\).

Opposite

Conceptually, discrete is the opposite of continuous.

Electronics

Conceptually, discrete is synonymous to “digital” in electronics.

Discrete mathematics

The part of mathematics that

studies the discrete.

International Mathematical Olympiad (IMO)

The most prestigious competition in mathematics.

Audience

World-smartest pre-university, pre-calculus students.

Frequency

Every year in a different country.

Fields medal

The most prestigious award in mathematics.

Frequency

Given every 4 years, starting 1936.

Algebra

Algebra

A branch of mathematics concerned with

symbolic manipulation.

Univariate

Involving one variable.

Multivariate

Involving more than one variable.

Bivariate

Multivariate and involving exactly two variables.

Monomial

An algebraic expression that can be written as a product of a constant and non-negative integer powers of finitely many variables.

Standard form

The standard form of a monomial is

\begin{equation*} a \prod_{i = 1}^k x_i^{n_i} = a x_1^{n_1} x_2^{n_2} \cdots x_k^{n_k} \end{equation*}

where

  • \(a\) is a constant or coefficient
  • \(x_i\) is the \(i\)-th variable or indeterminate
  • \(n_i\) is the non-negative integer exponent of \(x_i\).

Examples

Monomials include:

  • \(42\)
  • \(42 x\)
  • \(42 x^2\)
  • \(42 x y\)
  • \(42 x^1 y^2\)

Polynomial

An algebraic expression that can be written as a finite sum of monomials.

Standard form

The standard form of a univariate polynomial is

\begin{equation*} \sum_{i = 0}^{n} a_i x^i = a_n x^n + a_{n-1} x^{n-1} + \cdots + a_1 x + a_0 \end{equation*}

Examples

Polynomials include:

  • \(42\)
  • \(42 x\)
  • \(42 x^2\)
  • \(x + 1\)
  • \(x^2 + 2 x + 1\)
  • \(3 x^2 y + 2 x y - 7\)

Binomial

A polynomial that can be written as a sum of exactly two non-zero monomials.

Logic

Logic

A branch of discrete mathematics concerned with

formal reasoning.

Synonyms

  • Mathematical logic

Propositional logic

A branch of logic that deals with

propositions,
which can be true or false,

as well as,

arguments made of propositions,
which can be valid or invalid.

Synonyms

  • Zeroth-order logic
  • Propositional calculus

Statement

A declaration that is

either true or false,

so Boolean-valued.

By the number of logical connectives, either

atomic or molecular.

[7, Sec. 0.2], [12, Sec. 5.1.1]

Referenced by a propositional variable.

Synonyms

  • Proposition

Propositional variable

A variable that references a statement, typically denoted

\begin{equation*} P, Q, R, \ldots \end{equation*}

Tautology

A statement that is always true.

Explore

Compute whether a given molecular statement is a tautology.

  • Function scheme/tautology?

    Search truth tables for the absence of FALSE to identify tautologies.

    Builds on scheme/truth-table.

    <<scheme/truth-table>>
    
    (define (tautology? procedure arity)
      (not (member #f (map (lambda (row)
                             (car (reverse row)))
                           (truth-table procedure arity)))))
    
    <<scheme/tautology?>>
    
    (unless (tautology? (lambda (p) (or p #t)) 1) (raise #f))
    (unless (tautology? (lambda (p q) (or p q #t)) 2) (raise #f))
    (unless (tautology? (lambda (p q r) (or p q r #t)) 3) (raise #f))
    
    (when (tautology? (lambda (p) (or p)) 1) (raise #f))
    (when (tautology? (lambda (p q) (or p q)) 2) (raise #f))
    (when (tautology? (lambda (p q r) (or p q r)) 3) (raise #f))
    
    "tests passed"
    
    tests passed
    

Atomic statement

A statement with

exactly zero logical connectives.

The opposite of the molecular statement, conceptually.

[7, Sec. 0.2], [12, Sec. 5.1.1]

Synonyms

  • Atomic proposition
  • Atom

Molecular statement

A statement with

at least one logical connective.

The opposite of the atomic statement, conceptually.

[7, Sec. 0.2], [12, Sec. 5.1.1]

Synonyms

  • Molecular proposition
  • Molecule
  • Compound statement
  • Compound proposition

Truth table

A table with

the values of a molecular statement

evaluated at

all possible values of its propositional variables.

Implementation

Compute a truth table for the given molecular statement.

  • Function scheme/boolean-product
    • Implement
      (define (boolean-product length)
        "Return a list of all Boolean sequences of LENGTH."
        (let more-rows ((row (- (expt 2 length) 1)))
          (if (< row 0) (list)
              (cons (let more-columns ((column (- length 1)))
                      (if (< column 0) (list)
                          (cons (even? (quotient row (expt 2 column)))
                                (more-columns (- column 1)))))
                    (more-rows (- row 1))))))
      
    • Test
      <<scheme/boolean-product>>
      (unless (equal? (boolean-product 1)
                      '((#f)
                        (#t)))
        (raise #f))
      (unless (equal? (boolean-product 2)
                      '((#f #f)
                        (#f #t)
                        (#t #f)
                        (#t #t)))
        (raise #f))
      (unless (equal? (boolean-product 3)
                      '((#f #f #f)
                        (#f #f #t)
                        (#f #t #f)
                        (#f #t #t)
                        (#t #f #f)
                        (#t #f #t)
                        (#t #t #f)
                        (#t #t #t)))
        (raise #f))
      "tests passed"
      
      tests passed
      
  • Function scheme/truth-table
    • Implement

      Builds on scheme/boolean-product.

      <<scheme/boolean-product>>
      (define (truth-table procedure arity)
        "Return a truth table for PROCEDURE with ARITY."
        (map (lambda (inputs)
               (append inputs (list (apply procedure inputs))))
             (boolean-product arity)))
      
    • Test
      <<scheme/truth-table>>
      (unless (equal? (truth-table not 1)
                      '((#f #t)
                        (#t #f)))
        (raise #f))
      (unless (equal? (truth-table (lambda (p q) (and p q)) 2)
                      '((#f #f #f)
                        (#f #t #f)
                        (#t #f #f)
                        (#t #t #t)))
        (raise #f))
      (unless (equal? (truth-table (lambda (p q r) (or p q r)) 3)
                      '((#f #f #f #f)
                        (#f #f #t #t)
                        (#f #t #f #t)
                        (#f #t #t #t)
                        (#t #f #f #t)
                        (#t #f #t #t)
                        (#t #t #f #t)
                        (#t #t #t #t)))
        (raise #f))
      "tests passed"
      
      tests passed
      
  • Function scheme/org-truth-table

    Generate a truth table for presentation in Org documents.

    • Implement

      Builds on scheme/truth-table.

      Note. HEADERS are expected in LaTeX format.

      <<scheme/truth-table>>
      (define (org-truth-table procedure headers)
        (append (list (map (lambda (header)
                             (string-append "\\(" header "\\)"))
                           headers))
                (list (list))
                (list (append (list "/")
                              (make-list (- (length headers) 2) "")
                              (list "<")))
                (map (lambda (row)
                       (map (lambda (value)
                              (if value
                                  "true"
                                  "false"))
                            row))
                     (truth-table procedure
                                  (- (length headers)
                                     1)))))
      
    • Test
      <<scheme/org-truth-table>>
      (unless
          (equal? (org-truth-table not '("P" "\\lnot P"))
                  '(("\\(P\\)" "\\(\\lnot P\\)")
                    ()
                    ("/" "<")
                    ("false" "true")
                    ("true" "false")))
        (raise #f))
      (unless
          (equal? (org-truth-table (lambda (p q) (and p q))
                                   '("P" "Q" "P \\land Q"))
                  '(("\\(P\\)" "\\(Q\\)" "\\(P \\land Q\\)")
                    ()
                    ("/" "" "<")
                    ("false" "false" "false")
                    ("false" "true" "false")
                    ("true" "false" "false")
                    ("true" "true" "true")))
        (raise #f))
      "tests passed"
      
      tests passed
      

Logical connective

A logical connection between statements.

Connective Description Type Notation
negation not unary \(\lnot\)
conjunction and binary \(\land\)
disjunction or binary \(\lor\)
conditional if, then binary \(\implies\)
biconditional if and only if, then binary \(\iff\)
exclusive disjunction either, or binary \(\oplus\)

Synonyms

  • Connective

By type

Type Connects
unary one statement with itself
binary two statements with each other

By precedence

Most tightly to least tightly binding:

Precedence Connective
1 negation
2 conjunction
3 disjunction
4 conditional
5 biconditional

Parentheses set the precedence to zero, like in arithmetic.

Negation

A unary logical connective, denoted

\begin{equation*} \lnot P \qor \sim P \qor \bar{P} \qor !P \end{equation*}

and read

“not \(P\)”,

that is true if and only if

\(P\) is false.

Note. \(\lnot \lnot P \equiv P\).

[7, Sec. 0.2], [12, Sec. 5.1]

Synonyms

  • Logical not
  • Not

Truth table

The truth table for the statement \(\lnot P\) is

<<scheme/truth-table>>
(define (org-truth-table procedure headers)
  (append (list (map (lambda (header)
                       (string-append "\\(" header "\\)"))
                     headers))
          (list (list))
          (list (append (list "/")
                        (make-list (- (length headers) 2) "")
                        (list "<")))
          (map (lambda (row)
                 (map (lambda (value)
                        (if value
                            "true"
                            "false"))
                      row))
               (truth-table procedure
                            (- (length headers)
                               1)))))
(org-truth-table not '("P" "\\lnot P"))

Conjunction

A binary logical connective, denoted

\begin{equation*} P \land Q \end{equation*}

and read

“\(P\) and \(Q\)”,

that is true if and only if

both \(P\) and \(Q\) are true.

[7, Sec. 0.2], [12, Sec. 5.1]

Truth table

Construct the truth table for the statement \(P \land Q\).

<<scheme/org-truth-table>>
(org-truth-table (lambda (p q) (and p q)) '("P" "Q" "P \\land Q"))
\(P\) \(Q\) \(P \land Q\)
false false false
false true false
true false false
true true true

Iterated variant

Denote \(n\)-nary6 conjunction by

\begin{equation*} \bigwedge_{i = 1}^{n} x_i = x_1 \land x_2 \land \cdots \land x_{n - 1} \land x_n, \end{equation*}

or equivalently by

\begin{equation*} \bigwedge_{x \in S} x_1 \land x_2 \land \cdots \land x_{n - 1} \land x_n, \end{equation*}

given the set of operands \(S = \{x_1, x_2, \ldots, x_{n - 1}, x_n\}\).

Synonyms

  • Logical and
  • And

Disjunction

A binary logical connective, denoted

\begin{equation*} P \lor Q \end{equation*}

and read

\(P\) or \(Q\),

that is true if and only if

at least one of \(P\) and \(Q\) is true.

[7, Sec. 0.2], [12, Sec. 5.1]

Truth table

Construct the truth table for the statement \(P \lor Q\).

<<scheme/org-truth-table>>
(org-truth-table (lambda (p q) (or p q))
                 '("P" "Q" "P \\land Q"))
\(P\) \(Q\) \(P \land Q\)
false false false
false true true
true false true
true true true

Iterated variant

Denote \(n\)-nary7 disjunction by

\begin{equation*} \bigvee_{i = 1}^{n} x_i = x_1 \lor x_2 \lor \cdots \lor x_{n - 1} \lor x_n, \end{equation*}

or equivalently by

\begin{equation*} \bigvee_{x \in S} x_1 \lor x_2 \lor \cdots \lor x_{n - 1} \lor x_n, \end{equation*}

given the set of operands \(S = \{x_1, x_2, \ldots, x_{n - 1}, x_n\}\).

Synonyms

  • Logical or
  • Inclusive or
  • Or

Law of non-contradiction (LNC)

A tautology stating that, for every statement \(P\),

\(P\) and its negation \(\lnot P\) cannot be both true,

so

\begin{equation*} \forall P, \>\> \lnot ( P \land \lnot P ). \end{equation*}

Significance

The law of non-contradiction (LNC) is

the most fundamental law of propositional logic,

without which classical reasoning would collapse.

Relation to proofs

The law of non-contradiction (LNC) enables proof by contradiction.

Synonyms

  • Law of contradiction
  • Principle of non-contradiction

Law of excluded middle (LEM)

The tautology that states, for every statement \(P\),

either \(P\) is true or \(\lnot P\) is true,

so

\begin{equation*} \forall P, \>\> P \lor \lnot P. \end{equation*}

Usefulness

The law of excluded middle enables proof by cases.

Synonyms

  • Principle of excluded middle

De Morgan’s laws

A pair of tautologies that capture the relationship between

conjunction, disjunction, and negation,

where

\begin{align*} \lnot (P \land Q) & \iff \lnot P \lor \lnot Q \\ \lnot (P \lor Q) & \iff \lnot P \land \lnot Q. \end{align*}

Proof

By the definitions of equivalence and implication,

\begin{align*} & \lnot (P \land Q) \iff \lnot P \lor \lnot Q \\[1ex] & \quad \equiv ((\lnot (P \land Q) ) \implies (\lnot P \lor \lnot Q)) \land ((\lnot P \lor \lnot Q ) \implies (\lnot (P \land Q))) \\[1ex] & \quad \equiv (\lnot ( \lnot (P \land Q)) \lor (\lnot P \lor \lnot Q)) \land (\lnot (\lnot P \lor \lnot Q) \lor (\lnot (P \land Q))) \\[2ex] & \lnot (P \lor Q) \iff \lnot P \land \lnot Q \\[1ex] & \quad \equiv ((\lnot (P \lor Q)) \implies (\lnot P \land \lnot Q)) \land ((\lnot P \land \lnot Q) \implies (\lnot (P \lor Q))) \\[1ex] & \quad \equiv (\lnot (\lnot (P \lor Q)) \lor (\lnot P \land \lnot Q)) \land (\lnot (\lnot P \land \lnot Q) \lor (\lnot (P \lor Q))), \end{align*}

and those are tautologies, per

<<scheme/tautology?>>
(and (tautology? (lambda (p q)
                   (and (or (not (not (and p q)))
                            (or (not p) (not q)))
                        (or (not (or (not p) (not q)))
                            (not (and p q)))))
                 2)
     (tautology? (lambda (p q)
                   (and (or (not (not (or p q)))
                            (and (not p) (not q)))
                        (or (not (and (not p) (not q)))
                            (not (or p q)))))
                 2))
#t

so De Morgan’s laws are always true. \(\blacksquare\)

Conditional

A binary logical connective, denoted

\begin{equation*} P \implies Q \qor Q \impliedby P \end{equation*}
where \(P\) is the antecedent
  \(Q\) is the consequent,

and read

  “if \(P\), then \(Q\)”
or “\(Q\) if \(P\)”
or “\(P\) implies \(Q\)”,

that is true if and only if

\(P\) and \(Q\) are true or \(P\) is false,

so equivalent to

\begin{equation*} \lnot P \lor Q. \end{equation*}

Equivalent to its contrapositive but not to its converse.

[7, Sec. 0.2], [12, Sec. 5.1]

Synonyms

  • Implication
  • If

Truth table

Construct the truth table for the statement

\begin{equation*} P \implies Q \> \equiv \> \lnot P \lor Q. \end{equation*}
<<scheme/org-truth-table>>
(org-truth-table (lambda (p q) (or (not p) q))
                 '("P" "Q" "P \\implies Q"))
\(P\) \(Q\) \(P \implies Q\)
false false true
false true true
true false false
true true true

[7, Sec. 0.2], [12, Sec. 5.1]

Antecedent

The statement \(P\) in the conditional statement

\begin{equation*} P \implies Q \end{equation*}

that is said to be

sufficient, but not necessary, for the consequent \(Q\).

[7, Sec. 0.2]

Synonyms

  • Hypothesis

Consequent

The statement \(Q\) in the conditional statement

\begin{equation*} P \implies Q \end{equation*}

that is

necessary for the antecedent \(P\)

but does not necessitate it.

[7, Sec. 0.2]

Contrapositive

The

reversal and negation

of the constituents of the conditional

\begin{equation*} P \implies Q \end{equation*}

in the form

\begin{equation*} \lnot Q \implies \lnot P, \end{equation*}

which, unlike the converse and inverse, is

equivalent to the original statement,

that is

\begin{equation*} (P \implies Q) \equiv (\lnot Q \implies \lnot P) \end{equation*}

[7, Sec. 0.2]

Example

For example, the molecular statement

“if you are dancing, you are sweating”

is equivalent to

“if you not sweating, you are not dancing”.

Converse

The

reversal

of the antecedent and the consequent of the conditional

\begin{equation*} P \implies Q \end{equation*}

in the form

\begin{align*} & P \impliedby Q \\ \qor & Q \implies P, \end{align*}

which, unlike the contrapositive, is

not equivalent to the original statement,

so

\begin{equation*} (Q \implies P) \> \not \equiv \> (P \implies Q), \end{equation*}

but is

equivalent to its inverse,

so

\begin{equation*} (Q \implies P) \> \equiv \> (\lnot P \implies \lnot Q). \end{equation*}

[7, Sec. 0.2]

Example

The statement

“if you are dancing, you are sweating”

is not equivalent to

“if you are sweating, you are dancing”,

as you might be

sweating for a different reason.

Proof

Prove the statement

\begin{equation*} (P \implies Q) \> \not \equiv \> (Q \implies P). \end{equation*}

By the definition of the conditional and the biconditional,

\begin{align*} (P \implies Q) & \equiv (Q \implies P) \\ (\lnot P \lor Q) & \equiv (\lnot Q \lor P) \\ (\lnot P \lor Q) \implies (\lnot Q \lor P) \big) & \land \big( (\lnot Q \lor P) \implies (\lnot P \lor Q) \big) \\ \big( \lnot (\lnot P \lor Q) \lor (\lnot Q \lor P) \big) & \land \big( \lnot (\lnot Q \lor P) \lor (\lnot P \lor Q) \big), \end{align*}

which is not a tautology, as per

<<scheme/tautology?>>
(not (tautology? (lambda (p q)
                   (and (or (not (or (not p) q))
                            (or (not q) p))
                        (or (not (or (not q) p))
                            (or (not p) q))))
                 2))
#t

\(\blacksquare\)

Biconditional

A binary logical connective, denoted

\begin{equation*} P \iff Q \qor P \equiv Q, \end{equation*}

and read

  “\(P\) if and only if \(Q\)”
or “\(P\) is equivalent to \(Q\)”,

that is true if and only if

\(P\) and \(Q\) are both either true or false,

which is equivalent to

\begin{equation*} (P \implies Q) \> \land \> (Q \implies P). \end{equation*}

[7, Sec. 0.2], [12, Sec. 5.1]

Synonyms

  • Equivalence
  • If and only if
  • Iff

Truth table

Construct the truth table for the statement

\begin{align*} P \iff Q \> & \equiv \> (P \implies Q) \land (Q \implies P) \\ & \equiv \> (\lnot P \lor Q) \land (\lnot Q \lor P). \end{align*}
<<scheme/org-truth-table>>
(org-truth-table (lambda (p q)
                   (and (or (not p) q)
                        (or (not q) p)))
                 '("P" "Q"
                   "P \\iff Q"))
\(P\) \(Q\) \(P \iff Q\)
false false true
false true false
true false false
true true true

Exclusive disjunction

A binary logical connective, denoted

\begin{equation*} P \oplus Q, \end{equation*}

that is true if and only if

exactly one of \(P\) and \(Q\) is true,

which is equivalent to

\begin{equation*} (P \land \lnot Q) \lor (\lnot P \land Q) \end{equation*}

being a tautology.

[7, Sec. 0.2], [12, Sec. 5.1]

Conjunctive normal form

In conjunctive normal form, exclusive disjunction is

\begin{equation*} (P \lor Q) \land (\lnot P \lor \lnot Q). \end{equation*}

Synonyms

  • Exclusive or
  • Xor

Truth table

Construct the truth table for the statement

\begin{equation*} P \oplus Q \> \equiv \> (P \land \lnot Q) \lor (\lnot P \land Q). \end{equation*}
<<scheme/org-truth-table>>
(org-truth-table (lambda (p q)
                   (or (and p (not q))
                       (and (not p) q)))
                 '("P" "Q" "P \\oplus Q"))
\(P\) \(Q\) \(P \oplus Q\)
false false false
false true true
true false true
true true false

Mutual exclusivity

A joined property of the statements \(P\) and \(Q\), read

  “\(P\) excludes \(Q\)”,
or “\(P\) precludes \(Q\)”,
or “\(P\) contradicts \(Q\)”,

if and only if

at most one of \(P\) and \(Q\) is true,

which is equivalent to

\begin{equation*} \lnot (P \land Q) \end{equation*}

being a tautology.

Compared to exclusive disjunction

mutual exclusivity
at most one statement is true
exclusive disjunction
exactly one statement is true

Conjunctive normal form

In conjunctive normal form, mutual exclusivity is

\begin{equation*} \lnot P \lor \lnot Q, \end{equation*}

by De Morgan’s laws.

Synonyms

  • Disjoint statements

Explore

Construct the truth table for the statement

\begin{equation*} \lnot (P \land Q). \end{equation*}
<<scheme/org-truth-table>>
(org-truth-table (lambda (p q) (not (and p q)))
                 '("P" "Q" "\\lnot (P \\land Q)"))
\(P\) \(Q\) \(\lnot (P \land Q)\)
false false true
false true true
true false true
true true false

Argument

A set of statements, denoted

\begin{equation*} \begin{array}{ll} & P_1 \\ & \vdots \\ & P_n \\ \hline \therefore & Q \end{array} \end{equation*}

and read

“\(P_1\), …, \(P_n\), therefore \(Q\)”,

where

where \(P_i\) is the \(i\)th premise and
  \(Q\) is the conclusion.

Either valid or invalid.

Premise

Any statement in the argument

\begin{equation*} \begin{array}{ll} & P_1 \\ & \vdots \\ & P_n \\ \hline \therefore & Q \end{array} \end{equation*}

other than the conclusion \(Q\).

Conclusion

The statement \(Q\) in the argument

\begin{equation*} \begin{array}{ll} & P_1 \\ & \vdots \\ & P_n \\ \hline \therefore & Q \end{array} \end{equation*}

Rule of inference

A structure of a valid argument, that is one

whose conclusion is true
if all of its premises are true.

Synonyms

  • Valid argument

Modus ponens

A rule of inference in the form

\begin{equation*} \begin{array}{ll} & P \implies Q \\ & P \\ \hline \therefore & Q \\ \end{array} \end{equation*}

Example

If you are dancing, you are sweating.
You are dancing.
Therefore, you are sweating.

Fallacy

Any argument that is not valid.

Synonym

  • Invalid argument

Predicate logic

Propositional logic extended with

predicates and quantifiers

to work over

non-logical domains of discourse

like sets.

Synonyms

  • First-order logic
  • Predicate calculus

Example

Let \(P(x)\) mean “\(x\) is even”. Then

\begin{equation*} \forall x \>\> P(x) \iff \lnot P(x + 1) \end{equation*}

is a tautology.

Predicate

A statement-like declaration with \(n \geq 1\) free variables, denoted

\begin{equation*} P(x_1, \ldots, x_n), \end{equation*}
where \(P\) is the name and
  \(x_i\) is the \(i\)-th free variable.

that becomes a statement when quantified 8.

Quantifier

An operator that specifies

how many members of the universe

apply to the given variable, typically

all or some.

Turns a predicate into a statement, when fully quantified.

[7, Sec. 0.2]

Universal quantifier

The quantifier denoted

\begin{equation*} \forall x \>\> P(x), \end{equation*}

and read

“for all/each/every/all/any \(x\), \(P(x)\)”,

where \(P\) is the quantified predicate.

[7, Sec. 0.2]

Synonyms

  • For all
  • For each
  • For every

Relation to existential quantification

Equivalent to the existential quantifier under negation by

\begin{align*} \forall x \> \lnot P(x) \> & \equiv \> \lnot \exists x \> P(x) \\ \lnot \forall x \> P(x) \> & \equiv \> \exists x \> \lnot P(x), \end{align*}

which is akin to De Morgan’s laws, with \(\forall\) generalizing conjunction.

Example

There is no smallest integer \(k\) because

\begin{equation*} \forall k \in \mathbb{Z} \quad \exists \ell \in \mathbb{Z} \quad (\ell < k), \end{equation*}

that is

for every integer \(k\),
there exists some integer \(\ell\)
smaller than \(k\),

such as \(\ell = k - 1\).

Existential quantifier

A quantifier, denoted

\begin{equation*} \exists x \>\> P(x), \end{equation*}

and read

“there exists \(x\) such that \(P(x)\)”
or
“for some \(x\), \(P(x)\)”,

where \(P\) is the predicate that is

quantified.

[7, Sec. 0.2]

Synonyms

  • There exists
  • For some

Relation to universal quantification

Equivalent to the universal quantifier under negation by

\begin{align*} \exists x \> \lnot P(x) \> & \equiv \> \lnot \forall x \> P(x) \\ \lnot \exists x \> P(x) \> & \equiv \> \forall x \> \lnot P(x). \end{align*}

which is akin to De Morgan’s laws, with \(\exists\) generalizing disjunction.

Example

There is no greatest integer \(k\) because

\begin{equation*} \forall k \in \mathbb{Z} \quad \exists \ell \in \mathbb{Z} \quad (\ell > k), \end{equation*}

that is

for every integer \(k\),
there exists some integer \(\ell\)
greater than \(k\),

such as \(\ell = k + 1\).

Unique existential quantifier

A quantifier, denoted

\begin{align*} \exists! x \>\> P(x), \\ \exists_{= 1} x \>\> P(x), \end{align*}

and read

“there exists exactly one \(x\) such that \(P(x)\)”

which is equivalent to

\begin{equation*} \forall x \exists y : P(x) \iff x = y \end{equation*}

where \(P\) is the quantified predicate.

Synonyms

  • Exactly one

Set theory

Set theory

A field of discrete mathematics

concerned with sets.

Set

An unordered collection of distinct objects.

Importance

The most fundamental object in mathematics [7].

Definition

Either:

Member

Any object in a non-empty set.

Synonyms

  • Element

Cardinality

The number of members in the given set.

Notation

The cardinality of any given set \(S\) is denoted

\begin{equation*} |S|. \end{equation*}

Example

\begin{equation*} X = \{10, 20, 30\} \iff |X| = 3. \end{equation*}

Finiteness

Cardinality can be

  • finite,
    e.g. the Boolean domain, \(|\mathbb{B}|\)
  • countably infinite,
    e.g. the set of all integers \(|\mathbb{Z}|\)
  • uncountably infinite,
    e.g. the set of all reals \(|\mathbb{R}|\)

Empty set

The unique set with cardinality of exactly zero.

Notation

Using set enumeration, the empty set is defined as

\begin{equation*} \emptyset = \varnothing = \{\}. \end{equation*}

Finite set

The empty set or a set whose cardinality is a natural number.

Countably infinite set

A set whose cardinality is that of natural numbers.

Symbolical representation

The set \(S\) is countably infinite iff

\begin{equation*} |S| = |\mathbb{N}| = \aleph_0. \end{equation*}

Countable set

A set that is either finite or countably infinite.

Uncountable set

A set that is not countable.

Synonyms

  • Uncountably infinite set

Singleton

A set with cardinality of exactly one.

Synonyms

  • Unit set
  • One-point set

Equal sets

Any two sets whose all members are equal.

Disjoint sets

Sets with no members in common, so

\begin{equation*} S_1 \cap S_2 \cap \cdots = \varnothing. \end{equation*}

Set enumeration

A common way to define a set

by listing its members

in the form

\begin{equation*} \{ x_1, x_2, x_3, \ldots \}. \end{equation*}

Example

The set of all positive integers,

\begin{equation*} \mathbb{N} = \{1, 2, 3, \ldots\}. \end{equation*}

Alternative

Another common way to define a set is the set builder.

Set builder

A common way to define a set by

a predicate over its members

in the form

\begin{equation*} \{ x \mid P(x) \} \end{equation*}

where \(P\) is the predicate, read

the set of all \(x\) such that \(P(x)\).

Domain

The members can be constrained to a domain on the left-hand side,

\begin{equation*} \{ x \in \mathbb{D} \mid P(x) \} = \{ x \mid x \in \mathbb{D} \land P(x) \}. \end{equation*}

Example

The set of all positive integers,

\begin{equation*} \{x \in \mathbb{Z} \mid x > 0\}. \end{equation*}

Alternative notation

A colon (\(:\)) can be used in place of the vertical bar (\(\vert\)), so

\begin{equation*} \{ x \mid P(x) \} = \{ x : P(x) \}. \end{equation*}

Subset

A set that contains only members of the given set,

\begin{equation*} A \subseteq B \iff \forall a \in A, \> a \in B. \end{equation*}

Superset

A set that contains all members of the given set,

\begin{equation*} A \supseteq B \iff \forall b \in B, \> (b \in A). \end{equation*}

Union

The set containing

the members that are in any of the given sets,

\begin{equation*} S_1 \cup \cdots \cup S_n := \big\{ s \mid (s \in S_1) \lor \cdots \lor (s \in S_n) \big\}, \end{equation*}

where

  • \(n\) is the number of operands
  • \(S_i\) is the \(i\)-th operand.

Logic

The union \(A \cup B\) in set theory is

analogous to disjunction \(A \lor B\) in logic,

hence the similar symbols.

Big cup

The big union symbol

\begin{equation*} \bigcup_{i = 1}^{n} S_i = \bigcup_{S \in \mathcal{U}} S = S_1 \cup \ldots \cup S_n \end{equation*}

with \(\mathcal{U} = \{S_1, \ldots, S_n\}\).

Cardinality

Cardinality of finite sets is given by

\begin{align*} |A \cup B| & = |A| + |B| - |A \cap B| \\ |A \cup B \cup C| & = |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|. \end{align*}

Universe

The

superset of all sets under consideration,

forming the domain of discourse.

Notation

The universe is typically denoted \(\mathcal{U}\) or \(\mathbb{D}\).

Name

  • Universal set
  • Domain of discourse

Graph theory

Graph theory

A field of discrete mathematics

concerned with graphs.

Graph

A mathematical object that comprises

vertices interconnected with edges,

defined as an ordered pair of sets

\begin{equation*} G = (V, E), \end{equation*}
where \(V\) denotes the set of vertices and
  \(E\) denotes the set of edges,

with \(E\) different per its type:

Undirected graph

A graph \(G = (V, E)\) whose

set of edges \(E\) comprises \(2\)-member subsets of \(V\).

Synonyms

  • Graph

Reasoning

Declaring \(E\) as a set of sets, has two consequences:

  • no two vertices connect more than once, and
  • no vertex connects with itself.

Directed graph

A graph \(G = (V, E)\) whose

set of edges \(E\) comprises ordered pairs of vertices from \(V\).

Synonyms

  • Digraph

Edge

A connection between two vertices in a graph.

Synonyms

  • Arc

Degree

… of a vertex

The number of edges incident to the vertex.

Branching factor

The arithmetic mean of the degrees of all vertices in a graph.

Linear algebra

Linear algebra

A branch of mathematics concerned with

vector spaces and linear transformations.

References

  • Open-access books (*****)
  • Proprietary

    Warning. These sources are locked behind corporate paywalls or distributed unethically with DRM encryption and/or surveillance.

BibTeX

Alphabetically:

  • Linear algebra from elementary to advanced specialization [13]
    @Misc{cutrone-,
      author       = {Joseph W. Cutrone},
      title        = {Linear Algebra from Elementary to Advanced Specialization},
      organization = {{Johns Hopkins} University},
      type         = {online course},
      publisher    = {{Coursera}},
      url          =
                      {https://www.coursera.org/specializations/linear-algebra-elementary-to-advanced},
    }
    

Linear equation

Any equation in the form

\begin{equation*} a_1 x_1 + a_2 x_2 + \cdots + a_n x_n = c, \end{equation*}
where \(a_i\) is the \(i\)-th coefficient,
  \(x_i\) is the \(i\)-th variable or unknown, and
  \(c\) is some constant.

One variable

A linear equation of one variable has the form

\begin{equation*} y = ax + b \end{equation*}
where \(a \neq 0\) is the slope
  \(b\) is the \(y\)-intercept,

and its solution is

\begin{equation*} x = -\frac{b}{a}. \end{equation*}

Linear system

A set of \(m \geq 1\) linear equations in \(n\) variables,

\begin{equation*} \left\{ \begin{aligned} a_{11} x_1 + a_{12} x_2 + a_{13} x_3 + \cdots + a_{1n} x_n & = b_1 \\ a_{21} x_1 + a_{22} x_2 + a_{23} x_3 + \cdots + a_{2n} x_n & = b_2 \\ a_{31} x_1 + a_{32} x_2 + a_{33} x_3 + \cdots + a_{3n} x_n & = b_3 \\ \vdots & \\ a_{m1} x_1 + a_{m2} x_2 + a_{m3} x_3 + \cdots + a_{mn} x_n & = b_m. \end{aligned} \right. \end{equation*}

Relation to science

Linear systems are one of the key modeling tools of science.

Solution

Every linear system is

either consistent or inconsistent,

depending on its solution set.

Homogeneity

Every linear system is

either homogeneous or not.

Synonyms

  • System of linear equations

Solution set

The set of all variable assignments that satisfy a linear system.

Solution

A member of the solution set.

Consistent system

A linear system

with at least one solution.

Opposite

The consistent system is the opposite of the inconsistent system.

Inconsistent system

A linear system

with no solution.

Opposite

Not consistent.

Augmented matrix

A linear equation or linear system is inconsistent when

the last column of its augmented matrix is a pivot column.

Example

The augmented matrix

\begin{equation*} \begin{pmatrix} 1 & 5 & 2 & -6 \\ 0 & 4 & -7 & 2 \\ 0 & 0 & 0 & 5 \end{pmatrix} \end{equation*}

represents an inconsistent system, for per the last row

\begin{align*} (x_1, x_2, x_3) & = (0, 0, 0) \\ x_1 + x_2 + x_3 & = 5, \end{align*}

which is impossible, as

\begin{equation*} 0 + 0 + 0 \neq 5. \end{equation*}

Equivalent system

A joint property of linear systems whose solution sets are equal.

Field

The set \(F \supseteq \{0, 1\}\) in conjunction with:

  • the addition operation

    \(\forall a, b \in F \> : \> a + b \in F\)

and

  • the multiplication operation

    \(\forall a, b \in F \> : \> a b \in F\).

Link

The sole link between the two operations is

the distributivity of multiplication over addition.

Scalar

A member of the field \(F\), so

\begin{equation*} a \in F. \end{equation*}

Addition

The binary operation

\[\forall a, b \in F \> : \> a + b \in F,\]

where \(a, b\) are the terms,
  \(a + b\) is the resulting sum, and
  \(F\) is the containing field.

Properties

For all scalars \(\{a, b, c\} \subseteq F\), it

Iterated variant

Denote \(n\)-nary9 addition with “the big sigma”

\begin{equation*} \sum_{i = 1}^{n} x_i = x_1 + x_2 + \cdots + x_{n - 1} + x_n, \end{equation*}

or equivalently

\begin{equation*} \sum_{x \in S} x_1 + x_2 + \cdots + x_{n - 1} + x_n \end{equation*}

given the set of terms \(S = \{x_1, x_2, \ldots, x_{n - 1}, x_n\}\).

Synonyms

  • Sum
  • Big sigma

Multiplication

The binary operation

\begin{equation*} \forall a, b \in F \> : \> ab \in F, \end{equation*}

also denoted

\begin{equation*} a \cdot b \qand a \times b, \end{equation*}
where \(a, b\) are the factors,
  \(ab\) is the resulting product, and
  \(F\) is the containing field.

Properties

For all scalars \(\{a, b, c\} \subseteq F\), it

Iterated variant

Denote \(n\)-nary10 multiplication with “the big pi”

\begin{equation*} \prod_{i = 1}^{n} x_i = x_1 \times x_2 \times \cdots \times x_{n - 1} \times x_n, \end{equation*}

or equivalently

\begin{equation*} \prod_{x \in S} x_1 \times x_2 \times \cdots \times x_{n - 1} \times x_n \end{equation*}

given the set of terms \(S = \{x_1, x_2, \ldots, x_{n - 1}, x_n\}\).

Synonyms

  • Product
  • Big pi

Vector space

The set

\(V\) defined over the field \(F\)

that contains the zero vector, so

\begin{equation*} \vec{0} \in V, \end{equation*}

in conjunction with the vector addition operation

\begin{equation*} \forall \vec{u}, \vec{v} \in V \> : \> \vec{u} + \vec{v} \in V \end{equation*}

that satisfies all properties listed here

and scalar multiplication operation

\begin{equation*} \forall a \in F, \> \forall \vec{v} \in V \> : \> a \vec{v} \in V \end{equation*}

that satisfies all properties listed here.

which is the one and only

link between vector addition and scalar multiplication

in the vector space.

Synonyms

  • Linear space

Link

The sole link between the two operations is

the distributivity of vector scaling over vector addition.

Notation

To communicate that \(\forall \vec{v} \in V\),

\(\vec{v}\) has exactly \(n\) components,

write \(V^n\)

Vector

A member of a vector space denoted11

\begin{equation*} \vec{v} = \begin{pmatrix} v_1 \\ \vdots \\ v_n \end{pmatrix} \in V^n, \end{equation*}

given

\begin{equation*} \forall i : v_i \in F, \quad \end{equation*}
where \(v_i\) is the \(i\)-th scalar component of \(\vec{v}\),
  \(V^n\) is the vector space with \(\vec{v}\), and
  \(F\) is the field over which \(V^n\) is defined.

Synonyms

  • Column vector
  • Matrix column
  • Point

Intuition: Real space

Imagine in the real plane as either

  • the point \(P = (v_1, v_2)\) or
  • the line segment from the origin to \(P\).

Intuition: Standard basis

The vector notation

\begin{equation*} \begin{pmatrix} v_1 \\ \vdots \\ v_n \end{pmatrix} \end{equation*}

is shorthand for a linear combination of the standard basis

\begin{equation*} v_1 \vec{e}_1 + v_2 \vec{e_2}. \end{equation*}

https://thenumb.at/Functions-are-Vectors/

Zero vector

The member of a vector space

\begin{equation*} \vec{0} = \begin{pmatrix} 0 \\ 0 \\ \vdots \end{pmatrix} \in V^n, \end{equation*}

with

\begin{equation*} 0 \in F \end{equation*}
where \(0\) is the zero scalar and
  \(V^n\) is the vector space with \(\vec{v}\), and
  \(F\) is the field over which \(V^n\) is defined.

Synonyms

  • Origin

Unit vector

Any vector \(\vec{v}\) with the length of \(1\), so

\begin{equation*} \vec{v} \iff \| \vec{v} \| = 1. \end{equation*}

Vector addition

The binary operation, defined in terms of addition,

\begin{equation*} \vec{u} + \vec{v} = \begin{pmatrix} u_1 \\ \vdots \\ u_n \end{pmatrix} + \begin{pmatrix} v_1 \\ \vdots \\ v_n \end{pmatrix} = \begin{pmatrix} u_1 + v_1 \\ \vdots \\ u_n + v_n \end{pmatrix} \in V^n, \end{equation*}

with

\begin{equation*} \vec{u}, \vec{v} \in V^n, \quad \forall i : u_i, v_i \in F, \end{equation*}
where \(\vec{u}, \vec{v}\) are the terms,
  \(\vec{u} + \vec{v}\) is the resulting sum,
  \(V^n\) is the vector space with \(\vec{v}, \vec{u}\), and
  \(F\) is the field over which \(V^n\) is defined.

Intuition

Geometrically:

  1. Draw \(\vec{u}\) and \(\vec{v}\) head to tail.
  2. Draw \(\vec{u} + \vec{v}\) from the free tail to the free head.

[14]

Properties

For all vectors \(\{\vec{u}, \vec{v}, \vec{w}\} \subseteq V\), it

Synonyms

  • Vector sum

Implement

… in Scheme as

  • my-vector-add
    (define (my-vector-add vector . vectors)
      (if (null? vectors)
          vector
          (map + vector (apply my-vector-add vectors))))
    

Vector subtraction

The binary operation, defined in terms of vector addition and the additive inverse, as

\begin{equation*} \vec{u} - \vec{v} = \vec{u} + (-\vec{v}), \end{equation*}
where \(\vec{u}, \vec{v}\) are the terms,
  \(\vec{u} - \vec{v}\) is the difference.

Shares the algebraic properties of vector addition.

Synonyms

  • Vector difference

Implement

… in Scheme as

  • my-vector-subtract
    (define (my-vector-subtract vector . vectors)
      (if (null? vectors)
          vector
          (my-vector-add vector
                         (my-vector-additive-inverse
                          (apply my-vector-subtract
                                 vectors)))))
    

    in terms of my-vector-add.

  • my-vector-additive-inverse
    (define (my-vector-additive-inverse vector)
      (my-vector-scale vector -1))
    

    in terms of my-vector-scale.

Vector scaling

The binary operation defined, in terms of multiplication, by

\begin{equation*} a \vec{v} = a \begin{pmatrix} v_1 \\ \vdots \\ v_n \end{pmatrix} = \begin{pmatrix} a v_1 \\ \vdots \\ a v_2 \end{pmatrix} \in V^n, \end{equation*}

with

\begin{equation*} a \in F, \quad \vec{v} \in V^n, \quad \forall i : v_i \in F, \end{equation*}
where \(a\) is the scalar factor,
  \(\vec{v}\) is the vector factor,
  \(a \vec{v}\) is the resulting scaled vector,
  \(V^n\) is the vector space with \(\vec{v}\), and
  \(F\) is the field over which \(V^n\) is defined.

Intuition

Draw \(a \vec{v}\) as a copy of \(\vec{v}\) with

  • its length \(|a|\) times larger than that of \(\vec{v}\) and
  • its direction reversed if \(a < 0\).

Properties

Per its algebraic properties,

it:

Synonyms

  • Scalar multiplication

Implement

In Scheme:

  • my-vector-scale
    (define (my-vector-scale vector scalar)
      (map (lambda (component)
             (* scalar
                component))
           vector))
    

Vector length

The non-negative scalar

\begin{equation*} \| \vec{v} \| = \left\| \begin{pmatrix} v_1 \\ \vdots \\ v_m \end{pmatrix} \right\| = \sqrt{{v_1}^2 + \cdots + {v_m}^2} = \sqrt{\sum_{i = 1}^{m} v_i^2}. \end{equation*}

In terms of the dot product,

\begin{equation*} \| \vec{v} \| = \sqrt{\vec{v} \cdot \vec{v}}. \end{equation*}

Synonyms

  • Norm

Intuition

In the real plane \(\mathbb{R}^2\):

  1. Draw \(\vec{v}\) as a point \(P\).
  2. Measure the length from the origin to \(P\).

The correspondence with Pythagorean theorem:

  1. Recall that \(c^2 = a^2 + b^2\) and so \(c = \sqrt{a^2 + b^2}\).
  2. Set \(a = v_1\), \(b = v_2\), and \(c = \|\vec{v}\|\).
  3. Get \(\|\vec{v}\| = \sqrt{{v_1}^2 + {v_2}^2}\).

Growth

For vectors with bounded nonzero components, vector length grows asymptotically in the number of dimensions,

\begin{equation*} \left\| \begin{pmatrix} v_1 \\ \vdots \\ v_m \end{pmatrix} \right\| = \sqrt{{v_1}^2 + \cdots + {v_m}^2} = \sqrt{m \times \Theta(1)} = \Theta(\sqrt{m}), \end{equation*}

as seen with all-ones vectors

\begin{equation*} \left\| \begin{pmatrix} 1 \end{pmatrix} \right\| = \sqrt{1}, \quad \left\| \begin{pmatrix} 1 \\ 1 \end{pmatrix} \right\| = \sqrt{2}, \quad \left\| \begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix} \right\| = \sqrt{3}. \end{equation*}

Implement

… in Scheme as

  • my-vector-length
    (define (my-vector-length vector)
      (sqrt (my-vector-dot-product vector vector)))
    

    in terms of my-vector-dot-product.

Vector equation

A linear system written as a linear combination,

\begin{equation*} x_1 \vec{a}_1 + \cdots + x_n \vec{a}_n = \vec{b}. \end{equation*}

Equivalence with matrix equation

The vector equation

\begin{equation*} x_1 \vec{a}_1 + \cdots + x_n \vec{a}_n = \vec{b} \end{equation*}

is equivalent to the matrix equation

\begin{equation*} A \vec{x} = \vec{b}. \end{equation*}

Equivalence with augmented matrix

The vector equation

\begin{equation*} x_1 \vec{a}_1 + \cdots + x_n \vec{a}_n = \vec{b} \end{equation*}

is equivalent to the linear system with the augmented matrix

\begin{equation*} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} = \begin{pmatrix} \vphantom{\Big(} \vec{a}_1 & \cdots & \vec{a}_n & \vec{b} \end{pmatrix}. \end{equation*}

Equivalence with linear combination

The vector equation

\begin{equation*} x_1 \vec{a}_1 + \cdots + x_n \vec{a}_n = \vec{b}, \end{equation*}

asserts that

\(\vec{b}\) is a linear combination of \(\vec{a}_1, \ldots, \vec{a_n}\)

if and only if there exists a solution to the equivalent matrix equation

\begin{equation*} A \vec{x} = \vec{b} \end{equation*}

or the equivalent linear system with the augmented matrix

\begin{equation*} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} = \begin{pmatrix} \vphantom{\Big(} \vec{a}_1 & \cdots & \vec{a}_n & \vec{b} \end{pmatrix}. \end{equation*}

Dot product

A scalar measure of

vector similarity,

defined in a vector space \(\mathbb{R}^m\) with an orthonormal basis:

  • algebraically,

    as the matrix multiplication of a transposed vector and a vector, and so a linear combination, as

    \begin{equation*} \vec{u} \cdot \vec{v} = \vec{u}^\mathsf{T} \vec{v} = \sum_{i = 1}^{m} u_i v_i, \end{equation*}
  • geometrically,

    in terms of the vector length, as

    \begin{equation*} \vec{u} \cdot \vec{v} = \|\vec{u}\| \|\vec{v}\| \cos \theta, \end{equation*}

    where \(\theta\) is the angle between \(\vec{u}\) and \(\vec{v}\).

Synonyms

  • Inner product

    The dot product generalized from vectors to tensors.

Intuition

The product of

Its extrema are \(\pm \|\vec{u}\| \|\vec{v}\|\), and it

equals \(+\vert\vec{u}\vert\vert\vec{v}\vert\) when \(\vec{u}\) points in the same direction of \(\vec{v}\),
  \(0\) when \(\vec{u}\) is orthogonal to \(\vec{v}\), and
  \(-\vert\vec{u}\vert\vert\vec{v}\vert\) when \(\vec{u}\) points in the opposite direction of \(\vec{v}\).

Properties

  • Commutes, so

    \(\vec{u} \cdot \vec{v} = \vec{v} \cdot \vec{u}\);

  • distributes over vector addition, so

    \(\vec{u} \cdot (\vec{v} + \vec{w}) = \vec{u} \cdot \vec{v} + \vec{u} \cdot \vec{w}\);

  • associates with respect to scalar multiplication, so

    \((c \vec{u}) \cdot \vec{v} = c (\vec{u} \cdot \vec{v}) = \vec{u} \cdot (c \vec{v})\).

Dimensionality: Vectors

The dot product

\begin{equation*} a = \vec{u} \cdot \vec{v} \end{equation*}

of two vectors

  • \(\vec{u} \in \mathbb{R}^{n}\)
  • \(\vec{v} \in \mathbb{R}^{n}\)

is a scalar \(a \in \mathbb{R}\).

Dimensionality: Tensors

The dot product, generalized as the inner product,

decreases the number of dimensions,

whereas the outer product increases them.

Implement

In Scheme:

  • my-vector-dot-product
    (define (my-vector-dot-product vector other-vector)
      (apply + (map * vector other-vector)))
    

Outer product

A matrix with every possible product of elements from two vectors,

\begin{equation*} \vec{u} \otimes \vec{v} = \vec{u} \, \vec{v}^\mathsf{T} = \begin{pmatrix} u_1 \\ \vdots \\ u_m \end{pmatrix} \begin{pmatrix} v_1 & \cdots & v_n \end{pmatrix} = \begin{pmatrix} u_1 v_1 & \cdots & u_1 v_n \\ \vdots & \ddots & \vdots \\ u_m v_1 & \cdots & u_m v_n \end{pmatrix}. \end{equation*}

Synonyms

  • Tensor product (more generally)

Computing tools

The outer product is often

implicit in numerical computing tools,

including NumPy and TensorFlow,

via broadcasting.

Dimensionality: Vectors

The outer product

\begin{equation*} M = \vec{u} \otimes \vec{v} \end{equation*}

of two vectors

  • \(\vec{u} \in \mathbb{R}^{m}\)
  • \(\vec{v} \in \mathbb{R}^{n}\)

is a matrix \(M \in \mathbb{R}^{m \times n}\).

Dimensionality: Tensors

The outer product

increases the number of dimensions,

whereas the inner product decreases them.

Euclidean distance

The length of the vector difference

\begin{equation*} \| \vec{u} - \vec{v} \|. \end{equation*}

Intuit

In the real plane \(\mathbb{R}^2\):

  1. Draw \(\vec{u}, \vec{v}\) as points \(P\) and \(Q\).
  2. Measure the length of the line segment \(\overline{PQ}\).

Closed form

… is

\begin{equation*} \| \vec{u} - \vec{v} \| = \left\| \begin{pmatrix} u_1 \\ \vdots \\ u_m \end{pmatrix} - \begin{pmatrix} v_1 \\ \vdots \\ v_m \end{pmatrix} \right\| = \sqrt{{(u_1 - v_1)}^2 + \cdots + {(u_m - v_m)}^2}. \end{equation*}

Derive

… the closed form as

\begin{align*} \| \vec{u} - \vec{v} \| \tag{1} & = \left\| \begin{pmatrix} u_1 \\ \vdots \\ u_m \end{pmatrix} - \begin{pmatrix} v_1 \\ \vdots \\ v_m \end{pmatrix} \right\| \\[1ex] \tag{2} & = \sqrt{(\vec{u} - \vec{v}) \cdot (\vec{u} - \vec{v})} \\[1ex] \tag{3} & = \sqrt{\sum_{i = 1}^{m} (u_i - v_i) (u_i - v_i) } \\[1ex] \tag{4} & = \sqrt{\sum_{i = 1}^{m} {(u_i - v_i)}^2 } \\[1ex] \tag{5} & = \sqrt{{(u_1 - v_1)}^2 + \cdots + {(u_m - v_m)}^2} \end{align*}

with

  1. by vector,
  2. by vector length,
  3. by dot product,
  4. by exponentiation, and
  5. by summation.

Derive again

… the closed form as

\begin{align*} \| \vec{u} - \vec{v} \| \tag{1} & = \left\| \begin{pmatrix} u_1 \\ \vdots \\ u_m \end{pmatrix} - \begin{pmatrix} v_1 \\ \vdots \\ v_m \end{pmatrix} \right\| \\[1ex] \tag{2} & = \sqrt{(\vec{u} - \vec{v}) \cdot (\vec{u} - \vec{v})} \\[1ex] \tag{3} & = \sqrt{{(\vec{u} - \vec{v})}^\mathsf{T} (\vec{u} - \vec{v})} \\[1ex] \tag{4} & = \sqrt{ \begin{pmatrix} \vphantom{\bigg(} u_1 - v_1 & \cdots & u_m - v_m \end{pmatrix} \begin{pmatrix} u_1 - v_1 \\ \vdots \\ u_m - v_m \end{pmatrix} } \\[1ex] \tag{5} & = \sqrt{(u_1 - v_1) (u_1 - v_1) + \cdots + (u_m - v_m) (u_m - v_m)} \\[1ex] \tag{6} & = \sqrt{{(u_1 - v_1)}^2 + \cdots + {(u_m - v_m)}^2} \end{align*}

with

  1. by vector,
  2. by vector length,
  3. by the dot product in terms of the matrix transpose,
  4. by the matrix transpose,
  5. by matrix multiplication, and
  6. by exponentiation.

Synonyms

  • Vector distance

Implement

… in Scheme as

  • my-vector-euclidean-distance
    (define (my-vector-euclidean-distance vector other-vector)
      (my-vector-length (my-vector-subtract vector other-vector)))
    

    in terms of my-vector-length and my-vector-subtract.

Matrix

pl. matrices

A collection of vectors denoted12

\begin{equation*} A = A_{m \times n} = \begin{pmatrix} \vphantom{\Big(} \vec{a}_1 & \cdots & \vec{a}_n \end{pmatrix} = \begin{pmatrix} a_{11} & & \cdots & & a_{1n} \\ & \ddots & & & \\ \vdots & & a_{ij} & & \vdots \\ & & & \ddots & \\ a_{m1} & & \cdots & & a_{mn} \end{pmatrix}, \end{equation*}
where \(\vec{a}_j\) is the \(j\)-th column vector and
  \(a_{ij}\) is the entry in \(i\)-th row and \(j\)-th column.

Lower triangular matrix

A square matrix with only zeros above its main diagonal,

\begin{equation*} A = \begin{bmatrix} a_{11} & 0 & 0 \\ \vdots & \ddots & 0 \\ a_{n1} & \cdots & a_{nn} \end{bmatrix}. \end{equation*}

Upper triangular matrix

A square matrix with only zeros below its main diagonal,

\begin{equation*} A = \begin{bmatrix} a_{11} & \cdots & a_{1n} \\ 0 & \ddots & \vdots \\ 0 & 0 & a_{nn} \end{bmatrix}. \end{equation*}

Triangular matrix

A matrix that is lower triangular, upper triangular, or both and so diagonal.

Diagonal matrix

Matrix equality

A joint property of the matrices \(A\) and \(B\), denoted

\begin{equation*} A = B, \end{equation*}

iff \(A_{ij} = B_{ij}\) for all \(i\) and \(j\).

Matrix addition

The binary operation, defined in terms of vector addition,

\begin{align*} A + B & = \begin{pmatrix} \vphantom{\bigg(} \vec{a}_1 & \cdots & \vec{a}_n \end{pmatrix} + \begin{pmatrix} \vphantom{\bigg(} \vec{b}_1 & \cdots & \vec{b}_n \end{pmatrix} \\[1ex] & = \begin{pmatrix} \vphantom{\bigg(} \vec{a}_1 + \vec{b}_1 & \cdots & \vec{a}_n + \vec{b}_n \end{pmatrix} \end{align*}

with

\begin{equation*} A_{m \times n}, B_{m \times n}, \end{equation*}
where \(A, B\) are the terms and
  \(A + B\) is the sum.

Properties

For all matrices \(A, B, C\), it

Matrix scaling

The operation, defined in terms of vector scaling,

\begin{equation*} c A = c \begin{pmatrix} \vphantom{\bigg(} \vec{a}_1 & \vec{a}_2 & \cdots \end{pmatrix} = \begin{pmatrix} \vphantom{\bigg(} c \vec{a}_1 & c \vec{a}_2 & \cdots \end{pmatrix}, \end{equation*}
where \(c\) is the scalar factor,
  \(A\) is the matrix factor, and
  \(cA\) is the resulting product.

Synonyms

  • Matrix-scalar multiplication
  • Matrix-scalar product
  • Scalar multiplication

Properties

For all matrices \(A, B\) and scalars \(c, d\), it

Matrix-vector product

A linear combination of matrix columns using a vector of weights,

\begin{equation*} A \vec{b} = \begin{pmatrix} \vphantom{\bigg(} \vec{a}_1 & \cdots & \vec{a}_p \end{pmatrix} \begin{pmatrix} b_1 \\ \vdots \\ b_p \end{pmatrix} = b_1 \vec{a}_1 + \cdots + b_p \vec{a}_p. \end{equation*}

Matrix multiplication

A matrix whose columns are matrix-vector products as per

\begin{equation*} AB = A \begin{pmatrix} \vphantom{\bigg(} \vec{b}_1 & \cdots & \vec{b}_n \end{pmatrix} = \begin{pmatrix} \vphantom{\bigg(} A \vec{b}_1 & \cdots & A \vec{b}_n \end{pmatrix}. \end{equation*}

where

  • \(A, B\) are the factors,
  • \((AB)\) is the resulting product.

As row-column similarity

Viewing \(A\) by rows and \(B\) by columns, matrix multiplication computes pairwise dot products per

\begin{equation*} AB = \begin{pmatrix} \vec{a}_1 \\ \vdots \\ \vec{a}_m \end{pmatrix} \begin{pmatrix} \vec{b}_1 & \cdots & \vec{b}_n \end{pmatrix} = \begin{pmatrix} \vec{a}_1 \cdot \vec{b}_1 & \cdots & \vec{a}_1 \cdot \vec{b}_n \\ \vdots & \ddots & \vdots \\ \vec{a}_m \cdot \vec{b}_1 & \cdots & \vec{a}_m \cdot \vec{b}_n \end{pmatrix}. \end{equation*}

Dimensionality

The resulting dimensions are

\begin{equation*} A_{m \times p} B_{p \times n} = (AB)_{m \times n} \end{equation*}

where

  • \(A_{m \times p}, B_{p \times n}\) are the factors
  • \(AB\) is the resulting product.

Anti-definition

\(A \vec{b}\) and \(AB\) are

undefined if the number of columns in \(A\) does not match

  • the number of components in \(\vec{b}\) and
  • the number of rows in \(B\),

respectively, as

every column of \(A\) must be scaled

by some weight.

Properties

For all matrices \(A, B, C\), vectors \(\vec{u}, \vec{v}\), and scalars \(c\), it

Anti-properties

For some matrices \((A, B)\), it

  • does not commute, so

    \(A B \neq B A\)

  • does not cancel, so

    \(\bcancel{A} B = \bcancel{A} C \,\,\,\,\, \not \!\!\!\!\! \iff B = C\)

  • does not zero-multiply, so

    \(A B = 0 \,\,\,\,\, \not \!\!\!\!\! \iff A = 0 \lor B = 0\).

Example

Compute by hand:

\begin{flalign*} \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix} & = \begin{pmatrix} \vphantom{\Bigg(} \negthickspace \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 5 \\ 7 \end{pmatrix} & \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 6 \\ 8 \end{pmatrix} \negthickspace \end{pmatrix} \\ & = \begin{pmatrix} \negthickspace \vphantom{\Bigg(} 5 \begin{pmatrix} 1 \\ 3 \end{pmatrix} + 7 \begin{pmatrix} 2 \\ 4 \end{pmatrix} & 6 \begin{pmatrix} 1 \\ 3 \end{pmatrix} + 8 \begin{pmatrix} 2 \\ 4 \end{pmatrix} \negthickspace \end{pmatrix} \\ & = \begin{pmatrix} \negthickspace \vphantom{\Bigg(} \begin{pmatrix} 5 \\ 15 \end{pmatrix} + \begin{pmatrix} 14 \\ 28 \end{pmatrix} & \begin{pmatrix} 6 \\ 18 \end{pmatrix} + \begin{pmatrix} 16 \\ 32 \end{pmatrix} \negthickspace \end{pmatrix} \\ & = \begin{pmatrix} 19 & 22 \\ 43 & 50 \end{pmatrix}. & \end{flalign*}

Compute with NumPy:

import numpy

a = numpy.array([[1, 2],
                 [3, 4]])

b = numpy.array([[5, 6],
                 [7, 8]])

a @ b
[[19 22]
 [43 50]]

Synonyms

  • Matrix product

Matrix equation

A linear system written as a matrix multiplication,

\begin{equation*} A \vec{x} = \vec{b}. \end{equation*}

Synonyms

  • Matrix-vector equation

Equivalence: Vector equation

The matrix equation

\begin{equation*} A \vec{x} = \vec{b} \end{equation*}

is equivalent to the vector equation

\begin{equation*} x_1 \vec{a}_1 + \cdots + x_n \vec{a}_n = \vec{b}. \end{equation*}

Equivalence: Augmented matrix

The matrix equation

\begin{equation*} A \vec{x} = \vec{b} \end{equation*}

is equivalent to the linear system with the augmented matrix

\begin{equation*} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} = \begin{pmatrix} \vphantom{\Big(} \vec{a}_1 & \cdots & \vec{a}_n & \vec{b} \end{pmatrix}. \end{equation*}

Solutions and invertibility

If the matrix \(A\) in the matrix equation

\begin{equation*} A \vec{x} = \vec{b} \end{equation*}

is invertible, then a unique solution

\begin{equation*} \vec{x} = A^{-1} \vec{b} \end{equation*}

exists for each \(\vec{b} \in \mathbb{R}^n\).

Example: Homogeneous consistent system

Solve \(A \vec{x} = \vec{0}\), given

\begin{equation*} A = \begin{pmatrix} 1 & -2 & 3 \\ 2 & 1 & -1 \\ -3 & -4 & 5 \end{pmatrix}. \end{equation*}
  1. Write the augmented matrix and row-reduce it.

    \begin{align*} \begin{pmatrix} \vphantom{\Big(} A & \vec{0} \, \end{pmatrix} & = \begin{pmatrix} 1 & -2 & 3 & 0 \\ 2 & 1 & -1 & 0 \\ -3 & -4 & 5 & 0 \end{pmatrix} \\[2ex] \operatorname{rref} \begin{pmatrix} \vphantom{\Big(} A & \vec{0} \, \end{pmatrix} & = \begin{pmatrix} 1 & 0 & \frac{1}{5} & 0 \\ 0 & 1 & -\frac{7}{5} & 0 \\ 0 & 0 & 0 & 0 \end{pmatrix} \end{align*}
  2. Write the solution set in terms of basic variables.

    \begin{equation*} \begin{aligned} x_1 + \frac{1}{5} x_3 & = 0 \\ x_2 - \frac{7}{5} x_3 & = 0 \end{aligned} \quad \iff \quad \begin{aligned} x_1 & = -\frac{1}{5} x_3 \\ x_2 & = \frac{7}{5} x_3 \end{aligned} \end{equation*}
  3. Rewrite the solution set in the parametric form.

    \begin{equation*} \vec{x} = \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix} = \begin{pmatrix} -\frac{1}{5} x_3 \\ \frac{7}{5} x_3 \\ x_3 \end{pmatrix} = x_3 \begin{pmatrix} -\frac{1}{5} \\ \frac{7}{5} \\ 1 \end{pmatrix} = x_3 \begin{pmatrix} -1 \\ 7 \\ 5 \end{pmatrix}. \end{equation*}

    Note. This is a line in \(\mathbb{R}^3\).

Example: Non-homogeneous consistent system

Solve \(A \vec{x} = \vec{b}\), given

\begin{equation*} A = \begin{pmatrix} 1 & -2 & 3 \\ 2 & 1 & -1 \\ -3 & -4 & 5 \end{pmatrix} \qand \vec{b} = \begin{pmatrix} 3 \\ 9 \\ -15 \end{pmatrix}. \end{equation*}
  1. Write the augmented matrix and row-reduce it.

    \begin{align*} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} & = \begin{pmatrix} 1 & -2 & 3 & 3 \\ 2 & 1 & -1 & 9 \\ -3 & -4 & 5 & -15 \end{pmatrix} \\[2ex] \operatorname{rref} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} & = \begin{pmatrix} 1 & 0 & \frac{1}{5} & \frac{21}{5} \\ 0 & 1 & -\frac{7}{5} & \frac{3}{5} \\ 0 & 0 & 0 & 0 \end{pmatrix} \end{align*}
  2. Write the solution set in terms of basic variables.

    \begin{equation*} \begin{aligned} x_1 + \frac{1}{5} x_3 & = \frac{21}{5} \\ x_2 - \frac{7}{5} x_3 & = \frac{3}{5} \end{aligned} \quad \iff \quad \begin{aligned} x_1 & = -\frac{1}{5} x_3 + \frac{21}{5} \\ x_2 & = \frac{7}{5} x_3 + \frac{3}{5} \end{aligned} \end{equation*}
  3. Rewrite the solution set in the parametric form.

    \begin{equation*} \vec{x} = \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix} = \begin{pmatrix} -\frac{1}{5} x_3 + \frac{21}{5} \\ \frac{7}{5} x_3 + \frac{3}{5} \\ x_3 \end{pmatrix} = x_3 \begin{pmatrix} -\frac{1}{5} \\ \frac{7}{5} \\ 1 \end{pmatrix} + \begin{pmatrix} \frac{25}{5} \\ \frac{3}{5} \\ 0 \end{pmatrix} = x_3 \begin{pmatrix} -1 \\ 7 \\ 5 \end{pmatrix} + \begin{pmatrix} 25 \\ 3 \\ 0 \end{pmatrix} \end{equation*}

Example: Non-homogeneous inconsistent system

Solve \(A \vec{x} = \vec{b}\), given

\begin{equation*} A = \begin{pmatrix} 1 & -2 & 3 \\ 2 & 1 & -1 \\ -3 & -4 & 5 \end{pmatrix}, \quad \vec{b} = \begin{pmatrix} 3 \\ 9 \\ -8 \end{pmatrix}. \end{equation*}
  1. Write the augmented matrix and row-reduce it.

    \begin{align*} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} & = \begin{pmatrix} 1 & -2 & 3 & 3 \\ 2 & 1 & -1 & 9 \\ -3 & -4 & 5 & -8 \end{pmatrix} \\[2ex] \operatorname{rref} \begin{pmatrix} \vphantom{\Big(} A & \vec{b} \, \end{pmatrix} & = \begin{pmatrix} 1 & 0 & \frac{1}{5} & 0 \\ 0 & 1 & -\frac{7}{5} & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{align*}

    The last column is the pivot column, so the system is inconsistent.

Homogeneous equation

Any linear system that can be written as the matrix equation

\begin{equation*} A \vec{x} = \vec{0}. \end{equation*}

Root

Synonyms

  • Zero
  • \(x\)-intercept

Trivial root

Non-trivial root

A root with at least one free variable.

Identity matrix

A square matrix with \(1\)s on its main diagonal and \(0\)s elsewhere,

\begin{equation*} I_n = \begin{pmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{pmatrix}. \end{equation*}

Relation to basis

The column vectors of the identity matrix \(I_n\) are

the members of the standard basis \(\{ e_1, \ldots, e_n \}\).

Relation to matrix multiplication

Determinant

The determinant of the identity matrix is one,

\begin{equation*} |I_n| = 1. \end{equation*}

Real space

Any vector space over the field of real numbers.

Synonyms

  • Real vector space
  • Real coordinate space
  • Real \(n\)-space
  • Euclidean space (geometry)

Examples

The simplest real spaces are

in one, two, and three dimensions, respectively.

Subspace

A subset the real space \(H \in \mathbb{R}^n\) that

  • contains the zero vector \(\vec{0}\), so

    \begin{equation*} \vec{0} \in H, \end{equation*}
  • is closed under vector addition, so

    \begin{equation*} \forall \vec{u} \in H, \; \forall \vec{v} \in H \iff (\vec{u} + \vec{v}) \in H, \enspace \qand* \end{equation*}
  • is closed under scalar multiplication, so

    \begin{equation*} \forall c \in \mathbb{R}^1, \; \forall \vec{v} \in H \iff c \vec{v} \in H. \end{equation*}

Synonyms

  • Linear subspace
  • Vector subspace
  • Subspace

Examples

  • \(y = x\) is a subspace of \(\mathbb{R}^2\).
  • \(y = x^2\) is a subset but not a subspace of \(\mathbb{R}^2\).

Zero subspace

A trivial subspace with only the zero vector,

\begin{equation*} \{ \vec{0} \}. \end{equation*}

Basis and dimension

The basis of the zero subspace is the empty set and its dimension is zero.

Trivial subspace

A subspace of the given vector space \(V\) that is

either the zero subspace or \(V\) itself.

Real line

The real space, denoted

\begin{equation*} \mathbb{R}^1 \qor \mathbb{R}, \end{equation*}

and read “R one”, containing

all 1-component vectors defined over the real field.

Synonyms

  • Number line
  • Real number line
  • The set of all real numbers
  • The set of all reals
  • Reals

Subspaces

The real line contains the following subspaces exactly:

Dimension Copies Subspace
0 \(1 \times\) the zero subspace, trivially
1 \(1 \times\) \(\mathbb{R}^1\) itself, trivially

Real plane

The real space, denoted

\begin{equation*} \mathbb{R}^2, \end{equation*}

and read “R two”, containing

all 2-component vectors defined over the real field.

Synonyms

  • \(xy\) plane

Subspaces

The real plane contains the following subspaces exactly:

Dimension Copies Subspace
0 \(1 \times\) the zero subspace, trivially
1 \(\infty\)-many \(\mathbb{R}^1\) lines through the origin
2 \(1 \times\) \(\mathbb{R}^2\) itself, trivially

Real three-dimensional space

The real space, denoted

\begin{equation*} \mathbb{R}^3, \end{equation*}

and read “R three”, containing

all 3-component vectors defined over the real field.

Synonyms

  • Real 3D space

Subspaces

The real three-dimensional space contains the following subspaces exactly:

Dimension Copies Subspace
0 \(1 \times\) the zero subspace, trivially
1 \(\infty\)-many \(\mathbb{R}^1\) lines through the origin
2 \(\infty\)-many \(\mathbb{R}^2\) planes through the origin
3 \(1 \times\) \(\mathbb{R}^3\) itself, trivially

Origin

The “center” of the real space, corresponding to the zero vector.

Basis

Intuition

The “minimal” set of vectors that spans a subspace.

Standard basis

The basis of the real space \(\mathbb{R}^n\) is the set

\begin{equation*} E = \{\vec{e}_1, \ldots, \vec{e}_n\}, \end{equation*}

where \(\vec{e}_i\) is the \(i\)-th column of the identity matrix \(I_n\), so

\begin{equation*} \vec{e}_1 = \begin{pmatrix} 1 \\ 0 \\ 0 \\ \vdots \\ 0 \end{pmatrix}, \vec{e}_2 = \begin{pmatrix} 0 \\ 1 \\ 0 \\ \vdots \\ 0 \end{pmatrix}, \vec{e}_3 = \begin{pmatrix} 0 \\ 0 \\ 1 \\ \vdots \\ 0 \end{pmatrix}, \ldots, \vec{e}_n = \begin{pmatrix} 0 \\ 0 \\ 0 \\ \vdots \\ 1 \end{pmatrix}. \end{equation*}

Standard vector

Dimension

The cardinality of any basis of the given subspace \(H\), denoted

\begin{equation*} \dim H. \end{equation*}

Coefficient matrix

The matrix in the form

\begin{equation*} A = \begin{pmatrix} a_{11} & a_{12} & a_{13} & \cdots & a_{1n} \\ a_{21} & a_{22} & a_{23} & \cdots & a_{2n} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & a_{m3} & \cdots & a_{mn} \\ \end{pmatrix}, \end{equation*}

containing all coefficients of the linear system

\begin{equation*} \begin{cases} a_{11} x_1 + a_{12} x_2 + a_{13} x_3 + \cdots + a_{1n} x_n = b_1 \\ a_{21} x_1 + a_{22} x_2 + a_{23} x_3 + \cdots + a_{2n} x_n = b_2 \\ a_{31} x_1 + a_{32} x_2 + a_{33} x_3 + \cdots + a_{3n} x_n = b_3 \\ \vdots \\ a_{m1} x_1 + a_{m2} x_2 + a_{m3} x_3 + \cdots + a_{mn} x_n = b_m. \end{cases} \end{equation*}

Augmented matrix

A coefficient matrix with a supplementary column

vector containing right-hand side constants

of the associated linear system

\begin{equation*} \begin{cases} a_{11} x_1 + a_{12} x_2 + a_{13} x_3 + \cdots + a_{1n} x_n = b_1 \\ a_{21} x_1 + a_{22} x_2 + a_{23} x_3 + \cdots + a_{2n} x_n = b_2 \\ a_{31} x_1 + a_{32} x_2 + a_{33} x_3 + \cdots + a_{3n} x_n = b_3 \\ \vdots \\ a_{m1} x_1 + a_{m2} x_2 + a_{m3} x_3 + \cdots + a_{mn} x_n = b_m, \end{cases} \end{equation*}

in the form

\begin{equation*} (A | \vec{b}) = \begin{pmatrix} a_{11} & a_{12} & a_{13} & \cdots & a_{1n} & b_1 \\ a_{21} & a_{22} & a_{23} & \cdots & a_{2n} & b_2 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ a_{m1} & a_{m2} & a_{m3} & \cdots & a_{mn} & b_1 \end{pmatrix}. \end{equation*}

Row reduction

Row echelon form

A property of the matrix in which

denoted functionally as

\begin{equation*} \operatorname{ref}(A). \end{equation*}

Reduced row echelon form

A property of the matrix \(A\), denoted

\begin{equation*} \operatorname{rref}(A), \end{equation*}

where \(A\) is in the row echelon form and

Uniqueness

Per the

Uniqueness of the reduced echelon form

theorem, every matrix is row equivalent to

exactly one matrix in reduced row echelon form.

Elementary row operation

One of the following operations on the rows of a matrix:

interchange (swap)
exchange two rows
\(R_i \leftrightarrow R_j\)
scaling
replace a row a by a non-zero constant multiple of itself
\(R_i \gets kR_i\) with \(k \neq 0\)
replacement
replace any row a by the sum of itself and a multiple of another row
\(R_i \gets R_i + kR_j\) with \(i \neq j\)

No change in solutions

Performing elementary row operations on the augmented matrix

never changes the solution set

of the associated linear system.

Reversibility

All elementary row operations are reversible.

Row reduction

An algorithm for solving linear systems using elementary row operations.

Phase 1. Towards row echelon form.

  1. Write an augmented matrix \(M\) for the system.
  2. Move all zero rows to the bottom.
  3. For each row \(R\), from top to bottom:
    1. Scale \(R\) so that its leading entry is 1.
    2. For each row \(R'\) below \(R\):
      1. Replace \(R'\) so that the column below the leading one in \(R\) is 0.

Phase 2. Towards reduced row echelon form.

  1. Is the last column is a pivot column?
  2. For each row \(R\), bottom to top:
    1. For each row \(R'\) above \(R\):
      1. Replace \(R'\) so that the column above the leading one in \(R\) is 0.

Synonyms

  • Gaussian elimination

Example

Solve the linear system

\begin{align*} x + 2y + z & = 1 \\ 3x + y + 4x & = 0 \\ 2x + 2y + 3z & = 2. \end{align*}

Phase 1. Towards the row echelon form.

  1. Write an augmented matrix.

    \begin{equation*} M = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 3 & 1 & 4 & 0 \\ 2 & 2 & 3 & 2 \end{pmatrix}. \end{equation*}
  2. Move all zero rows.

    Nothing to do.

  3. Enumerating rows, top to bottom:

    For \(M_1\):

    1. Scale the selected row.

      Nothing to do.

    2. Replace the rows below the selected row.

      \(M_2 \gets M_2 + kM_1\) with \(k = -3\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ 3 + (-3) \cdot 1 & 1 + (-3) \cdot 2 & 4 + (-3) \cdot 1 & 0 + (-3) \cdot 1 \\ 2 & 2 & 3 & 2 \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & -5 & 1 & -3 \\ 2 & 2 & 3 & 2 \end{pmatrix}. \end{align*}

      \(M_3 \gets M_3 + kM_1\) with \(k = -2\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & -5 & 1 & -3 \\ 2 + (-2) \cdot 1 & 2 + (-2) \cdot 2 & 3 + (-2) \cdot 1 & 2 + (-2) \cdot 1 \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & -5 & 1 & -3 \\ 0 & -2 & 1 & 0 \end{pmatrix}. \end{align*}

    For \(M_2\):

    1. Scale the selected row.

      \(M_2 \gets kM_2\) with \(k = -\frac{1}{5}\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ -\frac{1}{5} \cdot 0 & -\frac{1}{5} \cdot (-5) & -\frac{1}{5} \cdot 1 & -\frac{1}{5} \cdot (-3) \\ 0 & -2 & 1 & 0 \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & -\frac{1}{5} & \frac{3}{5} \\ 0 & -2 & 1 & 0 \end{pmatrix}. \end{align*}
    2. Replace the rows below the selected row.

      \(M_3 \gets M_3 + kM_2\) with \(k = 2\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & -\frac{1}{5} & \frac{3}{5} \\ 0 + 2 \cdot 0 & -2 + 2 \cdot 1 & 1 + 2 \cdot (-\frac{1}{5}) & 0 + 2 \cdot \frac{3}{5} \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & -\frac{1}{5} & \frac{3}{5} \\ 0 & 0 & \frac{3}{5} & \frac{6}{5} \end{pmatrix}. \end{align*}

    For \(M_3\):

    1. Scale the selected row.

      \(M_3 \gets kM_3\) with \(k = \frac{5}{3}\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & -\frac{1}{5} & \frac{3}{5} \\ \frac{5}{3} \cdot 0 & \frac{5}{3} \cdot 0 & \frac{5}{3} \cdot \frac{3}{5} & \frac{5}{3} \cdot \frac{6}{5} \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & -\frac{1}{5} & \frac{3}{5} \\ 0 & 0 & 1 & 2 \end{pmatrix}. \end{align*}
    2. Replace the rows below the selected row.

      Nothing to do.

Phase 2.

  1. Is the last column is a pivot column?

    The system is consistent.

  2. Enumerating rows, bottom to top:

    For \(M_3\):

    1. Replace the rows above.

      \(M_2 \gets M_2 + kM_3\) using \(k = \frac{1}{5}\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 + \frac{1}{5} \cdot 0 & 1 + \frac{1}{5} \cdot 0 & -\frac{1}{5} + \frac{1}{5} \cdot 1 & \frac{3}{5} + \frac{1}{5} \cdot 2 \\ 0 & 0 & 1 & 2 \end{pmatrix} \\[2ex] & = \begin{pmatrix} 1 & 2 & 1 & 1 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 2 \end{pmatrix}. \end{align*}

      \(M_1 \gets M_1 + kM_3\) using \(k = -1\), so

      \begin{align*} M & \gets \begin{pmatrix} 1 + (-1) \cdot 0 & 2 + (-1) \cdot 0 & 1 + (-1) \cdot 1 & 1 + (-1) \cdot 2 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 2 \end{pmatrix} \\ & = \begin{pmatrix} 1 & 2 & 0 & -1 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 2 \end{pmatrix}. \end{align*}

    For \(M_2\):

    1. Replace the rows above.

    \(M_1 \gets M_1 + kM_2\) using \(k = -2\), so

    \begin{align*} M & \gets \begin{pmatrix} 1 + (-2) \cdot 0 & 2 + (-2) \cdot 1 & 0 + (-2) \cdot 0 & -1 + (-2) \cdot 1 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 2 \end{pmatrix} \\ & = \begin{pmatrix} 1 & 0 & 0 & -3 \\ 0 & 1 & 0 & 1 \\ 0 & 0 & 1 & 2 \end{pmatrix}. \end{align*}

    For \(M_1\):

    1. Replace the rows above.

      Nothing to do.

The only solution is \((x, y, z) = (-3, 1, 2)\).

Row equivalence

A joint property of the matrices \(A\) and \(B\) iff

there exists a sequence of elementary row operations

that transform \(A\) into \(B\).

Leading entry

The left-most entry of a non-zero row of a matrix.

Leading one

Pivot column

Any column of the given matrix that contains a pivot position.

Pivot position

A position in the matrix \(A\) that corresponds to

the position of a leading one

in the matrix \(B\), where \(B\) is

the reduced row echelon form

of \(A\).

Probability theory

Probability theory

A branch of mathematics

concerned with probability,

Foundations

Experiment

A procedure with a defined sample space.

Synonyms

  • Random experiment
  • Trial

Sample space

The set of all possible outcomes of an experiment.

Notation

Commonly denoted by

  • \(\Omega\) read “big omega”
  • \(S\).

Example

The sample space \(\Omega\) of the event of

getting two heads or two tails

in an experiment involving a sequence of

two flips of a fair coin

is the set

\begin{equation*} \Omega = \{ (\mathrm{H}, \mathrm{H}), (\mathrm{H}, \mathrm{T}), (\mathrm{T}, \mathrm{H}), (\mathrm{T}, \mathrm{T}) \}, \end{equation*}
where \(\mathrm{H}\) is a symbol denoting “heads”
  \(\mathrm{T}\) is a symbol denoting “tails”.

Outcome

A member of the sample space representing

a result of an experiment.

Example

One possible outcome of the event of

getting two heads or two tails

in an experiment involving a sequence of

two flips of a fair coin

is the ordered pair

\begin{equation*} (\mathrm{H}, \mathrm{H}), \end{equation*}
where \(\mathrm{H}\) is a symbol denoting “heads”
  \(\mathrm{T}\) is a symbol denoting “tails”.

Event

A subset of the sample space.

Notation

An event is commonly denoted with

a capital letter from the beginning of the alphabet,

such as \(A, B, C, D, E\).

Example

The event \(A\) of

getting two heads or two tails

in an experiment involving a sequence of

two flips of a fair coin

is the set

\begin{equation*} A = \{ (\mathrm{H}, \mathrm{H}), (\mathrm{T}, \mathrm{T}) \}, \end{equation*}
where \(\mathrm{H}\) is a symbol denoting “heads”
  \(\mathrm{T}\) is a symbol denoting “tails”.

Event space

A set of events under consideration.

Synonyms

  • Sigma-algebra
  • \(\sigma\)-algebra

Notation

Commonly denoted with

  • uppercase calligraphic Roman F \(\mathcal{F}\)
  • uppercase Greek sigma \(\Sigma\).

Probability (distribution)

A function over the event space that satisfies the probability axioms.

Notation

Probability is denoted \(\Pr(A)\) or \(\operatorname{P}(A)\) or \(P(A)\) and read “probability of \(A\)”.

Image

The codomain of probability is the real line \(\mathbb{R}\) and

its image is the real interval \([0, 1]\),

per the probability axioms.

Example

The probability of

getting two heads or two tails

in an experiment involving a sequence of

two flips of a fair coin

is the value

\begin{equation*} \Pr(A) = \frac{|A|}{|\Omega|} = \frac{2}{4} = \frac{1}{2}, \end{equation*}

with the event and the sample space

\begin{align*} \Omega & = \{ (\mathrm{H}, \mathrm{H}), (\mathrm{H}, \mathrm{T}), (\mathrm{T}, \mathrm{H}), (\mathrm{T}, \mathrm{T}) \} \\ A & = \{ (\mathrm{H}, \mathrm{H}), (\mathrm{T}, \mathrm{T}) \}, \end{align*}

respectively,

where \(\mathrm{H}\) is a symbol denoting “heads”
  \(\mathrm{T}\) is a symbol denoting “tails”.

Synonyms

  • Probability function

Probability axioms

The facts that the probability

  1. of an event is a non-negative real number,

    \[ \forall A, \> \Pr(A) \geq 0 \]

  2. of the sample space is the unit,

    \[ \Pr(\Omega) = 1 \]

  3. of mutually exclusive events is

    \[\Pr(A_1 \cup A_2 \cup \ldots) = \Pr(A_1) + \Pr(A_2) + \cdots\]

    or equivalently

    \[\Pr( \bigcup_{i = 1}^{\infty} E_i ) = \sum_{i = 1}^{\infty} \Pr(E_i).\]

Synonyms

  • Kolmogorov axioms

History

Introduced by Russian mathematician Andrey Kolmogorov in the 1930s.

Descriptive statistics

Descriptive statistics

A branch of statistics concerned with

describing data.

Sources

Individual

An object distinct from any other object.

Variable

A function of an individual.

Observation

An observed value of a variable.

Processing

Commonly normalized or standardized.

Synonyms

  • Observed value
  • Score
  • Raw score
  • Raw data
  • Data point
  • Primary datum

Normalization

Linear scaling of data points, commonly to the interval \([0, 1]\).

Computation

\begin{equation*} \frac{x - \min X}{\max X - \min X}, \end{equation*}
where \(X\) is a sample and
  \(x\) is a score in \(X\)

Synonyms

  • Normal score
  • Min-max normalization

Population

A partially observable set of individuals.

Intuition

The set whose parameters are estimated with statistics.

Parameter

A function of the population that

is unknown and estimated by a statistic.

Synonyms

  • Statistical parameter

Population size

A parameter representing

the cardinality of a population.

Notation

Often denoted \(n\) for the “number” of members.

Sample

A fully observable subset of the population.

Denote

Often denoted \(X\) or \(x\).

Synonyms

  • Statistical sample

Sample size

The statistic representing

the cardinality of a sample.

Notation

Commonly denoted

Statistic

A function of a sample.

Robust statistic

A statistic that is largely

resistant to outliers

and unexpected distributions.

Central tendency

A measure of

the center of a distribution.

Intuition

Represents a kind of “typical value”.

Examples

Synonyms

  • Center

Population mean

The parameter representing

the arithmetic mean of a variable within a population.

Notation

Commonly denoted \(\mu\) and read “mew”.

Estimation

Commonly estimated by the sample mean.

Theoretical foundations

Modeled after the expected value in probability theory.

Sample mean

The statistic representing

the arithmetic mean of a variable within the sample.

Notation

Denoted \(\bar{x}\) and read “\(x\) bar”.

Estimation

A common estimator of the population mean.

Robustness

Theoretical foundations

Modeled after the expected value in probability theory.

Law of large numbers

The fact that, as the sample size approaches the population size,

the sample mean approaches the population mean.

Simulation

Repeatedly increase the sample size from \(n = 1\) to the population size:

set.seed(0)

N <- 1000      # the population size
Z <- 0 : N     # the population
mu <- mean(Z)  # the population mean
D <- rep(0, N) # absolute deviations

for(n in 1 : N)
  D[n] <- mean(sample(Z, n)) - mu

plot(
  D,
  main = "A demonstration of the law of large numbers.",
  type = "l",
  lwd = 2,
  xlab = "Sample size",
  ylab = "Absolute deviation"
)
abline(h = 0)

Sampling distribution

The probability distribution of

a statistic across multiple samples.

Random variables

Drawing multiple samples from a population

makes the statistic into a random variable.

Sampling distribution of the sample mean

Notation

Commonly denoted \(\mu_{\bar{X}}\) read “mew sub bar \(X\)”.

Significance

Significant due to the central limit theorem.

Synonyms

  • Distribution of sample means
  • Distribution of the sample mean

Central limit theorem

The fact that, as the sample size grows,

the sampling distribution of the sample mean approaches the normal distribution,

with the expected value equal to the population mean,

regardless of the distribution of the population.

Significance

The most profound concept in statistics.

Symbolically

\begin{equation*} \mu_{\bar{X}} \sim \operatorname{N} \Big( \mu, \frac{\sigma^2}{n} \Big). \end{equation*}

Exploration in R

The sample mean of \(N = 10^3\) samples taken from a uniformly distributed population should distribute normally.

set.seed(0)

N <- 10^4
Z <- rep(1, N)

for(i in 1 : N) {
  X <- runif(10^3)
  Z[i] <- mean(X)
}

Y <- density(Z)

plot(
  main = "A demonstration of the central limit theorem.",
  Y,
  type = "l",
  lwd = 2,
  xlab = "Sample mean",
  ylab = "Density"
)
abline(v = 0.5)

Frequency (distribution)

A statistic counting

distinct values of a variable

within a sample.

Synonyms

  • Absolute frequency

Relative frequency (distribution)

Significance

Relative frequency estimates probability and

connects descriptive statistics to probability theory

under frequentist interpretation.

Computer science

Computer science (CS)

No definition yet.

Resources

Ethically licensed resources for computer science (alphabetically):

  • Algorithms [15]
    @Book{erickson-2019,
      author       = {Jeff Erickson},
      title        = {Algorithms},
      publisher    = {Self-published},
      year         = 2019,
      url          = {https://jeffe.cs.illinois.edu/teaching/algorithms/}
    }
    
  • Artificial intelligence: Foundations of computational agents [12]
    @Book{poole+2017,
      author       = {David L. Poole and Alan K. Mackworth},
      title        = {Artificial Intelligence},
      publisher    = {{Cambridge} University Press},
      year         = 2017,
      edition      = 2,
      subtitle     = {Foundations of Computational Agents},
      url          = {https://artint.info/},
      institution  = {University of {British Columbia}},
    }
    

Decision problem

A computational problem whose

all instance have the output either “yes” or “no”,

so a Boolean value.

Representation

Typically represented as a

set of inputs to which the answer is “yes”,

such as in SAT.

Divide and conquer

A problem-solving technique that

repeatedly divides a problem into sub-problems

until each sub-problem is trivial to solve.

Recursion

A divide-and-conquer technique where

every non-trivial sub-problem is solved with recursion13.

Steps

Recursion consists of

Synonyms

  • Self-reference

Base case

The step of recursion where the

current sub-problem is trivial

and no further recursion is needed.

Example

The first case in

\begin{equation*} x! = \begin{cases} 1 & x = 1 \\ x \cdot (x - 1)! & x \ne 1. \end{cases} \end{equation*}

Recursive case

The step of recursion where the

current sub-problem is non-trivial

and further recursion is needed.

Synonyms

  • Inductive case

Example

The second case in

\begin{equation*} x! = \begin{cases} 1 & x = 1 \\ x \cdot (x - 1)! & x \ne 1. \end{cases} \end{equation*}

Merge sort

A recursive sorting algorithm.

Aesthetics

The merge sort algorithm is beautiful.

Practice implementation

  • Phase 1: Split
    (defun my-merge-sort-split (list)
      "Split LIST into halves.
    
    Return a list containing two lists: a copy of first half of LIST and a
    copy of the second half.  If LIST has an odd number of elements, the
    first returned list is one element longer than the second.
    
    This function implements the split phase of the merge sort algorithm."
      (let ((length (length list)))
        (pcase length
          (0 (list nil nil))
          (1 (list list nil))
          (_ (let ((half (/ length 2)))
               (list (take half list)
                     (drop half list)))))))
    
    (ert-deftest my-merge-sort-split ()
      (should (equal (my-merge-sort-split '()) '(() ())))
      (should (equal (my-merge-sort-split '(1)) '((1) ())))
      (should (equal (my-merge-sort-split '(1 2)) '((1) (2))))
      (should (equal (my-merge-sort-split '(1 2 3)) '((1) (2 3))))
      (should (equal (my-merge-sort-split '(1 2 3 4)) '((1 2) (3 4)))))
    
  • Phase 2: Merge

    Merge two sorted lists by

    repeatedly taking the smaller of their first elements,

    collecting the results into a new sorted list.

    (defun my-merge-sort-merge (sorted-list other-sorted-list)
      "Return a sorted list with the elements of SORTED-LIST and
    OTHER-SORTED-LIST."
      (cond ((null sorted-list) other-sorted-list)
            ((null other-sorted-list) sorted-list)
            ((<= (car sorted-list)
                 (car other-sorted-list))
             (cons (car sorted-list)
                   (my-merge-sort-merge (cdr sorted-list)
                                        other-sorted-list)))
            ((> (car sorted-list)
                (car other-sorted-list))
             (cons (car other-sorted-list)
                   (my-merge-sort-merge (cdr other-sorted-list)
                                        sorted-list)))))
    
    • TODO Add tests
  • Merge sort
    (defun my-merge-sort (list)
      "Return a sorted copy of LIST."
      (if (< (seq-length list) 2) list
        (let ((halves (my-merge-sort-split list)))
          (my-merge-sort-merge (my-merge-sort (nth 0 halves))
                               (my-merge-sort (nth 1 halves))))))
    
    (ert-deftest my-merge-sort ()
      (should (equal (my-merge-sort '()) '()))
      (should (equal (my-merge-sort '(1)) '(1)))
      (should (equal (my-merge-sort '(1 2)) '(1 2)))
      (should (equal (my-merge-sort '(2 1)) '(1 2)))
      (should (equal (my-merge-sort '(1 2 3)) '(1 2 3)))
      (should (equal (my-merge-sort '(1 3 2)) '(1 2 3)))
      (should (equal (my-merge-sort '(2 1 3)) '(1 2 3)))
      (should (equal (my-merge-sort '(2 3 1)) '(1 2 3)))
      (should (equal (my-merge-sort '(3 1 2)) '(1 2 3)))
      (should (equal (my-merge-sort '(3 2 1)) '(1 2 3)))
      (should (equal (my-merge-sort '(1 2 3 4)) '(1 2 3 4)))
      (should (equal (my-merge-sort '(4 3 2 1)) '(1 2 3 4))))
    

Master theorem

A recurrence relation that yields

time complexity of recursive algorithm

\begin{equation*} T(n) = a T(n / b) + \mathcal{O}(n^d) = \begin{cases} n^d \log n & a = b^d \\ n^d & a < b^d \\ n^{\log_b a} & a > b^d \end{cases} \end{equation*}

where

  • \(a\) is the sub-problem count per recursive call,
  • \(n / b\) is the sub-problem size in terms of \(n\)
  • \(\mathcal{O}(n^d)\) is the time complexity of the reduction step.

Merge sort example

The merge sort algorithm

  • divides its input into two halves per recursive call
  • enumerates its input during the merge step

which gives

\begin{align*} a & = 2 \\ b & = 2 \quad \iff \quad n / b = n / 2 \\ d & = 1 \quad \iff \quad \mathcal{O}(n^d) = \mathcal{O}(n^1) \end{align*}

and so

\begin{equation*} a \stackrel{?}{=} b^d \quad \iff \quad 2 \stackrel{?}{=} 2^1 \quad \iff \quad 2 \stackrel{\checkmark}{=} 2 \end{equation*}

and so

\begin{align*} T(n) & = a T(n / b) + \mathcal{O}(n^d) \\ & = n^d \log n \\ & = n^d \log n = n^1 \log n \\ & = \boxed{n \log n}. \end{align*}

Generic search

A method for

finding goal vertices in a directed graph,

fundamental to solving computational problems.

Algorithm

The steps of generic search on \(G = (V, E)\), given

  • a starting vertex \(v_1 \in V\) and
  • a goal predicate \(g : (v_1, \ldots, v_k) \to \mathbb{B}\)

are:

  1. start with the frontier \(F = \{(v_1)\}\) and seen vertices \(S = \{v_1\}\)
  2. if \(|F| = 0\), return empty-handed (\(\bot\))
  3. remove some path \(p = (v_1, \ldots, v_k)\) from \(F\)
  4. when the goal predicate \(g(p)\) is true 14, return \(p\) as the solution
  5. select \(N = \{(v_1, v_2, \ldots, v_k, v^*) : v^* \notin S \land (v_k, v^*) \in E\}\)
  6. for each \(n \in N\), insert \(n\) to \(F\)
  7. for each \((v_1, v_2, \ldots, v_k, v^*) \in N\), insert \(v^*\) to \(S\)
  8. go to step 2.

Synonyms

The synonyms for the generic search include:

  • Generic graph-searching algorithm

Practice implementation

A clean-room implementation 15 of generic search in Elisp:

(defvar my-graph-search-debug nil
  "Print debug information during graphs search.")
(defun my-graph-search (frontier neighbors goalp add &optional seen)
  "Search a directed graph.

FRONTIER is a list of paths to be searched, NEIGHBORS is a function that
takes a path and returns all adjacent vertices to its `car', GOALP is a
function that takes a path and returns non-nil if the path reached the
goal, and ADD is a function that takes a frontier and a list of paths
and adds the paths to the frontier.

SEEN is a set of visited vertices, used internally.

In all paths, the most recently seen vertex is the first element."
  (if (not frontier) nil
    (let ((path (car frontier))
          (seen (or seen
                    (let ((seen (make-hash-table :test #'equal)))
                      (mapc (lambda (vertex) (puthash vertex t seen))
                            (mapcan #'identity frontier))
                      seen))))
      (when my-graph-search-debug
        (princ (format "Search: %s\n" path)))
      (if (funcall goalp path) path
        (my-graph-search
         (funcall add
                  (cdr frontier)
                  (mapcan (lambda (neighbor)
                            (if (gethash neighbor seen) nil
                              (puthash neighbor t seen)
                              (list (copy-sequence (cons neighbor path)))))
                          (funcall neighbors path)))
         neighbors goalp add seen)))))

Depth-first search (DFS)

A generic search that

tests some not-yet-searched adjacent vertex 16

and then searches it using DFS.

Computational complexity

The space and time complexity of DFS is

\[\mathcal{O}(bm) \qand \mathcal{O}(b^m)\]

respectively 17,

where \(b\) is the branching factor and
  \(m\) is the maximum search depth.

Implementation

DFS is implemented as

a generic search with a LIFO queue for the frontier.

Practice implementation

A clean-room implementation 18 of DFS in Elisp:

(defun my-depth-first-graph-search (frontier neighbors goalp)
  "Perform depth-first search (DFS) on a graph.

All arguments are like those for `my-graph-search', which see."
  (let ((lifo (lambda (frontier paths) (nconc paths frontier))))
    (my-graph-search frontier neighbors goalp lifo)))
(defalias 'my-dfs-graph-search #'my-depth-first-graph-search)

Test.

5d1d0c62-0840-4870-bc01-1d0cd6d2740e.svg
(let ((graph '((a b c d)
               (b a e i)
               (c a f)
               (d a g l)
               (e b i j)
               (f c)
               (g d k l)
               (i b e)
               (j e)
               (k g)
               (l d g)))
      (my-graph-search-debug t))
  (princ (format "Solution: %s\n"
                 (my-bfs-graph-search
                  '((a))
                  (lambda (path) (alist-get (car path) graph))
                  (lambda (path) (eq (car path) 'f))))))
Search: (a)
Search: (b a)
Search: (c a)
Search: (d a)
Search: (e b a)
Search: (i b a)
Search: (f c a)
Solution: (f c a)

Connection to logic

Breadth-first search (BFS)

A generic search that

tests all not-yet-searched adjacent vertices 20

and then searches them using BFS.

Computational complexity

The space and time complexity of BFS is

\[\mathcal{O}(b^{d + 1}),\]

where \(b\) is the branching factor and
  \(d\) is the depth of the shallowest solution.

Proof of complexity claims

Statements.

BFS runs in exponential time \(\mathcal{O}(b^{d + 1})\), where

  • \(b\) is the branching factor and
  • \(d\) is the depth of the shallowest solution.

BFS runs in linear time \(\mathcal{O}(|V| + |E|)\), where

Objective.

Prove the above statements equivalent for any complete graph \(K_n\).

Proof.

Lemma 1. Given \(|V| = n\), prove \(\mathcal{O}(|V| + |E|) = \mathcal{O}(n^2)\).

For any \(K_n\),

\begin{align*} |V| = n \> \iff \> |E| = \frac{n(n - 1)}{2}, \end{align*}

and so

\begin{align*} \mathcal{O}(|V| + |E|) & = \mathcal{O}(|V|) + \mathcal(|E|) && \text{distribute \(\mathcal{O}\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O} \big( n(n - 1) / 2 \big) && \text{substitute \(|V|\) and \(|E|\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O} \big( (n^2 - n) / 2 \big) && \text{distribute \(n\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O} \big( (1/2) (n^2 - n) \big) && \text{split fraction} \\[1ex] & = \mathcal{O}(n) + \mathcal{O} \big( (1 / 2) n^2 - (1 / 2) n \big) && \text{distribute \(1/2\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O}\big((1 / 2) n^2 \big) - \mathcal{O}\big((1 / 2) n \big) && \text{distribute \(\mathcal{O}\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O} \big( 1/2 \big) \, \mathcal{O} \big( n^2 \big) - \mathcal{O} \big( 1/2 \big) \, \mathcal{O} \big (n \big) && \text{distribute \(\mathcal{O}\)} \\[1ex] & = \mathcal{O}(n) + \mathcal{O}(1) \, \mathcal{O}(n^2) - \mathcal{O}(1) \, \mathcal{O}(n) && \text{standardize} \\[1ex] & = \mathcal{O}(n^2). && \text{simplify} \end{align*}

Lemma 2. Given \(|V| = n\), prove \(\mathcal{O}(b^{d + 1}) = \mathcal{O}(n^2)\).

In any \(K_n\), all vertices are of degree \(n - 1\) and adjacent, so

\[b = n - 1 \qand d = 1,\]

respectively, which gives

\begin{align*} \mathcal{O}(b^{d + 1}) & = \mathcal{O} \big( (n - 1)^{1 + 1} \big) && \text{substitute \(b\) and \(d\)} \\[1ex] & = \mathcal{O} \big( (n - 1)^2 \big) && \text{combine like terms} \\[1ex] & = \mathcal{O} \big( (n - 1)(n - 1) \big) && \text{take square} \\[1ex] & = \mathcal{O} \big( n^2 - 2n + 1 \big) && \text{multiply binomials} \\[1ex] & = \mathcal{O}(n^2) - \mathcal{O}(2n) + \mathcal{O}(1) && \text{distribute \(\mathcal{O}\)} \\[1ex] & = \mathcal{O}(n^2) - \mathcal{O}(n) + \mathcal{O}(1) && \text{standardize} \\[1ex] & = \mathcal{O}(n^2). && \text{simplify} \end{align*}

Given Lemma 1 and Lemma 2,

\begin{align*} \mathcal{O}(|V| + |E|) & \stackrel{?}{=} \mathcal{O}(b^{d + 1}) \\ \mathcal{O}(n^2) & \stackrel{\checkmark}{=} \mathcal{O}(n^2). \quad \blacksquare \end{align*}

Connection to logic

Implementation

BFS is implemented as

a generic search with a FIFO queue for the frontier.

Practice implementation

A clean-room implementation 22 of BFS in Elisp:

(defun my-breadth-first-graph-search (frontier neighbors goalp)
  "Perform depth-first search (DFS) on a graph.

All arguments are like those for `my-graph-search', which see."
  (let ((fifo #'nconc))
    (my-graph-search frontier neighbors goalp fifo)))
(defalias 'my-bfs-graph-search #'my-breadth-first-graph-search)

Test.

f006dac7-cfd8-4f61-91d4-38e877ce5105.svg
(let ((graph '((a b c d)
               (b a e i)
               (c a f)
               (d a g l)
               (e b i j)
               (f c)
               (g d k l)
               (i b e)
               (j e)
               (k g)
               (l d g)))
      (my-graph-search-debug t))
  (princ (format "Solution: %s\n"
                 (my-dfs-graph-search
                  '((a))
                  (lambda (path) (alist-get (car path) graph))
                  (lambda (path) (eq (car path) 'f))))))
Search: (a)
Search: (b a)
Search: (e b a)
Search: (j e b a)
Search: (i b a)
Search: (c a)
Search: (f c a)
Solution: (f c a)

Backtracking

A depth-first search that

avoids traversing irrelevant subgraphs.

Pure function

A function that

returns the same output for the same inputs

and

has no observable side effects.

Advantages

Using pure functions aids

Relation to mathematics

Memoization

A programming technique where

results of pure functions are cached

to avoid redundant computation.

Manual memoization in Python

# Memoized results.
ys = {}


def f(n):
    """Return the factorial."""

    # Check preconditions.
    assert int(n) == n, "Factorial of non-integral values is undefined"
    assert n >= 0, "Factorial of negative integers is undefined"

    # Return early in the base case.
    if n == 1:
        return 1

    # Return early with a memoized result.
    if n in ys:
        return n

    # Compute the result.
    y = n * f(n - 1)

    # Memoize the result.
    ys[n] = y

    # Return the result.
    return y


# Try it.
f(5)
120

Automatic memoization in Python

from functools import lru_cache


@lru_cache(maxsize=None)
def f(n):
    """Return the factorial."""

    # Check preconditions.
    assert int(n) == n, "Factorial of non-integral values is undefined"
    assert n >= 0, "Factorial of negative integers is undefined"

    # Compute the result.
    return n * f(n - 1) if n else 1


# Try it.
f(5)
120

Dynamic programming

A recursive programming technique where

the solutions to identical sub-problems are memoized.

Computational complexity

The discrete

amount of time or space

required for a complete execution of a given algorithm,

as a function of the input size.

Time complexity

A kind of computational complexity measured by the

number of elementary operations

performed by a given algorithm.

Elementary operation

Any computation whose

time complexity 23 is independent of the input size

of the measured algorithm.

Space complexity

A kind of computational complexity measured by the

number of memory locations

allocated by a given algorithm.

Asymptotic complexity

A kind of computational complexity that

emphasizes the order of growth

as the input size tends to infinity by

ignoring constant factors and lower-order terms.

Big O

A notation for asymptotic complexity where

\begin{equation*} f(n) = \mathcal{O}(g(n)) \end{equation*}

denotes the fact

\(f(n)\) grows on the order of \(g(n)\).

Example

The asymptotic complexity of

\begin{equation*} f(n) = 4n^3 + 3n^2 + 2n + 1 \end{equation*}

in Big O is derived by

\begin{align*} f(n) & = \mathcal{O}(4n^3 + 3n^2 + 2n + 1) \\ & = \mathcal{O}(n^3 + n^2 + n) \\ & = \mathcal{O}(n^3). \end{align*}

Order of growth

A function that

asymptotically describes growth rate,

notably in Big O notation:

\(\mathcal{O}(1)\)
constant
\(\mathcal{O}(\log n)\)
logarithmic
\(\mathcal{O}(n)\)
linear
\(\mathcal{O}(n \log n)\)
log-linear
\(\mathcal{O}(n^2)\)
quadratic
\(\mathcal{O}(n^3)\)
cubic
\(\mathcal{O}(n^c)\)
polynomial
\(\mathcal{O}(c^n)\)
exponential
\(\mathcal{O}(n!)\)
factorial

where

  • \(n\) is the input size
  • \(c\) is a constant.

Hardness

Relative complexity in terms of reductions, where

problem \(L\) is at least as hard as \(L'\)

if and only if

\begin{equation*} L' \leq_{\mathrm{R}} L, \end{equation*}

where \(R\) is the reduction type,

typically a polynomial-time reduction.

Complexity class

Notable classes

Notable complexity classes include

P, NP, NP-hard, and NP-complete.

P

The complexity class of decision problems whose

all instances are solvable in polynomial time.

Synonyms

  • Polynomial time class

NP

The complexity class of decision problems whose

all solutions are verifiable in polynomial time.

Synonyms

  • Nondeterministic polynomial-time class

NP-complete

A complexity class of problems that are

both NP and NP-hard.

Intuition

The hardest problems in NP.

Symbolic definition

\begin{equation*} L \in \text{NP-complete} \> \iff \> ( L \in \text{NP} ) \land ( L \in \text{NP-hard} ) \end{equation*}

Proofs

To prove a problem \(L\) is NP-complete,

reduce a known NP-complete problem to \(L\).

in polynomial time.

Discovery

NP-completeness was discovered by [16]24 who

proved that every problem in NP reduces to SAT,

and applied by [17] who showed it holds to

reduced 21 important combinatorial problems,

both receiving Turing Awards.

Synonyms

  • Nondeterministic polynomial-time complete class

NP-hard

Confusion

NP-hard is not a subset of NP, so its problems are

Discovery

After the discovery of NP-complete, it became apparent that

some problems are just as hard but are not decision problems

such as

  • “Does a tour of given maximum length exists?” (in NP and NP-complete)
  • “What is the shortest tour?” (not in NP, but just as hard or harder)

which gave birth to NP-hard.

Symbolic definition

\begin{equation*} L \in \text{NP-hard} \> \iff \> \forall L' \! \in \text{NP}, \>\> L' \leq_p L. \end{equation*}

Graph coloring (COL)

The decision problem of

coloring vertices of the given graph

such that

no adjacent vertices have the same color.

3-coloring (3COL)

A special case of COL with

exactly three colors.

Reduce

3SAT to 3COL, showing NP-completeness of the latter.

Perform a polynomial-time reduction using the graph below.

  1. Add three interconnected vertices, colored 1, 2, and 3.
  2. For each variable, add two uncolored vertices connected to 2.
  3. For each clause, add one Or gadget connected to:
    • the relevant variable vertices and
    • the vertex colored 1.

Given

\begin{equation*} F = c_1 \land c_2 \land \cdots = (\ell_{11} \lor \ell_{12} \lor \ell_{13}) \land (\ell_{21} \lor \ell_{22} \lor \ell_{23}) \land \cdots, \end{equation*}

connect the graph

0e31ff94-c745-49ab-a42b-985b2a1ac01c.svg

For example,

\begin{equation*} F = (\lnot x_1 \lor x_2 \lor \lnot x_2) \land (\lnot x_2 \lor x_3 \lor \lnot x_3) \end{equation*}

reduces to the graph

a2215ee0-36af-4e1f-8d8f-c8ff4fda71c5.svg

Check

… that Or gadget implements the 3-way disjunction

\begin{equation*} \ell_1 \lor \ell_2 \lor \ell_3. \end{equation*}

The Or gadget:

0ceb40bf-4e47-4aee-89db-b404445c4ee1.svg

No coloring exists when \(\ell_1 = \ell_2 = \ell_3 = 0\):

\(\ell_1\) \(\ell_2\) \(\ell_3\) \(c_1\) \(c_2\) \(c_3\) \(c_4\) \(c_5\)
0 0 0 n/a 1 2 2 0
0 0 0 n/a 2 2 1 0
0 0 0 1 n/a 2 2 0
0 0 0 2 n/a 2 1 0
0 0 0 1 2 n/a 0 2
0 0 0 2 1 n/a 0 2
0 0 0 1 2 2 n/a 0
0 0 0 2 1 2 n/a 0
0 0 0 1 2 2 0 n/a
0 0 0 2 1 2 0 n/a

Otherwise, a coloring exists:

\(\ell_1\) \(\ell_2\) \(\ell_3\) \(c_1\) \(c_2\) \(c_3\) \(c_4\) \(c_5\)
0 0 1 1 2 0 0 2
0 0 1 2 1 0 0 2
0 1 0 1 0 2 2 0
0 1 0 2 0 2 1 0
0 1 1 2 0 0 1 2
0 1 1 2 0 2 1 0
1 0 0 0 1 2 2 0
1 0 0 0 2 2 1 0
1 0 1 0 2 0 1 2
1 0 1 0 2 2 1 0
1 1 0 0 2 2 1 0
1 1 0 2 0 2 1 0
1 1 1 0 2 0 1 2
1 1 1 0 2 2 1 0
1 1 1 2 0 0 1 2
1 1 1 2 0 2 1 0

Boolean satisfiability problem (SAT)

A decision problem of determining

the satisfiability of a molecular statement,

typically in the conjunctive normal form.

Hardness

NP-complete with 3 or more literals per clause (3SAT).

Tips

  • Assign hard-to-satisfy vars first, to cut down the search space.
  • Can be made into an optimization problem of minimizing unsat clauses.

Synonyms

  • SAT
  • B-SAT
  • Satisfiability problem
  • Propositional satisfiability problem

Conjunctive normal form (CNF)

A molecular statement written as

a conjunction of disjunctions of possibly negated variables

\(\{ x_1, x_2, \ldots \}\) in the form

\begin{gather*} f = \underbrace{(\ell_{11} \lor \ell_{12} \lor \cdots)}_{\textstyle c_1} \land \underbrace{(\ell_{21} \lor \ell_{22} \lor \cdots)}_{\textstyle c_2} \land \cdots, \\[1ex] \text{with} \quad \forall (i, j), \> \quad \exists k \quad \text{such that} \quad \boxed{\ell_{ij} = x_k} \quad \text{or} \quad \boxed{\ell_{ij} = \lnot x_k}, \end{gather*}
where \(f\) is the formula in CNF,
  \(c_i\) is the \(i\)-th clause of \(f\), and
  \(\ell_{ij}\) is the \(j\)-th literal of \(c_i\).

3-satisfiability (3SAT)

A special case of SAT with

exactly three literals per clause.

The problem is NP-complete.

Explore

polynomial-time reduction from SAT,

proving NP-completeness of 3SAT.

For each clause \(c\) in a SAT instance,

  • Case 1: One literal

    The one-literal clause

    \begin{equation*} c = (\ell) \end{equation*}

    becomes four three-literal clauses

    \begin{align*} c'_1 & = (\ell \lor \hphantom{\lnot} y_1 \lor \hphantom{\lnot} y_2) \\ c'_2 & = (\ell \lor \hphantom{\lnot} y_1 \lor \lnot y_2) \\ c'_3 & = (\ell \lor \lnot y_1 \lor \hphantom{\lnot} y_2) \\ c'_4 & = (\ell \lor \lnot y_1 \lor \lnot y_2). \end{align*}
    • Proof

      By truth tables, with

      \begin{equation*} c' = (c'_1 \land c'_2 \land c'_3 \land c'_4), \end{equation*}

      we have

      \begin{align*} \begin{array}{ccc|ccccc} c & y_1 & y_2 & c'_1 & c'_2 & c'_3 & c'_4 & c' \\[1ex] \boxed{0} & 0 & 0 & 0 & 1 & 1 & 1 & \boxed{0} \\[0.25ex] \boxed{0} & 0 & 1 & 1 & 0 & 1 & 1 & \boxed{0} \\[0.25ex] \boxed{0} & 1 & 0 & 1 & 1 & 0 & 1 & \boxed{0} \\[0.25ex] \boxed{0} & 1 & 1 & 1 & 1 & 1 & 0 & \boxed{0} \\[0.25ex] \boxed{1} & 0 & 0 & 1 & 1 & 1 & 1 & \boxed{1} \\[0.25ex] \boxed{1} & 0 & 1 & 1 & 1 & 1 & 1 & \boxed{1} \\[0.25ex] \boxed{1} & 1 & 0 & 1 & 1 & 1 & 1 & \boxed{1} \\[0.25ex] \boxed{1} & 1 & 1 & 1 & 1 & 1 & 1 & \boxed{1} \\[0.25ex] \end{array} \> \> . \end{align*}

      \(\blacksquare\)

  • Case 2: Two literals

    The two-literal clause

    \begin{equation*} c = (\ell_1 \lor \ell_2) \end{equation*}

    becomes two three-literal clauses

    \begin{align*} c'_1 & = ( \ell_1 \lor \ell_2 \lor \hphantom{\lnot} y ) \\ c'_2 & = ( \ell_1 \lor \ell_2 \lor \lnot y ). \end{align*}
    • Proof

      By truth tables, with

      \begin{equation*} c' = (c'_1 \land c'_2), \end{equation*}

      we have

      \begin{align*} \begin{array}{ccc|cccc} \ell_1 & \ell_2 & y & c & c'_1 & c'_2 & c' \\[1ex] 0 & 0 & 0 & \boxed{0} & 0 & 1 & \boxed{0} \\[0.25ex] 0 & 0 & 1 & \boxed{0} & 1 & 0 & \boxed{0} \\[0.25ex] 0 & 1 & 0 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] 0 & 1 & 1 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] 1 & 0 & 0 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] 1 & 0 & 1 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] 1 & 1 & 0 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] 1 & 1 & 1 & \boxed{1} & 1 & 1 & \boxed{1} \\[0.25ex] \end{array} \> \> . \end{align*}

      \(\blacksquare\)

  • Case 3: Three literals

    The 3-literal clause

    \begin{equation*} c = ( \ell_1 \lor \ell_2 \lor \ell_3 ) \end{equation*}

    stays as is.

  • Case 4: More than three literals

    The \((k > 3)\)-literal clause

    \begin{equation*} c = ( \ell_1, \ell_2, \ell_3, \ldots, \ell_k ) \end{equation*}

    becomes \(k - 2\) three-literal clauses

    \begin{align*} c'_1 & = (\hphantom{\lnot} \ell_1 \lor \ell_2 \lor y_1) \\ c'_2 & = (\lnot y_1 \lor \ell_3 \lor y_2) \\ c'_3 & = (\lnot y_2 \lor \ell_4 \lor y_3) \\[1ex] \vdots & \\[1ex] c'_k & = (\lnot y_{k - 5} \lor \ell_{k - 3} \lor y_{k - 4}) \\ c'_{k - 1} & = (\lnot y_{k - 4} \lor \ell_{k - 2} \lor y_{k - 3}) \\ c'_{k - 2} & = (\lnot y_{k - 3} \lor \ell_{k - 1} \lor \ell_k) \end{align*}
    • Proof
      • Case 1: \(c\) is satisfiable
        • Case 1A: \(\ell_1 = 1\) or \(\ell_2 = 1\)

          Setting

          \begin{equation*} y_i = 0 \quad \forall i \end{equation*}

          satisfies all clauses:

          \begin{align*} c'_1 & = (\ell_1 \lor \ell_2 \lor y_1) \\ & = (\boxed{\ell_1} \lor \boxed{\ell_2} \lor 0) \\ & = 1 \\[2ex] c'_2 & = (\lnot y_1 \lor \ell_3 \lor y_2) \\ & = (\boxed{\lnot 0} \lor \ell_3 \lor 0) \\ & = 1 \\[1ex] & \vdots \\[2ex] c'_{k - 1} & = (\lnot y_{k - 4} \lor \ell_{k - 2} \lor y_{k - 3}) \\ & = (\boxed{\lnot 0} \lor \ell_{k - 2} \lor 0) \\ & = 1 \\[2ex] c'_{k - 2} & = (\lnot y_{k - 3} \lor \ell_{k - 1} \lor \ell_k) \\ & = (\boxed{\lnot 0} \lor \ell_{k - 2} \lor \ell_k) \\ & = 1. \end{align*}
        • Case 1B: \(\ell_{k - 1} = 1\) or \(\ell_k = 1\)

          Setting

          \begin{equation*} y_i = 1 \quad \forall i \end{equation*}

          satisfies all clauses:

          \begin{align*} c'_1 & = (\ell_1 \lor \ell_2 \lor y_1) \\ & = (\ell_1 \lor \ell_2 \lor \boxed{1}) \\ & = 1 \\[2ex] c'_2 & = (\lnot y_1 \lor \ell_3 \lor y_2) \\ & = (\lnot 1 \lor \ell_3 \lor \boxed{1}) \\ & = 1 \\[1ex] \vdots & \\[2ex] c'_{k - 1} & = (\lnot y_{k - 4} \lor \ell_{k - 2} \lor y_{k - 3}) \\ & = (\lnot 1 \lor \ell_{k - 2} \lor \boxed{1}) \\ & = 1 \\[2ex] c'_{k - 2} & = (\lnot y_{k - 3} \lor \ell_{k - 1} \lor \ell_k) \\ & = (\lnot 1 \lor \boxed{\ell_{k - 1}} \lor \boxed{\ell_k}) \\ & = 1. \end{align*}
        • Case 1C: \(\ell_i = 1\) with \(i \not\in \{1, 2, k - 1, k\}\)

          Setting

          \begin{equation*} y_j = \begin{cases} 1 & \text{for} \quad 1 \leq j \leq i - 2 \\ 0 & \text{for} \quad i - 1 \leq j \leq y_{k - 3} \end{cases} \end{equation*}

          satisfies all clauses:

          \begin{align*} c'_1 & = (\ell_1 \lor \ell_2 \lor y_1) \\ & = (\ell_1 \lor \ell_2 \lor \boxed{1}) \\ & = 1 \\[2ex] c'_2 & = (\lnot y_1 \lor \ell_3 \lor y_2) \\ & = (\lnot 1 \lor \ell_3 \lor \boxed{1}) \\ & = 1 \\[1ex] \vdots & \\[2ex] c'_{i - 1} & = (\lnot y_{i - 2} \lor \ell_i \lor y_{i - 1}) \\ & = (\lnot 1 \lor \boxed{1} \lor 0) \\ & = 1 \\[1ex] \vdots & \\[2ex] c'_{k - 1} & = (\lnot y_{k - 4} \lor \ell_{k - 2} \lor y_{k - 3}) \\ & = (\boxed{\lnot 0} \lor \ell_{k - 2} \lor 0) \\ & = 1 \\[2ex] c'_{k - 2} & = (\lnot y_{k - 3} \lor \ell_{k - 1} \lor \ell_k) \\ & = (\boxed{\lnot 0} \lor \ell_{k - 1} \lor \ell_k) \\ & = 1. \end{align*}
      • Case 2: \(c\) is not satisfiable

        For all \(i\), \(\ell_i = 0\).

        • Case 2A: \(y_1 = 0\)

          The first clause is not satisfied:

          \begin{align*} c'_1 & = (\ell_1 \lor \ell_2 \lor y_1) \\ & = (0 \lor 0 \lor \boxed{0}) \\ & = 0 \\[1ex] \vdots & \end{align*}
        • Case 2B: \(y_1 = 1\)

          To make all clauses satisfied

          \begin{equation*} y_1 = 1 \implies y_2 = 1 \implies \cdots \implies y_{k - 3} = 1 \end{equation*}

          but then the last clause is not satisfied:

          \begin{align*} c'_1 & = (\ell_1 \lor \ell_2 \lor y_1) \\ & = (0 \lor 0 \lor \boxed{1}) \\ & = 1 \\[1ex] \vdots & \\[2ex] c'_{k - 1} & = (\lnot y_{k - 4} \lor \ell_{k - 2} \lor y_{k - 3}) \\ & = (\lnot \boxed{1} \lor 0 \lor \boxed{1}) \\ & = 1 \\[2ex] c'_{k - 2} & = (\lnot y_{k - 3} \lor \ell_{k - 1} \lor \ell_k) \\ & = (\lnot \boxed{1} \lor 0 \lor 0) \\ & = 0. \end{align*}

          \(\blacksquare\)

SATLIB

The de facto standard

collection of SAT problems for benchmarking

and research in general.

Citation

[18]

@Misc{hoos+2011,
  author       = {Holger H. Hoos and Thomas St\"{u}tzle},
  title        = {{SATLIB}},
  year         = 2011,
  edition      = {1.4.4},
  subtitle     = {The Satisfiability Library},
  publisher    = {{Darmstadt} University of Technology},
  url          = {https://www.cs.ubc.ca/~hoos/SATLIB/}
}

GSAT

A stochastic greedy local search for SAT that

repeatedly flips the variable with greatest net gain,

breaking ties randomly.

[19]

Name

A short for “greedy SAT”.

Algorithm

  1. Start with a random variable assignment.
  2. If the formula is satisfied, return the current assignment.
  3. Flip a variable that maximizes the number of satisfied clauses. †
  4. If more flips are allowed, go to step 2.
  5. If more tries are allowed, go to step 1.
  6. Return a failure.

† Ties are broken randomly.

Variants

The variants of GSAT are

HSAT, WSAT, and TSAT,

or their combinations.

Alternatives

The WalkSAT family.

Citation

[19]:

@InProceedings{selman+1992,
  author       = {Selman, Bart and Levesque, Hector and Mitchell, David},
  title        = {A new method for solving hard satisfiability problems},
  booktitle    = {Proceedings of the Tenth National Conference on Artificial
                  Intelligence},
  year         = 1992,
  series       = {AAAI'92},
  pages        = {440-446},
  publisher    = {AAAI Press},
  isbn         = 0262510634,
  abstract     = {We introduce a greedy local search procedure called GSAT for
                  solving propositional satisfiability problems. Our
                  experiments show that this procedure can be used to solve
                  hard, randomly generated problems that are an order of
                  magnitude larger than those that can be handled by more
                  traditional approaches such as the Davis-Putnam procedure or
                  resolution. We also show that GSAT can solve structured
                  satisfiability problems quickly. In particular, we solve
                  encodings of graph coloring problems, N-queens, and Boolean
                  induction. General application strategies and limitations of
                  the approach are also discussed.GSAT is best viewed as a
                  model-finding procedure. Its good performance suggests that
                  it may be advantageous to reformulate reasoning tasks that
                  have traditionally been viewed as theorem-proving problems as
                  model-finding tasks.},
  numpages     = 7,
  location     = {San Jose, California},
  url          = {https://dl.acm.org/doi/10.5555/1867135.1867203}
}

HSAT

A variant of GSAT that

breaks ties in favor of the least recently flipped variable

in the current try.

[20]

Name

A short for “historic SAT”, as in “with memory”.

Citation

[20]:

@InProceedings{gent+1993,
  author       = {Gent, Ian P. and Walsh, Toby},
  title        = {Towards an understanding of hill-climbing procedures for
                  {SAT}},
  booktitle    = {Proceedings of the Eleventh National Conference on Artificial
                  Intelligence},
  year         = 1993,
  series       = {AAAI'93},
  pages        = {28-33},
  publisher    = {AAAI Press},
  isbn         = 0262510715,
  abstract     = {Recently several local hill-climbing procedures for
                  propositional satisfiability have been proposed which are
                  able to solve large and difficult problems beyond the reach
                  of conventional algorithms like Davis-Putnam. By the
                  introduction of some new variants of these procedures, we
                  provide strong experimental evidence to support our
                  conjecture that neither greediness nor randomness is
                  important in these procedures. One of the variants introduced
                  seems to offer significant improvements over earlier
                  procedures. In addition, we investigate experimentally how
                  performance depends on their parameters. Our results suggest
                  that runtime scales less than simply exponentially in the
                  problem size.},
  numpages     = 6,
  location     = {Washington, D.C.},
  url          = {https://dl.acm.org/doi/10.5555/1867270.1867275}
}

GWSAT

A variant of GSAT that

randomly selects a variable with a positive net gain,

with the “random walk” probability,

instead of the variable with the greatest gain.

[21]

Citation

[22]:

@InProceedings{selman+1993,
  author       = {Selman, Bart and Kautz, Henry},
  title        = {Domain-independent extensions to {GSAT}},
  booktitle    = {Proceedings of the 13th International Joint Conference on
                  Artifical Intelligence - Volume 1},
  year         = 1993,
  series       = {IJCAI'93},
  pages        = {290-295},
  address      = {San Francisco, CA, USA},
  publisher    = {Morgan Kaufmann Publishers Inc.},
  subtitle     = {Solving large structured satisfiability problems},
  abstract     = {GSAT is a randomized local search procedure for solving
                  propositional satisfiability problems (Selman et
                  al. 1992). GSAT can solve hard, randomly generated problems
                  that are an order of magnitude larger than those that can be
                  handled by more traditional approaches such as the
                  Davis-Putnam procedure. GSAT also efficiently solves
                  encodings of graph coloring problems, N-queens, and Boolean
                  induction. However, GSAT does not perform as well on
                  handcrafted encodings of blocks-world planning problems and
                  formulas with a high degree of asymmetry. We present three
                  strategies that dramatically improve GSAT's performance on
                  such formulas. These strategies, in effect, manage to uncover
                  hidden structure in the formula under considerations, thereby
                  significantly extending the applicability of the GSAT
                  algorithm.},
  numpages     = 6,
  location     = {Chambery, France}
}

TSAT

A variant of GSAT that uses

a tabu list with the most recently flipped variables.

[23]

Parameterization

[23] empirically found

the optimal length of the tabu list,

given \(n\) variables, to be

\begin{equation*} 0.01875 n + 2.8125. \end{equation*}

Citation

[23]:

@InProceedings{mazure+1997,
  author       = {Mazure, Bertrand and Sa\"{i}s, Lakhdar and Gr\'{e}goire,
                  \'{E}ric},
  title        = {Tabu search for {SAT}},
  booktitle    = {Proceedings of the Fourteenth National Conference on
                  Artificial Intelligence and Ninth Conference on Innovative
                  Applications of Artificial Intelligence},
  year         = 1997,
  series       = {AAAI'97/IAAI'97},
  pages        = {281-285},
  publisher    = {AAAI Press},
  isbn         = 0262510952,
  abstract     = {In this paper, tabu search for SAT is investigated from an
                  experimental point of view. To this end, TSAT, a basic tabu
                  search algonthm for SAT, is introduced and compared With
                  Selman et al. Random Walk Strategy GSAT procedure, in short
                  RWS-GSAT. TSAT does not involve the additional stochastic
                  process of RWS-GSAT. This should facilitate the understanding
                  of why simple local search methods for SAT work. It is shown
                  that the length of the tabu list plays a critical role in the
                  performance of the algorithm. Moreover surprising properties
                  about the (experimental) optimal length of the tabu list are
                  exhibited, raising interesting issues about the nature of
                  hard random SAT problems.},
  numpages     = 5,
  location     = {Providence, Rhode Island},
  url          = {https://cdn.aaai.org/AAAI/1997/AAAI97-044.pdf}
}

WalkSAT (WSAT)

A stochastic greedy local search for SAT that

operates on a randomly chosen unsat clause,

where it flips either

either a randomly chosen variable
or the variable with the least breakcount,

depending on the “random walk” probability.

[21]

Citation

[21]:

@InProceedings{selman+1994,
  author       = {Selman, Bart and Kautz, Henry A. and Cohen, Brain},
  title        = {Noise strategies for improving local search},
  booktitle    = {Proceedings of the Twelfth AAAI National Conference on
                  Artificial Intelligence},
  year         = 1994,
  series       = {AAAI'94},
  pages        = {337-343},
  publisher    = {AAAI Press},
  abstract     = {It has recently been shown that local search is surprisingly
                  good at finding satisfying assignments for certain
                  computationally hard classes of CNF formulas. The performance
                  of basic local search methods can be further enhanced by
                  introducing mechanisms for escaping from local minima in the
                  search space. We will compare three such mechanisms:
                  simulated annealing, random noise, and a strategy called
                  "mixed random walk". We show that mixed random walk is the
                  superior strategy. We also present results demonstrating the
                  effectiveness of local search with walk for solving circuit
                  synthesis and circuit diagnosis problems. Finally, we
                  demonstrate that mixed random walk improves upon the best
                  known methods for solving MAX-SAT problems.},
  numpages     = 7,
  location     = {Seattle, Washington},
  url          = {https://dl.acm.org/doi/10.5555/2891730.2891782}
}

Alternatives

The GSAT family.

Unsat

A short for

unsatisfied

in the context of SAT.

Example use

  • “# of unsat clauses”
  • “a clause goes unsat”

Flip

A short for

a negation of a variable

in the context of SAT.

Optimization problem

Maximization problem

An optimization problem where

the objective value is maximized

to find optimal solutions.

Minimization problem

An optimization problem where

the objective value is minimized

to find optimal solutions.

Optimal solution

Optimal solution enumeration

A classical way to

compute all optimal solutions to an optimization problem

by re-parameterizing it as a constraint satisfaction problem:

  1. find an optimal solution \(y^* = \max y\)
  2. remove the objective function \(y\)
  3. add the constraint \(y = y^*\)
  4. instruct the solver to print all solutions.

Set cover

The minimization problem of selecting the

smallest universe-covering subset

of a given set of sets.

Decision version

The decision version of set cover asks,

Is there a cover smaller than a given cardinality?

Duality

Set cover is the cover-packing dual of set packing.

Complexity

The optimization and decision versions of set cover are

NP-hard and NP-complete

respectively.

Computation

Assign all parts to suppliers, the fewer the better.

Table 1: Availability of parts, per supplier.
Supplier / Part A B C D E F G H
A x   x   x      
B   x x   x x    
C       x     x  
D x       x     x
E   x   x     x  
F x   x x       x
  • Imports
    import numpy as np
    from ortools.sat.python import cp_model as cp
    
  • Input
    \begin{equation*} A = \begin{pmatrix} a_{11} & & \cdots & & a_{1n} \\ & \ddots & & & \\ \vdots & & a_{ij} & & \vdots \\ & & & \ddots & \\ a_{m1} & & \cdots & & a_{mn} \end{pmatrix} \in {\{0, 1\}}^{m \times n} \end{equation*}
    table = np.array(TABLE)[2:, 1:]
    A = np.where(table == '', 0, 1)
    A
    
    1 0 1 0 1 0 0 0
    0 1 1 0 1 1 0 0
    0 0 0 1 0 0 1 0
    1 0 0 0 1 0 0 1
    0 1 0 1 0 0 1 0
    1 0 1 1 0 0 0 1
    m = A.shape[0]
    n = A.shape[1]
    (m, n)
    
    (6, 8)
    
  • Model
    model = cp.CpModel()
    
  • Variables

    The decision variables represent the inclusion of each of the \(m\) suppliers,

    \begin{equation*} x \in {\{ 0, 1 \}}^m. \end{equation*}
    x = [model.new_bool_var(name="x_%d" % i) for i in range(m)]
    
  • Constraints

    Every part is covered by some supplier,

    \begin{equation*} \forall j \sum_{i = 1}^{m} a_{ij} x_i \geq 1. \end{equation*}
    for j in range(n):
        model.add(sum(A[i, j] * x[i] for i in range(m)) >= 1)
    
  • Objective

    Define the objective function as

    \begin{equation*} y = \sum_{i = 1}^{m} x_i \end{equation*}

    and the objective value as

    \begin{equation*} y^* = \min y. \end{equation*}
    y = sum(x[i] for i in range(m))
    model.minimize(y)
    
  • Solution

    Find the optimum solution.

    solver = cp.CpSolver()
    status = solver.solve(model)
    assert status == cp.OPTIMAL
    y_star = int(solver.objective_value)
    print("y = %s" % y_star)
    print("x = %s" % [solver.value(x_i) for x_i in x])
    
    y = 3
    x = [0, 1, 1, 0, 0, 1]
    
  • Re-parameterize

    Prepare for optimal solution enumeration.

    model.clear_objective()
    model.add(sum(x[i] for i in range(m)) == y_star)
    
  • Find all optimal solutions
    class SolutionPrinter(cp.CpSolverSolutionCallback):
        def __init__(self):
            cp.CpSolverSolutionCallback.__init__(self)
            self.__solution_count = 0
    
        def on_solution_callback(self) -> None:
            self.__solution_count += 1
            if self.__solution_count > 1: print()
            print("Solution #%d" % self.__solution_count)
            print("x = %s" % [self.value(x_i) for x_i in x])
            print("y = %s" % y_star)
    
    printer = SolutionPrinter()
    solver.parameters.enumerate_all_solutions = True
    status = solver.solve(model, printer)
    
    Solution #1
    x = [0, 1, 1, 1, 0, 0]
    y = 3
    
    Solution #2
    x = [0, 1, 1, 0, 0, 1]
    y = 3
    
    Solution #3
    x = [0, 1, 0, 0, 1, 1]
    y = 3
    
    Solution #4
    x = [0, 1, 0, 1, 1, 0]
    y = 3
    

Synonyms

  • Minimum set cover

Set packing

The optimization problem of selecting the

largest disjoint subset

of a given set of sets.

Intuition

Maximizing the number of performed tasks, where

  • each subset is a task
  • each subset member is a single-task performer.

Decision version

The decision version of set packing asks,

Is there a packing larger than a given cardinality?

Duality

Set packing is the cover-packing dual of set cover.

Complexity

The optimization and decision versions of set packing are

NP-hard and NP-complete

respectively.

Computation

Assign as many tasks as possible, at most one per person.

Table 2: Capability of performing tasks, per person.
Task / Person A B C D E F G H
A x   x   x      
B   x x   x x    
C       x     x  
D x       x     x
E   x   x     x  
F x   x x       x
  • Imports
    import numpy as np
    from ortools.sat.python import cp_model as cp
    
  • Input
    \begin{equation*} A = \begin{pmatrix} a_{11} & & \cdots & & a_{1n} \\ & \ddots & & & \\ \vdots & & a_{ij} & & \vdots \\ & & & \ddots & \\ a_{m1} & & \cdots & & a_{mn} \end{pmatrix} \in {\{0, 1\}}^{m \times n} \end{equation*}
    table = np.array(TABLE)[2:, 1:]
    A = np.where(table == '', 0, 1)
    A
    
    1 0 1 0 1 0 0 0
    0 1 1 0 1 1 0 0
    0 0 0 1 0 0 1 0
    1 0 0 0 1 0 0 1
    0 1 0 1 0 0 1 0
    1 0 1 1 0 0 0 1
    m = A.shape[0]
    n = A.shape[1]
    (m, n)
    
    (6, 8)
    
  • Model
    model = cp.CpModel()
    
  • Variables

    The decision variables represent the inclusion of each of the \(m\) suppliers,

    \begin{equation*} x \in {\{ 0, 1 \}}^m. \end{equation*}
    x = [model.new_bool_var(name="x_%d" % i) for i in range(m)]
    
  • Constraints

    Every part is covered by some supplier,

    \begin{equation*} \forall j \sum_{i = 1}^{m} a_{ij} x_i \leq 1. \end{equation*}
    for j in range(n):
        model.add(sum(A[i, j] * x[i] for i in range(m)) <= 1)
    
  • Objective

    Define the objective function

    \begin{equation*} y = \sum_{i = 1}^{m} x_i \end{equation*}

    and the objective value

    \begin{equation*} y^* = \max y. \end{equation*}
    y = sum(x[i] for i in range(m))
    model.maximize(y)
    
  • Solution

    Find the optimum solution.

    solver = cp.CpSolver()
    status = solver.solve(model)
    assert status == cp.OPTIMAL
    y_star = int(solver.objective_value)
    print("y = %s" % y_star)
    print("x = %s" % [solver.value(x_i) for x_i in x])
    
    y = 2
    x = [0, 1, 1, 0, 0, 0]
    
  • Re-parameterize

    Prepare for optimal solution enumeration.

    model.clear_objective()
    model.add(y == y_star)
    solver.parameters.enumerate_all_solutions = True
    
  • Find all optimal solutions
    class SolutionPrinter(cp.CpSolverSolutionCallback):
        def __init__(self):
            cp.CpSolverSolutionCallback.__init__(self)
            self.__solution_count = 0
    
        def on_solution_callback(self) -> None:
            self.__solution_count += 1
            if self.__solution_count > 1: print()
            print("Solution #%d" % self.__solution_count)
            print("x = %s" % [solver.value(x_i) for x_i in x])
            print("y = %s" % y_star)
    
    printer = SolutionPrinter()
    status = solver.solve(model, printer)
    
    Solution #1
    x = [0, 0, 1, 1, 0, 0]
    y = 2
    
    Solution #2
    x = [0, 0, 1, 1, 0, 0]
    y = 2
    
    Solution #3
    x = [0, 0, 1, 1, 0, 0]
    y = 2
    
    Solution #4
    x = [0, 0, 1, 1, 0, 0]
    y = 2
    
    Solution #5
    x = [0, 0, 1, 1, 0, 0]
    y = 2
    

Synonyms

  • Maximum set packing

Zebra puzzle

There are five houses.

  1. The Englishman lives in the red house.
  2. The Spaniard owns the dog.
  3. Coffee is drunk in the green house.
  4. The Ukrainian drinks tea.
  5. The green house is immediately to the right of the ivory house.
  6. The Old Gold smoker owns snails.
  7. Kools are smoked in the yellow house.
  8. Milk is drunk in the middle house.
  9. The Norwegian lives in the first house.
  10. The man who smokes Chesterfields lives in the house next to the man with the fox.
  11. Kools are smoked in the house next to the house where the horse is kept.
  12. The Lucky Strike smoker drinks orange juice.
  13. The Japanese smokes Parliaments.
  14. The Norwegian lives next to the blue house.

Who drinks water? Who owns the zebra?

— Life International, December 17, 1962

Computation

Find all solutions of the Zebra puzzle using constraint programming,

limiting all constraints to conjunctive normal form.

  • Imports
    from enum import IntEnum
    from ortools.sat.python.cp_model import CpModel, CpSolver, CpSolverSolutionCallback
    
  • Model
    model = CpModel()
    
  • Attributes
    Animal Drink Cigarettes Color Nation
    dog coffee Chesterfield blue English
    fox orange juice Kool green Japanese
    horse milk Lucky Strike ivory Norwegian
    snails tea Old Gold red Spanish
    zebra water Parliament yellow Ukrainian
    attributes = ["ANIMAL", "DRINK", "CIGARETTES", "COLOR", "NATION"]
    animals = ["DOG", "FOX", "HORSE", "SNAILS", "ZEBRA"]
    drinks = ["COFFEE", "ORANGE_JUICE", "MILK", "TEA", "WATER"]
    cigarettes = ["CHESTERFIELD", "KOOL", "LUCKY_STRIKE", "OLD_GOLD", "PARLIAMENT"]
    colors = ["BLUE", "GREEN", "IVORY", "RED", "YELLOW"]
    nations = ["ENGLISH", "JAPANESE", "NORWEGIAN", "SPANISH", "UKRAINIAN"]
    
    Attribute = IntEnum("Attribute", attributes, start=0)
    Animal = IntEnum("Animal", animals, start=0)
    Drink = IntEnum("Drink", drinks, start=0)
    Cigarettes = IntEnum("Cigarettes", cigarettes, start=0)
    Color = IntEnum("Color", colors, start=0)
    Nation = IntEnum("Nation", nation, start=0)
    
  • Decision variables
    • \(1 \leq h \leq l\) with \(l = 5\) attributes
    • \(1 \leq i \leq m\) with \(m = 5\) houses
    • \(1 \leq j \leq n\) with \(n = 5\) attribute values
    l, m, n = 5, 5, 5
    
    \begin{equation*} \underset{1 \leq h \leq \ell}{X_h} = \begin{pmatrix} x_{h11} & & \cdots & & x_{h1n} \\ & \ddots & & & \\ \vdots & & x_{hij} & & \vdots \\ & & & \ddots & \\ x_{hm1} & & \cdots & & x_{hmn} \end{pmatrix} \in {\{0, 1\}}^{m \times n} \end{equation*}
    x = {
        (h, i, j): model.new_bool_var("x_{%d, %d, %d}" % (h + 1, i + 1, j + 1))
        for h in range(l)
        for i in range(m)
        for j in range(n)
    }
    
  • Constraints: Exactly one attribute value per house

    Knowing each row of every \(X_h\) must contain exactly one \(1\)

    \begin{equation*} \forall h \forall j \sum_{i = 1}^{m} x_{hij} = 1 \end{equation*}

    derive the conjunctive normal form

    \begin{align*} \forall h \forall j \sum_{i = 1}^{m} x_{hij} = 1 \quad \iff \quad & \forall h \forall j \Bigg( \sum_{i = 1}^{m} x_{hij} \leq 1 \Bigg) \land \Bigg( \sum_{i = 1}^{m} x_{hij} \geq 1 \Bigg) \\[1ex] \forall h \forall j \sum_{i = 1}^{m} x_{hij} \geq 1 \quad \iff \quad & \forall h \forall j (x_{h1j} \lor x_{h2j} \lor x_{h3j} \lor \cdots \lor x_{hmj}) \\[1ex] \forall h \forall j \sum_{i = 1}^{m} x_{hij} \leq 1 \quad \iff \quad & \forall h \forall j \forall i \forall q < i \> (\lnot x_{hij} \lor \lnot x_{hqj}) \end{align*}

    and compute

    for h in range(l):
        for j in range(n):
            model.add_bool_or(x[h, i, j] for i in range(m))
            for i1 in range(m):
                for i2 in range(i1):
                    model.add_bool_or(x[h, i1, j].Not(), x[h, i2, j].Not())
    
  • Constraints: Exactly one house per attribute value

    Knowing each column of every \(X_h\) must contain exactly one \(1\),

    \begin{equation*} \forall h \forall i \sum_{j = 1}^{n} x_{hij} = 1 \end{equation*}

    derive the conjunctive normal form as in the previous step

    \begin{align*} \forall h \forall i \quad & (x_{hi1} \lor x_{hi2} \lor x_{hi3} \lor \cdots \lor x_{hin}) \\ \forall h \forall i \forall j \forall q < j \quad & (\lnot x_{hij} \lor \lnot x_{hiq}) \\ \end{align*}

    and compute

    for h in range(l):
        for i in range(m):
            model.add_bool_or(x[h, i, j] for j in range(n))
            for j1 in range(n):
                for j2 in range(j1):
                    model.add_bool_or(x[h, i, j1].Not(), x[h, i, j2].Not())
    
  • Constraints: Fact 1

    The Englishman lives in the red house.

    Using

    \begin{align*} a & = x_{5, i, 1} \\ b & = x_{4, i, 4} \\ \end{align*}

    derive the conjunctive normal form by the definition of conditional

    \begin{equation*} \forall i \quad a \implies b \equiv (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.NATION, i, Nation.ENGLISH]
        b = x[Attribute.COLOR, i, Color.RED]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 2

    The Spaniard owns the dog.

    Using

    \begin{align*} a & = x_{5, i, 4} \\ b & = x_{1, i, 1} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.NATION, i, Nation.SPANISH]
        b = x[Attribute.ANIMAL, i, Animal.DOG]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 3

    Coffee is drunk in the green house.

    Using

    \begin{align*} a & = x_{2, i, 1} \\ b & = x_{4, i, 2} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.DRINK, i, Drink.COFFEE]
        b = x[Attribute.COLOR, i, Color.GREEN]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 4

    The Ukrainian drinks tea.

    Using

    \begin{align*} a & = x_{5, i, 5} \\ b & = x_{2, i, 4} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.NATION, i, Nation.UKRAINIAN]
        b = x[Attribute.DRINK, i, Drink.TEA]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 5

    The green house is immediately to the right of the ivory house.

    Using

    \begin{align*} a & = x_{4, i, 2} \\ b & = x_{4, (i - 1), 3} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1 but with \(\forall i > 1\)

    \begin{equation*} \forall i > 1 \quad (\lnot a \lor b) \end{equation*}

    noting that

    • the left-most house (\(i = 1\)) cannot be green
      as the ivory house would then be out of bounds
    • the right-most house (\(i = m\)) cannot be ivory
      as the green house would then be out of bounds
    \begin{equation*} (\lnot x_{4, 1, 2}) \land (\lnot x_{4, m, 3}) \end{equation*}

    and compute

    for i in range(m)[1:]:
        a = x[Attribute.COLOR, i, Color.GREEN]
        b = x[Attribute.COLOR, i - 1, Color.IVORY]
        model.add_bool_or(a.Not(), b)
    
    model.add_bool_or(x[Attribute.COLOR, 0, Color.GREEN].Not())
    model.add_bool_or(x[Attribute.COLOR, m - 1, Color.IVORY].Not())
    
  • Constraints: Fact 6

    The Old Gold smoker owns snails.

    Using

    \begin{align*} a & = x_{3, i, 4} \\ b & = x_{1, i, 4} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.CIGARETTES, i, Cigarettes.OLD_GOLD]
        b = x[Attribute.ANIMAL, i, Animal.SNAILS]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 7

    Kools are smoked in the yellow house.

    Using

    \begin{align*} a & = x_{3, i, 2} \\ b & = x_{4, i, 5} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.CIGARETTES, i, Cigarettes.KOOL]
        b = x[Attribute.COLOR, i, Color.YELLOW]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 8

    Milk is drunk in the middle house.

    Using

    \begin{equation*} a = x_{2, 3, 3} \end{equation*}

    derive the conjunctive normal form

    \begin{equation*} (a) \end{equation*}

    and compute

    a = x[Attribute.DRINK, 2, Drink.MILK]
    model.add_bool_or(a)
    
  • Constraints: Fact 9

    The Norwegian lives in the first house.

    Using

    \begin{equation*} a = x_{5, 1, 3} \end{equation*}

    derive the conjunctive normal form

    \begin{equation*} (a) \end{equation*}

    and compute

    a = x[Attribute.NATION, 0, Nation.NORWEGIAN]
    model.add_bool_or(a)
    
  • Constraints: Fact 10

    The man who smokes Chesterfields lives in the house next to the man with the fox.

    Using

    \begin{align*} a & = x_{3, i, 1} \\ b & = x_{1, (i + 1), 2} \\ c & = x_{1, (i - 1), 2} \\ \end{align*}

    derive the conjunctive normal form by the definition of conditional

    \begin{equation*} \forall i \quad \begin{cases} a \implies b \equiv (\lnot a \lor b) & i = 1 \\ a \implies b \lor c \equiv \lnot a \lor (b \lor c) \equiv (\lnot a \lor b \lor c) & 1 < i < m \\ a \implies c \equiv (\lnot a \lor c) & i = m \\ \end{cases} \end{equation*}

    and compute

    for i in range(m):
    
        # NOTE Use `x.get(...)' instead of `x[...]' to skip bound checking.
        a = x.get((Attribute.CIGARETTES, i, Cigarettes.CHESTERFIELD))
        b = x.get((Attribute.ANIMAL, i + 1, Animal.FOX))
        c = x.get((Attribute.ANIMAL, i - 1, Animal.FOX))
    
        if i == 0:
            model.add_bool_or(a.Not(), b)
        elif 0 < i < m - 1:
            model.add_bool_or(a.Not(), b, c)
        elif i == m - 1:
            model.add_bool_or(a.Not(), c)
    
  • Constraints: Fact 11

    Kools are smoked in the house next to the house where the horse is kept.

    Using

    \begin{align*} a & = x_{3, i, 2} \\ b & = x_{1, (i + 1), 3} \\ c & = x_{1, (i - 1), 3} \\ \end{align*}

    derive the conjunctive normal form as in Fact 10

    \begin{equation*} \forall i \quad \begin{cases} (\lnot a \lor b) & i = 1 \\ (\lnot a \lor b \lor c) & 1 < i < m \\ (\lnot a \lor c) & i = m \\ \end{cases} \end{equation*}

    and compute

    for i in range(m):
    
        # NOTE Use `x.get(...)' instead of `x[...]' to skip bound checking.
        a = x.get((Attribute.CIGARETTES, i, Cigarettes.KOOL))
        b = x.get((Attribute.ANIMAL, i + 1, Animal.HORSE))
        c = x.get((Attribute.ANIMAL, i - 1, Animal.HORSE))
    
        if i == 0:
            model.add_bool_or(a.Not(), b)
        elif 0 < i < m - 1:
            model.add_bool_or(a.Not(), b, c)
        elif i == m - 1:
            model.add_bool_or(a.Not(), c)
    
  • Constraints: Fact 12

    The Lucky Strike smoker drinks orange juice.

    Using

    \begin{align*} a & = x_{3, i, 3} \\ b & = x_{2, i, 2} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.CIGARETTES, i, Cigarettes.LUCKY_STRIKE]
        b = x[Attribute.DRINK, i, Drink.ORANGE_JUICE]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 13

    The Japanese smokes Parliaments.

    Using

    \begin{align*} a & = x_{5, i, 2} \\ b & = x_{3, i, 5} \\ \end{align*}

    derive the conjunctive normal form as in Fact 1

    \begin{equation*} \forall i \quad (\lnot a \lor b) \end{equation*}

    and compute

    for i in range(m):
        a = x[Attribute.NATION, i, Nation.JAPANESE]
        b = x[Attribute.CIGARETTES, i, Cigarettes.PARLIAMENT]
        model.add_bool_or(a.Not(), b)
    
  • Constraints: Fact 14

    The Norwegian lives next to the blue house.

    Using

    \begin{align*} a & = x_{5, i, 3} \\ b & = x_{4, (i + 1), 1} \\ c & = x_{4, (i - 1), 1} \\ \end{align*}

    derive the conjunctive normal form as in Fact 10

    \begin{equation*} \forall i \quad \begin{cases} (\lnot a \lor b) & i = 1 \\ (\lnot a \lor b \lor c) & 1 < i < m \\ (\lnot a \lor c) & i = m \\ \end{cases} \end{equation*}

    and compute

    for i in range(m):
    
        # NOTE Use `x.get(...)' instead of `x[...]' to skip bound checking.
        a = x.get((Attribute.NATION, i, Nation.NORWEGIAN))
        b = x.get((Attribute.COLOR, i + 1, Color.BLUE))
        c = x.get((Attribute.COLOR, i - 1, Color.BLUE))
    
        if i == 0:
            model.add_bool_or(a.Not(), b)
        elif 0 < i < m - 1:
            model.add_bool_or(a.Not(), b, c)
        elif i == m - 1:
            model.add_bool_or(a.Not(), c)
    
  • Solution

    Who drinks water?

    1. \(i\)-th house such that \(x_{2, i, 5} = 1\)
    2. \(j\)-th nation such that \(x_{5, i, j} = 1\)

    Who owns the zebra?

    • \(i\)-th house such that \(x_{1, i, 5} = 1\)
    • \(j\)-th nation such that \(x_{5, i, j} = 1\)
    class SolutionPrinter(CpSolverSolutionCallback):
        def __init__(self):
            CpSolverSolutionCallback.__init__(self)
            self.__solution_count = 0
    
        def __solution(self, attribute, value):
            i = next(i for i in range(m) if self.value(x[attribute, i, value]))
            j = next(j for j in range(n) if self.value(x[Attribute.NATION, i, j]))
            return Nation(j).name
    
        def on_solution_callback(self) -> None:
            self.__solution_count += 1
            w = self.__solution(Attribute.DRINK, Drink.WATER)
            z = self.__solution(Attribute.ANIMAL, Animal.ZEBRA)
            print("Solution #%d" % self.__solution_count)
            print("Who drinks water? %s." % w)
            print("Who owns the zebra? %s." % z)
    
    solver = CpSolver()
    solver.SearchForAllSolutions(model, SolutionPrinter())
    
    Solution #1
    Who drinks water? NORWEGIAN.
    Who owns the zebra? JAPANESE.
    

Traveling salesman problem (TSP)

Intuition

Given pair-wise distances between cities,

find the shortest tour that visits each city exactly once

and returns back to the starting point.

Decision version

The decision version of the traveling salesman problem asks,

Is there a tour shorter than a given length?

Is there a solution that does not exceed a given threshold?

Complexity

The optimization and decision versions of the traveling salesman problem are

NP-hard and NP-complete

respectively.

Sample instances

@Misc{reinelt-,
  author       = {Gerhard Reinelt},
  title        = {{TSPLIB}},
  subtitle     = {A library of sample instances for the {TSP} (and related
                  problems)},
  url          = {http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/}
}

Sample instance (pr76)

Instance pr76 from TSPLIB [24]:

City \(x\) \(y\)
1 3600 2300
2 3100 3300
3 4700 5750
4 5400 5750
5 5608 7103
6 4493 7102
7 3600 6950
8 3100 7250
9 4700 8450
10 5400 8450
11 5610 10053
12 4492 10052
13 3600 10800
14 3100 10950
15 4700 11650
16 5400 11650
17 6650 10800
18 7300 10950
19 7300 7250
20 6650 6950
21 7300 3300
22 6650 2300
23 5400 1600
24 8350 2300
25 7850 3300
26 9450 5750
27 10150 5750
28 10358 7103
29 9243 7102
30 8350 6950
31 7850 7250
32 9450 8450
33 10150 8450
34 10360 10053
35 9242 10052
36 8350 10800
37 7850 10950
38 9450 11650
39 10150 11650
40 11400 10800
41 12050 10950
42 12050 7250
43 11400 6950
44 12050 3300
45 11400 2300
46 10150 1600
47 13100 2300
48 12600 3300
49 14200 5750
50 14900 5750
51 15108 7103
52 13993 7102
53 13100 6950
54 12600 7250
55 14200 8450
56 14900 8450
57 15110 10053
58 13992 10052
59 13100 10800
60 12600 10950
61 14200 11650
62 14900 11650
63 16150 10800
64 16800 10950
65 16800 7250
66 16150 6950
67 16800 3300
68 16150 2300
69 14900 1600
70 19800 800
71 19800 10000
72 19800 11900
73 19800 12200
74 200 12200
75 200 1100
76 200 800

pr76 set from http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/

76-city problem (Padberg/Rinaldi)

76
1 3600 2300
2 3100 3300
3 4700 5750
4 5400 5750
5 5608 7103
6 4493 7102
7 3600 6950
8 3100 7250
9 4700 8450
10 5400 8450
11 5610 10053
12 4492 10052
13 3600 10800
14 3100 10950
15 4700 11650
16 5400 11650
17 6650 10800
18 7300 10950
19 7300 7250
20 6650 6950
21 7300 3300
22 6650 2300
23 5400 1600
24 8350 2300
25 7850 3300
26 9450 5750
27 10150 5750
28 10358 7103
29 9243 7102
30 8350 6950
31 7850 7250
32 9450 8450
33 10150 8450
34 10360 10053
35 9242 10052
36 8350 10800
37 7850 10950
38 9450 11650
39 10150 11650
40 11400 10800
41 12050 10950
42 12050 7250
43 11400 6950
44 12050 3300
45 11400 2300
46 10150 1600
47 13100 2300
48 12600 3300
49 14200 5750
50 14900 5750
51 15108 7103
52 13993 7102
53 13100 6950
54 12600 7250
55 14200 8450
56 14900 8450
57 15110 10053
58 13992 10052
59 13100 10800
60 12600 10950
61 14200 11650
62 14900 11650
63 16150 10800
64 16800 10950
65 16800 7250
66 16150 6950
67 16800 3300
68 16150 2300
69 14900 1600
70 19800 800
71 19800 10000
72 19800 11900
73 19800 12200
74 200 12200
75 200 1100
76 200 800
  • Optimal tour for pr76 (108159):
    import numpy as np
    import traveling_salesman as ts
    
    solution = np.array([1, 76, 75, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
                         74, 15, 16, 17, 18, 37, 36, 38, 39, 40, 34, 35, 33, 32,
                         29, 30, 31, 19, 20, 26, 27, 28, 43, 42, 54, 53, 52, 55,
                         56, 57, 58, 59, 60, 41, 61, 62, 63, 64, 73, 72, 71, 65,
                         66, 51, 49, 50, 67, 70, 68, 69, 47, 48, 44, 45, 46, 24,
                         25, 21, 22, 23])
    
    # tour = ???
    
    # TODO Convert solution to tour.
    
    instance = ts.read_instance('bib/pr76.tsp')
    distances = ts.distances(instance)
    distance = ts.distance(tour, distances)
    
    return f"Tour: \n {tour}" "\n\n" f"Distance: {distance}"
    

Computation in Python

  • Implement
    • traveling_salesman
      import sys
      
      import numpy as np
      import scipy as sp
      
      None
      
    • traveling_salesman.read_instance
      def read_instance(file_name):
      
          # Count the cities.
          count = None
          with open(file_name) as file:
              count = int(file.readline())
      
          # Load the city identifiers and coordinates.
          instance = np.loadtxt(
              file_name, dtype=int, comments=None, skiprows=1, max_rows=count
          )
      
          # TODO Validate the result.
      
          return instance
      
      None
      
    • traveling_salesman.distances
      def distances(instance):
      
          coordinates = instance[:, 1:]
      
          # Measure the Euclidean distances between all cities.
          distances = sp.spatial.distance_matrix(coordinates, coordinates)
      
          # Round the distances and convert them to integers.
          integral_distances = np.rint(distances).astype(int)
      
          return integral_distances
      
    • traveling_salesman.distance
      def distance(tour, distances):
      
          destinations = np.roll(tour, 1)
          distances_ = distances[tour, destinations]
          distance = np.sum(distances_)
      
          return distance
      
    • traveling_salesman.tour_random
      def tour_random(instance, distances, random):
          """Return indices for a random tour, and its distance"""
      
          tour = np.arange(len(instance))
      
          random.shuffle(tour)
      
          distance1 = distance(tour, distances)
      
          return tour, distance1
      
    • traveling_salesman.solution
      def solution(tour, instance):
          """Convert tour indices to city identifiers."""
      
          cities = instance[:, 0]
          solution = cities[tour]
      
          return solution
      
    • traveling_salesman.tour
      def tour(solution, instance):
          """Convert a solution into a tour."""
      
          cities = instance[:, 0]
      
          a = np.array([10,20,30])
      
          # solution = cities[tour]
      
          return solution
      
  • Use traveling_salesman.tour_random
    import numpy as np
    import traveling_salesman as ts
    
    instance = ts.read_instance('pr76.tsp')
    distances = ts.distances(instance)
    random = np.random.default_rng(0)
    tour, distance = ts.tour_random(instance, distances, random)
    solution = ts.solution(tour, instance)
    
    return f"Solution: \n {solution}" "\n\n" f"Distance: {distance:.2E}"
    
    Solution:
     [ 6 65 62 17 59 40 36 72 46  9 48 31 73 63  5 71 44 20 21 51 28 43 24 35
     11 54  3 75 61 76 14 12 18 53 29 37 19 45  4  2 67 25 22  1  7 23 27 38
     74 66 56 41 13 16 50 33 26 47 15 10 39 68 58 52 32 69 49  8 64 70 60 55
     30 42 57 34]
    
    Distance: 5.43E+05
    
  • Use traveling_salesman.tour_nearest_neighbor
    import traveling_salesman as ts
    
    instance = ts.read_instance('pr76.tsp')
    distances = ts.distances(instance)
    tour, distance = ts.tour_nearest_neighbor(instance, distances)
    solution = ts.solution(tour, instance)
    
    return f"Solution: \n {solution}" "\n\n" f"Distance: {distance:.2E}"
    
    Solution:
     [65 66 51 52 53 54 42 43 28 29 30 31 19 20  5  6  7  8  9 10 11 12 13 14
     15 16 17 18 37 36 35 34 40 41 60 59 58 57 63 64 62 61 55 56 50 49 48 44
     45 46 24 25 21 22 23  1  2  3  4 26 27 33 32 38 39 72 73 71 67 68 69 47
     70 76 75 74]
    
    Distance: 1.57E+05
    

Computation: Python, Nearest neighbor heuristic (NN)

  • Imports
    import sys
    
    import numpy as np
    import scipy as sp
    
  • Input

    The distance matrix

    \begin{equation*} D \in \mathbb{Z}^{n \times n}, \end{equation*}

    constructed as follows:

    1. load the instance pr76 from TSPLIB [24]
    2. measure the Euclidean distances between all cities
    3. round the distances and convert them to integers
    n = len(PR76)
    c = np.array(PR76)[:, 1:]
    d_float = sp.spatial.distance_matrix(c, c)
    d = np.rint(d_float).astype(int)
    
    Table 3: Instance PR76 from TSPLIB [24]
    City x y
    1 3600 2300
    2 3100 3300
    3 4700 5750
    4 5400 5750
    5 5608 7103
    6 4493 7102
    7 3600 6950
    8 3100 7250
    9 4700 8450
    10 5400 8450
    11 5610 10053
    12 4492 10052
    13 3600 10800
    14 3100 10950
    15 4700 11650
    16 5400 11650
    17 6650 10800
    18 7300 10950
    19 7300 7250
    20 6650 6950
    21 7300 3300
    22 6650 2300
    23 5400 1600
    24 8350 2300
    25 7850 3300
    26 9450 5750
    27 10150 5750
    28 10358 7103
    29 9243 7102
    30 8350 6950
    31 7850 7250
    32 9450 8450
    33 10150 8450
    34 10360 10053
    35 9242 10052
    36 8350 10800
    37 7850 10950
    38 9450 11650
    39 10150 11650
    40 11400 10800
    41 12050 10950
    42 12050 7250
    43 11400 6950
    44 12050 3300
    45 11400 2300
    46 10150 1600
    47 13100 2300
    48 12600 3300
    49 14200 5750
    50 14900 5750
    51 15108 7103
    52 13993 7102
    53 13100 6950
    54 12600 7250
    55 14200 8450
    56 14900 8450
    57 15110 10053
    58 13992 10052
    59 13100 10800
    60 12600 10950
    61 14200 11650
    62 14900 11650
    63 16150 10800
    64 16800 10950
    65 16800 7250
    66 16150 6950
    67 16800 3300
    68 16150 2300
    69 14900 1600
    70 19800 800
    71 19800 10000
    72 19800 11900
    73 19800 12200
    74 200 12200
    75 200 1100
    76 200 800
  • TODO Search: Use Boolean vector instead of copying

    A greedy algorithm that always travels to the nearest city.

    # Allocate memory.
    x = np.empty(n, dtype=int)
    
    # Start somewhere.
    x[0] = random.randint(0, n - 1)
    
    # Distance.
    y = 0
    
    # Visit all cities.
    for i in range(1, n):
    
        # Find the relevant distances.
        here = x[i - 1]
        distances_here = np.copy(d[here])
    
        # Exclude the visited cities.
        visited = x[:i]
        distances_here[visited] = sys.maxsize  # HACK No int NaN.
    
        # Find the closes city.
        there = np.argmin(distances_here)
    
        # Travel there.
        y += distances_here[there]
        x[i] = there
    
    # Return to the starting city.
    here = x[-1]
    y += d[here][x[0]]
    
    None
    
  • Postcondition: Permutation

    Make sure the output is a permutation of the input.

    np.array_equal(np.sort(x), np.arange(n))
    
  • Postcondition: Total distance

    Make sure the total distance is correct.

    assert y == np.sum(d[x, np.roll(x, 1)])
    
  • Solution
    print("x = %s" % x)
    print("y = %d" % y)
    
    x = [22 21 20 24 23 45 44 43 47 46 68 67 66 49 48 51 52 53 41 42 27 28 29 30
     18 19  4  5  6  7  8  9 10 11 12 13 14 15 16 17 36 35 34 33 39 40 59 58
     57 56 62 63 61 60 54 55 50 65 64 70 71 72 38 37 31 32 26 25  3  2  1  0
     74 75 73 69]
    y = 148816
    

Computation: Python, SAT + NN, permutation

  • Imports
    import numpy as np
    import scipy as sp
    from ortools.sat.python import cp_model as cp
    
  • Input

    The distance matrix

    \begin{equation*} D \in \mathbb{Z}^{n \times n}, \end{equation*}

    constructed as follows:

    1. load the instance pr76 from TSPLIB [24]
    2. measure the Euclidean distances between all cities
    3. round the distances and convert them to integers
    n = len(PR76)
    c = np.array(PR76)[:, 1:]
    d_float = sp.spatial.distance_matrix(c, c)
    d = np.rint(d_float).astype(int)
    
    Table 4: Instance PR76 from TSPLIB [24]
    City x y
    1 3600 2300
    2 3100 3300
    3 4700 5750
    4 5400 5750
    5 5608 7103
    6 4493 7102
    7 3600 6950
    8 3100 7250
    9 4700 8450
    10 5400 8450
    11 5610 10053
    12 4492 10052
    13 3600 10800
    14 3100 10950
    15 4700 11650
    16 5400 11650
    17 6650 10800
    18 7300 10950
    19 7300 7250
    20 6650 6950
    21 7300 3300
    22 6650 2300
    23 5400 1600
    24 8350 2300
    25 7850 3300
    26 9450 5750
    27 10150 5750
    28 10358 7103
    29 9243 7102
    30 8350 6950
    31 7850 7250
    32 9450 8450
    33 10150 8450
    34 10360 10053
    35 9242 10052
    36 8350 10800
    37 7850 10950
    38 9450 11650
    39 10150 11650
    40 11400 10800
    41 12050 10950
    42 12050 7250
    43 11400 6950
    44 12050 3300
    45 11400 2300
    46 10150 1600
    47 13100 2300
    48 12600 3300
    49 14200 5750
    50 14900 5750
    51 15108 7103
    52 13993 7102
    53 13100 6950
    54 12600 7250
    55 14200 8450
    56 14900 8450
    57 15110 10053
    58 13992 10052
    59 13100 10800
    60 12600 10950
    61 14200 11650
    62 14900 11650
    63 16150 10800
    64 16800 10950
    65 16800 7250
    66 16150 6950
    67 16800 3300
    68 16150 2300
    69 14900 1600
    70 19800 800
    71 19800 10000
    72 19800 11900
    73 19800 12200
    74 200 12200
    75 200 1100
    76 200 800
  • Model
    model = cp.CpModel()
    
  • Decision variables

    A permutation of the cities, forming a tour,

    \begin{equation*} x \in \mathbb{N}^{n} \end{equation*}
    x = [model.new_int_var(0, n - 1, f"x_{i}") for i in range(n)]
    
  • Auxiliary variables: Subscripts
    # Create auxiliary variables for subscripts with flattened distance indexes.
    s = [model.new_int_var(0, (n * n) - 1, f"s_{i}") for i in range(n)]
    
    # Constrain the values of the subscripts.
    for i in range(n):
        j = i + 1 if i < n - 1 else 0 # close the cycle
        model.add(s[i] == x[i] * n + x[j])
    
  • Auxiliary variables: Terms
    # Create auxiliary variables for terms.
    t = [model.new_int_var(0, d.max(), f"d_{i}") for i in range(n)]
    
    # Constrain the values of the terms.
    d_flat = d.flatten().tolist()
    for i in range(n):
        model.add_element(s[i], d_flat, t[i])
    
  • Hint
    x_ = [22, 21, 20, 24, 23, 45, 44, 43, 47, 46, 68, 67, 66, 49, 48, 51, 52, 53, 41, 42, 27, 28, 29, 30
    , 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 36, 35, 34, 33, 39, 40, 59, 58
    , 57, 56, 62, 63, 61, 60, 54, 55, 50, 65, 64, 70, 71, 72, 38, 37, 31, 32, 26, 25, 3, 2, 1, 0
    , 74, 75, 73, 69]
    
    for i in range(n):
        j = (i + 1) % n
        model.add_hint(x[i], x_[i])
        model.add_hint(s[i], x_[i] * n + x_[j])
        model.add_hint(t[i], d[x_[i]][x_[j]])
    
    None
    
  • Constraints: Permutation

    Each city must be visited exactly once.

    model.add_all_different(x)
    
  • Constraints: Rotational symmetry

    Fix the first city to eliminate all rotations.

    model.add(x[0] == x_[0])
    

    N.B. There is also a reversal symmetry that is harder to define.

  • Objective

    Minimize the tour length.

    \begin{align*} y = \sum_{i = 1}^{n - 1} d_{x_i, x_{i + 1}} + d_{x_n, x_1} \end{align*}

    N.B.

    • Indexing d with x[i] must be done with add_element.
    • Indexing d with add_element requires flattening of d.
    # Set the objective function.
    model.minimize(sum(t))
    
  • Solution

    Find a solution in the given time.

    solver = cp.CpSolver()
    solver.parameters.max_time_in_seconds = 60 * 1
    result = solver.solve(model)
    y = solver.objective_value
    tour = [solver.value(x_i) for x_i in x]
    print(f"Result: {result}")
    print(f"Distance: {y}")
    print(f"Tour: {tour}")
    
    Result: 2
    Distance: 148816.0
    Tour: [22, 21, 20, 24, 23, 45, 44, 43, 47, 46, 68, 67, 66, 49, 48, 51, 52, 53, 41, 42, 27, 28, 29, 30, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 36, 35, 34, 33, 39, 40, 59, 58, 57, 56, 62, 63, 61, 60, 54, 55, 50, 65, 64, 70, 71, 72, 38, 37, 31, 32, 26, 25, 3, 2, 1, 0, 74, 75, 73, 69]
    

Computation: Python, SAT, MTZ

  • Imports
    import numpy as np
    import scipy as sp
    from ortools.sat.python import cp_model as cp
    
  • Input

    The distance matrix

    \begin{equation*} D \in \mathbb{Z}^{n \times n}, \end{equation*}

    constructed as follows:

    1. load the instance pr76 from TSPLIB [24]
    2. measure the Euclidean distances between all cities
    3. round the distances and convert them to integers
    n = len(PR76)
    c = np.array(PR76)[:, 1:]
    d_float = sp.spatial.distance_matrix(c, c)
    d = np.rint(d_float).astype(int)
    
    Table 5: Instance PR76 from TSPLIB [24]
    City x y
    1 3600 2300
    2 3100 3300
    3 4700 5750
    4 5400 5750
    5 5608 7103
    6 4493 7102
    7 3600 6950
    8 3100 7250
    9 4700 8450
    10 5400 8450
    11 5610 10053
    12 4492 10052
    13 3600 10800
    14 3100 10950
    15 4700 11650
    16 5400 11650
    17 6650 10800
    18 7300 10950
    19 7300 7250
    20 6650 6950
    21 7300 3300
    22 6650 2300
    23 5400 1600
    24 8350 2300
    25 7850 3300
    26 9450 5750
    27 10150 5750
    28 10358 7103
    29 9243 7102
    30 8350 6950
    31 7850 7250
    32 9450 8450
    33 10150 8450
    34 10360 10053
    35 9242 10052
    36 8350 10800
    37 7850 10950
    38 9450 11650
    39 10150 11650
    40 11400 10800
    41 12050 10950
    42 12050 7250
    43 11400 6950
    44 12050 3300
    45 11400 2300
    46 10150 1600
    47 13100 2300
    48 12600 3300
    49 14200 5750
    50 14900 5750
    51 15108 7103
    52 13993 7102
    53 13100 6950
    54 12600 7250
    55 14200 8450
    56 14900 8450
    57 15110 10053
    58 13992 10052
    59 13100 10800
    60 12600 10950
    61 14200 11650
    62 14900 11650
    63 16150 10800
    64 16800 10950
    65 16800 7250
    66 16150 6950
    67 16800 3300
    68 16150 2300
    69 14900 1600
    70 19800 800
    71 19800 10000
    72 19800 11900
    73 19800 12200
    74 200 12200
    75 200 1100
    76 200 800
  • Model
    model = cp.CpModel()
    
  • Decision variables
    \begin{equation*} X \in {\{0, 1\}}^n \end{equation*}
    x = [[model.new_bool_var(f"x_{i, j}") for j in range(n)] for i in range(n)]
    
  • Auxiliary variables: Time of visitations

    Per-city discrete time of visitation.

    t = [model.new_int_var(0, n - 1, f"t_{i}") for i in range(n)]
    
  • Constraints: Arrivals

    Each city is arrived in exactly once.

    \begin{equation*} \forall i \in \{1, \ldots n\} - \{j\} \quad \sum_{j = 1}^{n} x_{ij} = 1 \end{equation*}
    for i in range(n):
        model.add(sum(x[i][j] for j in range(n) if i != j) == 1)
    
  • Constraints: Departures

    Each city is departed from exactly once.

    \begin{equation*} \forall j \in \{1, \ldots n\} - \{i\} \quad \sum_{i = 1}^{n} x_{ij} = 1 \end{equation*}
    for j in range(n):
        model.add(sum(x[i][j] for i in range(n) if j != i) == 1)
    
  • Constraints: Sub-tour elimination (MTZ)

    Miller–Tucker–Zemlin (MTZ) derivation 27:

    1. Introduce a time of visitation \(t_i \in \{1, \ldots, n - 1\}\).

      \begin{align*} x_{ij} = 1 & \implies t_i < t_j && \text{\(i\) visited before \(j\)} \\ & \implies t_j > t_i &&\text{swap sides} \\ & \implies t_j \geq t_i + 1 && \text{favor \(\geq\) over \(>\)} \\ \end{align*}
    2. Linearize the implication with the Big-M trick.

      \begin{equation*} t_j \geq t_i + 1 - M \cdot (1 - x_{ij}) \end{equation*}
    3. Half-way there!

      \begin{align*} x_{ij} = 1 & \stackrel{\checkmark}{\implies} t_j \geq t_i + 1 \\ x_{ij} = 0 & \stackrel{?}{\implies} t_j \geq t_i + 1 - M \end{align*}
    4. Find the smallest \(M\) that works for all \(t_i\) and \(t_j\).

      \begin{equation*} M = n \end{equation*}
    5. Substitute \(M\) for \(n\).

      \begin{equation*} t_j \geq t_i + 1 - n (1 - x_{ij}) \end{equation*}
    for i in range(n):
        for j in range(1, n):
            if i != j:
                model.add(t[j] >= t[i] + 1 - n * (1 - x[i][j]))
    
  • Constraints: Rotational symmetry breaking

    Fix the first city to eliminate all rotations.

    model.add(t[0] == 0)
    

    N.B. There is also a reversal symmetry that is harder to define.

  • Objective function
    \begin{equation*} y = \sum_{i = 1}^{n} \sum_{j = 1}^{n} d_{ij} x_{ij} \to \min \end{equation*}
    y = sum(d[i][j] * x[i][j] for i in range(n) for j in range(n))
    model.minimize(y)
    
  • Solution

    Find a solution in the given time.

    solver = cp.CpSolver()
    solver.parameters.max_time_in_seconds = 60 * 1
    result = solver.solve(model)
    y = solver.objective_value
    tour = sorted(range(len(t)), key=lambda i: solver.value(t[i]))
    print(f"Result: {result}")
    print(f"Distance: {y}")
    print(f"Tour: {tour}")
    
    Result: 2
    Distance: 110370.0
    Tour: [0, 22, 21, 20, 24, 23, 45, 44, 43, 47, 46, 68, 67, 69, 66, 49, 48, 54, 55, 56, 57, 58, 59, 40, 60, 61, 62, 63, 72, 71, 70, 64, 65, 50, 51, 52, 53, 41, 42, 27, 26, 25, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 73, 14, 15, 16, 17, 36, 35, 37, 38, 39, 33, 34, 32, 31, 28, 29, 30, 18, 3, 2, 1, 74, 75]
    

Computation: Python, SAT, MTZ (with NN)

  • Imports
    import numpy as np
    import scipy as sp
    from ortools.sat.python import cp_model as cp
    
  • Input

    The distance matrix

    \begin{equation*} D \in \mathbb{Z}^{n \times n}, \end{equation*}

    constructed as follows:

    1. load the instance pr76 from TSPLIB [24]
    2. measure the Euclidean distances between all cities
    3. round the distances and convert them to integers
    n = len(PR76)
    c = np.array(PR76)[:, 1:]
    d_float = sp.spatial.distance_matrix(c, c)
    d = np.rint(d_float).astype(int)
    
    Table 6: Instance PR76 from TSPLIB [24]
    City x y
    1 3600 2300
    2 3100 3300
    3 4700 5750
    4 5400 5750
    5 5608 7103
    6 4493 7102
    7 3600 6950
    8 3100 7250
    9 4700 8450
    10 5400 8450
    11 5610 10053
    12 4492 10052
    13 3600 10800
    14 3100 10950
    15 4700 11650
    16 5400 11650
    17 6650 10800
    18 7300 10950
    19 7300 7250
    20 6650 6950
    21 7300 3300
    22 6650 2300
    23 5400 1600
    24 8350 2300
    25 7850 3300
    26 9450 5750
    27 10150 5750
    28 10358 7103
    29 9243 7102
    30 8350 6950
    31 7850 7250
    32 9450 8450
    33 10150 8450
    34 10360 10053
    35 9242 10052
    36 8350 10800
    37 7850 10950
    38 9450 11650
    39 10150 11650
    40 11400 10800
    41 12050 10950
    42 12050 7250
    43 11400 6950
    44 12050 3300
    45 11400 2300
    46 10150 1600
    47 13100 2300
    48 12600 3300
    49 14200 5750
    50 14900 5750
    51 15108 7103
    52 13993 7102
    53 13100 6950
    54 12600 7250
    55 14200 8450
    56 14900 8450
    57 15110 10053
    58 13992 10052
    59 13100 10800
    60 12600 10950
    61 14200 11650
    62 14900 11650
    63 16150 10800
    64 16800 10950
    65 16800 7250
    66 16150 6950
    67 16800 3300
    68 16150 2300
    69 14900 1600
    70 19800 800
    71 19800 10000
    72 19800 11900
    73 19800 12200
    74 200 12200
    75 200 1100
    76 200 800
  • Model
    model = cp.CpModel()
    
  • Decision variables
    \begin{equation*} X \in {\{0, 1\}}^n \end{equation*}
    x = [[model.new_bool_var(f"x_{i, j}") for j in range(n)] for i in range(n)]
    
  • Auxiliary variables: Time of visitations

    Per-city discrete time of visitation.

    t = [model.new_int_var(0, n - 1, f"t_{i}") for i in range(n)]
    
  • Hint: Nearest neighbor heuristic
    # hint (tour)
    h = np.array([22, 21, 20, 24, 23, 45, 44, 43, 47, 46, 68, 67, 66, 49, 48, 51, 52, 53, 41, 42, 27, 28, 29, 30
    , 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 36, 35, 34, 33, 39, 40, 59, 58
    , 57, 56, 62, 63, 61, 60, 54, 55, 50, 65, 64, 70, 71, 72, 38, 37, 31, 32, 26, 25, 3, 2, 1, 0
    , 74, 75, 73, 69])
    
    # Rotate `h` so that the city `0' is first.
    h_0 = np.where(h == 0)[0][0]
    h = np.roll(h, -h_0)
    
    # edges
    e = set((h[k], h[k + 1]) for k in range(len(h) - 1))
    e = e.add((h[-1], h[0])) # wrap!
    
    for i in range(n):
        for j in range(n):
            if (i, j) in e:
                model.add_hint(x[i][j], 1)
            else:
                model.add_hint(x[i][j], 0)
    
    for k, i in enumerate(h):
        model.add_hint(t[i], k)
    
  • Constraints: Arrivals

    Each city is arrived in exactly once.

    \begin{equation*} \forall i \in \{1, \ldots n\} - \{j\} \quad \sum_{j = 1}^{n} x_{ij} = 1 \end{equation*}
    for i in range(n):
        model.add(sum(x[i][j] for j in range(n) if i != j) == 1)
    
  • Constraints: Departures

    Each city is departed from exactly once.

    \begin{equation*} \forall j \in \{1, \ldots n\} - \{i\} \quad \sum_{i = 1}^{n} x_{ij} = 1 \end{equation*}
    for j in range(n):
        model.add(sum(x[i][j] for i in range(n) if j != i) == 1)
    
  • Constraints: Sub-tour elimination (MTZ)

    Miller–Tucker–Zemlin (MTZ) derivation:

    1. Introduce a time of visitation \(t_i \in \{1, \ldots, n - 1\}\).

      \begin{align*} x_{ij} = 1 & \implies t_i < t_j && \text{\(i\) visited before \(j\)} \\ x_{ij} = 1 & \implies t_j > t_i &&\text{swap sides} \\ x_{ij} = 1 & \implies t_j \geq t_i + 1 && \text{favor \(\geq\) over \(>\)} \\ \end{align*}
    2. Replace the implication with a “Big-M constant”.

      \begin{equation*} t_j \geq t_i + 1 - M \cdot (1 - x_{ij}) \end{equation*}
    3. Half-way there!

      \begin{align*} x_{ij} = 1 & \stackrel{\checkmark}{\implies} t_j \geq t_i + 1 \\ x_{ij} = 0 & \stackrel{?}{\implies} t_j \geq t_i + 1 - M \end{align*}
    4. Find the smallest \(M\) that works for all \(t_i\) and \(t_j\).

      \begin{equation*} M = n \end{equation*}
    5. Substitute \(M\) for \(n\).

      \begin{equation*} t_j \geq t_i + 1 - n (1 - x_{ij}) \end{equation*}
    for i in range(n):
        for j in range(1, n):
            if i != j:
                model.add(t[j] >= t[i] + 1 - n * (1 - x[i][j]))
    
  • Constraints: Rotational symmetry

    Fix the first city to eliminate all rotations.

    model.add(t[0] == 0)
    

    N.B. There is also a reversal symmetry that is harder to define.

  • Objective function
    \begin{equation*} y = \sum_{i = 1}^{n} \sum_{j = 1}^{n} d_{ij} x_{ij} \to \min \end{equation*}
    y = sum(d[i][j] * x[i][j] for i in range(n) for j in range(n))
    model.minimize(y)
    
  • Solution

    Find a solution in the given time.

    solver = cp.CpSolver()
    solver.parameters.max_time_in_seconds = 60 * 1
    result = solver.solve(model)
    y = solver.objective_value
    tour = sorted(range(len(t)), key=lambda i: solver.value(t[i]))
    print(f"Result: {result}")
    print(f"Distance: {y}")
    print(f"Tour: {tour}")
    
    Result: 2
    Distance: 108234.0
    Tour: [0, 22, 21, 20, 24, 23, 45, 44, 43, 47, 46, 68, 67, 69, 66, 49, 48, 50, 65, 64, 70, 71, 72, 63, 62, 61, 60, 40, 59, 58, 57, 56, 55, 54, 51, 52, 53, 41, 42, 27, 26, 25, 28, 29, 19, 18, 30, 31, 32, 34, 33, 39, 38, 37, 35, 36, 17, 16, 15, 14, 73, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 74, 75]
    

Optimal solution

The actual optimal solution is:

x_ = np.array([1, 76, 75, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
                     74, 15, 16, 17, 18, 37, 36, 38, 39, 40, 34, 35, 33, 32,
                     29, 30, 31, 19, 20, 26, 27, 28, 43, 42, 54, 53, 52, 55,
                     56, 57, 58, 59, 60, 41, 61, 62, 63, 64, 73, 72, 71, 65,
                     66, 51, 49, 50, 67, 70, 68, 69, 47, 48, 44, 45, 46, 24,
                     25, 21, 22, 23]) - 1 # 0-based indices

# NOTE `if' closes the cycle.
sum(d[x_[i], x_[i + 1 if i < n - 1 else 0]] for i in range(n))
108159

N-queens

The constraint satisfaction problem of placing

\(n\) queens on an \(n \times n\) chessboard with no mutual threats.

Solve with home-grown SAT solver

  • Decision variables

    Denote the \(n\)-queens on an \(n \times n\) chess board by

    \begin{equation*} X \in \mathbb{B}^{n \times n}, \end{equation*}

    where \(x_{ij} = 1\) iff a queen is at row \(i\) in column \(j\).

    (defun my-queens-variables (size &optional transpose)
      "Return a list of decision variables for SIZE-queen problem.
    
    The result is a list of 3-element lists containing a variable, row, and
    column, all positive integers.  The lists are returned in row-major
    order for reproducibility and testability.  With TRANSPOSE, the board is
    transposed and the lists are returned in column-major mode."
      (mapcan (lambda (row)
                (mapcar (lambda (column)
                          (let ((variable (+ (* (1- row) size) column)))
                            (append (list variable)
                                    (or (and transpose (list column row))
                                        (list row column)))))
                        (number-sequence 1 size)))
              (number-sequence 1 size)))
    
    (ert-deftest my-queens-variables/2-queens/row-major ()
      (should (equal (my-queens-variables 2)
                     '((1 1 1) (2 1 2)
                       (3 2 1) (4 2 2)))))
    
    (ert-deftest my-queens-variables/2-queens/column-major ()
      (should (equal (my-queens-variables 2 t)
                     '((1 1 1) (2 2 1)
                       (3 1 2) (4 2 2)))))
    
    (ert-deftest my-queens-variables/3-queens/row-major ()
      (should (equal (my-queens-variables 3)
                     '((1 1 1) (2 1 2) (3 1 3)
                       (4 2 1) (5 2 2) (6 2 3)
                       (7 3 1) (8 3 2) (9 3 3)))))
    
    (ert-deftest my-queens-variables/3-queens/column-major ()
      (should (equal (my-queens-variables 3 t)
                     '((1 1 1) (2 2 1) (3 3 1)
                       (4 1 2) (5 2 2) (6 3 2)
                       (7 1 3) (8 2 3) (9 3 3)))))
    
  • Constraints: Population

    Enforce at least one queen in every row and column,

    \begin{equation*} \forall (1 \leq i \leq n) \quad \sum_{j = 1}^{n} x_{ij} \geq 1, \end{equation*}

    transformed to the conjunctive normal form as

    \begin{equation*} \forall i \bigvee_{j = 1}^{n} x_{ij} \qand \forall j \bigvee_{i = 1}^{n} x_{ij}. \end{equation*}
    (defun my-queens-at-least-one-per-row-clauses (size &optional transpose)
      "Return CNF clauses enforcing at least one queen per row.
    
    Implements constraints for SIZE-queens puzzle.  With non-nil TRANSPOSE,
    enforce at least one queen per column, instead of row.
    
    The result is a list of lists containing literals, non-zero integers
    representing variables, possibly negated.  All returned clauses and
    literals are sorted for testability and reproducibility."
      (sort (mapcar (lambda (row)
                      (mapcan (pcase-lambda (`(,variable ,row* _column))
                                (if (= row* row) (list variable)))
                              (my-queens-variables size transpose)))
                    (number-sequence 1 size))))
    
    (ert-deftest my-queens-at-least-one-per-row-clauses/size-2 ()
      (should (equal (my-queens-at-least-one-per-row-clauses 2)
                     '((1 2)
                       (3 4)))))
    
    (ert-deftest my-queens-at-least-one-per-row-clauses/size-2/transpose ()
      (should (equal (my-queens-at-least-one-per-row-clauses 2 t)
                     '((1 3)
                       (2 4)))))
    
    (ert-deftest my-queens-at-least-one-per-row-clauses/size-3 ()
      (should (equal (my-queens-at-least-one-per-row-clauses 3)
                     '((1 2 3)
                       (4 5 6)
                       (7 8 9)))))
    
    (ert-deftest my-queens-at-least-one-per-row-clauses/size-3/transpose ()
      (should (equal (my-queens-at-least-one-per-row-clauses 3 t)
                     '((1 4 7)
                       (2 5 8)
                       (3 6 9)))))
    
  • Constraints: Orthogonal threats

    Enforce at most one queen in every row and column,

    \begin{equation*} \forall (1 \leq i \leq n) \quad \sum_{j = 1}^{n} x_{ij} \leq 1, \end{equation*}

    transformed to the conjunctive normal form as

    \begin{align*} \forall (i, j) \> \forall (j' > j) \quad & (\lnot x_{ij} \lor \lnot x_{ij'}) \\ \forall (i, j) \> \forall (i' > i) \quad & (\lnot x_{ij} \lor \lnot x_{i'j}). \end{align*}
    (defun my-queens-at-most-one-per-row-clauses (size &optional transpose)
      "Return CNF clauses enforcing at most one queen per row.
    
    Implements constraints for SIZE-queens puzzle.  With non-nil TRANSPOSE,
    enforce at most one queen per column, instead of row.
    
    The result is a list of lists containing literals, non-zero integers
    representing variables, possibly negated.  All returned clauses and
    literals are sorted for testability and reproducibility."
      (let ((variables (my-queens-variables size transpose)))
        (sort (mapcan (pcase-lambda (`(,variable ,row ,column))
                        (mapcan (pcase-lambda (`(,variable* ,row* ,column*))
                                  (if (and (= row* row)
                                           (> column* column))
                                      (list (list (- variable*)
                                                  (- variable)))))
                                variables))
                      variables))))
    
    (ert-deftest my-queens-at-most-one-per-row-clauses/size-2 ()
      (should (equal (my-queens-at-most-one-per-row-clauses 2)
                     '((-4 -3) (-2 -1)))))
    
    (ert-deftest my-queens-at-most-one-per-row-clauses/size-2/transpose ()
      (should (equal (my-queens-at-most-one-per-row-clauses 2 t)
                     '((-4 -2) (-3 -1)))))
    
    (ert-deftest my-queens-at-most-one-per-row-clauses/size-3 ()
      (should (equal (my-queens-at-most-one-per-row-clauses 3)
                     '((-9 -8)
                       (-9 -7)
                       (-8 -7)
                       (-6 -5)
                       (-6 -4)
                       (-5 -4)
                       (-3 -2)
                       (-3 -1)
                       (-2 -1)))))
    
    (ert-deftest my-queens-at-most-one-per-row-clauses/size-3/transpose ()
      (should (equal (my-queens-at-most-one-per-row-clauses 3 t)
                     '((-9 -6)
                       (-9 -3)
                       (-8 -5)
                       (-8 -2)
                       (-7 -4)
                       (-7 -1)
                       (-6 -3)
                       (-5 -2)
                       (-4 -1)))))
    
    (ert-deftest my-queens-at-most-one-per-row-clauses/size-4 ()
      (should (equal (my-queens-at-most-one-per-row-clauses 4)
                     '((-16 -15)
                       (-16 -14)
                       (-16 -13)
                       (-15 -14)
                       (-15 -13)
                       (-14 -13)
                       (-12 -11)
                       (-12 -10)
                       (-12 -9)
                       (-11 -10)
                       (-11 -9)
                       (-10 -9)
                       (-8 -7)
                       (-8 -6)
                       (-8 -5)
                       (-7 -6)
                       (-7 -5)
                       (-6 -5)
                       (-4 -3)
                       (-4 -2)
                       (-4 -1)
                       (-3 -2)
                       (-3 -1)
                       (-2 -1)))))
    
  • Constraints: Diagonal threats

    Ensure at most one queen on every increasing and decreasing diagonal,

    \begin{align*} \forall (i, j) \> \forall(i' > i, j' > j) \quad & (i - j = i' - j') \implies (\lnot x_{ij} \lor \lnot x_{i'j'}) \\ \forall (i, j) \> \forall(i' > i, j' > j) \quad & (i + j = j' + j') \implies (\lnot x_{ij} \lor \lnot x_{i'j'}), \end{align*}

    derived from the constant difference between rows and columns on diagonals

    \begin{equation*} \begin{aligned} i' - i & = j' - j \\ i' + i & = j' + j \end{aligned} \iff \begin{aligned} i - i' & = +j - j' \\ i - i' & = -j + j' \end{aligned} \iff \begin{aligned} \Delta i & = + \Delta j \\ \Delta i & = - \Delta j \end{aligned} \iff \Delta i = \pm \Delta j \, . \end{equation*}
    (defun my-queens-not-sharing-diagonal-clauses (size &optional transpose)
      "Return CNF clauses enforcing at most one queen on decreasing diagonals.
    
    Implements constraints for SIZE-queens puzzle.  With non-nil TRANSPOSE,
    enforce at most most queen on increasing diagonals instead of decreasing
    diagonals.
    
    The result is a list of lists containing literals, non-zero integers
    representing variables, possibly negated.  All returned clauses and
    literals are sorted for testability and reproducibility."
      (sort (mapcan (pcase-lambda (`(,variable ,row ,column))
                      (mapcan (pcase-lambda (`(,variable* ,row* ,column*))
                                (when (or (and transpose
                                               (> row* row)
                                               (< column* column)
                                               (= (+ row column)
                                                  (+ row* column*)))
                                          (and (not transpose)
                                               (> row* row)
                                               (> column* column)
                                               (= (- row column)
                                                  (- row* column*))))
                                  (list (sort (list (- variable)
                                                    (- variable*))))))
                              (my-queens-variables size)))
                    (my-queens-variables size))))
    
    
    (ert-deftest my-queens-not-sharing-diagonal-clauses/size-2 ()
      (should (equal (my-queens-not-sharing-diagonal-clauses 2)
                     '((-4 -1)))))
    
    (ert-deftest my-queens-not-sharing-diagonal-clauses/size-2/anti-diagonal ()
      (should (equal (my-queens-not-sharing-diagonal-clauses 2 t)
                     '((-3 -2)))))
    
    (ert-deftest my-queens-not-sharing-diagonal-clauses/size-3 ()
      (should (equal (my-queens-not-sharing-diagonal-clauses 3)
                     '((-9 -5)
                       (-9 -1)
                       (-8 -4)
                       (-6 -2)
                       (-5 -1)))))
    
    (ert-deftest my-queens-not-sharing-diagonal-clauses/size-3/anti-diagonal ()
      (should (equal (my-queens-not-sharing-diagonal-clauses 3 t)
                     '((-8 -6)
                       (-7 -5)
                       (-7 -3)
                       (-5 -3)
                       (-4 -2)))))
    
    (ert-deftest my-queens-not-sharing-diagonal-clauses/size-4 ()
      (should (equal (my-queens-not-sharing-diagonal-clauses 4)
                     '((-16 -11)
                       (-16 -6)
                       (-16 -1)
                       (-15 -10)
                       (-15 -5)
                       (-14 -9)
                       (-12 -7)
                       (-12 -2)
                       (-11 -6)
                       (-11 -1)
                       (-10 -5)
                       (-8 -3)
                       (-7 -2)
                       (-6 -1)))))
    
  • Solver

    Define \(N\)-queens solver

    (defun my-queens-constraints (size)
      (append (my-queens-at-least-one-per-row-clauses size)
              (my-queens-at-least-one-per-row-clauses size t)
              (my-queens-at-most-one-per-row-clauses size)
              (my-queens-at-most-one-per-row-clauses size t)
              (my-queens-not-sharing-diagonal-clauses size)
              (my-queens-not-sharing-diagonal-clauses size t)))
    
    (defun my-queens (&optional size all-solutions-p)
      (let ((size (or size 8)))
        (my-sat-solve (my-queens-constraints size) all-solutions-p)))
    
  • Solution formatter
    (defun my-queens-chess-position (variable size)
      "Return a file-rank string for SOLUTION."
      (let ((file (+ ?a (mod (1- variable) size)))
            (rank (- size (/ (1- variable) size))))
        (format "%c%d" file rank)))
    
    (defun my-queens-chess-pieces (solution)
      "Return a list of piece-file-rank strings for SOLUTION."
      (let ((size (truncate (sqrt (length solution)))))
        (mapcan (pcase-lambda (`(,variable . ,value))
                  (when (= value 1)
                    (list (concat "q" (my-queens-chess-position variable size)))))
                solution)))
    
    (defun my-queens-render-solution (solution)
      "Return LaTeX source code that renders SOLUTION."
      (let ((size (truncate (sqrt (length solution)))))
        (format "\\chessboard[smallboard, maxfield=%s, setpieces={%s}]"
                (my-queens-chess-position size size)
                (string-join (sort (my-queens-chess-pieces solution)) ", "))))
    
    (defun my-queens-render-solutions (solutions)
      "Return LaTeX source code that renders SOLUTIONS."
      (string-join
       (append '("\\begin{flushleft}")
               (mapcar (lambda (solution)
                         (concat "  " (my-queens-render-solution solution)))
                       solutions)
               '("\\end{flushleft}"))
       "\n"))
    
    (ert-deftest my-queens-render-solutions/size-2 ()
      (should (equal (my-queens-render-solutions '(((1 . 1) (2 . 0)
                                                    (3 . 0) (4 . 1))))
                     "\\begin{flushleft}
      \\chessboard[smallboard, maxfield=b2, setpieces={qa2, qb1}]
    \\end{flushleft}")))
    
    (ert-deftest my-queens-render-solutions/size-2/different ()
      (should (equal (my-queens-render-solutions '(((1 . 0) (2 . 1)
                                                    (3 . 1) (4 . 0))))
                     "\\begin{flushleft}
      \\chessboard[smallboard, maxfield=b2, setpieces={qa1, qb2}]
    \\end{flushleft}")))
    
    (ert-deftest my-queens-render-solutions/size-2/multiple ()
      (should (equal (my-queens-render-solutions '(((1 . 1) (2 . 0)
                                                    (3 . 0) (4 . 1))
                                                   ((1 . 0) (2 . 1)
                                                    (3 . 1) (4 . 0))))
                     "\\begin{flushleft}
      \\chessboard[smallboard, maxfield=b2, setpieces={qa2, qb1}]
      \\chessboard[smallboard, maxfield=b2, setpieces={qa1, qb2}]
    \\end{flushleft}")))
    
    (ert-deftest my-queens-render-solutions/size-3 ()
      (should (equal (my-queens-render-solutions '(((1 . 0) (2 . 0) (3 . 0)
                                                    (4 . 0) (5 . 0) (6 . 0)
                                                    (7 . 0) (8 . 0) (9 . 1))))
                     "\\begin{flushleft}
      \\chessboard[smallboard, maxfield=c3, setpieces={qc1}]
    \\end{flushleft}")))
    
  • Solutions

    \(n = 1\)-queens trivial,

    (my-queens-render-solutions (my-queens 1 t))
    
    a84c42ac-1593-44dd-a441-6bacec2bafef.svg

    \(n = 2\) and \(n = 3\)-queens have no solutions

    (list (length (my-queens 2 t))
          (length (my-queens 3 t)))
    
    (0 0)
    

    \(n = 4\)-queens has two solutions,

    (my-queens-render-solutions (my-queens 4 t))
    
    50ed91b2-657e-4b3a-b70c-36478edfb72b.svg

    and the traditional \(n = 8\)-queens has

    (length (my-queens 8 t))
    
    92
    

    solutions, one of them being

    (my-queens-render-solutions (my-queens 8))
    
    80632ecf-ed08-4c4f-bd64-f6b279174638.svg

SAT solver

Practice implementation: Emacs Lisp

A simple CNF-only SAT solver in Emacs Lisp based on DFS and

the AC-3 (Arc Consistency #3) algorithm

from the 1970s.

  • Solver: Solving
    (defun my-sat-solve (clauses &optional all-solutions-p)
      "Solve a Boolean satisfaction problem.
    
    Returns a list containing one solution, or all solutions if
    ALL-SOLUTIONS is non-nil.
    
    The problem is specified as CNF (Conjunctive Normal Form), that is
    conjunctions of disjunctions.  Each clause is a list of integers
    representing literals, possibly negated."
      (let* ((domains (my-sat-domains clauses))
             (domains (my-sat-search clauses domains all-solutions-p)))
        (mapcar #'my-sat-solution domains)))
    
  • Solver: Creating domains
    (defun my-sat-domains (clauses)
      (mapcar (lambda (variable) (cons variable '(0 1)))
              (delete-dups (mapcar #'abs (apply #'append clauses)))))
    
    (ert-deftest my-sat-domains/no-clauses ()
      (should (equal (my-sat-domains '()) '())))
    
    (ert-deftest my-sat-domains/single-clause ()
      (should (equal (my-sat-domains '((1)))
                     '((1 . (0 1)))))
      (should (equal (my-sat-domains '((1 2)))
                     '((1 . (0 1))
                       (2 . (0 1))))))
    
    (ert-deftest my-sat-domains/multiple-clauses ()
      (should (equal (my-sat-domains '((1) (2)))
                     '((1 . (0 1))
                       (2 . (0 1)))))
      (should (equal (my-sat-domains '((1) (1 2) (3)))
                     '((1 . (0 1))
                       (2 . (0 1))
                       (3 . (0 1))))))
    
    (ert-deftest my-sat-domains/negated-literals ()
      (should (equal (my-sat-domains '((1 -2) (-1 2)))
                     '((1 . (0 1))
                       (2 . (0 1))))))
    
  • Solver: Extracting solutions
    (defun my-sat-solution (domains)
      "Return the solution contained in DOMAINS, if any.
    Returns a sorted alist with variables and their values."
      (catch 'abort
        (mapcar (pcase-lambda (`(,variable . (,value ,other-value)))
                  (if other-value (throw 'abort nil)
                    (cons variable value)))
                (sort domains))))
    
    (ert-deftest my-sat-solution ()
      (should (equal (my-sat-solution '((1 . (0)))) '((1 . 0))))
      (should (equal (my-sat-solution '((1 . (0)) (2 . (1)))) '((1 . 0) (2 . 1)))))
    
    (ert-deftest my-sat-solution/trivial ()
      ;; Two perspectives, single truth: trivially solved or no solution.
      (should (equal (my-sat-solution '()) '()))
      (should-not (my-sat-solution '())))
    
    (ert-deftest my-sat-solution/unsolved ()
      (should-not (my-sat-solution '((1 . (0 1)) (2 . (1)))))
      (should-not (my-sat-solution '((1 . (0)) (2 . (0 1))))))
    
    (ert-deftest my-sat-solution/unfeasible ()
      (should-not (my-sat-solution '((1 . (0 1)) (2 . ()))))
      (should-not (my-sat-solution '((1 . ()) (2 . (0 1))))))
    
  • Search
    ;;; Wandering through the search space.
    
    (defalias 'my-sat-mac #'my-sat-search)
    
    (defun my-sat-search (clauses domains &optional all-solutions-p branch)
      "Return a list of domains revised to solutions.
    
    If ALL-SOLUTIONS-P is non-nil, returns all solutions.  Otherwise,
    returns a single solution, if some exists.
    
    Implements the MAC (Maintaining Arc Consistency) algorithm."
      (let ((domains (my-sat-domains-copy domains branch)))
        (if-let* ((domains (my-sat-make-consistent clauses domains)))
            (cond ((my-sat-unfeasible-p domains) nil)
                  ((my-sat-solved-p domains) (list domains))
                  (t (let* ((branch-0 (my-sat-search clauses domains all-solutions-p 0))
                            (branch-1 (and (or (not branch-0) all-solutions-p)
                                           (my-sat-search clauses domains all-solutions-p 1))))
                       (append branch-0 branch-1)))))))
    
  • Search: Branching
    (defun my-sat-domains-copy (domains &optional branch)
      "Return a deep copy of DOMAINS.
    
    DOMAINS is an alist from variable numbers (positive integers) to
    domains (lists).
    
    With BRANCH, constrain the first undecided variable to BRANCH."
      (let ((copy (copy-tree domains)))
        (when branch
          (setcdr (rassoc '(0 1) copy)
                  (list branch)))
        copy))
    
    (ert-deftest my-sat-domains-copy/empty ()
      (should-not (my-sat-domains-copy '())))
    
    (ert-deftest my-sat-domains-copy/single-member ()
      (should (equal (my-sat-domains-copy '((1 . (0 1))))
                     '((1 . (0 1))))))
    
    (ert-deftest my-sat-domains-copy/single-member/pointer ()
      (let ((original '((1 . (0 1)))))
        (should-not (eq (my-sat-domains-copy original) original))))
    
    (ert-deftest my-sat-domains-copy/multiple-members ()
      (should (equal (my-sat-domains-copy '((1 . (0 1)) (2 . (0))))
                     '((1 . (0 1)) (2 . (0))))))
    
    (ert-deftest my-sat-domains-copy/branch/first ()
      (should (equal (my-sat-domains-copy '((1 . (0 1)) (2 . (0 1))) 1)
                     '((1 . (1)) (2 . (0 1))))))
    
    (ert-deftest my-sat-domains-copy/branch/second ()
      (should (equal (my-sat-domains-copy '((1 . (0)) (2 . (0 1))) 0)
                     '((1 . (0)) (2 . (0))))))
    
  • Search: Detecting dead-ends
    (defun my-sat-unfeasible-p (domains)
      "Return non-nil if DOMAINS cannot possibly lead to a solution."
      (rassoc nil domains))
    
    (ert-deftest my-sat-unfeasible-p ()
      (should (my-sat-unfeasible-p '((1 . ()))))
      (should (my-sat-unfeasible-p '((1 . (0 1)) (2 . ())))))
    
    (ert-deftest my-sat-unfeasible-p/nil ()
      (should-not (my-sat-unfeasible-p '())) ;; Trivially solved.
      (should-not (my-sat-unfeasible-p '((1 . (0 1)) (2 . (0 1)))))
      (should-not (my-sat-unfeasible-p '((1 . (0)) (2 . (1))))))
    
  • Search: Detecting solutions
    (defun my-sat-solved-p (domains)
      "Return non-nil if DOMAINS contain the solution."
      (all (pcase-lambda (`(_variable . (_value ,other-value)))
             (not other-value))
           domains))
    
    (ert-deftest my-sat-solved-p ()
      (should (my-sat-solved-p '())) ;; Trivially solved.
      (should (my-sat-solved-p '((1 . (0)))))
      (should (my-sat-solved-p '((1 . (0)) (2 . (1))))))
    
    (ert-deftest my-sat-solved-p/nil ()
      (should-not (my-sat-solved-p '((1 . (0 1)) (2 . (1)))))
      (should-not (my-sat-solved-p '((1 . (0)) (2 . (0 1))))))
    
  • Arc consistency

    AC3 algorithm

    (defalias 'my-sat-ac3 #'my-sat-make-consistent)
    
    (defun my-sat-make-consistent (clauses domains)
      "AC3 algorithm."
      (catch 'return
        (let ((domains (my-sat-domains-copy domains))) ;; TODO Do we need to?
          (named-let continue ((queue (my-sat-queue clauses)))
            (pcase queue
              ('() domains)                      ; Done.
              (`((,variable . ,clause) . ,queue) ; More to do.
               (progn
                 (pcase (my-sat-revise variable clause domains)
                   ('nil (continue queue domains)) ; No revision, continue.
                   ('(t . ()) (throw 'return nil)) ; No solution, abort.
                   (`(t . ,domain) ; Revision, update domains, queue neighbors, and continue.
                    (progn
                      (setcdr (assoc variable domains) domain)
                      (let* ((neighbors (my-sat-queue-neighbors clause clauses))
                             (queue (delete-dups (append neighbors queue))))
                        (continue queue domains))))))))))))
    
    (ert-deftest my-sat-make-consistent ()
      (should (equal (my-sat-make-consistent '((1 2) (3 4))
                                             '((1 . (0))
                                               (2 . (0 1))
                                               (3 . (0))
                                               (4 . (0 1))))
                     '((1 . (0))
                       (2 . (1))
                       (3 . (0))
                       (4 . (1))))))
    
    (ert-deftest my-sat-make-consistent/no-solution ()
      (let ((domains '((1 . (0))
                       (2 . (0))
                       (3 . (0))
                       (4 . (0)))))
        (should-not (my-sat-make-consistent '((1 2) (3 4)) domains))))
    
  • Arc consistency: Creating queue

    AC3 queue of variable-clause pairs

    (defun my-sat-queue (clauses)
      "Return a list of all variable-clause pairs in CLAUSES."
      (mapcan (lambda (clause)
                (mapcar (lambda (literal)
                          (let ((variable (abs literal)))
                            (cons variable clause)))
                        clause))
              clauses))
    
    (ert-deftest my-sat-queue ()
      (should (equal (my-sat-queue '((1 2) (2 3)))
                     '((1 . (1 2))
                       (2 . (1 2))
                       (2 . (2 3))
                       (3 . (2 3))))))
    
    (ert-deftest my-sat-queue/negated-literals ()
      (should (equal (my-sat-queue '((1 -2 3)))
                     '((1 . (1 -2 3))
                       (2 . (1 -2 3))
                       (3 . (1 -2 3))))))
    
  • Arc consistency: Re-queuing neighbors
    ;;; Extending the queue of variable-clause pairs.
    
    (defun my-sat-queue-neighbors (clause clauses)
      "Return the neighbors of CLAUSE VARIABLE within all CLAUSES.
    
    Neighbors are variables in CLAUSES other than CLAUSE that share at least
    one variable with CLAUSE."
    
      ;; Collect all variables mentioned in CLAUSE.
      (let ((variables (delete-dups (mapcar #'abs clause))))
    
        ;; Consider all CLAUSES.
        (mapcan (lambda (clause*)
                  (if (and
    
                       ;; Consider all CLAUSES except CLAUSE.
                       (not (equal clause* clause))
    
                       ;; Consider all CLAUSES that share variable(s) with CLAUSE.
                       (any (lambda (literal)
                              (member (abs literal) variables))
                            clause*))
    
                      ;; Map literals to variable-clause pairs.
                      (mapcar (lambda (literal)
                                (cons (abs literal) clause*))
                              clause*)))
    
                clauses)))
    
    (ert-deftest my-sat-neighbors ()
      (should (equal (my-sat-queue-neighbors '(1 -2) '((1 -2)
                                                       (-1 2 3)
                                                       (4)
                                                       (-2)))
                     '((1 . (-1 2 3))
                       (2 . (-1 2 3))
                       (3 . (-1 2 3))
                       (2 . (-2))))))
    
  • Arc consistency: Revising domains
    (defun my-sat-revise-to-empty (variable clause domains)
      "Attempt to revise the DOMAIN of VARIABLE in CLAUSE down to nothing.
    
    Return a cons cell with t and the revised domain of VARIABLE, or nil if
    no revision is possible.  DOMAINS is an alist of all variable domains
    that are lists."
      (if
    
          ;; Check if CLAUSE contains contradictory literals for VARIABLE:
          ;; both +VARIABLE and -VARIABLE.
          (and
           (any (lambda (literal) (eq literal (+ variable))) clause)
           (any (lambda (literal) (eq literal (- variable))) clause))
    
          ;; The CLAUSE is *self-contradictory*!
          ;; Return a domain, revised to nothing.
          (cons t '())))
    
    (defun my-sat-revise-to-value (value variable clause domains)
      "Attempt to revise the DOMAIN of VARIABLE in CLAUSE to VALUE.
    
    Return a cons cell with t and the revised domain of VARIABLE, or nil if
    no revision is possible.  DOMAINS is an alist of all variable domains
    that are lists."
    
      ;; Abort if there is nothing to revise.
      (let ((domain (alist-get variable domains)))
        (unless (or (null domain)
                    (equal domain (list value)))
    
          ;; Compute the "deciding literal" from VARIABLE and VALUE:
          ;; +VARIABLE if VALUE = 1
          ;; -VARIABLE if VALUE = 0.
          (let ((literal (pcase value
                           (0 (- variable))
                           (1 (+ variable))
                           (_ (error "Invalid value")))))
            (if (and
    
                 ;; Ensure the CLAUSE contains the "deciding literal".
                 (any (lambda (literal*) (= literal* literal)) clause)
    
                 ;; Ensure every literal in CLAUSE is one of:
                 ;; - the "deciding literal"
                 ;; - effective 0 as a positive literal with domain {0}
                 ;; - effective 0 as a negative literal with domain {1}.
                 (all (lambda (literal*)
                        (or (= literal* literal)
                            (let ((domain (alist-get (abs literal*) domains)))
                              (or (and (> literal* 0) (equal domain '(0)))
                                  (and (< literal* 0) (equal domain '(1)))))))
                      clause))
    
                ;; VARIABLE value is *forced* to have the given VALUE!
                ;; Return a domain, revised to only VALUE or nothing.
                (cons t (if (member value domain) (list value) '())))))))
    
    (defun my-sat-revise (variable clause domains)
      "Revise the DOMAIN of VARIABLE in CLAUSE, given all DOMAINS.
    
    Return a cons cell with t and the revised domain of VARIABLE, or nil if
    no revision is possible."
      (or (my-sat-revise-to-empty variable clause domains)
          (my-sat-revise-to-value 0 variable clause domains)
          (my-sat-revise-to-value 1 variable clause domains)))
    
    (ert-deftest my-sat-revise/regression ()
      (should-not (my-sat-revise 2 '(-2 -3) '((2 . (0)) (3 . (0))))))
    
    (ert-deftest my-sat-revise/no-change ()
      (should-not (my-sat-revise 1 '(1) '((1 . (1)))))
      (should-not (my-sat-revise 2 '(1 2) '((1 . (1)) (2 . (1))))))
    
    (ert-deftest my-sat-revise/no-change/regression ()
      (should-not (my-sat-revise 2 '(-1 -2) '((1 . (0)) (2 . (0 1))))))
    
    (ert-deftest my-sat-revise/revise-to-0 ()
      (should (equal (my-sat-revise 1 '(-1 2) '((1 . (0 1)) (2 . (0))))
                     '(t . (0)))))
    
    (ert-deftest my-sat-revise/revise-to-0/invalid ()
      (should (equal (my-sat-revise 1 '(-1) '((1 . (1))))
                     '(t . ()))))
    
    (ert-deftest my-sat-revise/revise-to-1 ()
      (should (equal (my-sat-revise 1 '(1 2) '((1 . (0 1)) (2 . (0))))
                     '(t . (1)))))
    
    (ert-deftest my-sat-revise/revise-to-1/invalid ()
      (should (equal (my-sat-revise 1 '(1) '((1 . (0))))
                     '(t . ()))))
    
    (ert-deftest my-sat-revise-duplicate ()
      (should (equal (my-sat-revise 1 '(1 1) '((1 . (0 1))))
                     '(t . (1)))))
    
    (ert-deftest my-sat-revise/contradiction ()
      (should (equal (my-sat-revise 1 '(1 -1) '((1 . (0 1))))
                     '(t . ()))))
    

Standard linear form

Find non-negative vector \(x \geq 0\) that maximizes the objective function \(c^{\mathsf{T}}x\) subject to the constraints \(Ax \leq b\).

Conversions to the standard linear form

\begin{align*} ax > b & \iff ax \geq b + \epsilon \\ ax < b & \iff ax \leq b - \epsilon \\ ax = b & \iff ax \leq b \land ax \geq b \\ ax \geq b & \iff -ax \leq -b \\ ax \leq b & \iff -ax \geq -b \\ \end{align*}

Linear constraint

A constraint in the form of a linear matrix inequality

\begin{equation*} ax \leq b, \end{equation*}

used in mixed-integer linear programming.

Big-M trick

A reification technique used to

linearize conditionals

in mixed-integer linear programming by

\begin{align*} \lambda = 1 & \implies ax \leq b \iff ax \leq b + M (1 - \lambda) \\ \lambda = 1 & \implies ax \geq b \iff ax \geq b - M (1 - \lambda) \end{align*}

where

and the “big M” satisfies

\begin{equation*} M \geq \max(ax) - b. \end{equation*}

Unordered remove

Constant-time \(\mathcal{O}(1)\) removal of an element of array whose order is insignificant.

Example

/* Example of "unordered remove". */

#include <stdio.h>

int a[] = {30, 10, 20, 50, 40};
int n = sizeof(a) / sizeof(a[0]);

int main() {

  a[1] = a[n - 1]; // Move the last element.
  n--;             // Remove the last element.

  printf("index,value\n");
  for (int i = 0; i < n; i++)
    printf("%d, %d\n", i, a[i]);
}

Turing award

The most prestigious award in computer science.

Frequency

Given annually by the ACM since 1966.

Intuition

Nobel prize of computer science, akin to the Fields medal in mathematics.

FLOPS

Floating point operations per second.

3090
29.28 for FP16

Floating-point number (FP) – Generalize the base + small binary/decimal notes

An approximation of reals in computing.

Format

In IEEE 754 binary16, binary32, and binary64:

\begin{equation*} x = \begin{cases} {(-1)}^{\alpha} 1.s \cdot 2^{e - b} & 0 < e < \max(e) && \text{normal} \\ {(-1)}^{\alpha} 0.s \cdot 2^{1 - b} & e = 0 && \text{subnormal} \\ {(-1)}^{\alpha} \infty & e = \max(e), s = 0 && \text{infinity} \\ \mathrm{NaN} & e = \max(e), s \neq 0 && \text{not a number signal} \\ \end{cases} \end{equation*}
  • \(\alpha\) is the sign bit
  • \(s\) is the significand
  • \(1.\), \(0.\) is the implicit bit
  • \(e\) is the \(E\)-bit exponent
  • \(b\) is the exponent bias \(b = 2^{E-1} - 1\)

(IEEE 754 also specifies base-\(10\) decimal formats.)

Precision (TODO ranges)

Precision Bits Identifiers Note
Half 16 = 1 + 5 + 10 FP16, float16, IEEE 754 binary16
Single 32 = 1 + 8 + 23 FP32, float32, float IEEE 754 binary32
Double 64 = 1 + 11 + 52 FP64, float64, double IEEE 754 binary64
Brain 16 = 1 + 8 + 7 BF16, bfloat16 used in ANNs

where bits are: sign, significand, and exponent (excluding the hidden bit).

Synonyms

  • Float
  • Double

Brain float

A float used in ANNs.

Format

Like IEEE 754 binary16 but with

  • \(E = 7\)-bit exponent \(e\)
  • \(8\)-bit significand \(s\)

Significand

Synonyms

  • Mantissa
  • Fraction
  • Coefficient

Freedom

Free software

Software that

guarantees all four essential freedoms.

Cost

Free software is

a matter of liberty,

not price28.

Synonyms

  • Libre software
  • Free and open-source software (FOSS)
  • Free/libre open-source software (FLOSS)

Four essential freedoms

The freedom to

  1. run
  2. study and modify
  3. distribute copies
  4. distribute modified copies

of free software.

General public license, version 3 (GPLv3)

A license that guarantees that software

can be (re-)distributed only as free software,

that is

no essential freedom can ever be taken away from it,

unlike in the case of

  • GPLv2 (General Public License, Version 2)
  • BSD (Berkeley Software Distribution)
  • MIT (Massachusetts Institute of Technology)

free software licenses.

References

What is free software?

BibTeX [25]:

@Misc{stallman-c,
  author       = {Richard Stallman and {contributors}},
  title        = {What is Free Software?},
  url          = {https://www.gnu.org/philosophy/free-sw.en.html},
  keywords     = {free-software},
  publisher    = {Free Software Foundation}
}
[1]
M. A. Clark, J. Choi, and M. Douglas, Biology, 2nd ed. OpenStax, Rice University, 2020. Available: https://openstax.org/details/books/biology-2e
[2]
J. S. Pearson, “Writing in the social and physical sciences,” in Contribute a verse: A guide to first year composition, T. L. Bennett, D. Gessell, and D. Edelman-Young, Eds., University of North Georgia Press, 2015, ch. 10, pp. 146–165. Available: https://ung.edu/university-press/books/introduction-to-first-year-composition/
[3]
B. Peirce, “On quantity, with special reference to collectional and mathematical infinity,” 1895.
[4]
G. Galilei, The assayer. Lynx, 1623.
[5]
G. Hartman, T. Siemers, B. Heinold, and D. Chalishajar, APEX calculus, Version 4.0. Self-published, 2018. Available: http://www.apexcalculus.com
[6]
R. H. Hammack, Book of proof, 3.4. Richard Hammack, 2018. Available: https://richardhammack.github.io/BookOfProof/
[7]
O. Levin, Discrete mathematics: An open introduction, 3th Edition, 5th Printing. Self-published, 2021. Available: http://discrete.openmathbooks.org
[8]
C. Stitz and J. Zeager, Essential precalculus. 2013. Available: https://opentext.uleth.ca
[9]
G. Hartman, Fundamentals of matrix algebra, 3rd ed. Self-published, 2011. Available: http://www.apexcalculus.com/other-texts
[10]
B. Yakir, Introduction to statistical thinking. The Hebrew University of Jerusalem; Self-published, 2011. Available: http://pluto.huji.ac.il/~msby/StatThink
[11]
S. Axler, Linear algebra done right, 4th ed. Springer, 2024. Available: https://linear.axler.net/
[12]
D. L. Poole and A. K. Mackworth, Artificial intelligence: Foundations of computational agents, 2nd ed. University of British Columbia; Cambridge University Press, 2017. Available: https://artint.info/
[13]
J. W. Cutrone, “Linear algebra from elementary to advanced specialization,” Johns Hopkins University; Coursera. Available: https://www.coursera.org/specializations/linear-algebra-elementary-to-advanced
[14]
E. W. Weisstein, “Wolfram MathWorld,” Wolfram Research. Available: https://mathworld.wolfram.com/
[15]
J. Erickson, Algorithms. Self-published, 2019. Available: https://jeffe.cs.illinois.edu/teaching/algorithms/
[16]
S. A. Cook, “The complexity of theorem-proving procedures,” in Proceedings of the third annual ACM symposium on theory of computing, in Stoc’71. Shaker Heights, Ohio, USA: Association for Computing Machinery, 1971, pp. 151–158. doi: 10.1145/800157.805047.
[17]
R. M. Karp, “Reducibility among combinatorial problems,” in Symposium on the complexity of computer computations, 1972. doi: 10.1007/978-1-4684-2001-2_9.
[18]
H. H. Hoos and T. Stützle, “SATLIB: The satisfiability library,” 2011, Darmstadt University of Technology. Available: https://www.cs.ubc.ca/~hoos/SATLIB/
[19]
B. Selman, H. Levesque, and D. Mitchell, “A new method for solving hard satisfiability problems,” in Proceedings of the tenth national conference on artificial intelligence, in Aaai’92. San Jose, California: AAAI Press, 1992, pp. 440–446. Available: https://dl.acm.org/doi/10.5555/1867135.1867203
[20]
I. P. Gent and T. Walsh, “Towards an understanding of hill-climbing procedures for SAT,” in Proceedings of the eleventh national conference on artificial intelligence, in Aaai’93. Washington, D.C.: AAAI Press, 1993, pp. 28–33. Available: https://dl.acm.org/doi/10.5555/1867270.1867275
[21]
B. Selman, H. A. Kautz, and B. Cohen, “Noise strategies for improving local search,” in Proceedings of the twelfth aaai national conference on artificial intelligence, in Aaai’94. Seattle, Washington: AAAI Press, 1994, pp. 337–343. Available: https://dl.acm.org/doi/10.5555/2891730.2891782
[22]
B. Selman and H. Kautz, “Domain-independent extensions to GSAT: Solving large structured satisfiability problems,” in Proceedings of the 13th international joint conference on artifical intelligence - volume 1, in Ijcai’93. Chambery, France: Morgan Kaufmann Publishers Inc., 1993, pp. 290–295.
[23]
B. Mazure, L. Saïs, and É. Grégoire, “Tabu search for SAT,” in Proceedings of the fourteenth national conference on artificial intelligence and ninth conference on innovative applications of artificial intelligence, in Aaai’97/iaai’97. Providence, Rhode Island: AAAI Press, 1997, pp. 281–285. Available: https://cdn.aaai.org/AAAI/1997/AAAI97-044.pdf
[24]
G. Reinelt, “TSPLIB: A library of sample instances for the TSP (and related problems).” Available: http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/
[25]
R. Stallman and contributors, “What is free software?,” Free Software Foundation. Available: https://www.gnu.org/philosophy/free-sw.en.html
[26]
R. Stallman and contributors, “GNU Emacs,” Free Software Foundation. Available: https://www.emacs.org/
[27]
R. Stallman and contributors, GNU Emacs manuals online. Free Software Foundation. Available: https://www.gnu.org/software/emacs/manual/
[28]
P. Stavrou, Available: https://protesilaos.com/
[29]
D. Wilson, “System crafters.” Available: https://systemcrafters.net/
NO_ITEM_DATA:stallman+2022
[31]
D. E. Knuth, “Literate programming,” The computer journal, vol. 27, no. 2, pp. 97–111, 1984, doi: 10.1093/comjnl/27.2.97.
[32]
P. Bruegel, “The tower of Babel,” 1563.
[33]
B. D’Arcus and contributors, “Citar.” Available: https://github.com/bdarcus/citar
[34]
B. D’Arcus et al., “Citation style language.” Accessed: Nov. 17, 2021. [Online]. Available: https://citationstyles.org
[35]
M. Vollmer et al., “It’s Magit! A Git porcelain inside Emacs.” Available: https://magit.vc/
[36]
J. Bernoulli, “Emacsair: Collecting frequent-committer miles.” Available: https://emacsair.me/
[37]
J. A. Donenfeld and contributors, “pass: The standard Unix password manager.” Available: https://www.passwordstore.org/
[38]
S. Sorensen, T. Calancha, and contributors, “Emacs password-store.” Available: https://git.zx2c4.com/password-store/tree/contrib/emacs
[39]
Z. Evgeny, “Telega manual.” Available: https://zevlg.github.io/telega.el/
[40]
“Telegram database library.” Available: https://core.telegram.org/tdlib

Emacs

Emacs

A freedom-respecting, user-programmable,

text-oriented computing environment.

Initialization (.emacs)

Bootstrap literate configuration.

Tangled to ~/.emacs.d/init.el.

  • Lexical binding (must be first)
  • Hide the toolbar
    (tool-bar-mode -1)
    
  • Debugging performance
    ;; Enable to debug startup performance.
    (when nil
      (defun my-load-log (file)
        (message "Loaded file: %s" file))
      (add-hook 'after-load-functions #'my-load-log))
    
  • Paths

    Copy the $PATH, $MANPATH, and LC_ALL variables from the interactive login shell to Emacs. This is necessary on macOS, as its GUI starts programs within a minimal environment.

    In practice, this fixes various problems, such as the *compilation* buffer not finding emacs, fortune not finding strfile, geiser-mode not finding guile, emacs not finding gcc for native compilation, as well as, spurious locale warnings.

    (when (eq window-system 'ns)
      (dolist (line (process-lines "bash" "-l" "-c" "export"))
        (when (string-match "^.* \\(.*?\\)=\\\"\\(.*\\)\\\"$" line)
          (let ((name (match-string 1 line))
                (value (match-string 2 line)))
            (setenv name value)
            (when (string= name "PATH")
              (setopt exec-path (split-string value path-separator)))))))
    

    (There is a package on MELPA for this, but it is unnecessarily complex, and we need to run this before the package system is initialized.)

    [I published this code for the wider Emacs community at StackOverflow.]

  • Enable inheritance

    Tangled directly to ~/.emacs to avoid

    org-babel-merge-params: No attachment directory for element (add :ID: or :DIR: property)
    
    (setopt org-attach-use-inheritance t)
    
  • Custom paths

    Use ID for directory name 29, with no splitting magic.

    Tangled directly to ~/.emacs to avoid

    org-babel-merge-params: No attachment directory for element (add :ID: or :DIR: property)
    
    (with-eval-after-load 'org-attach
      (setopt org-attach-id-to-path-function-list
              (list (defun my-org-attach-id-to-path (id)
                      (concat id "/")))))
    
  • Loading

    Always load the newest version of files.

    (Fixes the “newer than byte-compiled file; using older file” messages.)

    (setopt load-prefer-newer t)
    
  • TODO Learn to use package-vc

    Experiment. Enable the latest version from Git.

    N.B. org-directory cannot be used here yet.

    ;; (add-to-list 'load-path "~/org/src/org-mode/lisp")
    ;; (add-to-list 'load-path "~/org/src/doric-themes/")
    ;; (add-to-list 'load-path "~/org/src/modus-themes/")
    

    Finally, tangle Emacs Lisp from this file and load it.

  • Workaround: Tangling

    (Use IDs for tangle comments.)

    Stop Org from creating IDs.

    (setopt org-id-link-to-org-use-id 'create-if-interactive)
    
  • Workaround: PlantUML

    Hack.  Stop the PlantUML mode from contacting plantuml.com before configured for the first time.

    (setopt plantuml-server-url "")
    
  • Workaround: Python

    Hack.  Stop warning about “unable to guess Python indent”

    (setopt python-indent-guess-indent-offset-verbose nil)
    
  • Literate configuration

    Load the literate configuration file.

    (org-babel-load-file (file-name-concat org-directory "obtf.org"))
    
  • Pre-load files
    (find-file-noselect (file-name-concat org-directory "obtf.org"))
    

Recompilation

To recompile the entire Emacs from scratch, execute

true \
    && make extraclean \
    && ./autogen.sh \
    && ./configure \
           --with-tree-sitter \
           --with-sqlite3 \
           --with-native-compilation=aot \
    && make bootstrap --jobs 8 \
    && make install

Native compilation

To recompile all Lisp on load-path, execute

(native-compile-async load-path t)

Native compilation problems

ld: library 'emutls_w' not found
brew reinstall libgccjit

Similar projects

Funny

  • It takes lifetime

    It takes a lifetime to learn Emacs.
    The sooner you start, the longer it takes.

  • Tell me you use Emacs

    … without telling me you use Emacs

    I carry all my groceries with my pinky.

    pizzatorque @ Reddit

  • When you learn Emacs for the first time [video]
  • Misc
    • Cult of Undying Emacs (Reddit)
    • May your pinkies be free of suffering. (Reddit)

References

BibTeX

Alphabetically:

  • GNU Emacs [26]
    @Misc{stallman+a,
      author       = {Richard Stallman and {contributors}},
      title        = {{GNU} {Emacs}},
      publisher    = {Free Software Foundation},
      url          = {https://www.emacs.org/},
      keywords     = {free-software, emacs}
    }
    
  • GNU Emacs manuals online [27]
    @Manual{stallman+b,
      title        = {{GNU} {Emacs} Manuals Online},
      author       = {Richard Stallman and {contributors}},
      publisher    = {Free Software Foundation},
      url          = {https://www.gnu.org/software/emacs/manual/},
      keywords     = {free-software, emacs}
    }
    
  • Protesilaos Stavrou (Prot) [28]
    @Misc{stavrou-,
      author       = {Protesilaos Stavrou},
      url          = {https://protesilaos.com/},
      keywords     = {free-software/community, emacs/community}
    }
    
  • System crafters [29]
    @Misc{wilson-,
      author       = {David Wilson},
      title        = {System Crafters},
      url          = {https://systemcrafters.net/},
      keywords     = {free-software/community, emacs/community}
    }
    

Community

History

The original EMACS (“Editor Macros”) was a set of macros for the TECO editor, created in 1976 by David Moon and Guy Steele. The GNU Emacs was first released in 1985, by Richard Stallman, making it one of the oldest programs still in active development.

The wider Emacs ecosystem includes a large number of packages, including

  • text editors,
  • file managers,
  • mail clients,
  • terminal emulators,
  • and more.

Bug reports

Patches

  • TODO Write and contribute fortune-mode to Emacs
  • WAIT MacOS: Scale-down system Emoji font to mitigate layout problems (bug, bug)
  • DONE etc/NEWS: Fix unbalanced parentheses (bug, patch)
  • DONE Fix (NS) toolbar style and position (patch, bug)
  • DONE Release NS objects [co-authored]​ (bug, patch)
  • DONE NS: Correct the accessibility role of Emacs windows (patch, bug)
  • DONE Make sure Comint minibuffer password prompts end with space (code|bug)
  • DONE Ispell: Increase help timeout (code|bug)
  • DONE Ispell: Use “personal dictionary” terminology consistently (code|bug)
  • DONE Add Slovak QWERTY input method (code|bug)
  • DONE Improve code assistance for Scheme (code|bug)

Configuration

  • Identify myself
    (setopt user-full-name "Rudolf Adamkovič"
            user-mail-address "rudolf@adamkovic.org")
    
  • Ask before exiting

    Ask with yes-or-no-p before exiting or restarting Emacs.

    (setopt confirm-kill-emacs 'y-or-n-p)
    

Exiting and restarting

  • M-x save-buffers-kill-terminal RET (C-x C-c)
  • M-x restart-emacs RET

Anniversary

Be reminded of the day when I started using Emacs every year.

%%(org-anniversary 2021 04 05) /me starts using Emacs, %d year(s) ago

Shell

Show current working directory when executing commands

(setopt shell-command-prompt-show-cwd t)

Global keymap

Stored in global-map.

C-l is missing macros are missing too; split!

Files

C-x i Insert file contents at point  
C-x C-f Switch to or create buffer visiting file parent directories are created on save if needed

Narrowing

C-x n d Narrow to definition

Fundamentals

Mark and region

The transient-mark-mode highlights the region between the point and the active mark, which resembles the selection in other text editors. To deactivate the active mark, type C-g. To mark a position for future reference, type C-SPC C-SPC.

Pop mark repeatedly with C-u C-SPC C-SPC....

(setopt set-mark-command-repeat-pop t)

Note: M-k kills not just sentences but also “text blobs” that “look like” sentences, such as code or result blocks in the Org Mode.

To see line numbers temporarily, type M-g M-g. To see them permanently, type M-x display-line-numbers-mode. To see them permanently everywhere, such as when pair programming remotely, type M-x global-display-line-numbers-mode.

DONE Basic

Key Description
M-x execute command
M-: evaluate Emacs Lisp expression
C-/ undo
C-? redo
C-x z... repeat last command
C-q quote next character 32
C-g quit current action
C-x C-c quit Emacs

Related commands:

  • restart-emacs

Motion

Key Description Note
C-f go forward one character also <right> in LTR context
M-f go forward one word  
C-M-f go forward one expression  
C-n go forward one line also <down>
M-e go forward one sentence code statement  
C-<down> go forward one paragraph also M-}
C-v go forward one window height also M-<wheel-up>
C-b go backward one character also <left> in LTR context
M-b go backward one word  
C-M-b go backward one expression  
C-p go backward one line also up
M-a go backward one sentence or code statement  
C-<up> go backward one paragraph also M-{
M-v go backward one window height also M-<wheel-down>
C-a go to beginning of line  
M-m go to beginning of line content  
C-M-a go to beginning of definition  
M-< go to beginning of buffer  
C-e go to end of line  
C-M-e go to end of definition  
M-> go to end of buffer also <end>
M-g c go to character  
M-g g go to line number also M-g M-g

Killing and deleting

Key Description Note
C-d delete character forward or region
DEL delete character backward or region
C-w kill region a.k.a. cut
M-w save region as if killed a.k.a. copy
C-M-w append next kill to previous implicit for subsequent kills
M-DEL kill 33 to beginning of word or C-DEL
C-x DEL kill 33 to beginning of sentence  
M-d kill 33 to end of word  
C-k kill 33 to end of line beginning with C-u 0
M-k kill 33 to end of sentence  
M-z kill 33 to character (inclusive) the z stands for “zap”
C-S-DEL kill 33 current line  
C-y yank from kill ring pushes mark, with C-u exchange point/mark
M-y yank selected, push on kill ring pushes mark, with C-u exchange point/mark
M-y after yank yank previous, push on kill ring pushes mark

Mark and region (verified in global)

Key Description  
C-SPC push and activate mark 34  
C-u C-SPC... pop and go to mark repeat with C-SPC
C-x C-x exchange point and mark  
M-@ mark word  
M-h mark paragraph  
C-M-@ or C-M-SPC mark symbolic expression (“sexp”)  
C-M-h mark function definition (“defun”)  
C-x h mark buffer  

Lines

Key Description Note
C-j or RET insert newline  
C-m insert newline preserving syntax (e.g. indentation, comment, etc.)  
C-o insert newline without moving point open line
C-M-o insert newline without moving point, preserving indentation open line
M-^ join line or region of lines backward 35  
C-u M-^ join line forward  
C-S-DEL kill line  

Related commands:

  • reverse-region reverse the order of lines in region
  • sort-lines sort lines in region or file; reverse with C-u
  • delete-duplicate-lines delete duplicate lines in region (no need to sort); keep blanks with C-u C-u C-u

Whitespace

Key Description
M-\ delete whitespace around point
M-SPC cycle white space around point
C-x C-o collapse blank lines around point

Indenting and filling

Key Description  
C-M-\ indent region  
M-q indent definition or fill36 paragraph see also fill-column
C-x f set fill column  

Prose

Key Description
M-= count lines, sentences, words, and characters in region

Related commands:

  • repunctuate-sentences fix end-of-sentence punctuation forward

Case and capitalization

Key Description
M-u make word uppercase
M-l make word lowercase
M-c make word capitalized
C-x C-u 37 convert region to uppercase
C-x C-l 37 convert region to lowercase

Related commands:

  • capitalize-region

Commenting

Key Description Note
M-; add comment or un/comment lines DWIM
C-x C-; un/comment for non-empty line(s) dumb

Rectangles (rectangle-mark-mode)

Key Description Mnemonic
C-x SPC mark rectangle like C-SPC
C-x r k kill rectangle kill
C-x r y yank rectangle yank
C-x r M-w copy rectangle as kill like M-w
C-x r t edit rectangle type, also works on normal region

Transpositions

Key Description
C-t exchange characters around point
M-t exchange words around point
C-x C-t exchange current and previous line
C-m-t exchange current and previous sexp

Related commands:

  • M-x transpose-sentences
  • M-x transpose-paragraphs
  • M-x transpose-regions

Completions

Key Description  
M-/ complete word per surroundings dabbrev-expand

Counting

M-x count-words RET to

count lines, sentences, words, characters

in document or region

Configuration

Unbind some keys to avoid minimizing the frames by mistake or transposing letters everywhere unknowingly.

(put #'suspend-frame 'disabled t) ;; C-z, C-x C-z
(put #'iconify-frame 'disabled t) ;; s-m

Increase the undo limit from 160 kilobytes to 64 megabytes.

(setopt undo-limit (* 64 1024 1024))

Access OBTF headings quickly

Add C-c f to the global-map.

(defun my-org-find-note ()
  (interactive)
  (org-with-file-buffer "~/org/obtf.org"
    (org-with-wide-buffer
     (let ((org-refile-use-outline-path t)
           (org-refile-targets `((nil . (:maxlevel . 999)))))
       (save-mark-and-excursion
         (goto-char
          (let ((pa (org-refile-get-location "Note")))
            (org-refile-check-position pa)
            (nth 3 pa)))
         (let ((current-prefix-arg '(4)) ;; new buffer
               (org-indirect-buffer-display 'current-window))
           ;; NOTE If this is buggy (structural undo), copy and narrow.
           (call-interactively #'org-tree-to-indirect-buffer)))))))
(keymap-global-set "C-c f" #'my-org-find-note)

TODO SPLIT SPLIT SPLIT Configuration

……………. Mix

Use M-{ and M-} to move between paragraphs (instead of Org elements), to match other major modes.

;; (with-eval-after-load 'org
;;   (keymap-set org-mode-map "M-{" #'org-backward-paragraph)
;;   (keymap-set org-mode-map "M-}" #'org-forward-paragraph))

On macOS, if Emacs crashes after recompilation with

Killed: 9

due to

CODESIGNING 1 Taskgated Invalid Signature

then execute

$ codesign --force --deep --sign - nextstep/Emacs.app/Contents/MacOS/Emacs

(This should be a part of make install.)

Fix Org ctags issues?

(eval-after-load 'org-ctags
  (setopt org-ctags-open-link-functions nil))

Increase line spacing

(setopt line-spacing 0.1)

Follow symlinks

(setopt find-file-visit-truename t)

Emacs word wrapping

Key Description Note
C-x x t Toggle word wrapping truncate

Keep the length of lines below 80 characters for readability and compatibility.

Configure the graphical user interface (GUI)

Key Description Note
C-x C-M-= increase text size also s-= or C-<wheel-up> globally
C-x C-M-- decrease text size also s-- or C-<wheel-down> globally
C-x C-M-0 restore text size also s-0

(These are some horrible keyboard shortcuts.)

Do not use tabs for indentation. Also highlight them.

(setopt indent-tabs-mode nil)

Note: To remove tabs from a region, use untabify.

Make switch-to-buffer (C-x b) prompt with the previous buffer even if shown in another frame.

;;(defun other-buffer-advice (orig-fun &optional buffer visible-ok frame)
;;  (funcall orig-fun buffer t frame))
;;(advice-add 'other-buffer :around #'other-buffer-advice)

Remember the last editing position in files.

Saves the places to save-place-file.

(save-place-mode)

Treat sub-words as words.

The sub-word mode is handy to and jump in PascalCaseIdentifiers.

Enable the global minor mode and also diminish its mode line indicator.

(global-subword-mode)

Highlight URLs.

;; TODO Disable for Org (links are fontified with off-by-one underlines)
;; (global-goto-address-mode)

Open URLs with RET in addition to C-c RET.

Experiment. Temporarily disabled due to frequent accidental calls.

;; (keymap-set goto-address-highlight-keymap "RET" #'goto-address-at-point)
(setopt create-lockfiles nil
        use-dialog-box nil)

Replace the region upon typing over it.

(add-hook 'after-init-hook #'delete-selection-mode)

Do not blink the cursor.

Cons:

  • when switching windows, sometimes it is not clear which one is active
  • blinking is distracting

Pros:

  • With blink-cursor-blinks set to -1, the cursor blinks forever, which makes for a good “Emacs is busy” indicator.
;; (setopt blink-cursor-blinks -1)
(blink-cursor-mode -1)

Stop Customize from messing up any hand-written initialization files.

(setopt custom-file (make-temp-file "emacs-custom"))

(Do not use null-device, it breaks e.g. load-theme.)

Delete trailing whitespace on save.

(add-hook 'text-mode-hook #'delete-trailing-whitespace-mode)
(add-hook 'prog-mode-hook #'delete-trailing-whitespace-mode)

Enlarge the kill ring from \(120\) to \(2^{13} = 8192\) entries.

(setopt kill-ring-max 8192)

Do not add an entry to the kill ring if duplicates the top-most entry.

(setopt kill-do-not-save-duplicates t)

Do not add an entry to the kill ring if the entry consists just of whitespace.

(setopt kill-transform-function
        (lambda (string)
          (if (string-blank-p string) nil string)))

Save the (operating system) clipboard to the kill ring before replacing its content.

(setopt save-interprogram-paste-before-kill t)

Disable noisy warnings.

(setopt load-prefer-newer t
        byte-compile-warnings nil)

Do not show warnings during asynchronous native compilation.

(eval-when-compile (require 'comp))
(setopt native-comp-async-report-warnings-errors 'silent)

Use a single core, instead of half of them, to avoid choppiness.

(setopt native-comp-async-jobs-number 1)

Do not abbreviate Emacs Lisp outputs with ellipses.

https://www.gnu.org/software/emacs/manual/html_node/elisp/Output-Variables.html https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Eval.html

;; NOTE Not `custom'-settable.
(setq print-level nil
      print-length nil)
(setopt eval-expression-print-level nil
        eval-expression-print-length nil)

Define a TempEl template for quickly adding Emacs Lisp.

org-mode
(emacs
 "#+begin_src elisp :results silent" n
 q n
 "#+end_src")

Indent C-like languages, including Java, like this

some_function(
  some_argument,
  some_argument
)

instead of like this

some_function(
  some_argument,
  some_argument)
(add-hook 'c-mode-common-hook
          (lambda ()
            (c-set-offset 'arglist-intro '+)
            (c-set-offset 'arglist-close 0)))
  • Set the default font family, weight, and height

    Use the amazing Iosevka as the default font, and make it larger.

    (set-face-attribute 'default nil :family "Iosevka Term" :weight 'regular :height 200) ;; or 'medium
    (set-face-attribute 'fixed-pitch nil :family "Iosevka Term")
    

    (The Term variant has all characters at the same size; we use that to avoid “shifting” of text, e.g. in TUIs.)

  • Configure file name shadowing

    Render /a/b/~/c as ~/c and /a/b//c as /c in the minibuffer. Such paths are equivalent per the ancient Unix rules. In practice, shadowed paths make it easier to “restart” a path quickly when entering it manually.

    (with-eval-after-load 'rfn-eshadow
      (setopt file-name-shadow-properties
              '(invisible t intangible t)))
    
    (add-hook 'after-init-hook #'file-name-shadow-mode)
    

Mouse

Enable horizontal mouse scrolling. (Useful with touchpads.)

(setopt mouse-wheel-tilt-scroll t)

Enable context menus.

(add-hook 'after-init-hook #'context-menu-mode)

Do not scroll through images as if they were one line of text.

For my cheap mouse, it makes no difference. It actually does: images can be scrolled half-way through.

;;(pixel-scroll-precision-mode)

Display time

(with-eval-after-load 'time
  (setopt display-time-24hr-format t
          display-time-default-load-average nil
          display-time-format (progn (require 'org)
                                     (cdr org-time-stamp-formats))))
;; (add-hook 'after-init-hook #'display-time-mode)

Frame

A rectangular

screen area assigned to Emacs

by the operating system.

Content

Contains at least one window, with exactly one selected.

Key bindings

The key bindings defined in the global-map include:

Key Description  
C-x 5 0 delete frame  
C-x 5 1 delete other frames  
C-x 5 2 clone frame  
C-x 5 o switch to other frame  
C-x 5 b switch to buffer in other frame and select other frame
C-x 5 f find file in other frame and select other frame
C-x 5 d find directory in other frame and select other frame
C-x 5 p run project command in other frame and select other frame
C-x 5 5 run command in other frame and select other frame
M-<f10> toggle frame maximized or M-x toggle-frame-maximized RET

Configuration

  • Maximize on launch
    (add-hook 'after-init-hook
              (lambda ()
                (set-frame-parameter nil 'fullscreen 'maximized)))
    
  • Resize frames pixel-wise

    Resize the frame by pixels instead of by characters so that

    • a maximized frame fills the entire screen
    • Yabai can size frames precisely.
    (setopt frame-resize-pixelwise t)
    

Functions and macros

The relevant Elisp functions and macros to

  • Making
    • (make-frame &optional PARAMETERS)
    • (framep OBJECT)
  • Selecting
    • (selected-frame)
    • (with-selected-frame FRAME &rest BODY)
    • (select-frame FRAME &optional NORECORD)
    • (select-frame-by-name NAME)
  • Parameterizing
    • (frame-parameter FRAME PARAMETER)
    • (frame-parameters &optional FRAME)
    • (set-frame-parameter FRAME PARAMETER VALUE)
    • (modify-frame-parameters FRAME ALIST)
    • (modify-all-frames-parameters ALIST)

Window

An area inside of a frame used to display a buffer.

Belongs to exactly one frame.

Body and decorations

Left to right:

  1. left scroll bar
  2. left margin
  3. left fringe
  4. body
  5. right fringe
  6. right margin
  7. right scroll bar

Top to bottom:

  1. tab line
  2. header line
  3. body
  4. horizontal scroll bar
  5. mode line
  6. bottom divider

Key bindings

Defined in the global-map:

Key Description  
C-x 0 delete window  
C-x 1 delete other windows  
C-x 2 clone window below  
C-x 3 clone window to the right  
C-x o select other window w/ C-- in reverse, C-u N for offset
C-x 4 0 delete other window and kill its buffer  
C-x 4 c clone buffer in other window  
C-x 4 b switch to buffer in other window and select it
C-x 4 f find file in other window and select it
C-x 4 d find directory in other window and select it
C-x 4 p run project command in other window and select it
C-x 4 4 run command in other window and select it
C-x ^ enlarge window vertically 38
C-x } enlarge window horizontally 38
C-x { shrink window horizontally 38
C-x + balance windows  
C-x w t transpose layout  
C-x w f <left/right> flip layout horizontally  
C-x w f <up/down> flip layout vertically  

Functions and macros

  • (frame-selected-window &optional FRAME-OR-WINDOW)
  • (set-frame-selected-window FRAME WINDOW &optional NORECORD)
  • (with-selected-window WINDOW &rest BODY)

Selected frame

The exactly one frame that

has received input the most recently.

† For key bindings, functions, and macros, see frame.

Customizable variables

  • initial-frame-alist
    parameters applied to the initial frame
  • default-frame-alist
    parameters applied to all newly created frames

Normal hooks

  • before-make-frame-hook
  • after-make-frame-hook
  • after-setting-font-hook

Abnormal hooks

  • after-make-frame-functions
    called with the newly created FRAME

Parameterization

Identification:

  • name
  • title

Geometry:

  • left
  • top
  • width
  • height
  • min-width
  • min-height
  • fullscreen

Decorations:

  • left-fringe
  • right-fringe
  • right-divider-width
  • bottom-divider-width

Content:

  • foreground-color
  • font
  • line-spacing

Background:

  • background-mode
  • background-color
  • alpha-background

Cursor:

  • cursor-color
  • mouse-color

Misc:

  • alpha
  • minibuffer
  • undecorated
  • display

NeXTSTEP (now “macOS”):

  • ns-appearance
  • ns-transparent-titlebar

Selected window

The exactly one window that is

selected on the selected frame.

† For key bindings, functions, and macros, see window.

TODO Window configuration

Winner mode

A global minor mode that provides

undo-redo stack of window configurations.

Key bindings

Key binding Description Note
C-c <left> Undo window configuration  
C-c <right> Redo window configuration Works immediately after undo

Configuration

Enable the mode.

Enable the per-tab variant; see the Tab bar mode.

;; (add-hook 'after-init-hook #'winner-mode)

Buffer

Content displayed in a window.

Has exactly one major mode and zero or more minor modes.

Naming conventions

Each buffer has a unique buffer name.

NAME plain visiting file
*NAME* with earmuffs not visiting file
<SPC>NAME prefixed with space uninteresting for the user 39

Key bindings

Keys bound in the global-map:

Key Description  
C-x b switch to (possibly new) buffer… in selected window
C-x LEFT switch to previous buffer in selected window
C-x RIGHT switch to next buffer in selected window
C-x C-s save current buffer parent directories are created if needed
C-x k kill buffer…  
C-x x g revert buffer sync with backing file
C-x x r rename buffer…  
C-x x u rename buffer uniquely  

Renaming

Many commands create or update a named buffer, such as

Functions and macros

  • Buffer name
    • (buffer-name &optional BUFFER)
    • (buffer-file-name &optional BUFFER)
  • Buffer content
    • (buffer-string)
    • (buffer-substring START END)
    • (buffer-substring-no-properties START END)
  • Current buffer
    • (current-buffer)
    • (with-current-buffer BUFFER-OR-NAME &rest BODY)
  • Get or create buffer object
    • (get-buffer NAME)
    • (get-buffer-create NAME)

Current buffer

The exactly one buffer that is

displayed in the selected window.

† For key bindings, functions, and macros, see buffer.

Echo area

per-window

The area at the very bottom of the frame used to

  • show user messages

    with the message function, saved to the *Messages* buffer, and

  • special purposes, such as by the

    Minibuffer, Isearch, Vertico, Eldoc modes.

Related

  • message function
  • *Messages* buffer

Minibuffer

A special buffer and window shown in the echo area used for user input.

Runs the Minibuffer mode.

Configuration

  • Enable recursive minibuffers

    Recursive minibuffers enable interesting workflows, such as

    • yanking with selection/editing with M-y (yank-pop, calling yank-from-kill-ring)
    • using query-replace (M-%) on minibuffer content.
    (setopt enable-recursive-minibuffers t)
    (add-hook 'after-init-hook #'minibuffer-depth-indicate-mode)
    
  • Require short answers

    Take “y” for “yes” and “n” for “no”.

    (setopt use-short-answers t)
    

Prompting yes/no

(yes-or-no-p "Are you sure?")

Minibuffer mode

A major mode in used in the minibuffer.

Keymap

minibuffer-mode-map:

Key Description  
TAB complete input or show completion list  
RET or C-j complete or confirm complete input  
M-v select completions window  
<up> previous line or history item M-p for item
<down> next line or “future history” item M-n for item
C-r search history 40  
C-s search future history 40  
C-q quit  

Hooks

  • minibuffer-setup-hook
  • minibuffer-exit-hook
  • minibuffer-mode-hook

Configure

Make completions case insensitive.

Note. a.k.a. fold case

;; NOTE Not `custom'-settable.
(setq completion-ignore-case t)
(setopt read-file-name-completion-ignore-case t
        read-buffer-completion-ignore-case t)

Show completions immediately.

;; (setopt completion-eager-display t)

Completion list

A major mode in used for the *Completions* buffers.

Minibuffer mode.

Keymap

completion-list-mode-map

Configuration

Hide the unnecessary help message on the top of the completions buffer.

(setopt completion-show-help nil)

Enable efficient vertical scanning by showing completions in a single column and in a window with a constant height.

(setopt completions-format 'one-column
        completions-max-height 16)

Do not wrap long lines, that is truncate lines, in the completions buffer

Note.  Wrapped lines make tabular data, such as bibliographic references in Citar, do not look like “loose tea”.

(add-hook 'completion-list-mode-hook
          (lambda ()
            (setopt truncate-lines t)))

Sort completions by their position in the minibuffer-history.

(setopt completions-sort 'historical)

Update completions eagerly.

(setopt completion-eager-update t)

Keyboard macro

A sequence of

keys automatically typed

by Emacs.

Basic workflow

To record and execute a keyboard macro:

  1. Begin recording: C-x (
  2. Type some keys 41
  3. End recording: C-x )
  4. Execute: C-x e [e ...]

Automatic repeating

To re-execute a keyboard macro:

  • until the bell rings 42: type C-0 before executing
  • N > 0 times: type C-N before executing

Register

A temporary data storage in Emacs.

Key bindings: Point, windows, frames

C-x r SPC store point
C-x r w store windows
C-x r f store frames
C-x r j jump to stored point or restore windows/frames

… followed by some register.

Key bindings: Strings and numbers

C-x r s store a string in region
C-x r n store a number at point
C-x r + increment a stored number
C-x r i insert stored content into buffer

… followed by some register.

Configure: Disable previews

Auto-popups are annoying; use C-h to preview registers manually.

(setopt register-use-preview 'never)

Function

Command

An interactive function.

execute-extended-command (M-x)

Configuration

  • Hide irrelevant commands

    Hide commands in M-x that do not work with the current major mode.

    (setopt read-extended-command-predicate
            #'command-completion-default-include-p)
    

Enabling and disabling

To enable/disable a command for interactive use, execute

(put 'COMMAND 'disabled t)   ; disable
(put 'COMMAND 'disabled nil) ; enable

Interactive

Universal argument

An argument passed interactively to a function.

C-u DIGIT... COMMAND C-DIGIT... COMMAND

For example, C-u 10 C-p moves the point up 10 lines, passing 10 for ARG in (previous-line &optional ARG ....

Variable

Custom variable

Synonyms

  • User option

Point, mark, [active] region

The text between the point and the mark forms the region. After setting the first mark in a buffer, a region always exist in that buffer. To exchange the point and mark, type C-x C-x (exchange-point-and-mark).

Mode line

Configure the mode line.

Note: “Elements that are added to [the mode line] should normally end in a space (to ensure that consecutive ‘global-mode-string’ elements display properly)” NO_ITEM_DATA:stallman+2022.

Disable native scroll bars, as they follow fashion (becoming increasingly dumber and smaller).

(scroll-bar-mode -1)

Do not move point when scrolling.

(setopt scroll-preserve-screen-position 'point)

Show file size and column number in the mode line.

(size-indication-mode)

Save space

Hide superfluous whitespace and minor modes.

(setopt mode-line-compact t
        mode-line-collapse-minor-modes
        '(not flymake-mode
              overwrite-mode
              defining-kbd-macro))

Hook

A list of functions to call when some event occurs.

  • normal hook: named ...-hook takes no arguments
  • abnormal hook: named ...-functions takes arguments per its docs

remove-hook is interactive

Input method

Use the input methods to write in non-English languages, such as the slovak input method for the Slovak language.

Some useful options (for me):

  • tex for TeX mathematics,
  • emoji for Emoji pictograms,
  • slovak or slovak-querty for Slovak diacritics.

Use the TeX input method to enter non-ASCII characters, such as ellipsis (…) written as \ldots in TeX.

Table 7: Input methods and emoji
Key Description
C-u C-\ choose and enable input method
C-\ toggle input method
C-x \ toggle input method for one character
C-h C-\ describe input method
C-x 8 RET insert character
C-x 8 e s insert emoji
C-x 8 e r insert recent emoji

Remember the recently used input methods across Emacs sessions.

(with-eval-after-load 'savehist
  (add-to-list 'savehist-additional-variables
               'input-method-history))

Kill ring (kill, yank)

The kill ring resembles modern “clipboard managers” in that it keeps a stack of clipboard items.

Configuration

Major mode

A command that activates

the exactly one set of primary behaviors

for a buffer.

Adding the “propline”

  1. Set the major mode, if needed.
  2. Call (call-interactively #'add-file-local-variable-prop-line).

Automatic selection

  • (normal-mode)
  • (set-auto-mode 'keep-mode-if-same)

Automatic selection: Behind the scenes

Emacs determines the major mode as follows:

  1. First line43 is scanned for -*- mode: NAME -*- string.
  2. File name is matched against auto-mode-alist in .dir-locals.el.
  3. Shebang (#!) is matched against interpreter-mode-alist.
  4. Beginning of the buffer content is matched against magic-mode-alist.
  5. File name is matched against auto-mode-alist.
  6. Beginning of the buffer content is matched against magic-fallback-mode-alist.

Symbols

  • NAME-mode command
  • major-mode variable
  • NAME-mode-hook variable

Minor mode

A command that toggles

a secondary set of behaviors

for one or more44 buffers.

Global minor mode

A minor mode associated with more than one buffer.

Fundamental mode

The most general major mode, with

no specialized behaviors

and no hooks.

Text mode

A major mode for editing natural languages.

Disable spelling completions

Completions work well for code but not prose, so

(with-eval-after-load 'text-mode
  (setopt text-mode-ispell-word-completion nil))

Programming mode

An abstract major mode for editing code.

Customize

  • Customize line truncation

    Do not wrap long lines, as it breaks the structure for the reader.

    (with-eval-after-load 'prog-mode
      (add-hook 'prog-mode-hook
                (lambda ()
                  (setopt truncate-lines t))))
    
  • Customize display of line numbers

    Always show line numbers, as interpreters and compilers reference them often.

    (with-eval-after-load 'prog-mode
      (add-hook 'prog-mode-hook #'display-line-numbers-mode))
    

C

Key Description Note
C-M-a beginning of function definition akin beginning of sentence M-a
C-M-e end of function definition akin end of sentence M-e

Lisp

Configuration

  • Show the fill column

    Enable the Fill column indicator mode.

    (with-eval-after-load 'lisp-mode
      (add-hook 'lisp-mode-hook
                #'display-fill-column-indicator-mode)
      (add-hook 'lisp-data-mode-hook
                #'display-fill-column-indicator-mode))
    
  • Enable code folding

    Enable the Outline mode for code folding.

    (with-eval-after-load 'lisp
      (add-hook 'lisp-mode-hook #'outline-minor-mode)
      (add-hook 'lisp-data-mode-hook #'outline-minor-mode))
    

Column number

A global minor mode that

displays the column number in the mode line.

Configuration

  • Enable
    (add-hook 'after-init-hook #'column-number-mode)
    
  • Use 1-based column numbering

    Make the column indicator 1-based instead of 0-based.

    ;; Defined in `simple', so no `with-eval-after-load'.
    (setopt column-number-indicator-zero-based nil)
    

Calendar

A major mode for calendaring.

Entry points

  • M-x calendar RET

Key bindings

Selected Calendar key bindings defined in the calendar-mode-map.

Key Description Note
. go to today  
C-f go forward day also right
C-b go backward day also left
C-n go to next week also down
C-p go to previous week also up
o go to … asks for year/month
h show holiday name  
S show sunrise/sunset times  
q quit  

Configuration

  • Location

    Set the location to Bratislava, Slovakia.

    (with-eval-after-load 'calendar
      (setopt calendar-location-name "Bratislava, Slovakia"
              calendar-latitude 48.143889
              calendar-longitude 17.109722))
    
  • Date format

    Switch from the American date format to the ISO date format.

    (with-eval-after-load 'calendar
      (setopt calendar-date-style 'iso))
    
  • First day of the week

    Make weeks start on Monday instead of Sunday.

    (with-eval-after-load 'calendar
      (setopt calendar-week-start-day 1))
    
  • Marking today

    Mark the day representing today in the calendar.

    (with-eval-after-load 'calendar
      (add-hook 'calendar-today-visible-hook
                #'calendar-mark-today))
    
  • Marking holidays

    Mark the days representing holidays in the calendar.

    (with-eval-after-load 'calendar
      (setopt calendar-mark-holidays-flag t))
    
  • Local holidays

    Define the public holidays in Slovakia.

    (with-eval-after-load 'holidays
      (setopt holiday-local-holidays
              '((holiday-easter-etc +1 "Easter Monday")
                (holiday-easter-etc -2 "Good Friday")
                (holiday-fixed 01 01 "Establishment of the Slovak Republic")
                (holiday-fixed 01 06 "Epiphany")
                (holiday-fixed 05 01 "International Workers' Day")
                (holiday-fixed 05 08 "Victory over fascism")
                (holiday-fixed 07 05 "Saints Cyril and Methodius")
                (holiday-fixed 08 29 "Slovak National Uprising")
                (holiday-fixed 09 01 "Constitution Day, 1992")
                (holiday-fixed 09 15 "Day of Our Lady of the Seven Sorrows")
                (holiday-fixed 11 01 "All Saints' Day")
                (holiday-fixed 11 17 "Struggle for Freedom and Democracy")
                (holiday-fixed 12 24 "Christmas Eve")
                (holiday-fixed 12 25 "Christmas Day")
                (holiday-fixed 12 26 "St. Stephen's Day"))))
    
  • Other holidays

    Define other fun holidays.

    (with-eval-after-load 'holidays
      (setopt holiday-other-holidays
              '((holiday-fixed 01 01 "New Year's Day")
                (holiday-fixed 02 02 "Misc: Groundhog Day (US)")
                (holiday-fixed 02 12 "Darwin day")
                (holiday-fixed 03 08 "Misc: International Women's Day")
                (holiday-fixed 03 14 "Pi Day (03-14-YEAR)")
                (holiday-fixed 03 26 "National Science Appreciation Day (US)")
                (holiday-fixed 04 01 "Misc: April Fools' Day")
                (holiday-fixed 05 25 "Geek Pride Day")
                (holiday-fixed 05 25 "Towel Day")
                (holiday-fixed 06 01 "Misc: Children's Day")
                (holiday-fixed 12 24 "Festivus (Seinfeld)")
                (holiday-float 05 00 02 "Mother's Day")
                (holiday-float 06 00 03 "Father's Day")
                (holiday-float 11 04 04 "Thanksgiving (US)"))))
    
  • Selected holidays

    Select the holidays to show.

    (with-eval-after-load 'holidays
      (setopt calendar-holidays
              (append holiday-local-holidays
                      holiday-other-holidays)))
    

Package

Recipes

  • How to upgrade packages

    To upgrade the installed packages:

    1. Display the list of packages with M-x list-packages
    2. Type U to mark all upgradable packages
    3. Type x to execute the upgrades

Configuration

  • Repositories

    Enable the MELPA 45 package repository.

    (with-eval-after-load 'package
      (add-to-list 'package-archives
                   '("melpa" . "https://melpa.org/packages/"))
      (add-to-list 'package-archives
                   '("melpa-stable" . "https://stable.melpa.org/packages/")))
    
  • Upgrade built-in packages

    Upgrade built-in packages (in addition to 3rd party ones).

    (with-eval-after-load 'package
      (setopt package-install-upgrade-built-in t))
    
  • Fix GPG errors
    (with-eval-after-load 'package
      (setopt package-check-signature nil))
    
  • Reviews

    Temporarily disabled, as there is no “yes to all” when reviewing packages.

    Review package diffs before installation.

    ;; (with-eval-after-load 'package
    ;;   (setopt package-review-policy t))
    
  • Automatic installation

    Install the selected packages soon.

    (add-hook 'after-init-hook
              #'package-vc-install-selected-packages
              -99)
    
    (add-hook 'after-init-hook
              (lambda () (package-install-selected-packages t))
              -99)
    
    (add-hook 'after-init-hook
              (lambda () (package-vc-install-selected-packages))
              -99)
    

Agenda

  • WAIT Start using package-vc

    It will make it easier to contribute to Emacs packages.

    Waiting to be good enough:

    […] currently, built-in packages cannot be upgraded using package-vc-install.

    emacs#Fetching Package Sources

Isearch

A built-in minor mode in Emacs for interactive 46

search within the current buffer or active region.

Entry points

Entry point commands for Isearch bound in the global-map:

Key Description Note
C-s search for string forward repeatable
C-r search for string in reverse repeatable
C-M-s search for regexp forward repeatable
C-M-r search for regexp in reverse repeatable
M-% replace string in buffer or region 47 in reverse with C--
C-M-% replace regexp in b or region 47 in reverse with C--

Key bindings

Selected Isearch key bindings defined in the isearch-mode-map.

Key Description Note
RET exit 48
C-g revert to last successful search or abort  
C-s search forward  
C-r search backward  
C-q C-j insert newline quoted with C-q
M-e edit search string  
M-r toggle between literal and regexp search also M-s r
C-w extend match to word at point  
C-M-z extend match until character like zapping
M-s M-< go to first match  
M-s M-> go to last match  
M-s o preview all matches runs occur
M-s . search for symbol point dot as in xref
M-s M-. search for region or thing at point dot as in xref
M-s i toggle search invisible (e.g. folded)  

Note. To jump to the beginning of the match when searching forward, and vice versa, use C-r and C-s, respectively.

Configuration

  • Count matches

    Show the number of matches.

    (with-eval-after-load 'isearch
      (setopt isearch-lazy-count t))
    
  • Ignore diacritics

    Ignore diacritical marks.

    (with-eval-after-load 'isearch
      (setopt search-default-mode 'char-fold-to-regexp))
    
  • Ignore whitespace

    Ignore spaces, tabs, and newlines.

    (with-eval-after-load 'isearch
      (setopt isearch-lax-whitespace t
              search-whitespace-regexp "[ \t\r\n]+"))
    

Query replace

A built-in command in Emacs for

step-wise replacement of text

in the current buffer or active region.

Entry points

Entry point commands for query-replace bound in the global-map:

  • query-replace (M-%)
  • query-replace-regexp (C-M-%)

Keys

Selected query-replace key bindings defined in the query-replace-map.

Key Description Note
y replace and advance  
, replace and stay  
! replace remaining in buffer or region multi-buffer
Y replace remaining across buffers multi-buffer
n skip  
N skip to next buffer multi-buffer
C-l re-center  
u undo  
U undo all  
C-r recursive edit: pause C-M-c to resume
C-w recursive edit: delete match and pause C-M-c to resume
e edit replacement string, replace, and continue  
q exit also RET like in isearch

Re-centering with C-l works as well.

Histories

History support in query replace:

  1. Query string
    • history: previous query-replacement pairs
    • future history:
      1. the word at point, if any
      2. the last successful isearch string
      3. previous query and replacement strings
  2. Replacement string
    • history:
      1. the query string
      2. previous query and replacement strings
    • future history:
      1. the query string

(Search history with C-r and C-s, and navigate it with M-p and M-n.)

Patches

  • TODO Fontify key bindings in help
    • Reproduction steps:
      1. Execute query-replace (M-%).
      2. Enter any search and replace string.
      3. Type ? for help.
    • Expected results:

      Help with fontified key bindings.

    • Actual results:

      Help with key bindings that are not fontified.

Dired

A built-in major mode in Emacs that implements a

file manager.

Name

Dired stands for DIRectory EDitor.

Entry points

Defined in the global-map:

Key Description
C-x d open directory
C-x C-j jump to file associated with the current buffer

N.B. To just get the file name, type C-x C-f M-n.

Key bindings

Selected Dired key bindings defined in the dired-mode-map.

  • Display
    Key Description Note
    ( toggle details  
    s toggle sort by date prefix with C-u to sort by size
    * N show count and size numbers
    = diff file at point against…  
  • Navigation
    Key Description
    n next item
    p previous item
    ^ parent directory
  • Marks
    Key Description Note
    m mark * m
    u unmark 49  
    U unmark 49 all * !
    t toggle all * t
    % m regexp: mark mark
    % g regexp: mark containing grep
    * N number and size of marked files  
    w copy marked file names C-0 for absolute, C-1 for project-relative
  • Editing
    Key Description
    R rename or move
    C copy
    D delete
    Y symlink via relative path
    S symlink via absolute path
    M set mode (access bits)
    + create directory
  • Direct editing
    Key Description
    C-x C-q begin
    C-c C-c confirm
    C-c ESC abort
    • provided by the wdired minor mode
    • limited to one directory, unlike e.g. Oil in NeoVim
  • Opening and shelling out
    Key Description
    RET open file at point with Emacs
    W open file at point externally
    ! execute shell command on file(s)
    & execute shell command on file(s) asynchronously
  • Search and replace
    Key Description
    Q query and replace in files
    • save changes after Q with C-x s and !
    • to grep a directory, use find-grep-dired
  • Compression
    Key Description
    Z compress/decompress
    c compress together

    N.B. unzip in MacOS breaks Unicode!

    Do not use it to e.g. unzip Japanese music albums.

    It has been unfixed by Apple for many years.

    Instead, use MacOS proprietary GUI or

    ditto -V -x -k --sequesterRsrc --rsrc SOURCE.zip DESTINATION
    

Configuration

  • Hide details

    Hide file permissions, sizes, etc. by default.

    (with-eval-after-load 'dired
      (setopt dired-hide-details-hide-symlink-targets nil)
      (add-hook 'dired-mode-hook #'dired-hide-details-mode))
    
  • Hide temporary files

    Do not omit any files, such as C object files.

    (with-eval-after-load 'dired
      (setopt dired-omit-extensions nil))
    
  • Use trash

    Move to trash instead of deleting but not for remote file systems.

    (with-eval-after-load 'dired
      (setopt delete-by-moving-to-trash t
              remote-file-name-inhibit-delete-by-moving-to-trash t))
    
  • Ask less

    Copy and delete recursively without asking.

    (with-eval-after-load 'dired
      (setopt dired-recursive-copies 'always
              dired-recursive-deletes 'always))
    

    Kill buffers visiting deleted files without asking.

    (with-eval-after-load 'dired
      (setopt dired-clean-confirm-killing-deleted-buffers nil))
    
  • Stop creating too many buffers

    Disabled so that buffers are kept in other tabs and frames.

    Automatically kill intermediate directory buffers.

    ;; (with-eval-after-load 'dired
    ;;   (setopt dired-kill-when-opening-new-dired-buffer t))
    

    Do not open new windows when navigating with the mouse.

    (with-eval-after-load 'dired
      (keymap-set dired-mode-map "<mouse-2>" #'dired-mouse-find-file))
    
  • Enable direct editing of permissions

    Allow to edit permissions in wdired-mode (C-x C-q).

    (with-eval-after-load 'dired
      (setopt wdired-allow-to-change-permissions t))
    
  • Disable line wrapping

    Disable line wrapping to avoid “loose leaf tea” look.

    (with-eval-after-load 'dired
      (add-hook 'dired-mode-hook
                (lambda ()
                  (visual-line-mode -1)
                  (setopt truncate-lines t))))
    
  • Highlight the current line
    (with-eval-after-load 'dired
      (add-hook 'dired-mode-hook #'hl-line-mode))
    
  • Version control-aware renames

    When renaming version-controlled files, use the version control rename.

    (with-eval-after-load 'dired
      (setopt dired-vc-rename-file t))
    
  • Fix Apple crap

    Emulate the Unix ls command in Emacs Lisp because the one in MacOS:

    • cannot even output directories first and
    • cannot be patched easily.
    (with-eval-after-load 'dired
      (require 'ls-lisp))
    
    (with-eval-after-load 'ls-lisp
      (setopt ls-lisp-use-insert-directory-program nil
              ls-lisp-verbosity '(modes)
              dired-listing-switches
              (string-join '("--all"
                             "--group-directories-first"
                             "--human-readable"
                             "--no-group"))))
    

    Use ISO 8601 date-time format.

    • Always use the ISO 8601 date-time format, regardless of locale.
    • Always include the time, regardless of the last modification year.
    (setopt ls-lisp-use-localized-time-format t ; always use the below
            ls-lisp-format-time-list (make-list 2 "%Y-%m-%d %H:%M"))
    

    Hide MacOS garbage files quietly.

    (with-eval-after-load 'dired
      (setopt dired-omit-files "^\\.DS_Store\\|\\.localized$"
              dired-omit-verbose nil)
      (add-hook 'dired-mode-hook #'dired-omit-mode))
    
  • Guess command for video files

    Use the amazing MPV to open video files.

    (with-eval-after-load 'dired
      (add-to-list 'dired-guess-shell-alist-user
                   '("\\.\\(?:mpe?g\\|mp4\\|mkv\\|webm\\|avi\\)$" "mpv")))
    
  • Enable dual-pane operation

    Use Dired in the other window, if present, as the default target when copying, etc. 50

    (with-eval-after-load 'dired
      (setopt dired-dwim-target t))
    

Programmatic control

  • dired-get-marked-files

TRAMP

Syntax:

/PROTOCOL:[USER@]HOST:[PATH]

Example:

/scp:gpu:

Connects to gpu as per ~/.ssh/config.

Performance

Prefer scp over ssh.

Debugger

A built-in major mode in Emacs used for

debugging Emacs Lisp code.

Entry points

  • toggle-debug-on-error
  • debug-on-entry (choose function), cancel-debug-on-entry

Key bindings

Selected Debugger key bindings defined in the debugger-mode-map.

Key Description Mnemonic Note
d Step debug  
e Eval sexp in frame eval  
b Add breakpoint to frame breakpoint  
u Remove breakpoint from frame unmark [breakpoint]  
+ Multi-line pretty print   disable with -
. Unpack ellipsis   reset with g
RET Describe symbol at point enter docs  

Bell

Audible feedback 51 that

alerts the user to attract their attention,

and also

ends keyboard macro recording.

Ring

(ding)

Unit-testing

ding does not work in batch mode,

emacs --batch --eval \
'(let ((ring-bell-function (lambda () (print "ding")))) (ding))'


but it could be tested with cl-letf,

(require 'ert)
(should-error
 (progn (define-error 'ding "Ring my bell!")
        (cl-letf (((symbol-function 'ding)
                   (lambda () (signal 'ding nil))))
          (ding)))
 :type 'ding)
(ding)

Configuration

  • Be quiet

    Ring silently in the echo area.

    (defun my-ding ()
      (interactive)
      (message (propertize "Ding-dong!" 'face 'italic)))
    
    (setopt ring-bell-function #'my-ding)
    

Save Hist

A global minor mode to

save history variables

across Emacs sessions.

Configuration

  • Enable

    Enable the global minor mode.

    (savehist-mode)
    
  • Add selected rings

    Remember the content of some common built-in rings across Emacs sessions.

    (with-eval-after-load 'savehist
      (add-to-list 'savehist-additional-variables 'kill-ring)
      (add-to-list 'savehist-additional-variables 'search-ring)
      (add-to-list 'savehist-additional-variables 'regexp-search-ring)
      (add-to-list 'savehist-additional-variables 'mark-ring))
    
  • Add selected histories

    Remember some built-in histories across Emacs sessions.

    (with-eval-after-load 'savehist
      (add-to-list 'savehist-additional-variables 'file-name-history)
      (add-to-list 'savehist-additional-variables 'command-history)
      (add-to-list 'savehist-additional-variables 'extended-command-history)
      (add-to-list 'savehist-additional-variables 'shell-command-history)
      (add-to-list 'savehist-additional-variables 'compile-history))
    
  • Delete duplicates

    Delete duplicates when adding new history items.

    (setopt history-delete-duplicates t)
    

Commands

  • Delete history item

    Useful for redacting sensitive information and typos.

    (defun savehist-delete-history-item ()
      "Prompt for history variable and its element, then delete it."
      (interactive)
      (let* ((variables (append savehist-minibuffer-history-variables
                                savehist-additional-variables))
             (variable-name (completing-read "History variable: "
                                             variables
                                             nil
                                             t))
             (variable-symbol (intern variable-name))
             (variable (symbol-value variable-symbol))
             (element (completing-read "Element to delete: "
                                       variable
                                       nil
                                       t
                                       nil
                                       variable-symbol)))
        (set variable-symbol
             (delete element variable))))
    

Hi Lock

A built-in global minor mode in Emacs for

interactive highlighting of regular expressions.

Key bindings

Selected Hi Lock key bindings defined in the hi-lock-map.

Key 52 Description Note
C-x w . Highlight symbol at point  
C-x w h Highlight regexp defaults to active region
C-x w p Highlight regexp phrase defaults to active region
C-x w r Remove highlight… C-u to remove all

Configuration

  • Start automatically

    Enable the Hi Lock global minor mode on launch.

    (add-hook 'after-init-hook #'global-hi-lock-mode)
    
  • Auto-select colors

    Stop Hi Lock from asking about what face to use for highlighting.

    (with-eval-after-load 'hi-lock
      (setopt hi-lock-auto-select-face t))
    
  • Disable saving

    Stop Hi Lock from saving/loading patterns in file-local Hi-lock: comments.

    (with-eval-after-load 'hi-lock
      (setopt hi-lock-file-patterns-policy 'always))
    

Patches

  • WAIT Use active region for default values in more places (bug)
  • TODO Contribute string highlighting upstream

    Quoted highlight hi-lock-face-phrase-buffer (where to bind?):

    (defun my-hi-lock-string (string &optional face)
      "Set face of each occurrence of STRING to FACE."
      (interactive
       (list (hi-lock-read-regexp "String to highlight")
             (hi-lock-read-face-name)))
      (hi-lock-face-buffer (regexp-quote string) face))
    

Auto revert

A built-in global minor mode in Emacs that

automatically reloads buffers on external file changes.

Configuration

  • Decrease reload interval

    Reload buffers after 1 second instead of 5.

    (setopt auto-revert-interval 1)
    
  • Reload non-file buffers as well

    Reload non-file buffers that implement

    buffer-stale-function and revert-buffer-function,

    such as Dired,

    Note: If set after global-auto-revert-mode, it does not work.

    (setopt global-auto-revert-non-file-buffers t)
    
  • Revert buffers quietly

    Revert buffer contents silently.

    (setopt auto-revert-verbose nil)
    
  • Revert buffers without asking

    Revert buffers without asking, if they have no unsaved changes.

    (setopt revert-without-query '(".*"))
    
  • Enable

    Enable the global minor mode.

    (global-auto-revert-mode)
    

Fill

  • fill-paragraph (M-q)
  • fill-region
  • auto-fill-mode

Fill column

The column after which the line is wrapped by filling.

Configuration

Fill after the column 72 by default, as per:

Each line must be limited to 72 characters followed by the character sequence that denotes an end-of-line (EOL). The EOL sequence used by the RFC Editor will be the two-character sequence CR LF (Carriage Return followed by Line Feed). This limit includes any left-side indentation.

RFC 7994

For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

PEP 8: Style Guide for Python Code

(setopt fill-column 72)

Display fill column indicator

A minor mode that

displays a vertical rule, one column right of the fill column.

Warning: Non-idiomatic use

For now 53, we use the display fill column indicator to

show the maximum recommended line length

rather than the fill column.

Motivation

“Please keep the length of source lines to 79 characters or less, for maximum readability in the widest range of environments.”

GNU Coding Standards, Section 5.1

Limit all lines to a maximum of 79 characters. For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

PEP 8: Style Guide for Python Code

RFCs also use 72 for text, see e.g. RFC 7994

Entry points

  • M-x display-fill-column-indicator-mode RET (local)
  • M-x global-display-fill-column-indicator-mode RET (global)

Configuration

  • Set explicitly

    Show a vertical rule at column 79 by default, as per:

    “Please keep the length of source lines to 79 characters or less, for maximum readability in the widest range of environments.”

    GNU Coding Standards, Section 5.1

    Limit all lines to a maximum of 79 characters.

    PEP 8: Style Guide for Python Code

    (setopt display-fill-column-indicator-column 79)
    
  • DONE Report the problem with the Fill Column Indicator mode

    Fixed but I cannot find the commit; asked.

    notmuch-show:m25yat0z9a.fsf@me.com

Patches

  • TODO Add support for extra indicators

    Introduce a custom variable, such as

    display-fill-column-indicator-extra-columns,

    so we can have

    • fill column (72)
    • maximum recommended line length (79)

    highlighted 54

Display display line numbers

A minor mode that

displays line numbers on the left side of the window.

Entry points

  • M-x display-line-numbers-mode RET (local)
  • M-x global-display-display-line-numbers-mode RET (global)

Configuration

  • Increase default width

    Reserve space for 6 digits to

    avoid horizontally shifting content.

    (setopt display-line-numbers-width 6)
    

Hl Line

(add-hook 'after-init-hook #'global-hl-line-mode)

Browse URL

A typical call stack:

  1. browse-url-at-point is called (maybe)
  2. browse-url is called
  3. browse-url-handlers is consulted
  4. if no match, browse-url-default-handlers is consulted
  5. if no match, browse-url-browser-function is called

Default browser

Open URLs in LibreWolf.

(with-eval-after-load 'browse-url
  (setopt browse-url-browser-function #'browse-url-firefox
          browse-url-firefox-program "librewolf"))

Eldoc

Enable the global minor mode.

Now enabled by default.

Do not clear Eldoc after confirming a candidate.

(with-eval-after-load 'eldoc
  (with-eval-after-load 'corfu
    (eldoc-add-command #'corfu-insert)))

Update Eldoc on all Paredit commands.

(with-eval-after-load 'eldoc
  (with-eval-after-load 'paredit
    (dolist (command paredit-commands)
      (when (listp command)
        (eldoc-add-command (cadr command))))))

Outline

A minor mode

The mode binds keys under C-c @, but the cycling keys

Key Description
TAB show/hide current entry
S-TAB cycle: top headings, all headings, everything

The cycling keys works just with the point located on a heading line.

Configure!

Enable the simpler cycling keys, TAB and S-TAB.

(By default, the mode binds keys under C-c @.)

(setopt outline-minor-mode-cycle t)

Enable the minor mode.

(add-hook 'prog-mode-hook #'outline-minor-mode)

Do not collapse empty lines

(with-eval-after-load 'outline
  (setopt outline-blank-line t))

Show Paren

A minor mode that

highlights balanced pairs,

such as parentheses.

Configuration

  • Disable the global minor mode.

    (Reserve the mode for programming.)

    (with-eval-after-load 'paren
      (show-paren-mode -1))
    

    Highlight the parenthesized expression at point.

    (This is useful for not only Lisp but also other languages.)

    (add-hook 'prog-mode-hook #'show-paren-local-mode)
    

    Highlight parenthesized expressions without any delays.

    (with-eval-after-load 'paren
      (setopt show-paren-delay 0))
    

    Show context for offscreen parentheses in the echo area.

    (with-eval-after-load 'paren
      (setopt show-paren-context-when-offscreen t))
    

    Highlight entire expressions instead of just parentheses.

    (with-eval-after-load 'paren
      (setopt show-paren-style 'expression))
    

Flymake

M-n (flymake-goto-next-error) custom
Move point to the next error.
M-n (flymake-goto-prev-error) custom
Move point to the previous error.

Commands:

  • flymake-show-buffer-diagnostics
  • flymake-show-project-diagnostics

On-the-fly buffer checking

(require 'flymake)

Disable the legacy Flymake back end to avoid noise in logs.

(remove-hook 'flymake-diagnostic-functions
             'flymake-proc-legacy-flymake)

Use the keybindings recommended in the GNU Flymake manual.

(keymap-set flymake-mode-map "M-n" #'flymake-goto-next-error)
(keymap-set flymake-mode-map "M-p" #'flymake-goto-prev-error)

Smerge

A built-in minor mode for working with files with merge conflicts. Automatically enabled by Magit after typing RET on a file with conflicts.

Key Description
C-c ^ n move to the next conflict
C-c ^ p move to the previous conflict
C-c ^ u keep “upper” version
C-c ^ l keep “lower” version
C-c ^ a keep all
C-c ^ RET keep version at point

Hl Todo

Highlight TODO and similar keywords.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'hl-todo))
(add-hook 'after-init-hook #'global-hl-todo-mode)

SQLite

A built-in major mode for

visual editing of SQLite files.

Entry points

Agenda

  • TODO Learn the key bindings

SQL

A built-in major mode for

visual editing of SQLite code.

Configure

  • Set the SQL dialect

    Default to SQLite rather than ANSI SQL, as that is what we use.

    (with-eval-after-load 'sql
      (setopt sql-product 'sqlite))
    

My patches

  • DONE Update SQLite font-lock rules (bug, patch)

Agenda

  • TODO Learn the key bindings

Imenu

Configuration

  • No truncation

    Stop truncating items to 60 characters (unit test names, for example, can be longer).

    (with-eval-after-load 'imenu
      (setopt imenu-max-item-length nil))
    
  • No drill-downs

    Show a flat list instead of a drill-down hierarchy.

    (with-eval-after-load 'imenu
      (setopt imenu-flatten 'prefix
              imenu-level-separator " / "))
    
  • No dots

    Do not replace spaces with dots.

    (with-eval-after-load 'imenu
      (setopt imenu-space-replacement nil))
    
  • Automatic re-scanning

    Keep the menu up-to-date (and hide the *Rescan* item).

    (with-eval-after-load 'imenu
      (setopt imenu-auto-rescan t))
    

Eglot

Debugging

  • eglot-reconnect
  • eglot-shutdown
  • eglot-shutdown-all
  • eglot-events-buffer
  • eglot-code-actions

Key bindings

Key binding Description Mnemonic
M-. find definition key >
M-, return from find definition key <
C-M-. find apropos  
C-x 4 . find definition in other window  
C-x 5 . find definition in other frame  

Note: “apropos” means “names and signatures”.

Underline symbol at point

Eglot makes the symbol bold, but that is hard to see, especially with medium (not default) font weight.

(with-eval-after-load 'eglot
  (set-face-underline 'eglot-highlight-symbol-face t))

Fix “shifting text”

The default eglot-code-action-indicator “💡”

has a different height than normal text,

which makes the text shift vertically.

(with-eval-after-load 'eglot
  (setopt eglot-code-action-indicator "★"))

Reddit

Abbrev

Template

Given MODE, ABBREV, and EXPANSION:

(with-eval-after-load 'MODE-mode
  (define-abbrev-table 'MODE-mode-abbrev-table
    '(("ABBREV" "EXPANSION")))
  (add-hook 'MODE-mode-hook #'abbrev-mode))

Customize

  • Save silently
    (with-eval-after-load 'abbrev
      (setopt save-abbrevs 'silently))
    

Dabbrev

to match surrounding words …

M-/ dabbrev-expand expand word
C-M-/ dabbrev-completion complete word

Man

Tips:

(man “ascii”)

[mention woman as well; does not work on NS]

Non-standard capitalization

For historical reasons, the mode is capitalized Man-mode.

Focus window

When displaying a man page, make its window current.

(with-eval-after-load 'man
  (setopt Man-notify-method 'aggressive))

Woman

NS

Port.

Instrumentation

Before running Instruments 55, execute:

# Create NS "entitlement" for "task port" access, e.g. for debugger/profiler.
ENTITLEMENTS=entitlements.plist
cat > ${ENTITLEMENTS} << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.get-task-allow</key>
    <true/>
  </dict>
</plist>
EOF

# Force-sign (-f) Emacs libraries and application with ad‐hoc identity (-s -).
codesign -s - -f --entitlements ${ENTITLEMENTS} \
         nextstep/Emacs.app/Contents/MacOS/libexec/* \
         nextstep/Emacs.app

# Quick-run Emacs, without loading any configuration.
emacs -Q

Configuration

Disable the distracting icon in the title bar on Mac OS.

(setopt ns-use-proxy-icon nil)

View

A minor mode for viewing files.

Key binding Description Mnemonic
SPC Next page  
DEL Previous page  
< Beginning of buffer  
> End of buffer  
s Search forward  
r Search backward reverse
m Save position to register mark
Load position from register  
e Edit  

Transient

;; Transient is the library used to implement the keyboard-driven menus ;; in Magit. It is distributed as a separate package, so that it can be ;; used to implement similar menus in other packages.

Configuration

Enable C-n / C-p and C-s / C-r in transient.el popups.

(setopt transient-enable-popup-navigation t)

Bookmark

A marked position in a file.

(Similar to a register, hence mapped under C-x r.)

Key Command Description
C-x r m bookmark-set mark the position at point
C-x r b bookmark-jump jump to a bookmark

If used temporarily, clean up with bookmark-delete-all when done. To correct mistakes, use bookmark-rename and bookmark-delete.

Configure!

Save bookmarks immediately instead on kill-emacs.

Note. By default, the list of bookmarks resides in ~/.emacs.d/bookmarks, as per bookmark-default-file.

(with-eval-after-load 'bookmark
  (setopt bookmark-save-flag 1))

Project

A project managed by Emacs, namely project.el.

Table 8: Global keybindings
Key Description Mnemonic Note
C-x p f find file file like C-x C-f
C-x p g find regexp in files   then r to replace
C-x p r replace regexp in files replace  
C-x p D open root directory    
C-x p d find directory directory like C-x d
C-x p m open Magit    
C-x p t run or switch to terminal terminal new if prefixed C-u
C-x p b switch to buffer buffer like C-x b
C-x p k kill project buffers project like C-x k
C-x p p switch to project project  

Type M-n (the future history) to find file, its content, or a directory, matching the symbol at point.

Discover projects on launch.

(defcustom my-project-dir org-directory
  "The directory containing projects."
  :type '(string)
  :group 'my-project)
(defun my-project-discover-projects ()
  "Re-discover all projects in 'project-dir'."
  (interactive)
  (require 'project)
  (project-forget-zombie-projects)
  (project-forget-projects-under my-project-dir t)
  (project-remember-projects-under my-project-dir t))
(unless (project-known-project-roots)
  (my-project-discover-projects))

Use the entire project-prefix-map, instead of the commands listed in project-switch-commands, when switching the current project with C-x p p.

(with-eval-after-load 'project
  (setopt project-switch-use-entire-map t))

Trust .dir-locals in these projects.

(setopt safe-local-variable-directories
        (list (file-name-concat org-directory "src" "eg")))

Integrate with Magit

Re-bind56 C-x p v from project-vc-dir to Magit.

(with-eval-after-load 'project
  (keymap-set project-prefix-map "v" #'magit-project-status)
  (add-to-list 'project-switch-commands '(magit-project-status "Magit")))

Integrate with Vterm

Make C-x p [p..] t launch, or switch to, an in-project Vterm buffer.

(defun my-project-vterm ()
  "Start Vterm in the current project's root directory.
Modeled after `project-eshell', which see."
  (interactive)
  (defvar vterm-buffer-name)
  (let* ((default-directory (project-root (project-current t)))
         (vterm-buffer-name (project-prefixed-buffer-name "vterm"))
         (vterm-buffer (get-buffer vterm-buffer-name)))
    (if (and vterm-buffer (not current-prefix-arg))
        (pop-to-buffer-same-window vterm-buffer)
      (vterm t))))
(with-eval-after-load 'project
  (keymap-set project-prefix-map "t" #'my-project-vterm))

Xref

A built-in subsystem of Emacs for finding references, either specific to some major mode, if it provides the relevant Xref backend, or general, using a variant of grep. The specific references can be, for example, Lisp functions or LaTeX chapters.

Key bindings

  • Source buffer

    In a buffer with a major mode with Xref support:

    Key Description
    M-. go to definition
    M-, go back
    M-? find references

    Project search (C-x p g) also uses Xref.

  • Local

    Selected Dired key bindings defined in the xref--xref-buffer-mode-map.

    Key Description Note
    < go to first line or M-<
    C-n go to next line  
    C-p go to previous line  
    > go to last line or M->
    p show previous visible reference  
    n show next visible reference  
    P show first visible reference in previous group  
    N show first visible reference in next group  
    TAB visit reference and quit  
    RET visit reference  
    r query-replace all matches  
    g revert (refresh) buffer  
    q quit  

    Also see the Outline minor mode key bindings, notably C-TAB to see the overview.

Multiple buffers

Xref (re)uses a buffer named *xref*,

which must be renamed to stay around.

Configure

(with-eval-after-load 'xref

  ;; (C-x p g) project-find-regexp; with C-u, set directory and file name pattern
  (setopt xref-search-program 'ripgrep) ;; use ripgrep for C-x p g

  ;; Enable multi-line searching
  (add-to-list 'xref-search-program-alist
               (cons 'ripgrep
                     (concat (alist-get 'ripgrep xref-search-program-alist)
                             " --multiline")))

  ;; (setenv "RIPGREP_CONFIG_PATH" "~/")

  ;; add --threads 16 --hidden
  ;; logs the correct call someone filters out stuff in e.g. .emacs
  ;; perhaps project.el?
  ;; (with-eval-after-load 'xref
  ;;   (add-to-list 'xref-search-program-alist
  ;;                (cons 'ripgrep
  ;;                      "xargs -0 rg <C> --null -nH --no-heading --no-messages -e <R> --threads 16 --hidden")))

  )

Enable the Outline minor mode.

(with-eval-after-load 'xref
  (add-hook 'xref--xref-buffer-mode-hook #'outline-minor-mode))

Highlight current mode

(with-eval-after-load 'xref
  (add-hook 'xref--xref-buffer-mode-hook #'hl-line-mode))

.dir-locals file

A file that sets Emacs variables when editing the files in the containing directory or any of its subdirectories recursively.

The file contains a list of pairs in the form

(PAIR...)

where each PAIR either customizes a given major mode in any subdirectory

(MAJOR-MODE . ((VARIABLE-NAME . VARIABLE-VALUE)...))

or it customizes a given major mode in a specific subdirectory

(SUBDIRECTORY . ((MAJOR-MODE . ((VARIABLE-NAME . VARIABLE-VALUE)...))...))

Two common special cases exist:

  • If the MAJOR-MODE key equals nil, then the PAIR applies to all major modes.
  • If the VARIABLE-NAME key equals eval, then the VARIABLE-VALUE evaluates as executable code.

Explore!

Write a .dir-locals file that makes the project-compile command execute Make in the core subdirectory for all files in that subdirectory.

(("core" . ((nil . ((compile-command . "make -C core "))))))

File-local variables

At the beginning of file:

<nothing>
<nothing> # -*- variable1: value1; variable2: value2; ... -*-
<nothing>

At the end of file:

<nothing> # Local Variables:
<nothing> # variable1: value1
<nothing> # variable2: value2
<nothing> # End:

Make

A major mode for editing Make files.

Configuration

  • Show fill column
    (with-eval-after-load 'make-mode
      (add-hook 'makefile-mode-hook #'display-fill-column-indicator-mode))
    
  • Show whitespace

    In Make, tabs are significant, so visualize it.

    (with-eval-after-load 'make-mode
      (add-hook 'makefile-mode-hook #'whitespace-mode))
    

Fennel

A major mode for programming in Fennel.

Key bindings

Key Description Note
C-c C-z switch to/from REPL starts REPL if needed
C-c C-e evaluate top-level form at point  
C-c C-k reload current module other module with C-u

Configure

Install the packages.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'fennel-mode))

;; (let ((src-directory (file-name-concat org-directory "src" "fennel-mode")))
;;   (add-to-list 'load-path src-directory)
;;   (autoload #'fennel-mode (file-name-concat src-directory "fennel-mode") nil t)
;;   (add-to-list 'auto-mode-alist '("\\.fnl\\'" . fennel-mode)))

Enable LSP automatically.

(with-eval-after-load 'fennel-mode
  (add-hook 'fennel-mode-hook #'eglot-ensure))

Enable the improved “proto” (protocol-based) REPL.

(with-eval-after-load 'fennel-mode
  (add-hook 'fennel-mode-hook #'fennel-proto-repl-minor-mode))

Disable subpar Xref-like functionality in favor of TAGS.

(with-eval-after-load 'fennel-mode
  (keymap-set fennel-mode-map "M-." nil)
  (keymap-set fennel-mode-map "M-," nil))

Enable structural editing with Paredit.

(with-eval-after-load 'fennel-mode
  (add-hook 'fennel-mode-hook #'paredit-mode)
  (add-hook 'fennel-repl-mode-hook #'paredit-mode))

Add support for literate programming with Org Babel.

(add-hook 'after-init-hook
          (lambda ()
(with-eval-after-load 'org
  (when (featurep 'ob-fennel)
    (require 'ob-fennel)))))

Do not switch to REPL when reloading with C-c C-k (fennel-reload).

(with-eval-after-load 'fennel-mode
  (setopt fennel-mode-switch-to-repl-after-reload nil))

Show the fill column.

(with-eval-after-load 'fennel-mode
  (add-hook 'fennel-mode-hook
            #'display-fill-column-indicator-mode))

Enable code folding.

(with-eval-after-load 'fennel-mode
  (add-hook 'fennel-mode-hook #'outline-minor-mode))

Replace lambda followed by SPC with the lambda symbol λ.

(with-eval-after-load 'fennel-mode
  (define-abbrev-table 'fennel-mode-abbrev-table '(("lambda" "λ")))
  (add-hook 'fennel-mode-hook #'abbrev-mode))

Use the development version of fennel-ls in ~/src.

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               (cons 'fennel-mode
                     (list (file-name-concat org-directory
                                             "src"
                                             "fennel-ls"
                                             "fennel-ls")))))

Agenda

Paredit

A minor mode for that aids

structural correctness, navigation, and manipulation of Lisp code.

Author

Key bindings

Selected Paredit key bindings defined in the paredit-mode-map.

Key binding Description Note
(, [, { Insert a pair or wrap region in it C-u wrap many
M-( Wrap expression forward w/ C-u wrap many
M-s Unwrap expression a.k.a. splice
C-M-b Go expression backward  
C-M-p Go expression backward down  
C-M-u Go expression backward up  
C-M-f Go expression forward  
C-M-d Go expression forward down  
C-M-n Go expression forward up  
C-M-a Go to beginning of definition not Paredit, like C-a
C-M-e Go to end of definition not Paredit, like C-e
M-q Re-indent definition like M-q
C-M-q Re-indent expression forward not Paredit
C-M-SPC Mark expression forward like M-@, repeatable
C-M-@    
C-M-k Kill expression forward appends when repeated
C-M-DEL Kill expression backward appends when repeated
C-k Kill rest of expression forward  
M-S Split expression  
M-J Join left and right expressions  
M-R Raise expression also M-r
C-M-t Swap left and right expressions a.k.a. transpose
C-{ Push out first expression in list a.k.a. backward barf
C-} Push out last expression in list a.k.a. forward barf
C-( Pull in expression before list a.k.a. backward slurp
C-) Pull in expression after list a.k.a. forward slurp
C-M-x Evaluate definition e.g. procedure, test
C-M-h Mark definition e.g. procedure, test

Important. To insert a backslash (\) followed by a newline, type \ followed by C-j (paredit-newline).

Agenda

  • TODO Install paredit-everywhere

Configuration

  • Installation

    Install the package.

    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'paredit))
    
  • Activation

    Enable Paredit for

    What happens is:

    1. submit-emacs-patch calls lm-maintainers on NEWS
    2. lm-maintainers activates emacs-lisp-mode
    3. emacs-lisp-mode activates paredit-mode
    4. paredit-mode calls check-parens
    5. check-parens errors out and leaves cursor somewhere
    6. back in submit-emacs-patch, the mail is messed up
    (add-hook 'after-init-hook
              (lambda ()
                (with-eval-after-load 'lisp-mode
      (add-hook 'lisp-mode-hook #'paredit-mode)
      (add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
      (add-hook 'eval-expression-minibuffer-setup-hook #'paredit-mode))
    
                ))
    

Tips

  • When a kill/yank breaks balance, use C-q ( and C-q ) to fix it.

Contributions

  • WAIT [PATCH] paredit.el: Submit sexp on RET in eval-expression

    A temporary re-definition:

    (with-eval-after-load 'paredit
      (defun paredit-RET ()
        "Default key binding for RET in Paredit Mode.
    Normally, inserts a newline, like traditional Emacs RET.
    With Electric Indent Mode enabled, inserts a newline and indents
      the new line, as well as any subexpressions of it on subsequent
      lines; see `paredit-newline' for details and examples."
        (interactive)
        (cond
         ((minibufferp)
          (funcall (lookup-key minibuffer-mode-map (kbd "RET"))))
         ((paredit-electric-indent-mode-p)
          (let ((electric-indent-mode nil))
            (paredit-newline)))
         (t (newline)))))
    
  • WAIT Report Paredit not working in Eval Expression
  • TODO Fix Paredit shadowing M-s (the search map)
  • WAIT Suggest various improvements

    notmuch-show:m2o7wptzs6.fsf@me.com

    (E-mail communication with Taylor R Campbell, the author of Paredit.)

    https://paredit.org/cgit/paredit/plain/NEWS

    Paredit 25:

    • M-r (paredit-raise-sexp) now respects active mark in Transient Mark Mode.

    Other suggestions:

    • allow barfing of marked expressions
    • raising expressions along with nearby comments
    • allow joining of marked expressions

    https://paredit.org/cgit/paredit/plain/CREDITS

  • TODO Suggest a better new-line behavior

    \ followed by C-j inserts newline after \

    why not \ followed by RET?

  • TODO Suggest a better un-commenting

    Consider

    ;; <point>(foo)
    

    Then C-M-@ to mark the expression (foo) and M-; (paredit-comment-dwim). Do what I mean, i.e. uncomment the marked expression.

    Actual:

    ;; ;; (foo)
    

    Expected:

    (foo)
    

BibTeX

A major mode for editing BibTeX files.

Note. Do not set bibtex-maintain-sorted-entries to t to avoid errors, as the entries are “in tangled order”.

(with-eval-after-load 'bibtex
  (setopt bibtex-entry-format t
          bibtex-align-at-equal-sign t))

PlantUML

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'plantuml-mode))

Use the local PlantUML installation, instead of contacting plantuml.com.

(with-eval-after-load 'plantuml-mode
  (setopt plantuml-default-exec-mode 'executable))

;; (with-eval-after-load 'ob-plantuml
;;   (setopt org-plantuml-exec-mode 'plantuml))

Help mode

A built-in major mode in Emacs for viewing and navigating documentation.

Table 9: Help
Key Description Mnemonic
C-h R open manual read
C-h m describe active modes mode
C-h o describe symbol  
C-h f describe function function
C-h x describe command as per M-x
C-h v describe variable variable
C-h P describe package package
C-h C-\ describe input method as per C-\
C-h k describe key key
C-h b describe all bound keys bound keys
C-h l view interaction history “lossage”

See also the describe-face command.

To find a variable by its value, use apropos-value.

To view the recent input keystrokes, type C-h l or execute view-lossage.

To show the keybindings for the current unfinished command, called prefix, type C-h. For example, to see what keys can follow C-x, type C-x followed by C-h.

help-mode:

Key Description Mnemonic
l Navigate back left
r Navigate forward right
TAB Next link  
< Go to beginning like M-<
> Go to end like M->
SPC Page down  
S-SPC Page up  
s Find source code source
q Quit quit

Automatically select the help window when created.

(setopt help-window-select t)

Do not wait before echoing unfinished commands.

(setopt echo-keystrokes 1e-6)

Dictionary

A built-in major mode in Emacs for looking up words and phrases across dictionaries.

Entry point: M-x dictionary-search.

Related:

Configuration

On free and open-source systems, such as Debian Linux, install dict-* packages, such as dict-gcide with the community-maintained version of Webster’s 1913 dictionary or dict-moby-thesaurus Moby Thesaurus II.

The macOS jail, with its Homebrew, does not seem to offer any dictionary packages for dictd (RFC 2229), so use the Internet.

(with-eval-after-load 'dictionary
  (setopt dictionary-server "dict.org"))

Experiment.

Use the familiar Help mode for the interface.

Note. This does not work quite yet; reported:

notmuch-show:m2pm6nz5n0.fsf@me.com

(with-eval-after-load 'dictionary
  (setopt dictionary-search-interface 'help))

Calc

A built-in major mode in Emacs that provides a

programmable scientific calculator.

Entry point

Key Description
C-x * * toggle calculator
C-x * g grab region as vector
C-u C-x * g grab region as algebraic expression

Key bindings

Selected Calc key bindings defined in the calc-mode-map.

  • Stack
    Key Description Mnemonic
    TAB swap two topmost items  
    RET duplicate topmost item  
    DEL delete topmost item delete
    C-/ undo  
    y insert topmost item to other buffer  
    C-k kill topmost item or item at point kill
    M-k copy topmost item or item at point 57 kill
    q hide quit
  • Arithmetic
    Key Description Mnemonic
    + pop a pair of values, push their sum  
    - pop a pair of values, push their difference  
    * pop a pair of values, push their product  
    / pop a pair of values, push their quotient  
    n pop a value, push its additive inverse negation
  • Statistics
    Key Description Mnemonic
    u + pop a vector, push the sum of its scalars 58 +
    u M pop a row vector, push its arithmetic mean mean
    u S pop a row vector, push its standard deviation standard deviation
    u C pop a pair of vectors, push their covariance covariance

Configuration

  • Simplify user interface

    Hide non-essential user interface elements:

    • the startup message in the echo area
    • the “Emacs Calculator Mode” banner on top of the stack
    • the all-inputs “trail” window.
    (with-eval-after-load 'calc
      (setopt calc-inhibit-startup-message t)
      ;; NOTE Not `custom'-settable.
      ;; For many variables, Calc uses `defcalcmodevar' instead of `defcustom'.
      (setq calc-show-banner nil
            calc-display-trail nil))
    
  • Copy without line numbers

    Kill and copy as kill 59 without line numbers.

    (with-eval-after-load 'calc
      (setopt calc-kill-line-numbering nil))
    

Patches

  • Capitalize metric prefixes uniformly (bug, patch)

SQLite

A built-in major mode in Emacs that provides for editing SQL code.

TODO Update the list of keywords

TODO Make the null argument work in Org source blocks

TODO Avoid highlighting

SELECT "This is date";

Unexpected: “date” is highlighted as a type

World clock

A built-in major mode in Emacs that provides a world clock.

Entry point:

Run with elisp:world-clock, quit by typing q.

A message for the people sending “UTC offsets”:

Please, always tell your IANA time zone, so that we avoid DST and other issues. For example, Australia/Perth and not AWST (UTC+8). It is the best approach when working across time zones. If you do not know how to find your IANA time zone using your computer, use the website https://time.is/.

(with-eval-after-load 'time
  (setopt world-clock-buffer-name "*world clock*"))
(with-eval-after-load 'time
    (setopt world-clock-list
            '(;; ("America/Boise" "Aaron King - Enfluid")

              (;; "Europe/Paris"
               ;; "Europe/Dublin"
               ;; "Australia/Perth"
               ;; "Australia/Sydney"
               "America/Los_Angeles"    ; includes Seattle
               ;; "America/Anchorage"
               ;; "America/New_York"
               ;; "America/Edmonton"
               ;; "Europe/Zurich"
               "Mike Mee - Egghead Games")

              ("Europe/Dublin" "MTU Ireland")
              ("Europe/London" "Aljazeera London")
              ;; ("Asia/Riyadh" "Abdurahman 'Abe' Itani")
              )))

Highlight the current line

(with-eval-after-load 'time
  (add-hook 'world-clock-mode-hook #'hl-line-mode))

Comint

Enable colors.

(with-eval-after-load 'comint
  (setopt ansi-color-for-comint-mode t)
  (add-hook 'comint-output-filter-functions
            #'ansi-color-process-output))

Compilation

A major mode and minor mode in Emacs that aids

recognition of errors and warnings

on standard output (stdout) or error output (errout).

Alternatives

Compilation is a more advanced alternative to

  • M-! (shell-command)
  • M-& (async-shell-command).

Entry points

  • M-x compile RET
  • project-compile (C-x p c) for projects.
  • M-x recompile RET

Interactive mode

Execute with C-u to

enable standard input (stdin)

interactions via Comint.

Key bindings, major

Selected Compilation key bindings defined in the compilation-mode-map.

Key Description Note
TAB Go to next error  
n Go to next error locus auto-skips duplicates
M-n Go to next error locus, no select auto-skips duplicates
S-TAB Go to previous error  
p Go to previous error locus auto-skips duplicates
M-p Go to previous error locus, no select auto-skips duplicates
g Recompile C-u to edit command
C-c C-k Kill compilation (not the buffer)
q Quit  
SPC Clear buffer custom

Key bindings, minor

In other buffer:

Key Description
M-g n Go to next error locus
M-g p Go to previous error locus

Given a compilation error, its locus represents the position in the corresponding source file that caused it.

Concurrent compilations

At any point in time

at most one *compilation* buffer exists,

with compile and project-compile expecting the user to

rename *compilation* buffers

to run multiple compilations, where

  1. if the selected window hosts a buffer in Compilation major mode,

    its compilation process is replaced;

  2. else, if any window hosts a buffer named *compilation*,

    its compilation process is replaced;

  3. else,

    a new window and buffer named *compilation* is created

    and the compilation process runs there.

Configuration

  • Speed up long outputs
    ;; NOTE Not `custom'-settable.
    (setq read-process-output-max (* 64 1024 1024)
          process-adaptive-read-buffering nil)
    
  • Enable colors
    (with-eval-after-load 'compile
      (setopt ansi-color-for-compilation-mode t)
      (add-hook 'compilation-filter-hook
                #'ansi-color-compilation-filter))
    
  • Restart and save without asking
    (with-eval-after-load 'compile
      (setopt compilation-always-kill t
              compilation-ask-about-save nil))
    
  • Auto-scroll, stopping at the first error
    (with-eval-after-load 'compile
      (setopt compilation-scroll-output 'first-error))
    
  • When a compilation starts, select its window
    (with-eval-after-load 'compile
      (add-hook 'compilation-start-hook
                (lambda (process)
                  (select-window (get-buffer-window
                                  (process-buffer process))))))
    
  • Disable the confusing underlines
    (with-eval-after-load 'compile
      (setopt compilation-message-face 'italic))
    
  • Make SPC clear the buffer
    (with-eval-after-load 'compile
      (keymap-set compilation-mode-map
                  "SPC"
                  #'comint-clear-buffer))
    
  • Add completion support
    (with-eval-after-load 'compile
      (defun my-compilation-read-command (command)
        (completing-read "Compile command: " compile-history
                         nil nil nil
                         (if (equal (car compile-history) command)
                             '(compile-history . 1)
                           'compile-history)
                         command)))
    
    (with-eval-after-load 'compile
      (advice-add #'compilation-read-command
                  :override
                  #'my-compilation-read-command))
    

    Based on the idea in Issue 531.

  • Jump by matches, not errors

    By default, n and p jump by errors, not all matches, e.g. stack frames.

    (with-eval-after-load 'compile
      (setopt compilation-skip-threshold 0))
    

Patches

Compilation Shell

A minor mode with a subset of Compilation functionality, typically used in inferior process buffers.

C-M-p (compilation-previous-error)
Move point to the previous error.
C-M-n (compilation-next-error)
Move point to the next error.
RET (compilation-goto-error)
Visit the source of the error at point.

Diff

With VC, accessible via the vc-diff command, mapped to C-x v = globally and = in the vc-dir-mode.

Structural navigation

p and n
Move to the previous hunk or next hunk, respectively.
Commands: diff-hunk-prev and diff-hunk-next
[ C-u ] RET
Jump the corresponding source line.
With C-u, jump the old version.

Structural editing

s custom

Split the hunk at point.
Command: diff-split-hunk

Originally C-c C-s:

(with-eval-after-load 'diff-mode
  (keymap-set diff-mode-map "s" "C-c C-s"))
k
Kill the hunk at point.
Command: diff-hunk-kill

VC integration

v custom

Commit the diff.
Command: vc-next-action

Originally C-x v v:

(with-eval-after-load 'diff-mode
  (keymap-set diff-mode-map "v" "C-x v v"))

Tab bar

A global minor mode that shows buttons on top a frame that allow switching between window configurations.

Note. Not to be confused with the similarly looking Tab Line mode that focuses on buffers in windows instead of window configurations in frames.

Intuition

Similar to registers with window configurations but named, ordered, and visual.

Key bindings

Enabled for all frames with M-x tab-bar-mode RET.

Key Description Note
C-x t 2 duplicate tab like C-x 2
C-x t b ... new tab w/ buffer like C-x 4 b
C-x t f ... new tab w/ file like C-x 4 f
C-x t d ... new tab w/ directory like C-x 4 d
C-x t p ... new tab w/ project command like C-x 4 p
C-x t t ... new tab w/ command like C-x 4 4
C-x t RET switch to (possibly new) tab also <wheel-up>
C-TAB next tab also <wheel-up>
C-S-TAB previous tab also <wheel-down>
[C-u N] C-x t m move tab right, N times also S-<wheel-up>
C-u - [N] C-x t m move tab left, N times also S-<wheel-down>
C-x t r rename tab to …  
C-x t RET find tab named …  
C-x t 0 close tab like C-x 0
C-x t 1 close other tabs like C-x 1

Configuration

  • Enable

    Show the tab bar.

    (add-hook 'after-init-hook #'tab-bar-mode)
    
  • Automatic hiding

    Hide the tab bar if only one tab exists.

    (with-eval-after-load 'tab-bar
      (setopt tab-bar-show 1))
    
  • Enabled numbered shortcuts

    M-NUMBER like old Linux tty-s.

    (with-eval-after-load 'tab-bar
      (setopt tab-bar-tab-hints t
              tab-bar-select-tab-modifiers '(meta)))
    
  • History

    Enable per-tab history; replaces the Winner mode.

    (add-hook 'after-init-hook #'tab-bar-history-mode)
    

Agenda

  • TODO Fontify tab hints

    Use fontification to make tab hints more visible and easier to scan.

    • tab-bar-tab-hints custom variable
    • tab-bar-tab-name-format-hints function

Electric pair

(add-hook 'after-init-hook #'electric-pair-mode)

Rcirc

Run IRC with M-x irc RET.

Messages:

Command Description
/join CHANNEL join CHANNEL
MSG send a public message for everyone
NICK: MSG send a public message for NICK
/query NICK start a private conversation with NICK

Note. As for /query, “You don’t need to join any channels, if you just want to have one-to-one conversations with friends on the same network.” (Rcirc manual, 1.2)

To debug the client,

  1. set the rcirc-debug-flag variable to t and
  2. watch the *rcirc debug* buffer.

Configure

Set some defaults.

(with-eval-after-load 'rcirc
  (setopt rcirc-default-nick "rudy1983"
          rcirc-default-user-name "rudy1983"
          rcirc-default-full-name "Rudolf Adamkovič"))

Use SASL (Simple Authentication and Security Layer) to make LiberaChat work over VPNs and the like.

(with-eval-after-load 'rcirc
  (setopt rcirc-authinfo
          `(("irc.libera.chat"
             sasl
             ;; BUG The following field is not used.
             ;; To reproduce:
             ;; 1. Unset 'rcirc-default-user-name' above.
             ;; 2. Set 'rcirc-debug-flag' to 't'
             ;; 3. Watch "*rcirc debug*".
             "this-is-ignored"
             ,(password-store-get "libera-chat")))))

Automatically connect to interesting channels on LiberaChat.

(with-eval-after-load 'rcirc
  (setopt rcirc-server-alist
          `(("irc.libera.chat"
             :channels ("#emacs"
                        "#org-mode"
                        "#fennel"
                        "#systemcrafters")))))

Show mode-line notifications for new messages.

(with-eval-after-load 'rcirc
  ;; TODO Contribute this; [] is not shown initially but then stays.
  ;; either run this function or change 'rcirc-activity-string'
  ;; default.  Or, do not show anything where there are no
  ;; notifications (perhaps make two patches).
  (add-hook 'rcirc-mode-hook #'rcirc-track-minor-mode))

Notify only about the messages that mention us.

(with-eval-after-load 'rcirc
  ;; NOTE Not `custom'-settable.
  (setq-default rcirc-ignore-buffer-activity-flag t))

Hide uninteresting informational messages, such as people joining or quitting, for all but recently active nicks.

(with-eval-after-load 'rcirc
  ;; (add-to-list 'rcirc-omit-responses "AWAY")
  (add-hook 'rcirc-mode-hook #'rcirc-omit-mode))

Note.  To reconnect, execute the /reconnect command.

Contribute

  • Make (rirc) work non-interactively

    The after-init-hook requires a lambda with (rcirc nil).

LaTeX

A major mode for editing LaTeX files.

Do not fontify subscripts and superscripts.

(with-eval-after-load 'tex-mode
  (setopt tex-fontify-script nil))

Define TempEl templates for some common environments.

org-mode
(eq "\\begin{equation*}" n "  " r n > "\\end{equation*}")
(align "\\begin{align*}" n> "  " r n > "\\end{align*}")
(gather "\\begin{gather*}" n "  " r n > "\\end{gather*}")

Define TempEl template for “where tables”.

org-mode
(where > "#+attr_html: :border 0 :rules none" n
       > "|   <r> |     <r> | <l>     |" n
       > "| where | \\(" p "\\)    | is " p " |" n
       > "|       | \\(" p "\\)    | is " q " |")

Graphviz

Graphviz

Install the Emacs package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'graphviz-dot-mode))

Note. Homebrew* Graphviz configuration

installs the actual program.

Indent with 2 instead of 4 spaces.

(with-eval-after-load 'graphviz-dot-mode
  (setopt graphviz-dot-indent-width 2))

This is currently unused:

<<dot-theme>>

newrank = true  // rank across clusters
compound = true // allow edges between clusters

fontname = "Charter"
node [fontname = "Charter"]
edge [fontname = "Charter"]

Usage with noweb:

d094d068-e094-4ed5-98ec-f1d04053ffe0.svg

Face

Key bindings

C-u C-x = (describe-char)

Theme

Configuration

(add-hook 'after-init-hook
          (lambda ()
            ;; (load-theme 'tomorrow-night-deepblue)
            ;; (ef-themes-select 'ef-cherie)
            ;; (modus-themes-select 'modus-operandi-tinted)
            (progn
              (require 'doric-themes)
              (doric-themes-select 'doric-dark))))

Modus Themes

Highly legible built-in themes by Prot.

Commands

  • modus-themes-select
  • modus-themes-list-colors-current

Installation

Install the latest version, as the built-in version lags behind.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'modus-themes))

Configuration

  • Enable bold and italic

    Improve legibility with bold and italic styling.

    (setopt modus-themes-bold-constructs t
            modus-themes-italic-constructs t)
    

Patches

  • DONE Fix incorrect face inheritance affecting AUCTeX (pull)

Bug reports

  • DONE Propose to improve documentation regarding completion frameworks (mail)
  • DONE Propose to style Org plain lists with checkboxes (mail)
  • DONE Propose to scale Org titles (mail)
  • SKIP Improve shading of LaTeX environments (issue)

Doric Themes

Highly legible minimalist themes by Prot.

Patches

Bug reports

  • TODO Add missing Telega faces
    • For example telega-reaction-chosen is jarring.
    • Reproduction steps: (1) ! on a message, (2) choose a reaction.
  • TODO Add support for ERC

    Only one ERC face (erc-prompt-face) is listed in doric-themes.el.

Installation

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'doric-themes))

Configuration

  • TODO Highlight calendar events in

Org

A built-in major mode in Emacs that implements

a universal information organizer.

Recipes

  • Set starting number in lists

    To start a numbered list with a number other than one, type

    2. [@2] two
    3. three
    4. four
    

Key bindings

  • Visibility and narrowing
    Key Functionality Note
    TAB cycle visibility of subtree w/ C-u C-u, restore initial
    S-TAB cycle visibility of buffer w/ C-<NUM>, to level NUM
    C-c TAB show just children  
    C-c C-k show just branches  
    C-x n s narrow to subtree widen with C-x n w

    Point stays in place after S-TAB, so to fold everything but the heading at point, type S-TAB followed by TAB.

    Indirect buffers and sparse trees:

    Key Functionality Note
    C-c C-x b narrow to subtree in indirect buffer w/ C-u another one
    C-c / create sparse tree … quit with C-c
    C-c / t create TODO sparse tree quit with C-c

    Use completions to quickly navigate to any heading with org-goto.

  • Emphasis
    Key Description
    C-c C-x C-f * add bold emphasis
    C-c C-x C-f / add italic emphasis
    C-c C-x C-f _ add underline emphasis
    C-c C-x C-f = add verbatim emphasis
    C-c C-x C-f ~ add code emphasis
    C-c C-x C-f + add strike-through emphasis
  • Plain lists
    Key binding Description Mnemonic Note
    M-RET insert item    
    C-c C-x r promote item right same as headings
    C-c C-x R promote subtree Right same as headings
    C-c C-x l demote item left same as headings
    C-c C-x L demote subtree Left same as headings
    C-c C-c toggle checkbox   DWIM (Do What I Mean)
    C-c C-c renumber items   DWIM (Do What I Mean)
    C-c * convert to headlines    
    C-c - convert from headlines    
  • Blocks
    Key Description
    C-c C-, insert block
    C-c ' edit block with best major mode

    When inserting a block, Org offers the structure templates stored in the variable org-structure-template-alist.

  • Scheduling
    Key Description Mnemonic
    [C-u] C-c . Add or set active date[-time] stamp point in time
    [C-u] C-c ! Add or set inactive date[-time] stamp  
    [C-u] C-c C-s Set [or remove] SCHEDULED timestamp schedule
    [C-u] C-c C-d Set [or remove] DEADLINE timestamp deadline

    To create a date/time range, type C-c . immediately after the creation or change of a timestamp.

    Time range:

    Org uses and extends the Emacs calendar for date/time entry.

    Key Description
    C-v next 3 months in the calendar
    M-v previous 3 months in the calendar
    C-. go to today

    Org pre-selects today as the selected date. To confirm the selected date, type RET. To specify some other date, enter yyyy-mm-dd for year yyyy, month mm, and day dd, as per ISO 8601. To specify a date relative to the selected date, enter a query per the table below.

    Query Description
    yyyy-mm-dd on day yyyy-mm-dd
    hh:mm at time hh:mm
    hh:mm-hh:mm from hh:mm to hh:mm
    hh:mm+t from hh:mm to t hours after
    hh:mm+tu from hh:mm to t units u after
    d closest future day d
    m-d closest future day d in month m
    mon closest mon-day in the future
    -mon closest mon-day in the past
    Nmon N-th closest mon-day in the future
    -Nmon N-th closest mon-day in the past
    +N N days in the future
    -N N days in the past
    +Nw N weeks in the future
    -Nw N weeks in the past

    Org outputs the matching date and indicates the future with (=>F). To specify the time, append h:m for h hours and m minutes.

    To repeat, write

    <YYYY-MM-DD ddd [" " HH ":" MM ["-" HH ":" MM]] RNU>
    


    where R denotes the repeater type,
      N denotes the number of units, and
      U denotes the unit.

    For the repeater type R, + repeats N units U after the original date, .+ repeats N units U after marked done, and ++ repeats after kN units U after the original date with the least positive k that falls into the future. For the unit U, y represents a year, m a month, w a week, d a day, and h an hour.

    For more complicated queries, use the diary- functions. For example, the 3rd Saturday every month is <%%(diary-float t 6 3)>.

  • Paragraphs
    Key Functionality Note
    C-<down> go to next paragraph  
    C-<up> go to previous paragraph  
    M-<up> move line(s) up region-aware, like headings/lists
    M-<down> move line(s) down region-aware, like headings/lists
  • Structural navigation

    The key bindings for navigating the outline match the standard key bindings for navigating plain text.

    Key Functionality Mnemonic Note
    M-} next element, same kind    
    C-c C-n next heading next like C-n
    C-c C-f next heading, same level forward like C-f
    M-{ previous element, same kind    
    C-c C-p previous heading previous like C-p
    C-c C-b previous heading, same level back like C-b
    C-c C-u parent heading up  
    C-c C-j jump to heading jump  
  • Structural editing
    Key Description Mnemonic Note
    M-RET insert heading at point   TODO with S-
    C-RET insert heading at end of subtree   TODO with S-
    C-c C-q toggle tag    
    C-c C-x p set property property  
    C-c C-x o toggle ORDERED property ordered  
    M-<up> move subtree(s) backward    
    M-<down> move subtree(s) forward    
    M-<right> promote heading(s)   or C-c C-x r
    M-S-<right> promote subtree   or C-c C-x R
    M-<left> demote heading(s)   or C-c C-x l
    M-S-<left> demote subtree   or C-c C-x L
    C-c C-w refile subtree   like C-w
    C-c M-w refile subtree copy   like M-w
    M-h mark (next) element   repeatable

    The kill ring key bindings match the standard Emacs key bindings for plain text. These work anywhere in the subtree, not just on headings.

    Key Description Notes
    C-c @ mark subtree  
    C-c C-x C-w kill (cut) subtree like C-w
    C-c C-x M-w copy subtree like M-w
    C-c C-x C-y yank (paste) subtree like C-y, w/ C-u below, w/ C-u C-u as child
    C-c C-x DEL delete subtree like DEL, custom
    C-c ^ sort subtree by … also works in tables

    Just the level 1, 2, and 3 headings appear in the table of contents and have prominent titles throughout the document. Any further headings do not.

  • Tables
    Key Functionality Note
    <down> go to next column  
    <up> go to previous column  
    M-<up> move row(s) up region-aware, like headings/lists
    M-<down> move row(s) down region-aware, like headings/lists
    M-S-<down> insert row also C-o like open line/row
    M-S-<up> delete row  
    TAB go to next column  
    S-TAB go to previous column  
    M-<right> move column right like headings/lists
    M-<left> move column left like headings/lists
    M-S-<right> insert column  
    M-S-<left> delete column  
    C-c + sum column  
    C-c \vert{} convert region to table CSV/TSV/space-separated
    C-c reformat table DWIM (Do What I Mean)

    Useful functions:

    • org-table-transpose-table-at-point (swap rows and columns)
    • org-lint
    • Formulas

      $1, $2, … are the columns

      formatting: round column 1 to 4 decimal places: #+tblfm: $1=$1;%.4f

  • Tasks
    Key Description Notes
    C-S-RET insert TODO item extends C-RET, w/ C-u below
    C-c C-t toggle or advance TODO state on S-<left> and S-<right> on heading
    C-c , set priority  
    C-c C-c update [/] or [%] progress indicator  
    C-c C-x o Toggle ordered property  
  • Misc
    Key Description  
    C-c C-e save export region or document prefix with C-u to re-export
    C-c C-x C-v toggle inline images  


    Key Description
    M-; toggle COMMENT (heading or code)
    C-c ; toggle COMMENT (heading)
    C-^ sort headings, lists, tables, etc.

    (COMMENT headings do not export or tangle.)

    An image with a thumbnail:

    Insert a Table of Contents section:

    #+TOC: headlines 2
    

    Insert a page break to LaTeX exports:

    #+LATEX: \clearpage
    
  • Mathematics (LaTeX)

    LaTeX fragments and environments

    C-c C-x C-l works as follows:

    Context Description
    fragment at point toggle preview for fragment
    no fragment at point show previews for section or region
    with C-u hide previews for section
    with C-u C-u show previews for buffer
    with C-u C-u C-u hide previews for buffer
  • Links
    • Selected keybindings
      Key Description
      RET or C-c C-o follow link at point
      C-c & go back
      C-c C-l insert link
      C-c M-l insert last stored link

      C-c C-l offers the last link copied with org-store-link (C-c l) but also file: links with completion.

      To follow links in tables, one must use C-c C-o instead of RET.

    • Contribute!
    • Org ID
      • Complete ID links

        Add minimal completion for ID links.

        1. Type C-c C-l (org-insert-link).
        2. Type id RET.
        3. Select the target heading.
        4. Type the description, followed by RET.
        ;; (with-eval-after-load 'org
        ;;   (org-link-set-parameters
        ;;    "id"
        ;;    :complete (lambda (&optional arg)
        ;;                (concat "id:"
        ;;                        (org-id-get-with-outline-path-completion)))))
        
    • Custom global keybindings
      (keymap-global-set "C-c l" #'org-store-link)
      
    • Use RET to follow the link at point
      (with-eval-after-load 'org
        (setopt org-return-follows-link t))
      
    • LaTeX/Org configuration

      Note: No CDLaTeX mode here because it adds too much magic for too little gain.

      Render LaTeX using SVG and at \(2 \times\) scale.

      Note: TikZ previews do not work with dvisvgm. The previews do work with imagemagick, but they render at \(1 \times\) scale and thus become blurred.

      (with-eval-after-load 'org
        (setopt org-preview-latex-default-process 'dvisvgm)
        ;; "Transparent" to go with the `org-block' face.
        (plist-put org-format-latex-options :background "Transparent")
        (plist-put org-format-latex-options :scale 2))
      

      Preview PDF files inside of Emacs.

      (with-eval-after-load 'org
        (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs)))
      

      Do not pollute the file system with LaTeX image previews (ltximg).

      (with-eval-after-load 'org
        (setopt org-preview-latex-image-directory
                (file-name-concat org-directory
                                  (file-name-as-directory "tex"))))
      
  • Clocks
    Key Context Description Mnemonic
    C-c C-x C-i heading Clock in in
    C-c C-x C-o heading Clock out out
    C-c C-x C-j anywhere Find last clock jump
    C-c C-c clocked time Recalculate duration DWIM

    Related to clocking (timers):

    C-c C-x ; set timer

    Tips:

    • The timer expects H:MM:SS but accepts M and MM as well.
    • To see the remaining time in the mode line, start the timer first and clock in second.
    • Clock tables

      To insert a clock table, type C-c C-x x and choose clocktable, or execute M-x org-clock-report RET. To update a clock table, type C-c C-c on its +BEGIN line.

    • Mode line indicator

      When clocked in, the mode-line indicator shows the total clocked time by default. This can be changed by setting the CLOCK_MODELINE_TOTAL property, or equivalently the org-clock-mode-line-total variable. The interesting values are current, to see the time of the current clock, and today, to see the total time clocked today.

    • Contribute
      • DONE org-clock: Improve the clock-out message [mail]​ [patch]​
      • TODO Fix superfluous whitespace in clock tables

        With :step day, the first line is always blank.

    • Configure!

      Remember the running clock and the clock history across Emacs sessions.

      (Org suffers from NIH, and so saving org-clock-history or org-clock-stored-history does not work with the savehist mode.)

      (with-eval-after-load 'org-clock
        (setopt org-clock-persist t))
      
      (with-eval-after-load 'org
        (org-clock-persistence-insinuate))
      

      Automatically remove clocks started by mistake and stopped after a couple of seconds.

      (with-eval-after-load 'org-clock
        (setopt org-clock-out-remove-zero-time-clocks t))
      

      Measure duration only in hours and minutes, not days, hours, and minutes.

      (Important for invoices.)

      (with-eval-after-load 'org
        (setopt org-duration-format 'h:mm))
      
  • Footnotes

    To define and reference a footnote, type [fn:NAME] as in

    To print "Hello, World!" [fn:1] …
    …
    [fn:1] Per the tradition, started by Brian Kernighan in 1978.
    
    Key Description
    C-c C-x f Jump to, add, sort, normalize, or delete footnote(s) †
    C-c C-c Jump to definition or reference

    † With a footnote at point, the command behaves just like C-c C-c. To see the available actions, which enables deleting the footnote, for example, call the command with the universal prefix C-u.

    Place footnotes at the end of the current section instead of the special “Footnotes” section at the end of the document.

    (with-eval-after-load 'org
      (setopt org-footnote-section nil))
    
  • Images

    Use the first #+attr_ width for images.

    (with-eval-after-load 'org
      (setopt org-image-actual-width nil))
    

Configuration

  • Speed commands [configuration]
    • Enable

      Enable speed commands.

      (with-eval-after-load 'org
        (setopt org-use-speed-commands t))
      
    • Custom commands
      (with-eval-after-load 'org
        (add-to-list 'org-speed-commands '("s" . org-schedule))
        (add-to-list 'org-speed-commands '("d" . org-deadline))
        (add-to-list 'org-speed-commands '("w" . org-copy-subtree))
        (add-to-list 'org-speed-commands '("y" . org-paste-subtree))
        (add-to-list 'org-speed-commands '("&" . org-mark-ring-goto)))
      

      Prefix org-paste-subtree with C-u to paste below.

  • Disable archiving

    I keep archiving by mistake, so disable

    C-c C-x C-a org-archive-subtree-default
    C-c C-x C-s org-archive-subtree

    by

    (with-eval-after-load 'org
      (put 'org-archive-subtree 'disabled t)
      (put 'org-archive-subtree-default 'disabled t))
    
  • TODO Set org-default-notes-file
  • ???
    (with-eval-after-load 'org
      (setopt org-goto-interface 'outline-path-completion
              org-goto-max-level 100
              org-outline-path-complete-in-steps nil))
    
  • ???

    Remove the validation link from HTML exports.

    (with-eval-after-load 'org
      (setopt org-html-validation-link ""))
    
  • ???

    Export with curly quotes.

    (with-eval-after-load 'org
      (setopt org-export-with-smart-quotes t))
    
  • Enable mouse navigation

    Disabled. This breaks the o (open link) speed command.

    ;; (with-eval-after-load 'org
    ;;   (require 'org-mouse))
    
  • ???

    Bind “go to next/previous link” to M-n and M-p, as recommended in the manual.

    (with-eval-after-load 'org
      (define-key org-mode-map (kbd "M-n") #'org-next-link)
      (define-key org-mode-map (kbd "M-p") #'org-previous-link))
    
  • ???

    Reuse the current window to edit source code with C-c ' (org-edit-special). Also, do not ask about returning to said window.

    (with-eval-after-load 'org
      (setopt org-src-ask-before-returning-to-edit-buffer nil))
    
  • ???

    Do not align property keys/values.

    (with-eval-after-load 'org
      (setopt org-property-format "%s %s"))
    
  • ???

    Disable implicit logging of state changes. Instead, require explicit setting of the LOGGING property. Further, when explicitly enabled, log into LOGBOOK drawers instead of directly under headings.

    (Note that this also includes the completion of repeated tasks that may not change state visibly.)

    (with-eval-after-load 'org
      (setopt org-log-into-drawer t
              org-log-repeat nil))
    
  • Simplify DONE fontification

    Do not fontify the entire DONE headline.

    (setopt org-fontify-done-headline nil)
    
  • Fix capitalization of EFFORT property

    Strangely, the EFFORT property is capitalized as Effort, unlike any other property.

    ;; NOTE Not `custom'-settable.
    (setq org-effort-property "EFFORT")
    
    • TODO Contribute this upstream
  • ??? (this is not even Org)

    Do not lower subscripts or raise superscripts, as it causes misaligned tables.

    ;; (with-eval-after-load 'tex-mode
    ;;   (dolist (face '(superscript subscript))
    ;;     (set-face-attribute face nil :height 'unspecified)))
    
  • Left-align tags

    Place tags directly after the heading title, instead of attempting to right-align them.

    (with-eval-after-load 'org
      (setopt org-tags-column 0))
    
  • Use expert interfaces
    (with-eval-after-load 'org
      (setopt org-use-fast-todo-selection 'expert))
    
  • Enable Markdown export

    Enable the Markdown exporter.

    (with-eval-after-load 'ox
      (require 'ox-md))
    
  • Markdown mode (not even Org)

    Install the Markdown mode for editing Markdown exports.

    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'markdown-mode))
    
  • Enable syntax highlighting in HTML exports

    Syntax-highlight code in HTML exports.

    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'htmlize))
    
  • ???
    (with-eval-after-load 'org-fold
      (setopt org-fold-catch-invisible-edits 'show-and-error))
    

    Make C-RET insert a new heading after the current line instead of after the current subtree.

  • Improve syntax highlighting

    Crank up fontification efforts for

    • LaTeX fragments
    • Org entities
    • Org quote and verse blocks.
    (with-eval-after-load 'org
      (setopt org-fontify-quote-and-verse-blocks t
              org-highlight-latex-and-related '(native entities)))
    

    Note. org-highlight-latex-and-related does not include script, as it caused

    nondeterministic fontification issues

    with verbatim, code, or links containing underscores.

  • ???

    Show inline images and respect #+attr_org when sizing them.

    (with-eval-after-load 'org
      (setopt org-startup-with-link-previews t))
    
  • ???

    Move the point with C-a and C-e intelligently. In headings, skip the leading stars, to-do keywords, and trailing ellipsis. In plain lists, skip the bullets and checkboxes. To go to the beginning or end of line proper, type C-a or C-e again.

    (with-eval-after-load 'org
      (setopt org-special-ctrl-a/e t))
    
  • ???

    Show more context when jumping to headings, for example, from Org Agenda.

    (setopt org-fold-show-context-detail t)
    
  • Speed commands
    • Learn!

      These commands are active when the point is at the first star of a heading. To jump there, type C-c C-p/n, for example.

      Speed commands, available when

      Key Description    
      n Go to next heading next like C-c C-n
      p Go to previous heading previous like C-c C-p
      f Go to next sibling forward like C-c C-f
      b Go to previous sibling backward like C-c C-b
      u Go to parent heading up like C-c C-u
      j Jump to heading jump like C-c C-j
      o Open link open like C-c C-o
      F Go to next block   repeatable with f
      B Go to previous block   repeatable with b
      D Move subtree down    
      U Move subtree up    
      R Promote subtree Right like C-c C-x R
      r Promote heading right like C-c C-x r
      L Demote subtree Left like C-c C-x L
      l Demote heading left like C-c C-x l
      k Kill subtree    
      w Copy subtree as if killed   custom, prefix with count
      y Yank killed subtree   custom, C-u below
      i Insert heading    
      t Advance to-do state    
      , Set priority    
      s Set scheduled date   custom, unset w/ C-u
      d Set deadline   custom, unset w/ C-u
      e Set effort time   like C-c C-x e
      : Set tags    
      / Create sparse tree    
      ^ Sort children    
      I Clock in In  
      O Clock out Out  
      & Jump to previous position   custom, like C-c &
      1 Set priority 1, so #A    
      2= Set priority 2, so #B    
      3 Set priority 3, so #C    
      0 Unset priority    
  • ???

    Do not warn about upcoming deadlines.

    (The agenda sees the future just fine.)

    (setopt org-deadline-warning-days 0)
    
  • ???

    Make imenu index all headings.

    (setopt org-imenu-depth 999)
    
  • ???

    Export in modern HTML5 instead of XHTML Strict.

    [Disabled because HTML5 has no table group separators because org-html-table-default-attributes is ignored and tbody HTML tags have no border. This should be fixed in Org, as the default HTML5 export should have them.]

    (setopt org-html-doctype "html5"
            org-html-html5-fancy t)
    
  • My source block indentation
    (with-eval-after-load 'org
      (setopt org-edit-src-content-indentation 0))
    

Meetup

Bug reports

  • TODO var resolution is dependent on order

    versus

  • WAIT Fix export of parameterized LaTeX environments to HTML/SVG

    notmuch-show:m2zfr296wp.fsf@adamkovic.org

    Once fixed, remove useless “environment wrappers” from all notes.

  • DONE Fix typos (mail, patch)

Statistic cookies

Write [0/0], refresh with C-c C-c.

  • Counts TODO headings by default
  • Set the :COOKIE_DATA: property checkbox to count plain - [X] Task-s.

Note: Plain tasks do not block.

Patches

Note. Do not forget to prefix the mail subject with [PATCH].

Patches (some of these are Emacs)

Contributions

Maintenance commands

  • Remove links

    Remove all links quickly.

    (defun my-org-remove-links ()
      "Replace [[DESCRIPTION][TARGET]] with DESCRIPTION in the region or buffer."
      (interactive)
      (save-excursion
        (replace-regexp org-bracket-link-regexp "\\2"
                          nil
                          (if (region-active-p) (region-beginning) (point-min))
                          (if (region-active-p) (region-end) (point-max)))))
    
  • Align all tables
    (defun my-org-align-all-tables ()
      (interactive)
      (org-table-map-tables 'org-table-align 'quietly))
    

    — taken from https://orgmode.org/worg/org-hacks.html#align-tables-in-file

Org Python

Use Python 3.

(with-eval-after-load 'ob-python
  (setopt org-babel-python-command "python3"))

Org Agenda

Org Agenda summarizes a set of Org files (org-agenda-files).

From anywhere, type:

Key Description Note
C-c a Open agenda custom

Note: The agenda indicates inherited tags by appending an extra colon (:) to them.

Selected key bindings

Key Description Note
n next item item, not line, custom
p previous item item, not line, custom
g refresh all items  
RET open item go to SCHEDULED=/=DEADLINE
SPC open item in other window  
TAB open item in other window and select  
F open item in other window and auto-follow disable with F, Follow mode
t advance item to-do keyword  
[C-u] s set item [un]-schedule custom, C-c C-s
[C-u] d set item [un]-deadline custom, C-c C-d
, set item priority  
i clock in custom, C-c C-x C-i
o clock out custom, C-c C-x C-o
e set effort custom, C-c C-x e

In Org, and Org Agenda by the extension below, C-' cycles through org-agenda-files.

Bulk editing

Key Description
m mark for bulk actions
B s bulk schedule
B d bulk deadline
B t bulk advance TODO status

Org mode: Habits

(with-eval-after-load 'org
  (setopt org-habit-show-habits-only-for-today t))

Do not show consistency graphs in the agenda.

(with-eval-after-load 'org-agenda
  (setopt org-habit-following-days 0
          org-habit-preceding-days 7))

Something:

Key Description
C-c c Capture information
C-c a Show agenda
(with-eval-after-load 'org
  (setopt org-reverse-note-order t
          org-refile-allow-creating-parent-nodes 'confirm))
(keymap-global-set "C-c c" #'org-capture)

Configure!

Simplify frequently used keybindings.

  • C-s (org-agenda-schedule) to s,
  • C-d (org-agenda-deadline) to d,
  • C-c C-x C-i (org-agenda-clock-in) to i, and
  • C-c C-x C-o (org-agenda-clock-out) to o.
(with-eval-after-load 'org-agenda
  (keymap-set org-agenda-mode-map "s" #'org-agenda-schedule)
  (keymap-set org-agenda-mode-map "d" #'org-agenda-deadline)
  (keymap-set org-agenda-mode-map "i" #'org-agenda-clock-in)
  (keymap-set org-agenda-mode-map "o" #'org-agenda-clock-out))

Make n (next) and p (previous) move by agenda items instead of lines.

Note. Originally, per-item movement is bound to the capital letters N and P.

(with-eval-after-load 'org-agenda
  (keymap-set org-agenda-mode-map "n" #'org-agenda-next-item)
  (keymap-set org-agenda-mode-map "p" #'org-agenda-previous-item))

Move the point to the first item after showing the agenda.

(with-eval-after-load 'org-agenda
  (add-hook 'org-agenda-finalize-hook
            (lambda ()
              (org-agenda-next-item 1)
              (recenter-top-bottom))))

Summarize all Org files.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-files
          (list (file-name-concat org-directory
                                  "obtf.org"))))

Use the current window for Org Agenda and skip deadline pre-warnings for scheduled items.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-window-setup 'current-window
          org-agenda-skip-deadline-prewarning-if-scheduled t))

Experiment. Do not show future “copies” of repeated tasks.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-show-future-repeats nil))

Make C-c a open the agenda buffer.

If the buffer already exist, do not recreate it. Instead, switch to it and refresh its content, in order to maintain the cursor position.

(defun my-org-agenda ()
  (interactive)
  (if-let* ((buffer (get-buffer "*Org Agenda*")))
      (progn (switch-to-buffer buffer)
             (org-agenda-redo))
    (org-agenda nil "a" nil)))
(keymap-global-set "C-c a" #'my-org-agenda)

Hide the separators.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-block-separator nil))

Show tags next to their headings.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-align-tags-to-column 0))

Show items with active timestamp first and habits last.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-sorting-strategy
          '((agenda ts-up habit-down time-up priority-down category-up)
            (todo priority-down todo-state-up category-up)
            (tags priority-down todo-state-up category-up)
            (search category-keep alpha-up))))

Show military time as 09:00 instead of 9:00.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-time-leading-zero t))

Hide the time grid to save space.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-use-time-grid nil))

Spell out deadlines and scheduled times in full.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-deadline-leaders
          '("DEADLINE:"
            "DEADLINE(-%d):"
            "DEADLINE(+%d):")
          org-agenda-scheduled-leaders
          '(""
            "(%d)")))
(with-eval-after-load 'org-agenda
  (defun org-agenda-format-date-not-aligned+ (date)
    "Format a DATE string like `org-agenda-format-date-aligned' but
without any alignment."
    (replace-regexp-in-string
     " +" " " (org-agenda-format-date-aligned date))))

;; (with-eval-after-load 'org-agenda (require 'org-habit))

Hide the private tag (not the headings tagged with it).

(with-eval-after-load 'org
  (setopt org-agenda-hide-tags-regexp "private"))
(with-eval-after-load 'org-agenda
  (setopt
   org-agenda-custom-commands
   '(("a" "Agenda"
      ((agenda
        ""
        ((org-agenda-overriding-header "Today")
         (org-agenda-start-on-weekday nil)
         (org-agenda-span 1)
         (org-agenda-format-date 'org-agenda-format-date-not-aligned+)
         (org-agenda-day-face-function (lambda (_) 'calendar-today))
         (org-agenda-dim-blocked-tasks nil)
         (org-agenda-skip-function
          '(org-agenda-skip-entry-if 'todo 'done))))
       (agenda
        ""
        ((org-agenda-overriding-header "\nTomorrow")
         (org-agenda-start-day "+1d")
         (org-agenda-span 1)
         (org-agenda-format-date 'org-agenda-format-date-not-aligned+)
         (org-agenda-dim-blocked-tasks nil)
         (org-habit-show-habits-only-for-today nil)
         (org-habit-scheduled-past-days 0)))
       (tags-todo
        "-TODO=\"WAIT\"+PRIORITY=\"A\""
        ((org-agenda-overriding-header "\nNext Up")
         (org-agenda-todo-ignore-timestamp 'all)
         (org-agenda-todo-ignore-scheduled 'all)
         (org-habit-show-habits nil)))
       (agenda
        ""
        ((org-agenda-overriding-header "\nWeek Ahead")
         (org-agenda-start-day "+2d")
         (org-agenda-start-on-weekday nil)
         (org-agenda-span 7)
         (org-agenda-dim-blocked-tasks nil)
         (org-habit-show-habits nil)))
       (tags-todo
        "+TODO=\"WAIT\"-PRIORITY=\"C\""
        ((org-agenda-overriding-header "\nWaiting")
         (org-agenda-todo-ignore-timestamp 'all)
         (org-agenda-todo-ignore-scheduled 'all)
         (org-habit-show-habits nil))))))))
(with-eval-after-load 'org-agenda
  (setopt org-agenda-prefix-format "* % c% e%? t%? s"
          org-agenda-skip-scheduled-if-deadline-is-shown t
          org-agenda-skip-scheduled-if-done t
          org-agenda-skip-deadline-if-done t
          org-agenda-sort-notime-is-late nil))

Honor tag-based ignore-options for TODO items in Org Agenda.

(with-eval-after-load 'org-agenda
  (setopt org-agenda-tags-todo-honor-ignore-options t))

Enforce dependencies in Org and reflect them in Org Agenda.

(with-eval-after-load 'org-agenda
  (setopt org-enforce-todo-dependencies t
          org-agenda-dim-blocked-tasks 'invisible))

To ignore dependencies locally, set the :NOBLOCKING: property to t.

Typeset events in bold to not miss them.

(with-eval-after-load 'org-agenda
  (defun org-agenda-theme+ (&optional theme)
    (set-face-bold 'org-agenda-calendar-sexp t)))
(with-eval-after-load 'org-agenda (org-agenda-theme+))
(with-eval-after-load 'org-agenda
  (add-hook 'enable-theme-functions #'org-agenda-theme+))

Highlight the current line.

(with-eval-after-load 'org-agenda
  (add-hook 'org-agenda-mode-hook #'hl-line-mode))

Contribute!

  • TODO Fix pluralization
    • Reproduction steps:
      1. Mark some item in org-agenda
    • Expected results:

      “1 entry marked for bulk action” in the echo area.

    • Actual result:

      “1 entries marked for bulk action” in the echo area.

  • TODO Implement section-wise navigation
    • existing: org-agenda-previous-item
    • existing: org-agenda-next-item
    • missing: org-agenda-previous-item
    • missing: org-agenda-next-item
  • TODO Highlight entered day in org-schedule

Org Babel

A set of Emacs Lisp functions for executing source code within Org documents. A built-in Emacs package. Enables “literate programming” [31].

bruegel-1563.jpg
Figure 1: The Tower of Babel by [32],
oil on wood panel, 114cm × 155cm

Key bindings

Key Description On opening # Mnemonic
C-c C-v n go to next block n next
C-c C-v p go to previous block p previous
C-c C-c execute block at point e DWIM, execute
C-c C-v b execute all blocks in buffer b buffer
C-c C-v s execute all blocks in subtree s subtree
C-c C-v l load block at point in session l load
C-c C-v k remove results for block at point k kill
C-c C-v d split block at point d demarcate
C-c C-v t tangle all blocks in buffer ‡ t tangle
C-c C-v v preview noweb expansion v view

‡ Tangle only

  • the block at point, prefix with C-u,
  • the blocks for the tangle file of the block at point, C-u C-u

Source block headers

Document-wide arguments:

#+property: header-args:R+ :session *R*

Subtree-wide arguments, conveniently added with C-c C-x p:

* Heading
  :PROPERTIES:
  :HEADER-ARGS:R+: :session *R*
  :END:

Block-wide arguments:

#+begin_src R+ :session *R*

Block-wide multi-line arguments:

#+header: :cache yes
#+begin_src R+ :session *R*

(Every line that begins with #+ or * needs escaping with ,.)

  • Inline source blocks

    Inline source blocks:

    src_c[:exports code]{printf("Hello World!")}
    

    Traps:

    • Document-wide #+property: header-args do not apply.
    • By default :exports is set to results.
  • The eval header argument

    Determines when to evaluate the SRC block.

    Value Evaluate   Example
    no-export manually literate configuration
    no never syntax definition

    † Matches the legacy :eval never and :noeval.
    ‡ Matches the legacy :eval never-export.

  • The async header argument

    To run code asynchronously, set :session NAME and :async yes.

  • The tangle header argument
    Value Tangle  
    no not ever  
    yes always
    file always to file  

    † Tangles to a file that matches the document name.

  • The tangle-mode header argument
    Value Description
    oXXX use the octal value XXX
    rwxrwxrwx use the ls style
    +x use the chmod style
  • The padline header argument

    Useful when tangling multiple blocks into the same file.

    Value Newline Note
    no none  
    yes before/after each code block default
  • The comments header argument

    Combined with :tangle, determines the kind of commentary added to the tangled file.

    Value Commentary
    no none
    link file and heading
    org preceding text
    both same as link plus org
  • The results header argument

    The :results header argument takes four classes of values:

    Class Value Results Note
    handling none discarded  
    handling silent in echo area  
    handling replace in Org file default
    handling append in Org file after old  
    handling prepend in Org file before old  
    collection value as value default
    collection output as standard output  
    type verbatim as text  
    type table as table  
    type list as list  
    type file in file see :file, :file-ext, :file-desc, :file-mode
    type vector same as table  
    type scalar same as verbatim  
    format code as code  
    format pp as code, pretty-printed  
    format org as code, Org markup  
    format graphics as image often combined with file
    format link as resource see file type
    format raw raw Org
    format drawer raw Org a drawer  
    format html raw HTML  
    format latex raw LaTeX  
    format graphics same as link  

    † For non-block elements, raw implies append because the end of output is impossible to recognize reliably. To avoid appending, add :wrap raw.

  • The exports header argument

    The :exports header defines what to export:

    Value Exports Note
    code just code default
    results just results  
    both both code and results  
    none nothing  

    If a SRC block exists just to produce side effects, such as running a program written in C and OpenGL, use :results silent :eval no-export.

  • The cache header argument

    To cache the RESULTS, use the :cache yes argument. Org Babel will jot down the checksum of the SRC block under the RESULTS header and re-execute the SRC block just when its content changes. The :cache argument enables fast exporting and publishing, but it does not work with the :session header argument because sessions hold state.

  • The colnames header argument
    nil
    1. Drop the column names from the input, if tabular.
    2. Run the code block, possibly producing results.
    3. Re-add the column names, if any, to the results, if tabular.

    N.B. Column names are row #1 if row #2 is a horizontal rule.

    yes
    1. Drop the first row from the input, if tabular.
    2. Run the code block, possibly producing results.
    3. Re-add the row, if any, to the results, if tabular.
    no
    Do not pre-process tabular inputs.
  • The noweb header argument

    Add :noweb yes to enable inclusion of #+NAME:-d source blocks with <<name>> and their results with <<name()>> or <<name(var-name = var-value)>>.

    To include multiple blocks, set the noweb-ref header argument to name instead of using #+NAME:, possibly at a file or subtree level.

    • To export nothing in place of <<name>>, set :noweb strip-export.
    • To export un-expanded <<name>>, set :noweb no-export.

    Learn more in the Emacs manual.

    By default, every line gets the same prefix; disable this with :noweb-prefix no.

    Preview noweb expansions with C-c C-v v or C-c C-v C-v.

  • The prologue and epilogue header arguments

    Side-effecting setup and teardown code.

  • The dir and mkdir header argument
    dir
    working directory
    if 'attach (or string attach), the (org-attach-id-dir) is used
    mkdir
    if t, dir is created if it does not exists
  • shebang

    N.B. With a shebang set, the file is automatically made executable.

  • Common combinations of header arguments
    • show standard output
      :results output verbatim
      Example: Showcase raw output from R, not converted to a table.
    • show the output image named out.png
      :results graphics file :file out.png
      Example: Showcase a histogram computed in R.
    • just attempt to highlight syntax
      :eval no
      Example: Showcase SQL syntax definitions with highlighted keywords.
    • just highlight and check syntax on demand with C-c C-C
      :results none :eval no-export
      Example: Showcase a function definition in Scheme.
    • just perform side effects on demand with C-c C-c
      :results silent :eval no-export
      Example: Showcase a C program that uses OpenGL for its output.

Hacking

To get the results from a source block marked with #+NAME:A, execute (org-sbe "A").

Configuration

  • Stop asking silly questions

    Do not ask before executing code with C-c C-c.

    (with-eval-after-load 'ob-core
      (setopt org-confirm-babel-evaluate nil))
    
  • Generate SVG file-names

    Create a snippet for exporting SVG files.

    org-mode
    (svg ":file svg/" (org-id-uuid) ".svg")
    
  • Edit in current window
    (with-eval-after-load 'org
      (setopt org-src-window-setup 'current-window))
    
  • Load selected languages
    (with-eval-after-load 'org
      (org-babel-do-load-languages 'org-babel-load-languages
                                   '((C . t)
                                     (R . t)
                                     (dot . t)
                                     (emacs-lisp . t)
                                     (java . t)
                                     (latex . t)
                                     (lisp . t)
                                     (lua . t)
                                     (makefile . t)
                                     (org . t)
                                     (plantuml . t)
                                     (python . t)
                                     (scheme . t)
                                     (shell . t)
                                     (sqlite . t))))
    

Patches

Bugs

  • WAIT Make org-babel-detangle more robust

    Started a discussion: here

    • [ ] pluralize “%d code blocks”
    • [X] prefer IDs over headings
    • [ ] fix “Buffer … modified; kill anyway?” message
    • [ ] take into consideration user customizations:
      • org-babel-tangle-comment-format-beg
      • org-babel-tangle-comment-format-end
    • org-tangle-mode minor mode
      • highlight tangle comments
      • make tangle comments clickable (jump to Org)
      • make tangle comments foldable
  • SKIP Fix “inverted” behavior of `colnames’ header argument
  • TODO ob-python: Last expression not reported with :=

    The walrus operator := is ignored:

    x := 5
    
  • TODO ob-python: async breaks colnames

    async makes the colnames disappear:

    x = [[1, 2, 3], [4, 5, 6]]
    x
    
    1 2 3
    4 5 6
  • TODO Report or fix broken table parsing
    import pandas as pd
    
    return pd.DataFrame({pd.Timestamp("20240910")}).transpose()
    
               0
    0 2024-09-10
    
    import pandas as pd
    
    return pd.DataFrame({pd.Timestamp("20240910")}).transpose()
    
      0  
    0 Timestamp (2024-09-10 00:00:00)
  • DONE Stop downcasing keywords when demarcating blocks
  • DONE Stop swallowing table column groups

    [discussion]​

    Needed for my truth table generator.

Org Publish

A part of Org that

exports multiple files if they have changes.

Key bindings

In the export dispatcher org-export-dispatch (C-c C-e):

Key Description Mnemonic
P p Publish current project project
P f Publish current file file
P a Publish all all
P x Publish selected… extra

Prefix with C-f to force-export.

Org Cite & Citar

… Citar by [33]

The part of Org that manages citations. Comes with Emacs.

Table 10: Org Cite extended with Citar and CSL.
Package Symbol Type Job
Org Cite oc built-in citations in Org mode
Org Cite CSL oc-csl built-in CSL support for Org Cite
CSL processor citeproc external CSL parser Org Cite CSL
Citar citar external extra user interface

N.B. CSL stands for the Citation Style Language [34].

The insert processor manages adding and editing citations.

Every citation a reference and some citations have type, such as text, and suffix, such as 42 for page 42. The syntax:

[cite/type:@reference suffix].

Global shortcuts:

Key Description Notes
C-c b Open bibliography custom

Org mode shortcuts:

Key Context Action
C-c C-x @ in text insert citation, C-u for type
C-c C-x @ on citation type change citation type
C-c C-x @ on citation reference change citation reference
C-c C-x @ next to citation reference add extra reference

The activate processor The part of Org Cite that makes citations “active” by fontifying them, adding a keymap, and the like. On a citation, type

Key Action
C-c C-x DEL delete citation
C-c C-x k kill citation

Org Cite follow processor manages “opening” citations on RET.

Configure!

Install Citar.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'citar)
  (add-to-list 'package-selected-packages 'citeproc))

Use Citar.

(with-eval-after-load 'org
  (setopt org-cite-insert-processor 'citar
          org-cite-follow-processor 'citar
          org-cite-activate-processor 'citar))

Define the global keybinding.

(keymap-global-set "C-c b" #'citar-open)

Fix Citar keybindings.

Make RET open the citation instead of add a newline.

(with-eval-after-load 'citar-org
  (keymap-set citar-org-citation-map "RET" #'org-open-at-point))

Make citar find library files.

(with-eval-after-load 'citar
  (setopt citar-library-paths
          (list (file-name-concat (progn
                                    (require 'org)
                                    org-directory)
                                  "bib"))))

Find the library files for the given key as

  • key.extension or
  • key~variant.extension for any variant.

By default, per the citar-library-file-extensions, any extension works.

(setopt citar-file-additional-files-separator "~")

Remember the recently used bibliographic items across Emacs sessions.

(with-eval-after-load 'savehist
  (add-to-list 'savehist-additional-variables 'citar-history))

Locate the BibTeX files.

(let ((bibliography (list (file-name-concat (progn
                                              (require 'org)
                                              org-directory)
                                            "obtf.bib"))))
  (setopt org-cite-global-bibliography bibliography
          citar-bibliography bibliography))

Do not wait for the user to select multiple items.

(with-eval-after-load 'citar
  (setopt citar-select-multiple nil))

Use the CSL citation processor and find the CSL styles.

Per the Introducing citations! article.

(with-eval-after-load 'org
  (setopt org-cite-csl-styles-dir org-directory
          org-cite-export-processors '((t csl))))

Workaround: Do not show the “unknown processor csl” error when attempting to export a document with the #+PRINT_BIBLIOGRAPHY but with no intext citations.

(with-eval-after-load 'ox
  (require 'oc-csl))

Completions.

;;  (with-eval-after-load 'org
;;    (add-hook 'org-mode-hook #'citar-capf-setup))

Contribute to Org Cite

  • TODO Improve extending citations

    C-c C-x @ with the point immediately after, but not inside, a citation should extend it. This is to make it easier to cite two sources. Also, this is how time stamps work too in Org.

  • DONE Auto-complete PRINT_BIBLIOGRAPHY with a trailing colon

Contribute to Citar

  • TODO Investigate why Citar makes Org slow
    • Reproduction steps:
      1. Open a large Org file.
      2. Run profiler-start.
      3. Hold C-v (scroll-up-command).
      4. Run profiler-report.

    Citar, re-parsing its bibliography, uses most of the time.

  • DONE bug Too much bold?
  • DONE bug Fix vanilla Emacs completions
  • DONE bug Do not rebind C-d and C-k

Contribute to citeproc.el

  • DONE bug Make single quotes in BibTeX fields work again
  • DONE bug Capitalize across leading interpunction
  • DONE bug Fix “{{Literal}}” authors
  • DONE bug Fix title capitalization
  • DONE bug Support no-date citations

Org Invoice

A simple invoice generator for the Org mode.

TODO Make undo work after capturing

Example

  • Input

    Upon M-x org-capture RET followed by i for “invoice”, the program lets the user interactively find a work log, which is a heading with INVOICE-specific :PROPERTIES:, possibly inherited, and a :LOGBOOK: with work time, such as

    *** TODO Work Gerhold & Hettinger :private:
    :PROPERTIES:
    :INVOICE_FROM: Walton Koss
    :INVOICE_FROM+: 1725 Eagle Street
    :INVOICE_FROM+: Olney 62450, Illinois
    :INVOICE_FROM+: United States
    :INVOICE_TO: Gerhold & Hettinger
    :INVOICE_TO+: 3861 Hillside Street
    :INVOICE_TO+: Chandler 85226, Arizona
    :INVOICE_TO+: United States
    :INVOICE_CONTACT: Mail: walton.koss@zieme.com
    :INVOICE_CONTACT+: Phone: +1 832-254-8919
    :INVOICE_LEGAL: Employer (EIN): 616096016
    :INVOICE_LEGAL+: Tax (PTIN): P14674212
    :INVOICE_WORK: free software development
    :INVOICE_TOTAL_HOURLY: 60
    :INVOICE_TOTAL_CURRENCY: USD
    :INVOICE_BANK: Location: United States
    :INVOICE_BANK+: Name: Walton Koss
    :INVOICE_BANK+: Account: 78887890
    :INVOICE_BANK+: Routing: 483487800
    :INVOICE_BANK+: Type: checking
    :END:
    :LOGBOOK:
    CLOCK: [2023-11-14 Tue 13:58]--[2023-11-14 Tue 20:48] =>  6:50
    CLOCK: [2023-11-14 Tue 08:37]--[2023-11-14 Tue 13:05] =>  4:28
    CLOCK: [2023-11-13 Mon 18:06]--[2023-11-13 Mon 20:14] =>  2:08
    :END:
    
    
  • Output

    The work log above results in a capture invoice-001.org that looks as follows: .

    Note.  Output files are created in the org-directory.

    #+title: Invoice 001
    #+options: title:nil author:nil date:nil toc:nil
    #+latex_header: \input{org-invoice+header.tex}
    
    #+begin_example
    FROM                                    TO
    | Walton Koss                           | Gerhold & Hettinger
    | 1725 Eagle Street                     | 3861 Hillside Street
    | Olney 62450, Illinois                 | Chandler 85226, Arizona
    | United States                         | United States
    
    CONTACT                                 DATE
    | Mail: walton.koss@zieme.com           | <2023-12-01 Fri>
    | Phone: +1 832-254-8919
    
    LEGAL                                   WORK
    | Employer (EIN): 616096016             | free software development
    | Tax (PTIN): P14674212
    
    DETAILS
    | [2023-11-14 Tue 13:58]--[2023-11-14 Tue 20:48] =>  6:50
    | [2023-11-14 Tue 08:37]--[2023-11-14 Tue 13:05] =>  4:28
    | [2023-11-13 Mon 18:06]--[2023-11-13 Mon 20:14] =>  2:08
    
    TOTAL                                   BANK
    | hours * (USD / hour)                  | Location: United States
    | = 13.43 * 60.00                       | Name: Walton Koss
    | = 806.00 USD                          | Account: 78887890
                                            | Routing: 483487800
    THANK YOU.                              | Type: checking
    #+end_example
    
    org-invoice+example.svg

Lisp code

Note.  All global symbols are suffixed with + to avoid name clashes.

All code resides in the org-invoice.el file:

;;; -*- lexical-binding: t -*-

;; Author: Rudolf Adamkovic <rudolf@adamkovic.org>

;; This program is free software: you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by the Free
;; Software Foundation, either version 3 of the License, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.

;; You should have received a copy of the GNU General Public License along with
;; this program. If not, see <https://www.gnu.org/licenses/>.

;;; Customization

;; TODO Customize elsewhere.
(defcustom my-org-invoice-work-log-id nil
  "Org ID of the section with invoice details and clocked hours"
  :type '(string)
  :group 'my-org-invoice)

;; TODO Customize elsewhere.
(defcustom my-org-invoice-list-id nil
  "Org ID of the section to which invoices are generated"
  :type '(string)
  :group 'my-org-invoice)

(defcustom my-org-invoice-title "Invoice"
  "The title used for invoices, suffixed with the invoice number."
  :type '(string)
  :group 'my-org-invoice)

(setopt my-org-invoice-work-log-id "0914DEB9-E2AD-46BF-8DAC-4B48E3F81359"
        my-org-invoice-list-id "D4B2836B-9856-425B-9527-4372A0C5AB5C")

;;; Templating functions

(defun my-org-invoice-number (&optional lastp)
  "Return the number of the next, or the last, invoice."
  (let ((last-invoice-number
         (save-window-excursion
           (org-id-goto my-org-invoice-list-id)
           (and (org-goto-first-child)
                (named-let recur ((greatest-number 0))
                  (let* ((current-number
                          (let ((heading (org-get-heading t t t t)))
                            (and (string-match
                                  (format "%s %s"
                                          (regexp-quote my-org-invoice-title)
                                          (rx (group (one-or-more digit))))
                                  heading)
                                 (string-to-number (match-string 1 heading)))))
                         (greatest-number
                          (if current-number
                              (max current-number greatest-number)
                            greatest-number)))
                    (if (outline-get-next-sibling)
                        (recur greatest-number)
                      greatest-number)))))))
    (if lastp
        last-invoice-number
      (if last-invoice-number
          (1+ last-invoice-number)
        1))))

(defun my-org-invoice-title ()
  "Return the title for the next invoice."
  (format "%s %d" my-org-invoice-title (my-org-invoice-number)))

(defun my-org-invoice-file-name ()
  "Return the file name for the next invoice."
  (format "invoice-%d" (my-org-invoice-number)))

(defun my-org-invoice-property (name &optional index)
  "Return the INDEX-th PROPERTY value, padded for templating."
  (save-window-excursion
    (org-id-goto my-org-invoice-work-log-id)
    (string-pad
     (or (nth (or index 0)
              (let ((separator "\n"))
                (when-let* ((all (let ((org-property-separators
                                        (list (cons (regexp-quote name) separator))))
                                   (org-entry-get (point) (concat "INVOICE_" name)))))
                  (string-split all separator))))
         "n/a")
     (length
      (format "%%(%s)"
              (string-join
               (append (list (symbol-name #'my-org-invoice-property))
                       (list (format "\"%s\"" name))
                       (list (if index (number-to-string index) nil)))
               " "))))))

(defun my-org-invoice-details (prefix)
  "Return the \"Details\" section as a list of strings."
  (save-window-excursion
    (org-id-goto my-org-invoice-work-log-id)
    (goto-char (org-log-beginning))
    (named-let recur ((lines '()))
      (if (eq (org-element-type (org-element-at-point-no-context)) 'clock)
          (let ((line (buffer-substring-no-properties (point) (pos-eol))))
            (forward-line)
            (recur (cons (string-replace org-clock-string prefix line) lines)))
        (string-join (reverse lines) "\n")))))

(defun my-org-invoice-total (index)
  "Return the INDEX-th value of the TOTAL section, padded for templating."
  (save-window-excursion
    (org-id-goto my-org-invoice-work-log-id)
    (when-let* ((rate-string (org-entry-get (point) "INVOICE_TOTAL_HOURLY"))
                (rate (string-to-number rate-string))
                (currency (org-entry-get (point) "INVOICE_TOTAL_CURRENCY")))
      (string-pad
       (let ((hours (/ (float (org-clock-sum-current-item)) 60)))
         (pcase index
           (0 (format "hours * (%s / hour)" currency))
           (1 (format "= %.2f * %.2f" hours rate))
           (2 (format "= %.2f %s" (* hours rate) currency))
           (_ "n/a")))
       (length
        (format "%%(%s)"
                (string-join
                 (append (list (symbol-name #'my-org-invoice-total))
                         (list (if index (number-to-string index) nil)))
                 " ")))))))

;;; Installation

(with-eval-after-load 'org-capture
  (add-to-list 'org-capture-templates
               `("i" "Invoice" entry (id ,my-org-invoice-list-id)
                 (file "org-invoice+template.org")
                 :prepend t
                 :jump-to-captured t
                 :empty-lines-after 1)))

Template

For historical reasons, invoices have a somewhat “old school” look, featuring

  • thick, monospace typography,
  • prominent header and footer rules.

The footer includes the total number of pages to prevent fraud.

  • The org-invoice+template.org file
    * %(my-org-invoice-title)
    :PROPERTIES:
    :EXPORT_FILE_NAME: %(my-org-invoice-file-name)
    :EXPORT_OPTIONS: title:nil author:nil date:nil toc:nil
    :EXPORT_LATEX_HEADER: \input{org-invoice+header.tex}
    :END:
    
    #+begin_example
    FROM                                    TO
    | %(my-org-invoice-property "FROM" 0)   | %(my-org-invoice-property "TO" 0)
    | %(my-org-invoice-property "FROM" 1)   | %(my-org-invoice-property "TO" 1)
    | %(my-org-invoice-property "FROM" 2)   | %(my-org-invoice-property "TO" 2)
    | %(my-org-invoice-property "FROM" 3)   | %(my-org-invoice-property "TO" 3)
    
    CONTACT                                 DATE
    | %(my-org-invoice-property "CONTACT" 0)| %^t
    | %(my-org-invoice-property "CONTACT" 1)
    
    LEGAL                                   WORK
    | %(my-org-invoice-property "LEGAL" 0)  | %(my-org-invoice-property "WORK")
    | %(my-org-invoice-property "LEGAL" 1)
    
    DETAILS
    %(my-org-invoice-details "|")
    
    TOTAL                                   BANK
    | %(my-org-invoice-total 0)             | %(my-org-invoice-property "BANK" 0)
    | %(my-org-invoice-total 1)             | %(my-org-invoice-property "BANK" 1)
    | %(my-org-invoice-total 2)             | %(my-org-invoice-property "BANK" 2)
                                            | %(my-org-invoice-property "BANK" 3)
    THANK YOU.                              | %(my-org-invoice-property "BANK" 4)
    #+end_example
    
  • The org-invoice+header.tex file
    \usepackage[vmargin=1in]{geometry}
    \usepackage{titling}
    \usepackage{fancyhdr}
    \usepackage{lastpage}
    
    \pagestyle{fancy}
    \setlength{\headheight}{15pt}
    
    \newcommand{\fancylayout}{
      \fancyhf{}
      \renewcommand{\headrulewidth}{0.5pt}
      \renewcommand{\footrulewidth}{0.5pt}
      \fancyhead[C]{\texttt{\thetitle}}
      \fancyfoot[C]{\texttt{\thepage~of~\pageref*{LastPage}}}
    }
    
    \fancypagestyle{plain}{\fancylayout}
    \fancylayout
    
  • Mail template (Egghead Games)
    message-mode
    (invoice
     "From: \"Rudolf Adamkovič\" <rudolf@adamkovic.org>" n
     "To: Mike Mee <admin@eggheadgames.com>" n
     "Subject: Invoice " (number-to-string (my-org-invoice-number 'last)) n
     "--text follows this line--" n
     "Please see the attached invoice." q n n
     "Thank you!" n n
     "Rudy" n n
     "<#part type=\"application/pdf\" filename=\"~/org/invoice-"
     (number-to-string (my-org-invoice-number 'last))
     ".pdf\" disposition=attachment><#/part>" n
     "--" n
     (progn
       (save-excursion (notmuch-fcc-header-setup))
       (fortune-to-signature)
       mail-signature))
    

Fortune

A traditional Unix program, and also an Emacs package, that prints a random “fortune cookie”.

Populate!

Wrapped to 72 columns.

Fortune uses the strfile format. See man strfile.

[Unfortunately, the strfile format does not support comments, so we cannot say “this file is tangled”.

"Arguing that you don't care about the right to privacy because you have
nothing to hide is no different than saying you don't care about free
speech because you have nothing to say."

--- Edward Snowden, 2015
"Logic is a science of the necessary laws of thought, without which no
employment of the understanding and the reason takes place."

--- Immanuel Kant, 1785
"'Contrariwise,' continued Tweedledee, 'if it was so, it might be; and
if it were so, it would be; but as it isn't, it ain't.  That's logic.'"

--- Lewis Carroll, Through the Looking Glass, 1871/1872
"Programming reliably -- must be an activity of an undeniably
mathematical nature […] You see, mathematics is about thinking, and
doing mathematics is always trying to think as well as possible."

--- Edsger W. Dijkstra, 1981
"I love deadlines.  I love the whooshing noise they make as they go by."

--- Douglas Adams, The Salmon of Doubt, 2002
"Be especially critical of any statement following the word
'obviously.'"

--- Anna Pell Wheeler, 1883-1966
"Mathematics takes us still further from what is human into the region
of absolute necessity, to which not only the actual world, but every
possible world, must conform."

--- Bertrand Russell, 1902
"Genius is 1% inspiration and 99% perspiration."

--- Thomas Alva Edison, 1932
"The introduction of suitable abstractions is our only mental aid to
organize and master complexity."

--- Edsger Wybe Dijkstra, 1930-2002
"It is no paradox to say that in our most theoretical moods we may be
nearest to our most practical applications."

--- Alfred North Whitehead, 1861-1947
"The power of mathematics rests on its evasion of all unnecessary
thought."

--- Ernst Mach, 1838-1916
"The whole science is nothing more than a refinement of everyday
thinking."

--- Albert Einstein, 1879-1955
"One can begin to reason only when a clear picture has been formed in
the imagination."

--- Walter Warwick Sawyer, Mathematician's Delight, 1943
"Thinking is a momentary dismissal of irrelevancies."

--- Richard Buckminster Fuller, 1969
"Simplicity is complexity resolved."

--- Constantin Brâncuși, 1876-1957
"Chop your own wood and it will warm you twice."

--- Henry Ford; Francis Kinloch, 1819; Henry David Thoreau, 1854
"'Obvious' is all too often a synonym for 'wrong'."

--- Jeff Erickson, Algorithms, 2019

[15, Sec. 0.6]

"All you have to do is write one true sentence.  Write the truest
sentence that you know."

--- Ernest Miller Hemingway (1899-1961)
"It is far better to have a question that can't be answered than an
answer that can't be questioned."

--- Carl Sagan
"Great minds discuss ideas; average minds discuss events; small minds
discuss people."

--- Anna Eleanor Roosevelt (1884-1962)
"We shall not cease from exploration
 And the end of all our exploring
 Will be to arrive where we started
 And know the place for the first time"

--- T. S. Eliot, Little Gidding, Four Quartets, 1943
"Those who cannot remember the past are condemned to repeat it."

--- George Santayana, Life of Reason: Reason in Common Sense, 1905
"I do not fear death.  I had been dead for billions and billions of
years before I was born, and had not suffered the slightest
inconvenience from it."

--- Mark Twain, paraphrased
"All of humanity's problems stem from man's inability to sit quietly in
a room alone."

--- Blaise Pascal, Pensées, 1670
"I have only made this letter longer because I have not had the time to
make it shorter."

--- Blaise Pascal, The Provincial Letters, 1657
"An intelligent hell would be better than a stupid paradise."

--- Victor Hugo, Ninety-Three (1874)
"It is better to have 100 functions operate on one data structure than
10 functions on 10 data structures."

--- Alan Perlis
The first 90 percent of the code accounts for the first 90 percent of
the development time. The remaining 10 percent of the code accounts for
the other 90 percent of the development time.

--- Tom Cargill, Bell Labs
"For every problem you can't solve, there's a simpler problem that you
also can't solve."

--- Hendrik Lenstra
"It is not the strongest of the species that survives, not the most
intelligent that survives.  It is the one that is the most adaptable to
change."

--- Charles Darwin
To the unknown journeyman
I promise to show courage
in upholding my vows

What is an adventure
if not the commitment
to keep going?

--- Protesilaos Stavrou, To the unknown journeyman, 2025
Thorny rose
beautiful and untouchable
like the dreams

I never dared to pursue
you are the flower
my garden was made for"

--- Protesilaos Stavrou, Rose, 2025
There are only two hard things in computer science: cache invalidation
and naming things.

--- Phil Karlton
There are only two hard things in computer science: cache invalidation,
naming things, and off-by-one errors.

--- Phil Karlton, Leon Bambrick

Test

fortune ~/fortune
"Those who cannot remember the past are condemned to repeat it."

--- George Santayana, Life of Reason: Reason in Common Sense, 1905

Configure!

Use the fortunes tangled above.

(setopt fortune-file "~/fortune")

When using a fortune in signatures, add a mail address too.

(setopt fortune-sigend
        (string-join
         '(""
           "Rudolf Adamkovič <rudolf@adamkovic.org> [he/him]"
           "http://adamkovic.org")
         "\n"))

Edit fortune files in text-mode instead of fundamental-mode.

(add-to-list 'auto-mode-alist '("fortune\\'" . text-mode))

Inhibit the splash screen in favor of the scratch buffer.

(setopt inhibit-startup-screen t)

Add a fortune on top of the *scratch* buffer.

(setopt initial-scratch-message
        (with-temp-buffer
          (delay-mode-hooks (funcall initial-major-mode))
          (require 'fortune)
          (let ((fortune-buffer-name (current-buffer)))
            (fortune-in-buffer nil))
          (newline)
          (goto-char 0)
          (let ((comment-empty-lines t))
            (comment-region (point-min) (point-max)))
          (fill-paragraph)
          (buffer-string)))

Agenda

  • TODO Fix non-interactive use of the fortune function

    When called non-interactively, the function does not default FILE to fortune-file.

Minad Stack

A suite of packages that

improve ergonomics of Emacs,

developed by:

  • Daniel Mendler (Minad)
  • Omar Camarena (Oantolin)

Agenda

  • TODO Retry Consult
  • TODO Try Cape
  • TODO Try Embark

Vertico

A fast and practical

minibuffer completion UI

for Emacs.

Author

Created by

Daniel Mendler (minad),

who is also the author of

Corfu, Marginalia, Jinx

and other well-known Emacs packages.

Name

Vertico stands for

VERTical Interactive COmpletion.

Key bindings

Key Description
RET Confirm selected candidate
M-RET Confirm current input
<down> Select next candidate
<up> Select previous candidate
M-< Select first candidate
M-> Select last candidate
M-p Insert previous history item
M-n Insert next history item

Installation

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'vertico))

Global minor mode

Enable the global minor mode.

(add-hook 'after-init-hook #'vertico-mode)

Multiform

Enable per-category and per-command customizations.

(add-hook 'vertico-mode-hook #'vertico-multiform-mode)

Mouse support

N.B. Vertico Mouse mode is incompatible with the Pixel Scroll Precision mode (bug).

(add-hook 'after-init-hook #'vertico-mouse-mode)

File completions

Delete path components with DEL

(with-eval-after-load 'vertico-multiform
  (add-to-list 'vertico-multiform-categories
               '(file (:keymap . vertico-directory-map))))

Buffer completions

Sort buffers like list-buffers does 60

(with-eval-after-load 'vertico-multiform
  (add-to-list 'vertico-multiform-categories
               '(buffer (vertico-sort-function . nil))))

Agenda

  • DONE Report project-find-file ignoring file-name-history (bug)

Corfu

A fast and practical

in-buffer completion UI

for Emacs.

Author

Created by

Daniel Mendler (minad),

who is also the author of

Vertico, Marginalia, Jinx

and other well-known Emacs packages.

Name

Corfu stands for

COmpletion in Region FUnction.

Installation

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'corfu))

Global minor mode

(add-hook 'after-init-hook #'global-corfu-mode)

Autocomplete

Complete automatically and immediately, without waiting for TAB.

(with-eval-after-load 'corfu
  (setopt corfu-auto t
          corfu-auto-delay 0.05))

Minibuffer support

Enable completions for eval-expression (C-:) in the minibuffer.

N.B. Disabled by default, even with global-corfu-mode enabled.

(with-eval-after-load 'corfu
  (add-hook 'eval-expression-minibuffer-setup-hook
            #'corfu-mode))

No pre-selection

Automatic pre-selection of the first candidate gets into the way of typing code.

;; (with-eval-after-load 'corfu
;;   (setopt corfu-preselect 'prompt))

Sorting

Sort most recently used candidates by first.

(with-eval-after-load 'savehist
  (add-to-list 'savehist-additional-variables
               'corfu-history))
(with-eval-after-load 'corfu
  (corfu-history-mode))

Alternatives

  • Built-in, minimalist Completion preview mode

Marginalia

Fast and practical

annotations for minibuffer completions

in Emacs.

Author

Created by

Daniel Mendler (minad),

who is also the author of

Vertico, Corfu, Jinx

and other well-known Emacs packages.

Installation

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'marginalia))

Global minor mode

(add-hook 'after-init-hook #'marginalia-mode)

Simpler file listings

Do not annotate files with permissions and dates (not needed, so noise).

(with-eval-after-load 'marginalia
  (setopt marginalia-annotators
          (assq-delete-all 'file marginalia-annotators))
  (setopt marginalia-annotator-registry
          (assq-delete-all 'project-file marginalia-annotators)))

Orderless

A completion style for Emacs. It splits user input into components, and then searches the completion candidates for those components in any order. For example, foo bar and bar foo both match foo bar.

Configure!

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'orderless))

Set the preferred completion style. 61

;; NOTE Not `custom'-settable.
(setq completion-category-defaults nil)
(add-hook 'after-init-hook
          (defun my-orderless-init ()
            (require 'orderless)
            (setopt completion-styles '(orderless))))

Allow other separators than the space character and respect escaping.

(with-eval-after-load 'orderless
  (setopt
   orderless-component-separator
   (defun my-orderless-split (string)
     "Split STRING on +/- signs and spaces, if not escaped."
     (let ((encode
            (lambda (string)
              (let* ((string (string-replace "\\ " "!S!" string))
                     (string (string-replace "\\-" "!M!" string))
                     (string (string-replace "\\+" "!P!" string)))
                string)))
           (decode
            (lambda (string)
              (let* ((string (string-replace "!S!" " " string))
                     (string (string-replace "!M!" "-" string))
                     (string (string-replace "!P!" "+" string)))
                string))))
       (let* ((string-coded (funcall encode string))
              (components-coded (split-string string-coded " +\\|\\-\\|\\+" t))
              (components (mapcar decode components-coded)))
         components)))))
;; Test un-escaped components.
(cl-assert (equal (my-orderless-split "a-b-c") '("a" "b" "c")))
(cl-assert (equal (my-orderless-split "a+b+c") '("a" "b" "c")))
(cl-assert (equal (my-orderless-split "a b c") '("a" "b" "c")))

;; Test un-escaped trailing component separator.
(cl-assert (equal (my-orderless-split "a-b-") '("a" "b")))
(cl-assert (equal (my-orderless-split "a+b+") '("a" "b")))
(cl-assert (equal (my-orderless-split "a b ") '("a" "b")))

;; Test un-escaped leading component separator.
(cl-assert (equal (my-orderless-split "-b-c") '("b" "c")))
(cl-assert (equal (my-orderless-split "+b+c") '("b" "c")))
(cl-assert (equal (my-orderless-split " b c") '("b" "c")))

;; Test escaped components.
(cl-assert (equal (my-orderless-split "a\\-b\\-c") '("a-b-c")))
(cl-assert (equal (my-orderless-split "a\\+b\\+c") '("a+b+c")))
(cl-assert (equal (my-orderless-split "a\\ b\\ c") '("a b c")))

;; Test escaped trailing component separator.
(cl-assert (equal (my-orderless-split "a\\-b\\-") '("a-b-")))
(cl-assert (equal (my-orderless-split "a\\+b\\+") '("a+b+")))
(cl-assert (equal (my-orderless-split "a\\ b\\ ") '("a b ")))

;; Test escaped leading component separator.
(cl-assert (equal (my-orderless-split "\\-b\\-c") '("-b-c")))
(cl-assert (equal (my-orderless-split "\\+b\\+c") '("+b+c")))
(cl-assert (equal (my-orderless-split "\\ b\\ c") '(" b c")))

;; Test mixed, escaped and un-escaped, components.
(cl-assert (equal (my-orderless-split "a-b\\-c") '("a" "b-c")))
(cl-assert (equal (my-orderless-split "a+b\\+c") '("a" "b+c")))
(cl-assert (equal (my-orderless-split "a b\\ c") '("a" "b c")))

Prompted by https://github.com/oantolin/orderless/issues/102.

Fold diacritics, such that Bézier matches Bezier.

(with-eval-after-load 'orderless
  (add-to-list 'orderless-matching-styles #'char-fold-to-regexp))

Bind the space key in the minibuffer to a component separator.

;; (keymap-set minibuffer-local-completion-map "SPC" "+")
;; (keymap-set minibuffer-local-filename-completion-map "SPC" "+")

Contribute!

Jinx

A spell checker frontend for Emacs.

Author

Created by

Daniel Mendler (minad),

who is also the author of

Vertico, Corfu, Marginalia

and other well-known Emacs packages.

Architecture

Frontend to Enchant, a

C library/program that “wraps” different spelling libraries,

out of which

we use GNU Aspell.

Key bindings

Entry points:

Key Description Note
M-$ Correct word at point C-u for buffer
C-M-$ Change language  

and within M-$ (jinx-correct):

  • @ save the word in a personal dictionary
  • * save the word in-file

Installation

Install the Emacs package

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'jinx))

and the backend programs

brew "enchant"
brew "aspell"

† Tangled to ~/Brewfile for Homebrew.

Custom startup

Enable the global minor mode when Emacs launches.

(add-hook 'after-init-hook #'global-jinx-mode)

Custom key bindings

Use the same key bindings as the built-in Flyspell.

(with-eval-after-load 'jinx
  (keymap-global-set "M-$" #'jinx-correct)
  (keymap-global-set "C-M-$" #'jinx-languages))

Custom backend selection

Use Aspell and Aspell only.

*:aspell

† Tangled to ~/.config/enchant/enchant.ordering.

Custom camel-case processing

Spellcheck CamelCase words separately.

camel-case true

† Tangled to ~/.aspell.conf.

Custom location of personal dictionaries

ln -fs ~/org/jinx-en.dic ~/.config/enchant/en_US.dic
ln -fs ~/org/jinx-sk.dic ~/.config/enchant/sk.dic

Custom personal dictionary for English

ACK
ACM
ADB
ADHD
AFK
AGI
ALU
AMS
ANOVA
APA
ARP
ARPA
ASN
AST
ASW
AUCTeX
AVL
Adamkovic
Adamkovicova
Adamkovicová
Aeron
Alexa
Allopatric
Alza
Andrej
Animalia
Archaea
Arrabbiata
Aupark
Autoconf
BBDB
BBIAB
BCE
BFS
BGP
BMI
BNF
BOL
BRB
Bandcamp
Banksy
Bitlbee
Bitstream
Blockchain
Bo
Bolognese
Bonduelle
Booyakasha
Brewfile
Buckminster
Buffett
Bézier
CAPF
CDF
CDN
CEROD
CGA
CGPA
CLI
CMOS
CNF
CNNs
COVID
CPUs
CQ
CSL
CSP
CSV
CTP
CVE
CWE
Calc
Cassini
ChatGPT
Chibi
Chordata
Citar
Colemak
Collatz
Convolutional
Ctrl
Cyn
DDL
DFS
DHCP
DIY
DM
DML
DNS
DOM
DRM
DSL
DSLs
DVCS
DWIM
Dabbrev
De
Dired
Dmitry
EBNF
ECL
EE
EG
EI
ELPA
EMMS
EMR
EOL
EP
EQ
ERC
ERT
ESS
ETL
EWW
Edsger
EduPage
Eglot
Eldoc
Elfeed
Elisp
Elsevier
EmacsConf
Enfluid
Eshell
Etruscans
Eukarya
Eukaryota
Eulerian
FFI
FFmpeg
FIDE
FIXME
FML
FOMO
FOSS
FPU
FPUs
FTW
Fastmail
Fastmail's
FennelConf
Fetchmail
Fi
Flexbox
Flycheck
Flymake
Flyspell
Freshona
GC
GCD
GDPR
GLFW
GPG
GPL
GPLv
GPT
GRE
GTD
GTK
GTP
Galilei
Gantt
Gaulli
Geiser
Gmail
Gradle
Grammarly
Graphviz
Gregor
Guernica
HB
HD
HDF
HDL
HDLs
HIIT
HIPAA
HN
HOPL
HPC
HSPA
HTTPS
HW
Hagia
Homebrew
Hox
IAB
IANA
IAP
IAPs
IBAN
ICANN
IEC
IFI
IM
IMAP
IPv
IQR
ISPs
IaaS
Ihor
ImageMagick
Imenu
Immanuel
Inkscape
Iosevka
Ipsum
Isearch
JIT
JMAP
JNI
JSON
JVM
Jazeera
Jitsi
Jupyter
KNN
Kanata
Karabiner
Katarína
Katka
Kenwood
Khronos
Kickstarter
Kiki
Kiki's
Kinesis
Kishsaver
Kofola
Kruskal
Kruskal's
LANs
LDL
LEDs
LGTM
LHS
LLC
LLDB
LLM
LLN
LLVM
LMAO
LMS
LOC
LOL
LSA
LSP
LTE
LUNTER
Lamac
Lamport
Latium
Leopoldov
Levenshtein
Libera
Liberapay
Lidl
Liptov
Lorem
Lovatts
Lua
Lua's
Lunter
Lysippos
MBTI
MC's
MDA
MELPA
MIMO
MITM
MOLAP
MOTC
MPD
MPV
MRU
MSE
MTA
MTU
MUA
MVC
MVVM
Magit
Maildir
Makefile
Makefiles
Mammalia
MathJax
Matplotlib
Matús
Mee
Microfilament
Milankovitch
Minecraft
Moby
Moodle
NAND
NAS
NDP
NIST
NLP
NOP
NTP
NTPs
Naur
Neumann
Niels
Noam
Noosphere
Notre
Noweb
NumPy
Nyxt
OAuth
OER
OGG
OLAP
OLTP
OMG
OMSCS
OO
OOP
OOTB
OSD
OSI
OSS
OTP
Okazaki
Ollama
OpenGL
OpenStax
Operandi
Orderless
Orphée
Overfitting
PCNA
PDFs
PGF
PMF
PNG
POSIX
POV
PRNG
PRNGs
PUK
PaaS
Pandoc
Paredit
Pell
Penicillium
Pergamon
Petri
Petrzalka
Phong
Photometria
Photoshop
Plantae
Polykleitos
Prim's
Prolog
Prot
Protesilaos
Protista
Punics
QMK
QOL
QoS
Quicksort
Quora
RDBMS
RDMS
README
REPL
RESULTADO
RET
RGB
RGBA
RGBs
RHS
RMS
RNG
ROLAP
RPG
RPN
RSA
RSS
RTOS
Rainwave
Rclone
Readline
Reddit
Redux
Rendez
Retrospectivelly
Ripgrep
Rodentia
SASL
SBCL
SCP
SDF
SDK
SDL
SEM
SFTP
SHA
SIAF
SICP
SMB
SMS
SMTP
SNP
SPM
SPS
SRFI
SSD
SSL
SVG
SVGs
SaaS
Saadia
Salutis
Schengen
Schermerhorn
Scikit
Sed
Sendmail
Shimon
SomaFM
Spotify
Springer
Sri
Staedtler
Stallman
Studenohorská
Subspaces
Sympatric
TAI
TAOCP
TBD
TBH
TCP
TLA
TLD
TLDR
TLS
TOC
TODO
TTL
TTP
TTYL
TTYT
TUI
TYVM
Tabata
Tae
Tatras
Telega
TempEl
Tesco
Tetris
Texinfo
Theodor
TikZ
Tristimulus
Trnava
UDP
UI
UML
URI
USD
UTF
UUID
UUIDs
UX
UoPeople
Upwork
VC
VCS
VHDL
VM
VPN
VPNs
VR
VRAM
Vemondo
Verilog
Vertico
Visegrád
Vivandi
Vivendi
Vizing's
Vorbis
Vterm
WD
WDYT
WIP
WRT
WTF
WTH
WebDAV
WebGL
Welch
Welch's
Wi
WiMAX
Wikimedia
Wolfenstein
Wybe
XHTML
XMPP
XSD
XSLT
XSS
XT
Xcode
Xeon
YAML
YMMW
Yabai
Yay
ZFS
Zettelkasten
Zipf
Zotero
Zsh
abiotic
accessor
accessors
accrete
accuracies
actin
activations
acyclic
adenosine
adjugate
advices
affordance
affordances
aftershow
agarose
agentic
agglomerative
agroforestry
aldose
algorithmically
algorithmics
alist
alists
allocative
allocatively
allosteric
alphanumerics
aminophenol
amphibolic
amphiphilic
anabolic
analytics
anaphase
aneuploid
aneuploidity
aneuploidy
animatable
anoxic
anticodon
anticodons
antiderivative
antiderivatives
applicative
aquaporin
arXiv
arachidonic
archeological
archeology
architected
arg
args
arity
ars
ary
aspartate
assed
asymptotics
async
atomicity
authenticator
autocrine
autoencoder
autoencoders
autoinducer
autoinducers
automagical
automagically
automata
autosomal
autosome
autosomes
autotroph
backend
backends
backgrounded
backgrounding
backlight
backlink
backlinks
backoff
backpropagation
backtrace
bacteriophage
bacteriophages
bangers
basicity
baz
benchmarking
benzoyl
biconditional
biconditionals
bigraph
bigraphs
bijection
bijections
bijective
bikeshed
bikeshedded
bikeshedding
bilayer
bilayers
biofilm
biofilms
biogeochemical
bioinformatics
biome
biomes
biometric
biometrics
biotic
bitwise
biword
blockchain
boolean
booleans
bootable
borderless
breakpoint
brutalist
bugfix
bugfixes
bumpon
bumpons
butene
buttonize
buttonized
buttonizes
butyne
bytecode
cadherin
cadherins
caementicium
callee
capacitive
capita
capsid
carbonyl
carboxyl
cardinality
catabolic
catabolism
catabolize
cellulase
centrioles
centroid
centroids
centromere
centromeres
centrosome
centrosomes
changelog
chaperone
chaperones
chatspeak
checkboxes
checksums
chevets
chiptune
chiptunes
chorded
chording
chromatid
chromatids
cis
clicky
codebase
codebases
codetermination
codomain
cofactor
cofactors
cohesin
colchicine
coli
colinear
colorings
columnarly
combinational
combinatorial
combinatorics
comparator
comparators
composability
composable
compositional
computability
computable
config
configs
configurator
conformational
const
contractionary
contrapositive
convolutional
coroutine
coroutines
corvus
counterintuitive
covalently
crafter
crafters
crasher
crashers
crashlog
crypto
cryptographic
cryptology
cryptosystem
cryptosystems
customizable
customizations
cyanobacteria
cyber
cyberattack
cyberattacks
cybersecurity
cyclooxygenase
cyclopentane
cytidine
cytokinesis
cytoskeleton
cytoskeletons
cytosol
da
dalton
dataset
datasets
deallocate
deallocated
debbugs
debounce
debounced
debouncing
decile
deciles
decondense
decrypt
deduplicate
deduplicated
deinitialization
dendrogram
denoised
denoising
deoxynucleotides
deoxyribonucleic
deoxyribonucleoside
deoxyribonucleosides
deoxyribose
dephosphorylated
dephosphorylates
dephosphorylation
deprioritize
dequeue
dequeuing
dereference
dereferenced
dereferencing
derivate
derivates
desaturated
deserialization
deserialize
deserializes
desmosome
destructor
destructors
destructure
destructured
destructures
destructuring
destructurings
detangle
detangles
detangling
devs
diagonalizable
diagonalization
diagonalize
dialogs
dideoxynucleoside
dideoxynucleotide
dideoxynucleotides
differentiator
differentiators
dimensionality
diphosphate
dir
dirs
disaccharide
disaccharides
discoverable
disjunction
disjunctions
disruptor
disruptors
distributivity
distro
distros
docstring
docstrings
dogfooding
domus
downcase
downcased
downcasing
downscaled
drawable
drawables
dsfasd
dumbing
durables
durations
dvisvgm
ebook
ebooks
eco
effector
effectors
eg
eigenbases
eigenbasis
eigenspace
eigenspaces
electrochemical
electronegative
electronegativity
electrophoresis
embeddable
embeddings
encodings
endergonic
endian
endianness
endocytosis
endomembrane
endoperoxide
endoplasmic
endosymbiosis
endosymbiotic
enshittification
enshittified
enshittifies
enshittify
enthalpy
enum
epigenetic
epigenetics
equilibria
ergodicity
estradiol
ethene
ethyne
eukaryote
eukaryotic
euploid
euploids
euploidy
eval
executables
exergonic
exobrain
exobrains
exocytosis
expirable
exploitability
extensibility
extrema
extremophiles
facto
factsheet
fi
finalizer
finalizers
fixup
fixups
fn
foldable
fontification
fontified
fontifies
fontify
fontifying
formatter
formatters
frameset
framesets
franca
freewrite
freewriting
frontend
frontends
fu
fuzzer
galactose
gameplay
geeking
genotype
genotypes
geofence
geofences
geofencing
getter
getters
glioma
gliomas
glitchy
globals
glycemic
glycolipid
glycolipids
glycolysis
glycoprotein
glycoproteins
glycosidic
glyphs
grayscale
greenwashing
grissini
guanosine
hackability
hackable
hackathon
hackathons
hackerdom
hacky
haplodiploid
helicase
heterotroph
heterotrophic
heterotrophs
heterozygotes
hexaploid
hexose
hexoses
histone
histones
hittability
hoc
homebrew
homoiconic
homoiconicity
homolog
homologs
homomorphic
hostname
hotspot
hotspots
hydrochloric
hydrogens
hydrolyse
hydrolysed
hydrophile
hydrophiles
hydrophobe
hydrophobes
hydroxy
hydroxyl
hyperparameter
hyperparameters
hypertonic
hypotonic
iFixit
iconographer
iconographers
icosahedron
iff
impactful
importances
incentivize
incentivized
incentivizes
incentivizing
influencer
influencers
ing
init
initializer
initializers
injective
inlined
inlines
inlining
inplace
insulae
integrations
intercellular
interkinesis
internetworking
interop
interoperate
interphase
interpretable
interpunction
interquartile
intracellular
intramolecular
intron
introns
invariants
invertibility
invertible
isotonic
isozymes
iterable
iteratively
janky
jibber
jitter
js
juxtacrine
kai
karyogram
karyograms
karyotype
kb
kcal
kerning
ketose
keycap
keycaps
keymap
keymaps
kilocalories
kinetochore
kinetochores
laude
lemmatization
libc
libre
ligand
ligands
ligase
lingua
linkable
linkify
linter
linters
lipoprotein
lipoproteins
localizable
logistician
lookups
looper
lossy
lyse
lysed
lyses
lysis
lysosome
macromolecule
macromolecules
maildir
maildirs
malabsorption
maltase
maltases
masse
matcher
matchers
mb
memoization
memoize
memoized
metaheuristic
metaheuristics
metaphase
methionine
methylated
methylation
methylguanosine
microcontroller
microcontrollers
microfilaments
micrograph
micrographs
microtubule
microtubules
middleware
migrator
migrators
millivolt
millivolts
minibuffer
minibuffers
minified
minifies
minify
mipmap
mipmaps
miscapitalize
miscapitalized
miscategorize
miscategorized
miscategorizes
miscommunicate
miscommunicated
misconfigured
mistyped
modally
modularity
modularize
modus
moka
moldable
monads
monohybrid
monohybrids
monoid
monoids
monomial
monophosphate
monosaccharide
monosaccharides
monosomic
monosomies
monosomy
monospace
monospaced
mothership
multinomial
mutator
myosin
namespace
namespaces
nanometers
natively
nauseam
nd
neoclassically
neurodevelopmental
nilly
nils
nitrous
nitty
nm
nondisjunction
nonparametric
nonpolar
nonsister
noob
noobs
normals
normies
notmuch
nucleobase
nucleobases
nucleoid
nucleoplasm
nucleosome
nucleosomes
nucleotides
nullability
nullable
octoploid
offscreen
operon
operons
optimality
optionals
organismal
orrery
orthogonally
ortholinear
ortholinearity
orthonormal
osmolarity
osmoregulation
ouchy
outliner
outliners
overfit
overfits
overfitting
overwind
overwinding
overwinds
overwound
paleo
paracrine
parallelepiped
parallelize
parallelotope
parameterizable
parameterization
parameterizes
parameterizing
parametrization
parametrize
parametrized
params
paren
parens
parsers
passcode
paywalled
penne
pentose
pentoses
percept
perceptron
perceptrons
percepts
performant
permalink
permuterm
peroxisome
peroxisomes
personalization
phenotypes
phenotypic
pho
phosphatase
phosphatases
phosphodiester
phospholipid
phospholipids
phosphorylated
phototrophs
phragmoplast
phragmoplasts
phylogenetic
phytosterol
pinout
pinouts
pixelated
plasmid
plasmids
plasmodesma
plasmodesmata
plasmolysis
plist
ploidy
poleis
polis
polyadenylation
polygots
polymerase
polypeptide
polypeptides
polyploid
polyploidy
polyribosome
polyribosomes
polysaccharide
polysaccharides
polysome
polysomes
pomodoro
pomodoros
ponens
porcellus
postamble
postcondition
postconditions
potentiometer
pragma
pragmas
pre
precalculus
prefetch
prefetched
prefetches
preimage
prepend
preprocess
preprocessing
preprocessor
presynaptic
previewable
primality
primase
probabilistically
procedurally
profiler
profilers
programmability
programmatically
prokaryote
prokaryotes
prometaphase
propene
propertize
propertized
propertizes
prophase
propyne
proselint
prostaglandin
protist
protists
proto
prototyped
prover
provers
psytrance
purposive
pushup
pushups
pyridine
pyruvates
quantile
quantiles
quartile
quartiles
quaternary
quintiles
qux
radix
ramen
rasterization
rebase
rebased
rebasing
recommender
recommenders
recompiles
recycler
recyclers
redox
reducibility
reductional
refactorings
reflectance
reflectances
reflog
reflow
reflowable
reflowables
reflows
regressor
reimplement
reimplementation
reinstantiate
reinstantiated
reinstantiates
reinstantiation
remapper
renderer
replicability
repo
repos
repressor
repressors
repro
reproducibility
reproducibly
repunctuate
repunctuating
repunctuation
resizable
resolvers
respondee
responder
reticulum
retrowave
ribonucleic
ribose
riboses
ribosomal
ribosome
ribosomes
roadmap
rollout
rollouts
runtime
runtimes
salutis
sandboxed
sandboxing
satisfiability
satisfiable
sawtooth
scalability
scalable
sceneries
schemas
scientia
scm
scriptability
scriptable
scrollable
sexp
sexps
sextile
sextiles
shader
shaders
shippable
shockwaves
sigmoid
silluetes
sirupy
skewness
snRNA
softmax
soonish
speciation
specular
spiritlessness
spliceosome
spliceosomes
src
stacktrace
stacktraces
stateful
statins
stderr
stdin
stdout
stemmer
sterol
sterols
storyboarding
strobing
stromatolite
struct
structs
subarray
subarrays
subdir
subdirectories
subdirectory
subdirs
subgraph
subgraphs
subnet
subnets
suboptimal
subpixel
subpixels
subqueries
subquery
subscripting
subsequence
subshell
subshells
substatements
substring
substrings
subtask
subtree
subtrees
subtyping
subview
subviews
subword
sucrase
sulcus
sulfhydryl
summa
summand
summands
sundried
superclass
superclasses
supercoiled
supercoiling
superset
surjection
surjections
surjective
syllabi
symbolicate
symbolicated
symlink
symlinks
synapsis
synaptonemal
synchronizer
synthase
synthwave
syscall
tabu
tappable
teardown
teleported
teleporting
teleports
telomerase
telomere
telomeres
telophase
templating
testability
tetrad
tetrads
texel
texels
th
thymidine
tmp
tock
tokenization
tokenize
tokenized
tokenizer
tokenizers
tokenizes
tollens
tonicity
toolkits
topoisomerase
totient
touchpad
touchpads
traceback
tracebacks
trainings
transactional
transcode
transcoder
transcoders
transcoding
transcriptional
transformational
translational
translocated
translocations
transmembrane
transpilation
transpile
transpiles
transpiling
transposon
transposons
transversion
triacylglycerol
trichotomy
tricorder
tricorders
triose
trioses
triphosphate
triphosphates
trisomic
trisomies
trisomy
truecasing
truthy
tubulin
turgor
txt
un
unapologetically
unary
unbuffered
unclosed
uncomment
uncommented
uncomputability
uncomputable
unculturable
underwind
underwinding
underwinds
underwound
undirected
unelaborately
unevaluated
unfinishedness
unflag
unflagged
unhide
unicast
univariate
unmark
unmerged
unmuted
unpriced
unsatisfiable
unstage
unstaged
untracked
upcase
upcases
upcasing
upsert
upstreamed
upstreaming
upthread
uptime
uridine
utils
valine
vararg
varargs
variadic
vectorization
vectorize
vectorized
vectorizes
vectorizing
virtualize
virtualized
virtualizes
von
webdev
welfarist
welfarists
whitespace
wireframe
wireframed
wireframes
wireframing
woohoo
yay
zumba
Xception
ReLU
Adamkovič
LaTeX
Mendler
Keras
KerasTuner
TinyML
SoftMax
ImageNet
logit
logits
overcomplicate
overcomplicating
overcomplicated
overcomplicates
misclassify
misclassification
interpretability
Adamkovičová
autocomplete
multiset
uncountably
frequentist
Xapian
LibreWolf
OpenDNS
enshittificaiton
mins
userland
lexically
pointee
pointees
subscripted
recomputation
homerow
Prot's
searchability
Kangas
deallocating
deallocator
deallocators
Bialetti
snoozy
snoozier
snooziest
GNUstep
deprioritizes
Taipo
Monkeytype
backlit
cancellable
kLOC
µm
PlantUML
autoload
autoloads
gyoza
gyozas
upcased
mojito
nic
UIKit
snus
transpiler
disassembler
toolchain
L'Oréal
mattifying
finiteness
unselect
unselected
unselects
lexeme
lexemes
Kleene
backronym
Festivus
TikTok
copyable
nestable
assignables
subtour
subtours
instantiation
Forsyth
unsat
Helvetica
Perlis
upcast
upcasted
upvote
upvotes
untyped
complect
complecting
biconditionally
UIs
Snussie
Longjing
linearize
linearizes
Linearization
plists
Comint
Wowser
fakebit
upgradable
NeXTSTEP
Rcirc
Smerge
emojify
inequal
reinstalls
decompile
decompiled
decompilation
nonatomic
debounces
Nvidia
recency
unroasted
undoable
nullary
fancify
fancified
pretraining
Fortran
viewport
Anthropic
unescaped
significand
oolong
oolongs
Tcl
Tk
Qwen
HRPLs
LRPLs
SRFIs
Wilcoxon
reframe
reframed
reframes
reframing
prepends
prepending
prepended
Spearman
preload
preloads
preloaded
preloading
viewports
Emacsen
Meganeko
unimodal
trimodal
multimodal
bimodality
trimodality
multimodality
Hartigan
Brompton
quantized
puffery
symlinked
idempotence
speciesism
vendored
trillionaire
trillionaires
popcount
differentiability
sharding
exponentials
misconfiguration
nonnegative
monomials
bivariate
microadventure
microadventures
libs
degreaser
renderers

Custom personal dictionary for Slovak

vegánske
Hviezdoslav
Hviezdoslavovo
pinčes
pinec
Peťo
Karlovka

My bug reports

  • DONE Fix hyphenated words with Aspell (237)

TempEl

An extension of the built-in Tempo package for inserting templates.

TODO Explore and document tempel-map

Learn

The templates are stored in tempel-path file(s) with the following syntax:

8a28d0d2-9afc-40fd-911b-ecf8754952ec.svg

where CONTENT is one of

Value Result Mnemonic
FORM Emacs Lisp computation †  
p placeholder placeholder
(s NAME) named placeholder stored
q quit placeholder quit
r active region \dagg region
(p LISP [NAME] [NO-INSERT]) computed/named placeholder placeholder
n newline character  
> indentation  

† Often a string literal.
\dagg If no active region exists, acts as q.

Configure

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'tempel))

Complete globally on C-M-<tab>.

Note. The officially recommended binding is M-+.

(keymap-global-set "C-M-<tab>" #'tempel-complete)

Move between placeholders with TAB and S-TAB.

Note. The official bindings are M-{ and M-}.

(with-eval-after-load 'tempel
   (keymap-set tempel-map "TAB" #'tempel-next)
   (keymap-set tempel-map "S-TAB" #'tempel-previous))

Find the templates in the *-templates.* files in the org-directory.

(with-eval-after-load 'tempel
  (setopt tempel-path
          (file-name-concat (progn
                              (require 'org)
                              org-directory)
                            "obtf.eld")))

Create a template for defining new templates. Meta!

org-mode
(template
 "#+begin_src lisp-data :tangle "
 (concat (file-name-base buffer-file-name) "-templates.eld") n
 (p "MODE") "-mode" n
 "(" (p "NAME") n "  " q ")" n
 "#+end_src")

Notmuch

CLI mail indexer and

MUA for Emacs

powered by it.

Installation

Tangled to the Homebrew bundle Brewfile:

brew "notmuch"
brew "isync" # mbsync

and on the Emacs side 62:

(add-to-list 'load-path "/opt/homebrew/share/emacs/site-lisp/notmuch/")
(autoload #'notmuch "notmuch" nil t)

Fun name

Notmuch is not much of an email program. It doesn’t receive messages (no POP or IMAP support). It doesn’t send messages (no mail composer, no network code at all). And for what it does do (email search) that work is provided by an external library, Xapian. So if Notmuch provides no user interface and Xapian does all the heavy lifting, then what’s left here? Not much.

https://notmuchmail.org/

Basic operation

  • Synchronize
    notmuch new
    
  • Re-index and compact
    notmuch reindex '*'
    notmuch compact
    

My notmuch config

Configure the notmuch indexer

The notmuch program

indexes the content of given maildirs for fast search,

based on the ~/.notmuch-config config file:

# .notmuch-config - Configuration file for the Notmuch mail system
# For more information about Notmuch, see https://notmuchmail.org

[database]
path = /Users/rudy/.notmuch/
hook_dir = /Users/rudy/.notmuch/hooks/
mail_root = /Users/rudy/.notmuch/mail/

[index]
# Requires re-index upon change.
# Dashes not allowed on the left-hand side.
header.list = List-Id

[user]
name = Rudolf Adamkovič
primary_email = rudolf@adamkovic.org

[new]
tags = unread
ignore = /.*/(.uidvalidity|.mbsyncstate.*)$/

[search]
exclude_tags = muted;deleted;spam

My mbsync config

A free software utility that

synchronizes maildirs via the IMAP protocol

per the ~/.mbsyncrc file:

  • Installation
  • Configuration

    The ~/.mbsyncrc configuration file:

    IMAPAccount fastmail
    Host imap.fastmail.com
    User rudolf@adamkovic.org
    PassCmd "pass fastmail+mail"
    TLSType IMAPS
    AuthMechs Login
    
    IMAPStore FastmailRemote
    Account fastmail
    
    MaildirStore FastmailLocal
    Path ~/.notmuch/mail/
    Inbox ~/.notmuch/mail/INBOX
    
    Channel fastmail
    Far :FastmailRemote:
    Near :FastmailLocal:
    Patterns INBOX Sent Spam
    Expunge Both
    CopyArrivalDate yes
    Create Near
    SyncState *
    

My pre-new hook

The ~/.notmuch/hooks/pre-new script

runs just before notmuch new,

written in Bash:

# Notmuch `pre-new' hook.
#
# This script:
# 1. moves messages from/to correct folders
# 2. synchronizes messages with the IMAP server.
#
# Rudolf Adamkovič <rudolf@adamkovic.org>
# Licensed under the General Public License (GPL), version 3.

set -e

# Signature:
#   file QUERY...
# Description:
#   Return all files matching QUERY.
files() {
    QUERY=$@
    notmuch search --output=files --exclude=false ${QUERY}
}

# Signature:
#   move DIR QUERY...
# Description:
#   Move COUNT files matching QUERY to DIR.
move() {
    DIR=${1} QUERY=${@:2}
    FILES=($(files ${QUERY}))
    for FILE in ${FILES[*]}; do
        # if test -f ${FILE}; then
            TO_FILE=${FILE##*/} # just basename
            TO_FILE=${TO_FILE/,U=[0-9]*/} # no metadata
            mv ${FILE} ${DIR}/${TO_FILE}
        # fi
    done
}

# Signature:
#   delete QUERY...
# Description:
#   Delete all files matching QUERY.
delete() {
    QUERY=${@}
    FILES=($(files ${QUERY}))
    if test ${#FILES[*]} -gt 0; then
        rm -f ${FILES[*]}
    fi
}

# Signature:
#   sync
# Description:
#   Synchronize with IMAP server.
sync() {
    if ping -c 1 imap.fastmail.com &>/dev/null; then
        mbsync --all
    else
        echo "IMAP server unreachable." >&2
    fi
}

ROOT=$(notmuch config get database.mail_root)
mkdir -p ${ROOT}

echo "Deleting..."
delete is:deleted
delete is:draft

echo "Moving spam in..."
move ${ROOT}/Spam/new is:spam and not folder:Spam and is:unread
move ${ROOT}/Spam/cur is:spam and not folder:Spam and not is:unread

echo "Moving spam out..."
move ${ROOT}/INBOX/new folder:Spam and not is:spam and is:unread
move ${ROOT}/INBOX/cur folder:Spam and not is:spam and not is:unread

echo "Syncing..."
sync

My post-new hook

# Notmuch `post-new' hook.
#
# This script:
# 1. re-tags all messages as spam or not
# 2. tags hardcore spammers.
#
# Rudolf Adamkovič <rudolf@adamkovic.org>
# Licensed under the General Public License (GPL), version 3.

set -e

echo "Re-tagging spam..."
notmuch tag +spam folder:Spam
notmuch tag -spam not folder:Spam

echo "Tagging hardcore spammers..."
notmuch tag +spam \
    from:@robinhood.com or \
    from:@golemclub.sk

My default MUA

Use Notmuch for mail everywhere in Emacs, including

  • compose-mail (C-x m)
  • compose-mail-other-window (C-x 4 m)
  • report-emacs-bug
  • org-submit-bug-report

and other entry points.

(setopt read-mail-command #'notmuch
        mail-user-agent (progn
                          (require 'notmuch-mua)
                          'notmuch-user-agent))

My disabled archiving

(with-eval-after-load 'notmuch
  (setopt notmuch-archive-tags nil))

My agenda

  • TODO Explore notmuch-tree

My patches

  • WAIT Fix saved-search buffer titles (thread)
  • WAIT Make notmuch-hello take into account line numbers
  • WAIT Re-layout notmuch-hello on window width changes
  • TODO Make the logo work with dark themes
    • Emacs now uses proper colors for SVGs (bug).

Notmuch Hello

A major mode in Notmuch that

shows all saved searches and tags,

and upon selection

opens them in Search mode.

Key bindings

To open it, type the global key binding C-c M.

Key Description Note
n go to next line custom, also C-n
p go to previous line custom, also C-p
f go character forward custom, also C-f
n go character backward custom, also C-n
G synchronize mail and refresh custom, async
m compose new message Notmuch-wide
s search all messages Notmuch-wide
g refresh Notmuch-wide

My autoload

(autoload #'notmuch-hello "notmuch-hello" nil t)

My entry point

Define the global key binding.

Note: Use notmuch instead of notmuch-hello, as it auto-loads.

(keymap-global-set "C-c M" #'notmuch)

My asynchronous polling

The notmuch-poll command, bound to G in notmuch-common-keymap,

polls synchronously, which makes it useless,

so we write my-notmuch-poll that is asynchronous:

(with-eval-after-load 'notmuch
  (defun my-notmuch-poll ()
    "Run `notmuch new' asynchronously and refresh relevant buffers."
    (interactive)
    (let ((buffer "*Notmuch new*"))
      ;; Call `notmuch new' asynchronously.
      (let ((process (notmuch-start-notmuch
                      "notmuch-new"
                      buffer
                      (lambda (process event)
                        (when (eq (process-status process) 'exit)
                          (when (get-buffer buffer)
                            (kill-buffer buffer))
                          (notmuch-refresh-all-buffers)
                          (message "Polling mail... done")))
                      "new")))
        ;; HACK The `internal-default-process-filter' does not auto-scroll.
        (set-process-filter process
                            (lambda (process output)
                              (when (buffer-live-p (process-buffer process))
                                (with-current-buffer (process-buffer process)
                                  (insert output)
                                  (let ((win (get-buffer-window)))
                                    (when (window-live-p win)
                                      (set-window-point win (point-max))))))))
        ;; Show progress.
        (let ((display-buffer-alist `((,(regexp-quote buffer)
                                       (display-buffer-in-side-window)
                                       (window-height . 8)))))
          (display-buffer buffer))))))
(with-eval-after-load 'notmuch
  (keymap-set notmuch-common-keymap "G" #'my-notmuch-poll))

My support for sexp queries

(with-eval-after-load 'notmuch
  (defun my-notmuch-query (sexp)
    "Return string query equivalent to SEXP."
    (format "sexp:\"%s\""
            (string-replace "\"" "\"\""
                            (format "%S" sexp)))))
(with-eval-after-load 'notmuch
  (unless (equal (notmuch-config-get "built_with.sexp_queries") "true")
    (user-error "Notmuch not built with sexps.")))

My saved searches

  • Step 1: Define
    • Area
      • Banks
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-area-banks ()
            "Notmuch saved search 'Area: Banks'."
            '(and (tag unread)
                  (or (from "transferwise.com")
                      (from "wise.com")
                      (from "mbank.sk")
                      (from "intl.paypal.com")
                      (from "paypal.com")
                      (from "przelewy24.pl")
                      (from "stripe.com")
                      (from "slsp.sk")))))
        
      • School
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-area-school ()
            "Notmuch saved search 'Area: School'."
            '(and (tag unread)
                  (or (from "uopeople.edu")
                      (from "coursera.org")
                      (from "yammer.com")
                      (from "proctoru.com")
                      (from "edupage.org")
                      (from "edupage.info")
                      (from "slobodnaskola.sk")
                      (from "cit.ie")
                      (from "mycit.ie")
                      (from "mtu.ie")
                      (from "instructure.com")
                      (from "digitary.net")))))
        
      • Services
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-area-services ()
            "Notmuch saved search 'Area: Services'."
            '(and (tag unread)
                  (or (from "apple.com")
                      (from "noreply@google.com")
                      (from "accounts.google.com")
                      (from "itunes.com")
                      (from "o2.com")
                      (from "o2.sk")
                      (from "stackoverflow.email")
                      (from "upwork.com")
                      (from "orange.sk")
                      (from "ocs.sun.orange.sk")
                      (from "orange@staffino.com")
                      (from "noreply-purchases@youtube.com")
                      (from "payments-noreply@google.com")
                      (from "sdf.org")
                      (from "discord.com")
                      (from "support@fastmail.com")
                      (from "newsletter@fastmail.com")
                      (from "@fastmail.help")
                      (from "website@huggingface.co")
                      (from "@overleaf.com")
                      (from "harvestapp.com")
                      (from "forecastapp.com")
                      (from "anthropic.com")
                      (from "claude.com")
                      (from "openai.com")
                      (from "deepseek.com")
                      (from "link.com")
                      (from "wikimedia.org")))))
        
      • Stores

        Includes delivery services.

        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-area-stores ()
            "Notmuch saved search 'Area: Stores'."
            '(and (tag unread)
                  (or (subject "Fastmail renewal")
                      (subject "Fastmail payment")
                      (from "ikea.com")
                      (from "bandcamp.com")
                      (from "makeupandvanityset.com")
                      (from "amazon.com")
                      (from "amazon.co.uk")
                      (from "ebay.com")
                      (from "mailer.humblebundle.com")
                      (from "alza.sk")
                      (from "e-smoke.sk")
                      (from "digitalstores.co.uk")
                      (from "restaumatic.com")
                      (from "ikelp.com")
                      (from "bistro.sk")
                      (from "takeaway.com")
                      (from "comgate.cz")
                      (from "123kurier.sk")
                      (from "shopifyemail.com")
                      (from "overenezakaznikmi.sk")
                      (from "tnt.com")
                      (from "fedex.com")
                      (from "gls-slovakia.sk")
                      (from "wolt.com")
                      (from "crowdsupply.com")
                      (from "packeta.sk")
                      (from "futunatura.sk")
                      (from "gopay.cz")
                      (from "veglife.sk")
                      (from "drmax.sk")
                      (from "pizza-primavera.sk")
                      (from "b2b.gpe.cz")
                      (from "vapeklub.sk")
                      (from "shipstation.com")
                      (from "sps-sro.sk")
                      (from "slposta.sk")
                      (from "moergo.com")
                      (from "dhl.com")
                      (from "snussie.com")
                      (from "europouches.com")
                      (from "menice110v.sk")
                      (from "amazon.de")
                      (from "incon.sk")
                      (from "dpd.sk")
                      (from "liekobox.sk")
                      (from "payout.one")
                      (from "loopearplugs.com")
                      (from "faltradxxs.de")
                      (from "vincita.co.th")
                      (from "aramex.com")))))
        
      • Work
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-area-work ()
            "Notmuch saved search 'Area: Work'."
            '(and (tag unread)
                  (or (from "eggheadgames.com")
                      (list "eggheadgames.github.com")
                      (from "mikemee@pobox.com")
                      (list "enfluid.github.com")))))
        
    • Git
      • Citar
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-citar ()
            "Notmuch saved search 'Git: Citar'."
            '(and (tag unread)
                  (list "emacs-citar.github.com"))))
        
      • Corfu
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-corfu ()
            "Notmuch saved search 'Git: Corfu'."
            '(and (tag unread)
                  (list "corfu.minad.github.com"))))
        
      • GPTel
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-gptel ()
            "Notmuch saved search 'Git: GPTel'."
            '(and (tag unread)
                  (list "gptel.karthink.github.com"))))
        
      • Agent Shell
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-agent-shell ()
            "Notmuch saved search 'Git: Agent Shell'."
            '(and (tag unread)
                  (list "agent-shell.xenodium.github.com"))))
        
      • Magit
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-magit ()
            "Notmuch saved search 'Git: Magit'."
            '(and (tag unread)
                  (list "magit.github.com"))))
        
      • Marginalia
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-marginalia ()
            "Notmuch saved search 'Git: Marginalia'."
            '(and (tag unread)
                  (list "marginalia.minad.github.com"))))
        
      • Mentor
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-mentor ()
            "Notmuch saved search 'Git: Mentor'."
            '(and (tag unread)
                  (list "mentor.skangas.github.com"))))
        
      • Sensor Watch
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-sensor-watch ()
            "Notmuch saved search 'Git: Sensor Watch'."
            '(and (tag unread)
                  (list "joeycastillo.github.com"))))
        
      • Textbooks
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-textbooks ()
            "Notmuch saved search 'Git: Textbooks'."
            '(and (tag unread)
                  (or (list "discrete-book.oscarlevin.github.com")
                      (list "APEXCalculus.github.com")))))
        
      • Vertico
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-vertico ()
            "Notmuch saved search 'Git: Vertico'."
            '(and (tag unread)
                  (list "vertico.minad.github.com"))))
        
      • Other
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-git-other ()
            "Notmuch saved search 'Git: Other'."
            `(and (tag unread)
                  (or (and (or (from "@github.com")
                               (list "github.com"))
                           (not (or ,(my-notmuch-search-git-citar)
                                    ,(my-notmuch-search-git-corfu)
                                    ,(my-notmuch-search-git-gptel)
                                    ,(my-notmuch-search-git-agent-shell)
                                    ,(my-notmuch-search-git-magit)
                                    ,(my-notmuch-search-git-marginalia)
                                    ,(my-notmuch-search-git-mentor)
                                    ,(my-notmuch-search-git-sensor-watch)
                                    ,(my-notmuch-search-git-textbooks)
                                    ,(my-notmuch-search-git-vertico))))
                      (or (from "gitlab@mg.gitlab.com")
                          (to "gitlab.com"))))))
        
    • List
      • Egghead Games (unused)
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-egghead-games ()
            "Notmuch saved search 'List: Egghead Games'."
            '(and (tag unread)
                  (or (to "eg@lists.sr.ht")
                      (list "~eg/eg.lists.sr.ht")))))
        
      • Emacs Bug
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emacs-bug ()
            "Notmuch saved search 'List: Emacs Bug'."
            '(and (tag unread)
                  (or (from "bug-gnu-emacs@gnu.org")
                      (from "help-debbugs@gnu.org")
                      (from "@debbugs.gnu.org")
                      (to "@debbugs.gnu.org")
                      (list "bug-gnu-emacs.gnu.org")))))
        
      • Emacs Devel
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emacs-devel ()
            "Notmuch saved search 'List: Emacs Devel'."
            '(and (tag unread)
                  (or (to "emacs-devel@gnu.org")
                      (list "emacs-devel.gnu.org")))))
        
      • Emacs Help
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emacs-help ()
            "Notmuch saved search 'List: Emacs Help'."
            '(and (tag unread)
                  (or (to "help-gnu-emacs@gnu.org")
                      (to "gnu-emacs-help@gnu.org")
                      (list "help-gnu-emacs.gnu.org")))))
        
      • Emacs Org
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emacs-org ()
            "Notmuch saved search 'List: Emacs Org'."
            '(and (tag unread)
                  (or (to "emacs-orgmode@gnu.org")
                      (list "emacs-orgmode.gnu.org")))))
        
      • Emacs tangents
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emacs-tangents ()
            "Notmuch saved search 'List: Emacs Tangents'."
            '(and (tag unread)
                  (to "emacs-tangents@gnu.org"))))
        
      • EMMS (Emacs Multimedia System)
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-emms ()
            "Notmuch saved search 'List: EMMS'."
            '(and (tag unread)
                  (or (to "emms-help@gnu.org")
                      (list "emms-help.gnu.org")))))
        
      • Fennel
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-fennel ()
            "Notmuch saved search 'List: Fennel'."
            '(and (tag unread)
                  (or (to "fennel@lists.sr.ht")
                      (to "fennel-ls@lists.sr.ht")
                      (list "~technomancy/fennel.lists.sr.ht")
                      (list "~xerool/fennel-ls@lists.sr.ht")
                      (and (from "noreply@notifs.matrix.org")
                           (subject "Fennel"))
                      (and (from "outgoing@sr.ht")
                           (subject "~technomancy/fennel"))))))
        
      • Geiser
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-geiser ()
            "Notmuch saved search 'List: Geiser'."
            '(and (tag unread)
                  (or (to "geiser-users@nongnu.org")
                      (list "geiser-users.nongnu.org")))))
        
      • Lua (dead)
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-lua ()
            "Notmuch saved search 'List: Lua'."
            '(and (tag unread)
                  (or (to "lua-l@lists.lua.org")
                      (list "lua-l.lists.lua.org")))))
        
      • Modus Themes (dead?)
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-modus-themes ()
            "Notmuch saved search 'List: Modus Themes'."
            '(and (tag unread)
                  (or (to "modus-themes@lists.sr.ht")
                      (list "~protesilaos/modus-themes.lists.sr.ht")))))
        
      • Notmuch
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-notmuch ()
            "Notmuch saved search 'List: Notmuch'."
            '(and (tag unread)
                  (or (to "notmuch@notmuchmail.org")
                      (list "@notmuchmail.org")))))
        
      • s7
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-list-s7 ()
            "Notmuch saved search 'List: s7'."
            '(and (tag unread)
                  (or (to "cmdist@ccrma.Stanford.EDU")
                      (list "cmdist.ccrma.stanford.edu")))))
        
    • Maildir
      • All Drafts
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-maildir-all-drafts ()
            "Notmuch saved search 'Maildir: All Drafts'."
            '(or (tag "draft")
                 (folder "Drafts"))))
        
      • All Sent
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-maildir-all-sent ()
            "Notmuch saved search 'Maildir: All Sent'."
            '(folder "Sent")))
        
      • Spam
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-maildir-spam ()
            "Notmuch saved search 'Maildir: Spam'."
            '(and (tag unread)
                  (or (tag "spam")
                      (folder "Spam")))))
        
    • Misc
      • YouTube
        (with-eval-after-load 'notmuch
          (defun my-notmuch-search-misc-youtube ()
            "Notmuch saved search 'Misc: YouTube'."
            '(and (tag unread)
                  (from "noreply@youtube.com"))))
        
  • Step 2: Update
    (with-eval-after-load 'notmuch
      (let* ((functions #'(my-notmuch-search-area-banks
                           my-notmuch-search-area-school
                           my-notmuch-search-area-services
                           my-notmuch-search-area-stores
                           my-notmuch-search-area-work
                           my-notmuch-search-git-citar
                           my-notmuch-search-git-corfu
                           my-notmuch-search-git-gptel
                           my-notmuch-search-git-agent-shell
                           my-notmuch-search-git-magit
                           my-notmuch-search-git-marginalia
                           my-notmuch-search-git-mentor
                           my-notmuch-search-git-sensor-watch
                           my-notmuch-search-git-textbooks
                           my-notmuch-search-git-vertico
                           my-notmuch-search-git-other
                           my-notmuch-search-list-egghead-games
                           my-notmuch-search-list-emacs-bug
                           my-notmuch-search-list-emacs-devel
                           my-notmuch-search-list-emacs-help
                           my-notmuch-search-list-emacs-org
                           my-notmuch-search-list-emacs-tangents
                           my-notmuch-search-list-emms
                           my-notmuch-search-list-fennel
                           my-notmuch-search-list-geiser
                           my-notmuch-search-list-lua
                           my-notmuch-search-list-modus-themes
                           my-notmuch-search-list-notmuch
                           my-notmuch-search-list-s7
                           my-notmuch-search-maildir-all-drafts
                           my-notmuch-search-maildir-all-sent
                           my-notmuch-search-maildir-spam
                           my-notmuch-search-misc-youtube))
             (named-sexps
              (mapcar (lambda (search)
                        (if-let* ((doc-regexp "Notmuch saved search '\\\(.*\\\)'.")
                                  (doc (documentation search t))
                                  (name (and (string-match doc-regexp doc)
                                             (match-string 1 doc))))
                            (cons name (funcall search))
                          (user-error
                           "Function `%s' documentation not matching: %s"
                           search doc-regexp)))
                      functions))
             (inbox
              (list :name "! Inbox"
                    :query (my-notmuch-query
                            `(and (tag unread)
                                  (not (or ,@(mapcar #'cdr named-sexps)))))))
             (searches
              (mapcar (lambda (sexp)
                        (list :name (car sexp)
                              :query (my-notmuch-query (cdr sexp))))
                      named-sexps)))
        (setopt notmuch-saved-searches
                (cons inbox searches))))
    

My sorting

(with-eval-after-load 'notmuch
  (setopt notmuch-saved-search-sort-function
          #'notmuch-sort-saved-searches))

My motions

Do not require holding down C- to move point in notmuch-hello.

(with-eval-after-load 'notmuch
  (keymap-set notmuch-hello-mode-map "n" #'next-line)
  (keymap-set notmuch-hello-mode-map "p" #'previous-line)
  (keymap-set notmuch-hello-mode-map "f" #'forward-char)
  (keymap-set notmuch-hello-mode-map "b" #'backward-char))

My initial cursor position

Position the cursor on the first saved search.

(with-eval-after-load 'notmuch
  (defun notmuch-hello-move-point-to-first-search+ ()
    (interactive)
    (let ((first-search-name (plist-get (car notmuch-saved-searches) :name))
          (first-widget (progn (widget-forward 1) (widget-at))))
      (while (not (equal (widget-text (widget-at)) first-search-name))
        (widget-forward 1)
        (when (equal (widget-at) first-widget)
          (user-error "Button not found: %s" first-search-name))))))
(with-eval-after-load 'notmuch
  (add-hook 'notmuch-hello-refresh-hook
            (lambda ()
              (when (bobp)
                (notmuch-hello-move-point-to-first-search+)))))

My indentation

Do not indent buttons to save screen estate.

(with-eval-after-load 'notmuch
  (setopt notmuch-hello-indent 0))

My hidden logo

Hide the logo to save screen estate 63.

(with-eval-after-load 'notmuch
  (setopt notmuch-show-logo nil))

My hidden line numbers

Hide line numbers (to work around a bug).

(with-eval-after-load 'notmuch-hello
  (add-hook 'notmuch-hello-mode-hook
            (lambda ()
              (display-line-numbers-mode -1))))

My hidden recent searches

Do not include the list of recent searches in notmuch-hello.

(with-eval-after-load 'notmuch
  (delete 'notmuch-hello-insert-recent-searches notmuch-hello-sections))

Notmuch Search

A major mode in Notmuch that

shows a list of threads

that match a search query.

A Notmuch buffer that shows a list of threads matching a search query. Conceptually,

Hierarchy

Conceptually,

Key bindings

Key Description Note
n next message  
p previous message  
k tag64 as read custom, thread or region
K tag64 as spam custom, thread or region
C-/ undo tagging  
TAB show unread messages custom, à la Gnus
m compose new message Notmuch-wide
+ add tag  
- remove tag  
* tag all  
s search all messages Notmuch-wide
g refresh Notmuch-wide

My autoload

(autoload #'notmuch-search "notmuch-search" nil t)

My sorting

Show the newest messages first.

(with-eval-after-load 'notmuch
  (setopt notmuch-search-oldest-first nil))

My fontification

Fontify the messages tagged as deleted or spam

Strike through all deleted and spam messages.

(with-eval-after-load 'notmuch
  (setopt notmuch-search-line-faces
          '(("unread" . notmuch-search-unread-face)
            ("deleted" . notmuch-tag-deleted)
            ("spam" . notmuch-tag-deleted))))

My columns

For each thread, show only

  • the number of unread/read messages
  • a star if unread messages exist
  • the subject.
(with-eval-after-load 'notmuch
  (setopt notmuch-search-result-format
          (list (cons "count" "%9s ")
                (cons (lambda (_ thread)
                        (concat
                         (propertize
                          (if (member "unread" (plist-get thread :tags))
                              "*" " ")
                          'face
                          'notmuch-tag-face)
                         " "))
                      "")
                (cons "subject" "%s"))))

My “show all” functionality

Show both read and unread messages with TAB.

(with-eval-after-load 'notmuch
  (defun my-notmuch-search-show-read ()
    "Suppress `unread' tag filtering in `notmuch-search'."
    (interactive)
    (when (boundp 'my-notmuch-search-showing-unread)
      (user-error "Already showing read messages"))
    (let ((buffer-name (concat (buffer-name) "/ALL")))
      (if-let* ((buffer (get-buffer buffer-name)))
          (switch-to-buffer buffer)
        (progn
          (notmuch-search-edit-search
           (string-replace (format "%s" '(tag unread))
                           (format "%s" '(or (tag read) (not (tag read))))
                           (notmuch-search-get-query)))
          (rename-buffer buffer-name)
          (setq-local my-notmuch-search-showing-unread t))))))
(with-eval-after-load 'notmuch
  (keymap-set notmuch-search-mode-map
              "TAB"
              #'my-notmuch-search-show-read))

My integration with Org

Link threads from Org.

  • Store
    (with-eval-after-load 'org
      (defun my-org-notmuch-search-store-link ()
        "Store a link to a Notmuch thread."
        (interactive)
        (require 'notmuch)
        (when (eq major-mode 'notmuch-search-mode)
          (org-store-link-props
           :type "notmuch"
           :link (concat "notmuch-search:" (notmuch-search-find-thread-id t))
           :description (notmuch-search-find-subject)))))
    
  • Follow
    (with-eval-after-load 'org
      (defun my-org-notmuch-search-follow-link (path)
        (interactive)
        (require 'notmuch)
        (notmuch-search (concat "thread:" path))))
    
  • Register
    (with-eval-after-load 'org
      (org-link-set-parameters
       "notmuch-search"
       :store #'my-org-notmuch-search-store-link
       :follow #'my-org-notmuch-search-follow-link))
    

My tagging

Replace the universal tagging interface with simple unread and spam tagging.

To toggle back, use Notmuch Show or notmuch-undo (C-/).

(with-eval-after-load 'notmuch

  (defun my-notmuch-search-tag-read (&optional extra-tag)
    "Mark thread or region as read."
    (interactive)
    (let ((region (notmuch-interactive-region)))
      ;; Remove the unread tag and add the extra tag if specified.
      (notmuch-search-tag (cons "-unread"
                                (if extra-tag
                                    (cons (concat "+" extra-tag) nil))))
      ;; If not operating on a region, advance to the next thread.
      (when (equal (nth 0 region)
                   (nth 1 region))
        (notmuch-search-next-thread))))

  (defun my-notmuch-search-tag-spam ()
    "Mark thread as read and spam."
    (interactive)
    (my-notmuch-search-tag-read "spam"))

  (keymap-set notmuch-search-mode-map "k" #'my-notmuch-search-tag-read)
  (keymap-set notmuch-search-mode-map "K" #'my-notmuch-search-tag-spam))

My agenda

  • TODO Highlight “joined” mailing list threads

    These seem to get the replied tag.

Notmuch Show

A major mode in Notmuch that

shows a single email thread.

Parent

Conceptually, Notmuch Show is a

child of the Notmuch Search.

Key bindings

Key Description Note
SPC next page or message custom
n next message custom, originally N
p previous message custom, originally P
k toggle read tag custom
K toggle spam tag custom
C-/ undo tagging  
r reply  
R reply to all crucial for mailing lists
w save attachments  
V view source  

My autoload

(autoload #'notmuch-show "notmuch-show" nil t)

My disabled auto-mark as read

Do not mark messages as read automatically.

(with-eval-after-load 'notmuch
  (setopt notmuch-show-mark-read-tags nil))

My indentation

Do not indent the messages to save screen space.

(with-eval-after-load 'notmuch
  ;; NOTE Not `custom'-settable.
  (setq-default notmuch-show-indent-content nil))

My motions

Make the motion keys n and p take into account all messages, not just the open ones 65

(with-eval-after-load 'notmuch
  (let ((map notmuch-show-mode-map))
    (keymap-set map "n" #'notmuch-show-next-message)
    (keymap-set map "p" #'notmuch-show-previous-message)))

My message advancement

Make SPC to advance to the next page or message, never crossing threads.

(with-eval-after-load 'notmuch
  (keymap-set notmuch-show-mode-map
              "SPC"
              #'notmuch-show-advance))

Make S-SPC return back to the previous message.

(with-eval-after-load 'notmuch
  (keymap-set notmuch-show-mode-map
              "S-SPC"
              #'notmuch-show-previous-message))

My link handling

Open links at point with RET.

(with-eval-after-load 'notmuch
  (defun my-notmuch-show-browse-url-or-toggle-message ()
    (interactive)
    (if (thing-at-point 'url)
        (goto-address-at-point)
      (notmuch-show-toggle-message))))
(with-eval-after-load 'notmuch
  (keymap-set notmuch-show-mode-map
              "RET"
              #'my-notmuch-show-browse-url-or-toggle-message))

My integration with Org

Link messages from Org.

  • Store
    (with-eval-after-load 'org
      (defun my-org-notmuch-show-store-link ()
        "Store a link to a Notmuch message."
        (interactive)
        (require 'notmuch)
        (when (eq major-mode 'notmuch-show-mode)
          (org-store-link-props
           :type "notmuch"
           :link (concat "notmuch-show:" (notmuch-show-get-message-id t))
           :description (notmuch-show-get-subject)))))
    
  • Follow
    (with-eval-after-load 'org
      (defun my-org-notmuch-show-follow-link (path)
        (require 'notmuch)
        (notmuch-show (concat "id:" path))))
    
  • Register
    (with-eval-after-load 'org
      (org-link-set-parameters
       "notmuch-show"
       :store #'my-org-notmuch-show-store-link
       :follow #'my-org-notmuch-show-follow-link))
    

My disabled key bindings

Leave the C-<tab> for the Tab Bar mode.

(with-eval-after-load 'notmuch
  (keymap-set notmuch-show-mode-map "C-<tab>" nil))

My tagging

Replace the tagging interface with a simple unread and spam toggle.

(with-eval-after-load 'notmuch

  (defun my-notmuch-show-toggle-unread ()
    "Toggle unread tag and advance to next message."
    (interactive)
    (notmuch-show-tag
     (list (if (member "unread" (notmuch-show-get-tags))
               "-unread" "+unread")))
    (notmuch-show-next-message))

  (defun my-notmuch-show-toggle-spam ()
    "Toggle spam tag and advance to next message."
    (interactive)
    (notmuch-show-tag
     (if (member "spam" (notmuch-show-get-tags))
         '("-spam" "+unread") '("+spam" "-unread")))
    (notmuch-show-next-message))

  (keymap-set notmuch-show-mode-map "k" #'my-notmuch-show-toggle-unread)
  (keymap-set notmuch-show-mode-map "K" #'my-notmuch-show-toggle-spam))

Notmuch Message

A major mode in Notmuch for

email composition.

Key bindings

Key Action Command
C-c C-c send message-send-and-exit
C-c C-k cancel message-kill-buffer
C-c C-d save draft  
C-c C-a attach file mml-attach-file
C-c C-f s change subject message-change-subject
m compose new message Notmuch-wide
s search all messages Notmuch-wide
g refresh Notmuch-wide

My signature

Append a signature to outgoing mail messages.

(with-eval-after-load 'message
  (add-hook 'message-signature-setup-hook
            #'fortune-to-signature))

My auto-kill

Kill the buffer after successfully sending the contained message.

(with-eval-after-load 'message
  (setopt message-kill-buffer-on-exit t))

My FCC header

Incorporate sent messages into the database 66 for immediate availability.

(with-eval-after-load 'notmuch
  (setopt notmuch-fcc-dirs "Sent -unread"))

My From: header

Include the From header to make the message easier to quote elsewhere.

(with-eval-after-load 'notmuch
  (setopt notmuch-message-headers
          '("From" "Subject" "To" "Cc" "Date")))

My Fwd: prefix

Use the standard Fwd: subject prefix instead of the GNUS-default [NAME].

(with-eval-after-load 'message
  (setopt message-make-forward-subject-function
          '(message-forward-subject-fwd)))

My SMTP configuration (sendmail.el)

(with-eval-after-load 'sendmail
  (setopt send-mail-function #'smtpmail-send-it))
(with-eval-after-load 'smtpmail
  (let ((server "smtp.fastmail.com"))
    (setopt smtpmail-smtp-server server
            smtpmail-servers-requiring-authorization (regexp-quote server)
            smtpmail-smtp-service 465
            smtpmail-stream-type 'ssl)))

Put the SMTP credentials to the ~/.authinfo.gpg file:

echo \
    machine smtp.fastmail.com \
    login rudolf@adamkovic.org \
    password "$(pass fastmail+mail)" \
    | gpg \
          --batch \
          --encrypt \
          --recipient rudolf@adamkovic.org \
          --trust-model always \
          --output ~/.authinfo.gpg

My mailcap

Per Unix standards, we use mailcap files to associate MIME types with their viewers.

text/html; open %s a

My subject check

Before sending a message, signal an error

if the subject line is empty 67.

(with-eval-after-load 'notmuch
  (add-hook 'notmuch-mua-send-hook
            #'notmuch-mua-subject-check))

My attachment check

Before sending a message, signal an error

if the message mentions attachments but none exist 68.

(with-eval-after-load 'notmuch
  (add-hook 'notmuch-mua-send-hook
            #'notmuch-mua-attachment-check))

Simple HTML renderer (SHR)

A rudimentary HTML rendered built into Emacs. Used, for example, by the Emacs Web Wowser or Notmuch.

Proportional fonts and custom colors make these hard to read. For instance, the mails from Humble Bundle are hard to read. Thus, we disable custom fonts and colors.

(setopt shr-use-fonts nil
        shr-use-colors nil)

Limit the width to make the rendered content readable.

(The value should match the fill-column-indicator.)

(setopt shr-width 72)

Insidious Big Brother Database (BBDB)

A simple contact database for Emacs.

Consists of per-contact records with fields.

M-x bbdb

Key Description  
c Create record  
n Go to next record  
p Go to previous record  
i Insert field  
TAB Go to next field  
S-TAB Go to previous field custom, originally DEL
e Edit field  
b Search  
C-k Delete record or field  
m Send mail  

Configure!

Make S-TAB go to the previous field, like DEL.

(with-eval-after-load 'bbdb
  (keymap-set bbdb-mode-map "<backtab>" #'bbdb-prev-field))

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'bbdb))

Store contacts outside of the emacs.d directory.

(with-eval-after-load 'bbdb
  (setopt bbdb-file
          (file-name-concat org-directory
                            "contacts.bbdb")))

Show anniversaries, such as birthdays, in the Emacs Diary and thus the Emacs Calendar and thus the Org Agenda.

;; TODO with-eval-after-load 'bbdb?
(with-eval-after-load 'diary
  (bbdb-initialize 'anniv))

Complete mail addresses in the Message mode.

(add-hook 'message-mode
          (lambda ()
            (bbdb-initialize 'message)))

Enable case-insensitive search.

(with-eval-after-load 'bbdb
  (setopt bbdb-case-fold-search t))

Disable the North American Numbering Plan (NANP) for phone numbers.

(with-eval-after-load 'bbdb
  (setopt bbdb-phone-style nil))

Highlight the current line

(with-eval-after-load 'bbdb
  (add-hook 'bbdb-mode-hook #'hl-line-mode))

Avy

An Emacs package for

moving the point quickly

using tree-style navigation.

Author

Avy is written by

Oleh Krehel (abo-abo),

the creator of Hydra, Ivy, and other well-known Emacs packages.

Install the package

Install the Avy package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'avy))

Custom key binding

Bind one of the Avy entry-point functions to C-c g for “go to character”.

(keymap-global-set "C-c g" #'avy-goto-char)

Custom targets

Make Avy jump not only between windows but also between frames.

(with-eval-after-load 'avy
  (setopt avy-all-windows 'all-frames))

Custom dimming

Dim the foreground, but not the background, when Avy is active.

;; (with-eval-after-load 'avy
;;   (setopt avy-background t)
;;   (set-face-background 'avy-background-face nil))

Magit

A Git interface, or “porcelain”, for Emacs, created by [35]. As of 2023, maintained by [36].

Key bindings

  • Entry point
    C-x g File dispatch follow up with g for status
    C-x p v Project status  
  • File dispatch

    Let us see first if it works well…

  • Navigation and selection
    Key Description Mnemonic / Note
    n go to next item next
    p go to previous item previous
    ^ go to parent up
    j n jump to untracked new
    j u jump to unstaged unstaged
    j s jump to staged staged
    j f u jump to fetched unmerged fetch unmerged
    j p u jump to push unmerged push unmerged
    j z jump to stashes  
    M-n go to next sibling next
    M-p go to previous sibling previous
    C-SPC begin/end selection  

    Imenu works too.

  • ???
    k kill selection or thing at point commit, stash, etc.
  • Staging
    Key Description / Command Mnemonic / Note
    s stage selected stage
      magit-stage  
    S stage all changed “big” stage
      magit-stage-modified with C-u, include untracked
    u unstage selected unstage
      magit-unstage-file  
    U unstage all “big” unstage
      magit-unstage-all  
  • Commits

    In the status buffer:

    Key Description / Command Mnemonic / Note
    c c commit staged changes commit
      magit-commit-create  
    c w reword last commit message word
      magit-commit-reword rewrites history
    c e add staged changes to last commit extend
      magit-commit-extend rewrites history
    c F add staged changes to existing commit fix
      magit-commit-instant-fixup rewrites history

    In the commit buffer:

    Key Description Mnemonic
    C-c C-c confirm commit confirm
    C-c C-k cancel commit kill
    M-p show previous message previous
    M-n show next message next
  • Logs
    Key Context Description Mnemonic
    l l status log log
    l o status log for revision, branch, tag log, other
    l a status log for all log, all
    l r status reflog log, reflog
    d d log show diff  

    Currently, no way exists to copy messages from the log en masse. For a workaround, use the vc mode: (1) type M-x vc-dir and then (2) type l for the log. (bug)

  • Checkouts and branches
    Key Context Description Mnemonic
    b b status, log checkout revision, branch, or tag  
    b c status, log create and checkout new branch branch, create
    b k status, log delete branch branch, kill
  • Reverting
    Key Description Note
    V V revert commit to revert current in status, prefix with C-SPC
  • Rebasing
    r w reword commit message reword
    r e main RET rebase branch onto main  
  • Stashing
    Key Description Mnemonic
    z z stash everything  
    z i stash staged index
    z x stash all but staged excluding index
    z p apply and drop stash pop
    z a apply stash apply
    z k drop stash kill

    Alternatively, to kill a stash, move the point to it and press k.

  • Remotes
    Key Description Mnemonic
    M a add remote add
    M k remove remote kill
    M r rename remote rename
    f a fetch from all remotes fetch
    F p pull (fetch & merge) rebase local commits with -r
    P p push force-push with -f
  • Patches
    Key Description Note
    W c c create patch at point or region
    w w apply patch (Git) adds commits
    w a apply patch (Unix) changes files
  • Resets
    X s Move HEAD, keep changes soft reset
    X h Move HEAD, discard changes hard reset

    Some use cases:

    • undo commits: (1) soft-reset to parent
    • merge recent commits: (1) soft-reset to parent, (2) commit together
    • move recent commits to another branch: (1) create branch, (2) hard-reset
    • delete recent commits: (1) hard-reset
  • Diffs

    In a diff:

    Key Description Note
    j jump to/between files/changes M-g i for Imenu
    RET visit file before/after change in other window w/ C-u
    [C-u] C-RET visit file in working tree in other window w/ C-u
    + show more context  
    - show less context  
    v reverse change file, hunk, region, etc.
    C add changelog entry for change  

Tips

  • How to diff merge commits

    Diffs of merge commits are actively misleading unless diffed explicitly against the relevant parent 69:

    git diff SHA^1 SHA # against the merge-into target
    git diff SHA^2 SHA # against the merge source
    

    In Magit, the former is dr RET (Diff, Range, accept default).

  • How to compare the working tree with some commit
    1. Open magit-status with C-x p m.
    2. If the commit is not in sight, type ll to see the log.
    3. Move the point over commit.
    4. Type d for magit-diff and r for range.
    5. Retrieve the commit hash from future history by typing M-n twice.
    6. Type RET to confirm.
  • How to reverse a committed file, hunk, or region
    1. Select the file, hunk, or region.
    2. Type v for magit-reverse.

    Note. magit-reverse bound to v is not to be confused with magit-revert bound to V.

  • How to pull with a dirty work tree

    If the remote changes do not overlap with the local ones, there is no need to stash, pull, and un-stash. Instead, in magit-status:

    1. Fetch (f p) the remote changes.
    2. Commit the local changes as normal (c c).
    3. Pull (F p) to merge the fetched changes.
    4. Push (P p).
  • How to compare current commit to some other
    1. Open magit-status with C-x p m.
    2. Soft-reset to the other commit with X s.
    3. See the changes.

    In any file with changes, see its diff with C-x V d (magit-file-dispatch).

    To return back to the original commit, open magit status with C-x p m and pull with F p.

  • How to create a branch with unmerged commits

    Committed to master, or some other branch, by mistake?

    1. Open magit-status.
    2. Type b S to branch out as a spin-out.
  • How to cherry-pick commits from other branches
    1. Open magit-status.
    2. Switch to the destination branch, if needed.
    3. Type l o to see a log of some other branch (or l a to see all)
    4. Select the source branch.
    5. Select one or more commits.
    6. Type A A to apply the cherry picks.
  • How to find the history of specific region
    1. Mark the region.
    2. Execute magit-file-dispatch bound to C-x V l
    3. Type l to see the log.
  • How to find the history of specific files

    For a single file, execute magit-file-dispatch bound to C-c M-g per

    1. Open magit-status.
    2. Type l to see the log operations.
    3. Type -- to limit the log to certain files.
    4. Select one or more files.
    5. Type l again to see the log.
  • How to modify or split existing commits
    1. Open magit-status.
    2. Move the point to the commit.
    3. Type r to rebase and m to modify the commit.
    4. Type x to reset.
    5. Type head~ or head~1 to reset to 1 commit older than head.
    6. Make commits, accessing the previous commit messages with M-p.
    7. Type r to rebase and r to continue (finish) it.
  • How to resolve merge conflicts

    To resolve a conflict directly in Magit, type k on the conflicting file and select which side to keep:

    Key Description
    k o keep ours
    k t keep theirs

    This works on hunks too.

    Alternatively, type RET to open a file with the Smerge mode enabled.

    Actually, Smerge key bindings work on hunks.

  • How to diff endpoints of a commit range
    1. Mark the commit range.
    2. Type dd.
  • How to generate changelog for existing commit

    With a clean magit-status 70:

    1. Move the point to the commit.
    2. Revert changes with r.
    3. Initiate the commit with c c.
    4. Execute M-x magit-generate-changelog RET. Now C-c C-i followed by l g (git-commit-insert-changelog-gnu)
    5. Copy the generate changelog.
    6. Cancel the commit with C-c C-k.
    7. Reset back to the clean magit-status.

    I now recommend the new alternatives, , which use Magit’s own implementation and are accessible from the same transient menu used to insert commit trailers (`git-commit-insert-trailer` on `C-c C-i` (hm, maybe I should rename that now)).

Configure!

  • Control git version control system (VCS) with magit

    Install the package.

    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'magit))
    

    Experiment.

    https://patch-diff.githubusercontent.com/raw/magit/magit/pull/4860.patch

    (setopt magit-git-executable "/usr/bin/git")
    

    In logs, show the absolute date instead of the age of commits.

    (with-eval-after-load 'magit
      (let ((margin '(t "%Y-%m-%d %H:%M " magit-log-margin-width t 18)))
        (setopt magit-log-margin margin
                magit-reflog-margin margin)))
    

    Show the fill column indicator when writing commit messages.

    (with-eval-after-load 'git-commit
      (add-hook 'git-commit-setup-hook
                #'display-fill-column-indicator-mode))
    

    Split the window for git diffs but nothing else.

    (with-eval-after-load 'magit
      (setopt magit-display-buffer-function
              #'magit-display-buffer-same-window-except-diff-v1))
    

    Show character-level differences in git diffs.

    (with-eval-after-load 'magit
      (setopt magit-diff-refine-hunk 'all))
    
  • Auto-reveal invisible content

    Auto-reveal content around point if hidden.

    (with-eval-after-load 'magit
      (add-hook 'magit-diff-visit-file-hook
                (defun my-magit-reveal-point-if-invisible ()
                  "Reveal the point if in an invisible region."
                  (if (derived-mode-p 'org-mode)
                      (org-reveal '(4))
                    (require 'reveal)
                    (reveal-post-command)))))
    

    Doom Emacs issue 4895

  • Show git status in the fringe with diff-hl

    Install the diff-hl package.

    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'diff-hl))
    

    Show uncommitted git changes in the fringe.

    (add-hook 'after-init-hook #'global-diff-hl-mode)
    

    Show uncommitted changes in Dired.

    ;; (with-eval-after-load 'dired
    ;;   (add-hook 'dired-mode-hook #'diff-hl-dired-mode))
    

    Refresh diff-hl in lockstep with magit.

    (with-eval-after-load 'diff-hl
      (with-eval-after-load 'magit
        (add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh)
        (add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)))
    
    (setopt diff-hl-draw-borders nil)
    

    Reserve C-TAB key for the Tab Bar mode.

    (with-eval-after-load 'magit-status
      (keymap-set magit-log-mode-map "C-<tab>" nil)
      (keymap-set magit-status-mode-map "C-<tab>" nil)
      (keymap-set magit-diff-mode-map "C-<tab>" nil)
      (keymap-set magit-revision-mode-map "C-<tab>" nil))
    
  • Context-aware status

    On magit-status (C-x g), go to the part that is

    relevant to the current file-visiting buffer 71.

    (with-eval-after-load 'magit
      (setopt magit-status-goto-file-position t))
    
  • Quiet auto-saving

    Automatically save files without asking.

    (with-eval-after-load 'magit
      (setopt magit-save-repository-buffers 'dontask))
    

Contribute!

  • TODO Improve magit-bind-magit-project-status
    old
    nil or t
    new
    nil or t or character to use
  • WAIT issue Report highlighting/selection problems
  • WAIT issue Fix isearch in magit-status

Password Store

An Emacs frontend for pass by [37] written by [38].

Key Description Mnemonic
C-c p p Copy password password
C-c p t Copy one-time password (OTP) password
C-C p u Copy username username
C-c p U Copy URL URL
C-c p f Copy field field
C-c p b Browse URL browse
C-c p d Show pass directory directory

Install backend

brew "gnupg"
brew "pinentry-mac"
brew "pass"
brew "pass-otp"

Configure!

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'password-store)
  (add-to-list 'package-selected-packages 'password-store-otp))

Define “unsafe” copy functions.

Unlike the password-store-copy function that copies the passwords, these functions do not clear the copied information from the kill ring.

(defun my-password-store-copy (field)
  (interactive)
  (require 'password-store)
  (let ((entry (completing-read (concat "Get '" field '" field from: ")
                                (password-store-list)
                                nil
                                'require-match)))
    (password-store-copy-field entry field)))
(defun my-password-store-copy-url ()
  "Add the 'url' field for selected entry into the kill ring."
  (interactive)
  (my-password-store-copy "url"))
(defun my-password-store-copy-username ()
  "Add the 'username' field for selected entry into the kill ring."
  (interactive)
  (my-password-store-copy "username"))

Define the keys.

(keymap-global-set "C-c p p" #'password-store-copy)
(keymap-global-set "C-c p t" #'password-store-otp-token-copy)
(keymap-global-set "C-c p u" #'my-password-store-copy-username)
(keymap-global-set "C-c p U" #'my-password-store-copy-url)
(keymap-global-set "C-c p f" #'password-store-copy-field)
(keymap-global-set "C-c p e" #'password-store-edit)
(keymap-global-set "C-c p b" #'password-store-url)

GPTel

Keyboard shortcuts

gptel-send (C-c RET)
submit the prompt at point

With C-u, to customize how the prompt is sent, importantly:

  • -b add buffer to context
  • -f add file to context

Install the package

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'gptel))

Define the models to use

(with-eval-after-load 'gptel
  (let ((models '(llama3.1:8b
                  phi4:14b
                  deepseek-r1:14b
                  gemma3:12b-it-qat)))
    (setopt gptel-model (car models)
            gptel-backend (gptel-make-ollama "Ollama"
                            :host "localhost:11434"
                            :stream t
                            :models models)
            gptel-default-mode 'org-mode)))

Unfortunately, DeepSeek R1 does not support tools.

Models like MFDoom/deepseek-r1-tool-calling:14b do not work well.

Simplify the default directive

The default directive, containing

“large language model living in Emacs”,

confuses some reasoning models, such as the DeepSeek R1.

(with-eval-after-load 'gptel
  (add-to-list 'gptel-directives '(default . "Respond concisely.")))

Define emacs_lisp_evaluate_expression tool

(defun my-eval-string (string)
  "Evaluate STRING like `eval' does."
  (pcase-let ((`(,expression . ,length) (read-from-string string)))
    (unless (= (length string) length)
      (user-error "Invalid expression (did you forget `progn'?): %s" string))
    (eval expression)))
(with-eval-after-load 'gptel
  (add-to-list
   'gptel-tools
   (gptel-make-tool
    :category "Emacs"
    :name "emacs_lisp_evaluate_expression"
    :function (lambda (expression)
                ;; HACK Llama does not know what a sexp is.
                (let ((expression-fixed (format "(progn %s)" expression)))
                  (let ((inhibit-message t))
                    (message "LLM executing: %s" expression))
                  (my-eval-string expression)))
    :description "Evaluate Emacs Lisp symbolic expresssion"
    :args '(( :name "sexp"
              :type "string"
              :description "The expression to be evaluated.")))))

Disable tools by default

DeepSeek and Phi do not support tools which makes GPTel error out.

(with-eval-after-load 'gptel
  (setopt gptel-use-tools nil))

TODO Ask to highlight <think>...</think>

TODO Make gptel-rewrite work with CoT models like DeepSeek

Mentor

A torrent client for Emacs.

Author

Created by

Stefan Kangas (skangas),

one of the core Emacs maintainers.

Installation

  • Backend: CLI rtorrent client

    Tangled to the Homebrew bundle Brewfile:

    brew "rtorrent"
    
  • Frontend
    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'mentor))
    

Key bindings

Key Description Mnemonic
l Add magnet link link
g Refresh  
RET View files in Mentor  
v View files in Dired view
K Delete torrent and its files kill
+ Increase torrent/file priority  
- Decrease torrent/file priority  
q Quit as in “bury” quit
Q Quit as in “stop” quit

Configuration

  • Set destination directory

    Save all data to tmp/torrents.

    (with-eval-after-load 'mentor
      (let ((directory (file-name-concat org-directory "tmp" "torrents")))
        (unless (file-exists-p directory)
          (make-directory directory t))
        (setopt mentor-rtorrent-download-directory
                directory)))
    
  • Enable state restoration

    Remember state after relaunch 72

    (with-eval-after-load 'mentor
      (setopt mentor-rtorrent-keep-session t))
    
  • Highlight current line

    Highlight the current line.

    (with-eval-after-load 'mentor
      (add-hook 'mentor-mode-hook #'hl-line-mode)
      (add-hook 'mentor-files-mode-hook #'hl-line-mode))
    
  • Disable trash
    (with-eval-after-load 'mentor
      (add-hook 'mentor-mode-hook
                (lambda ()
                  (setq-local delete-by-moving-to-trash nil))))
    

Patches

  • WAIT Add support for deleting by moving to trash (25, 27)
  • TODO Capitalize menus in Title Case

    This is to match the default Emacs style.

Bug reports

  • WAIT Fix the type of mentor-view-columns (bug)

Agenda

  • TODO Try transmission.el (repo)

Edit Indirect

A 3rd-party Emacs package for

editing regions in separate buffers

à la org-edit-special (C-c ') in Org 73.

Install

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'edit-indirect))

Customize key bindings

Unbind C-c C-c, keeping only C-c ' à la org-edit-src-code in Org.

(with-eval-after-load 'edit-indirect
  (keymap-unset edit-indirect-mode-map "C-c C-c"))

Customize header line

Show help à la org-edit-src-code in Org.

(defun my-edit-indirect-prepare ()
  (setq-local header-line-format
              (substitute-command-keys
               "Edit, then exit with `\\[edit-indirect-commit]' or abort with \
`\\[edit-indirect-abort]'")))
(with-eval-after-load 'edit-indirect
  (add-hook 'edit-indirect-after-creation-hook
            #'my-edit-indirect-prepare))

Edit string at point

Useful for editing e.g. SQLite (with -*- mode: sql -*-) embedded in Lisp 74.

(defun my-edit-string-at-point ()
  "Edit string at point in an indirect buffer."
  (interactive)
  (let* ((point (point))
         (outer-bounds (save-excursion
                         (unless (looking-at "\"")
                           (search-backward-regexp "\""))
                         (bounds-of-thing-at-point 'sexp)))
         (inner-bounds (cons (1+ (car outer-bounds))
                             (1- (cdr outer-bounds)))))
    (with-current-buffer
        (edit-indirect-region (car inner-bounds)
                              (cdr inner-bounds)
                              'display-buffer)
      (goto-char (- point (car outer-bounds))))))
(keymap-set prog-mode-map "C-c '" #'my-edit-string-at-point)

Telega

A hackable Telegram instant messaging (IM) client for Emacs written by [39]. Based on the official, first-party, open-source Telegram Database Library [40].

Key Description Note
RET Send message as Org markup custom
C-u RET Send message as plain text custom
! react to message requires latest TDLib
e edit message quit with C-c C-k
r reply to message quit with C-c C-k
d delete message  
M-g > go to last message also M-g r
M-g ! go to last unread reaction requires latest TDLib
C-c C-f attach media as file with C-u
S save attached media telega-msg-save
C-c C-s filter (search for) messages quit with C-c C-c

Configure!

Install the package.

We use the package from the stable MELPA to avoid manual re-compilation of the underlying Telegram Database Library (TDLib) as it evolves.

(with-eval-after-load 'package
  ;; (add-to-list 'package-pinned-packages '(telega . "melpa-stable"))
  (add-to-list 'package-selected-packages 'telega))

Use the Telegram Database Library installed via Homebrew.

(with-eval-after-load 'telega
  (setopt telega-server-libs-prefix "/opt/homebrew/"))

Configure the Telega mode line and global keybinding.

(with-eval-after-load 'telega
  (setopt telega-filters-custom nil)
  (setopt telega-mode-line-string-format
          '(" ℡" (:eval (telega-mode-line-unread-unmuted)) " "))
  (add-hook 'telega-ready-hook #'telega-mode-line-mode))
(keymap-global-set "C-c i" #'telega) ; "i" for instant messaging

Do not confuse by using the Unicode triangles character as “Telegram logos”.

(with-eval-after-load 'telega
  (setopt telega-symbol-telegram ""
          telega-symbol-mode ""))

Send Org markup on RET and plain text on C-u RET.

(By default, RET sends plain text and C-u RET sends Markdown.)

(with-eval-after-load 'telega
  (setopt telega-chat-input-markups
          '("org" nil nil)))

Disable the avatars, as they are messed up because Emojis on macOS are taller than normal text.

(with-eval-after-load 'telega
  (setopt telega-chat-show-avatars nil))

Enable spell checking.

(with-eval-after-load 'telega
  (add-hook 'telega-chat-mode-hook #'jinx-mode))
  • Use ISO date format
    (with-eval-after-load 'telega
      (dolist (key (mapcar #'car telega-date-format-alist))
        (add-to-list 'telega-date-format-alist
                     (cons key
                           (if (eq key 'today)
                               "%H:%M"
                             "%Y-%m-%d %a %H:%M")))))
    
  • Fix broken UI decorations

    Use emojis instead of SVGs for UI decorations 75.

    (with-eval-after-load 'telega
      (setopt telega-symbols-emojify nil))
    
  • Customize current line highlighting

    Highlight the current line.

    (with-eval-after-load 'telega
      (add-hook 'telega-root-mode-hook #'hl-line-mode))
    

Contribute!

PDF Tools

Key Description Mnemonic Note
o show outline outline table of contents
f find and follow a link follow  
l go back left also B
r go forward right  
q bury buffer quit with C-u, kill
C-c C-r t toggle theming theme  
+ zoom in    
- zoom out    
0 zoom reset    
n next page   good for slides
p previous page   good for slides
P fit to page    
W fit to page width    

How to search a directory with PDF files for a regular expression?

  1. Open dired.
  2. Mark some PDF files.
  3. Execute pdf-occur-dired-do-search.
  4. Enter a search query.
  5. Type g to search again with a new query.

Configure!

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'pdf-tools))
(add-hook 'after-init-hook
          (lambda ()
            (when (package-installed-p 'pdf-tools)
              (pdf-loader-install 'no-query))))

Make the g key typed in a pdf-occur buffer search again with a new query. The pdf-tools package provides this functionality by default, albeit under the less practical C-u g key binding.

(with-eval-after-load 'pdf-occur
  (defun pdf-occur-search-again+ ()
    (interactive)
    (pdf-occur-assert-occur-buffer-p)
    (setq-local pdf-occur-search-string (pdf-occur-read-string))
    (pdf-occur-revert-buffer))
  (keymap-set pdf-occur-buffer-mode-map "g" #'pdf-occur-search-again+))

Make M-RET in the PDF Outline not just follow the link but also hide the outline, and do the same for the mouse-1 click. Retain the old behavior under M-RET, effectively swapping RET and M-RET.

(with-eval-after-load 'pdf-outline
  (keymap-set pdf-outline-buffer-mode-map
              "RET"
              #'pdf-outline-follow-link-and-quit)
  (keymap-set pdf-outline-buffer-mode-map
              "<mouse-1>"
              #'pdf-outline-follow-link-and-quit)
  (keymap-set pdf-outline-buffer-mode-map
              "M-RET"
              #'pdf-outline-follow-link))

Map C-s to incremental search forward (isearch-forward).

(with-eval-after-load 'pdf-view
  (keymap-set pdf-view-mode-map "C-s" #'isearch-forward))

Fake the missing auto-load for dired search.

(autoload #'pdf-occur-dired-do-search "pdf-occur" nil '(dired-mode))

Search PDF files in the Citar library.

A better way: Search the outlines.

(defun pdf-occur-search-citar-library-files+ ()
  "Search PDF files in the Citar library."
  (interactive)
  (require 'pdf-occur)
  (require 'citar)
  (let ((query (pdf-occur-read-string))
        (documents (mapcan (lambda (path)
                             (directory-files path t "\\.pdf$"))
                           citar-library-paths)))
    (pdf-occur-search documents query)))
  • Configure: Match the theme

    Automatically (re-)theme PDF Tools buffers.

    N.B. Toggle temporarily with =C-c C-r t.

    ;; (with-eval-after-load 'pdf-tools
    ;;    (add-hook 'pdf-view-mode-hook #'pdf-view-themed-minor-mode))
    
    (with-eval-after-load 'pdf-tools
      (defun my-pdf-tools-refresh-themed-buffers (_theme)
        (dolist (buffer (buffer-list))
          (with-current-buffer buffer
            (when (and (eq major-mode 'pdf-view-mode) pdf-view-themed-minor-mode)
              (pdf-view-refresh-themed-buffer t))))))
    
    (with-eval-after-load 'pdf-tools
      (add-hook 'enable-theme-functions
                #'my-pdf-tools-refresh-themed-buffers))
    
  • Configure: Hide line numbers

    Hide the line numbers, if global-display-line-numbers-mode is enabled.

    (with-eval-after-load 'pdf-view
      (add-hook 'pdf-view-mode-hook
                (lambda ()
                  (interactive)
                  (display-line-numbers-mode -1))))
    

Contribute!

Other modes :)

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'csv-mode)
  (add-to-list 'package-selected-packages 'json-mode))

Install a major mode for editing YAML (YAML Ain’t Markup Language).

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'yaml-mode))

XML/YAML (split)

Indent XML (Extensible Markup Language) with 4 spaces.

(with-eval-after-load 'nxml
  (let ((indent 4))
    (setopt nxml-child-indent indent
            nxml-attribute-indent indent
            nxml-outline-child-indent indent)))

BNF

BNF

Add support for editing BNF (Backus-Naur Form) grammars.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'ebnf-mode))

Fun modes

Table 11: Fun modes
Key Description
picture-mode ASCII drawing
artist-mode ASCII drawing

In the picture-mode, moving up (C-p) or down (C-n) never moves the point to a different column. This can help, for example, when reading logs. To return to the previous major mode from the picture-mode, type C-c C-c.

AUCTeX

Learn

C-c C-a Render and show

To align tables, execute M-x align-current.

Install and configure

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'auctex))

Recommended in the manual:

(with-eval-after-load 'tex
  (setopt TeX-auto-save t
          TeX-parse-self t
          TeX-master nil))

From https://emacs.stackexchange.com/questions/19472/how-to-let-auctex-open-pdf-with-pdf-tools:

(with-eval-after-load 'tex
  ;;
  ;; Use pdf-tools to open PDF files
  (setopt TeX-view-program-selection '((output-pdf "PDF Tools"))
          TeX-source-correlate-start-server t)
  ;;
  ;; Update PDF buffers after successful LaTeX runs
  (add-hook 'TeX-after-compilation-finished-functions
            #'TeX-revert-document-buffer))

Agent shell

Emacs package for

project-wide interactions with LLM agents

powered by ACP (Agent Client Protocol).

(Re)-entry point

agent-shell (C-x p a, custom)

  • (re-)opens for the current project
  • captures region (useful on re-entry)

Key bindings

  • Viewport view mode
    Key Description Note
    n Item: Next moves across pages
    p Item: Previous moves across pages
    RET Item: Expand/Collapse  
    f Page forward  
    b Page backward  
    r Reply optionally with region
    s Set session mode e.g. accept all edits
    v Set session model  
    C-c C-o Toggle Comint interface  
  • Viewport edit mode
    Key Description
    C-c C-c Send buffer
    C-c C-k Kill buffer
    C-c C-p Last view
    C-c C-o Toggle Comint interface

Configuration

  • Installation
    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'agent-shell))
    
    (with-eval-after-load 'package-vc
      (add-to-list
       'package-vc-selected-packages
       '(agent-shell-math-renderer
         :url "https://github.com/alberti42/agent-shell-math-renderer"))
      (add-to-list
       'package-vc-selected-packages
       '(agent-shell-attention
         :url "https://github.com/ultronozm/agent-shell-attention.el")))
    
  • Installation: ACP
    brew "codex-acp"
    
  • Interaction mode

    Prefer the new “viewport” (card-like) interaction mode over Comint (shell-like).

    (with-eval-after-load 'agent-shell
      (setopt agent-shell-prefer-viewport-interaction t))
    
  • Entry point key binding
    (with-eval-after-load 'project
      (keymap-set project-prefix-map "a" #'agent-shell))
    
  • Less flare

    Calm down the UI.

    (with-eval-after-load 'agent-shell
      (setopt agent-shell-show-welcome-message nil
              agent-shell-show-context-usage-indicator nil
              agent-shell-show-config-icons nil
              agent-shell-header-style 'text
              agent-shell-permission-icon "!"
              agent-shell-thought-process-icon "∮" ; or ⟳, useless
              agent-shell-busy-indicator-frames 'circle
              agent-shell-buffer-name-format
              (lambda (_agent project)
                ;; Modeled after `project-prefixed-buffer-name'.
                (concat "*"
                        project
                        "-"
                        (downcase (symbol-name 'agent-shell))
                        "*"))))
    
  • Explicit captures

    Only capture active region when executing agent-shell.

    (setopt agent-shell-context-sources '(region))
    
  • Providers

    Configure LLMs to be used:

    • DeepSeek open-weight model
    • OpenAI Codex proprietary model (temporary, hopefully)
    (with-eval-after-load 'agent-shell
      (setopt agent-shell-agent-configs
              (list (agent-shell-openai-make-codex-config)
                    (agent-shell-opencode-make-agent-config)))
      (let ((model "DeepSeek/DeepSeek V4 Pro"))
        (setopt agent-shell-opencode-default-model-id model
                agent-shell-opencode-default-session-mode-id model)))
    
  • Line wrapping
    (with-eval-after-load 'agent-shell
      (add-hook 'agent-shell-hook #'visual-line-mode)
      (add-hook 'agent-shell-viewport-view-mode-hook #'visual-line-mode)
      (add-hook 'agent-shell-viewport-edit-mode-hook #'visual-line-mode))
    
  • Enable math rendering
    (with-eval-after-load 'agent-shell
      (require 'agent-shell-math-renderer)
      (setq agent-shell-math-renderer-enabled t))
    
  • Enable attention indicator

    Show

    (with-eval-after-load 'agent-shell
      (setopt agent-shell-attention-lighter "AI:%d")
      (agent-shell-attention-mode))
    

Agenda

  • Bugs
  • Patches
    • DONE Add simple blinking circle as busy indicator
    • TODO Use more standard buffer names

      Instead of NIH agent-shell-buffer-name-format, use project-prefixed-buffer-name. See project-compilation-buffer-name-function.

    • TODO Allow for nil agent-shell-thought-process-icon
    • TODO Make header line separators (➤) configurable
    • TODO Print “All done” heading when LLM is done
    • TODO Fix superfluous empty space when header is disabled

System prompt

Going forward, the rules are:

  • Do not stage or commit, I will.
  • Do tell me about every new function upfront.
  • Do tell me about every updated function upfront.
  • Do not change files until explicitly asked to.

Image

A keymap and major mode for viewing images.

Entry points

  • Open an image file via find-file or the like.
  • Place the cursor on some image.

Key bindings

Key Description Note
i + zoom in with repeatable +
i - zoom out with repeatable -
i r rotate with repeatable r
i o save original  

Unix

Podman

  1. podman machine init (download Linux VM)
  2. podman machine start [MACHINE] (start Linux VM)
  3. podman machine set --rootful (if root is required in VM, e.g. ports < 1024)
  4. podman machine stop [MACHINE] (start Linux VM)

MacOS (maybe):

sudo /opt/homebrew/Cellar/podman/5.7.1/bin/podman-mac-helper install
podman machine stop
podman machine start

Vterm

A terminal emulator for Emacs.

Entry points

  • M-x vterm RET
    (reuses the buffer named *vterm* if exists)
  • M-x my-project-vterm RET (C-x p t)
    (reuses the buffer named *$PROJECT-vterm* if exists)

Key bindings

Selected Vterm key bindings defined in the vterm-mode-map.

Key binding Description Note
C-c C-t toggle the “copy mode” copy and exit with RET
C-y yank a.k.a. paste
C-l clear buffer  
C-q quote next key press e.g. C-q C-x sends C-x, custom

Learn how to copy text

To copy text,

  1. type C-c C-t to enter the vterm-copy-mode
  2. mark the region76 that is to be copied as a kill
  3. type RET to copy the marked text and exit the vterm-copy-mode

Install

Install the package.

(with-eval-after-load 'package
  (add-to-list 'package-selected-packages 'vterm))

Configuration

  • Automatic compilation

    Automatically compile the C module when needed.

    (with-eval-after-load 'vterm
      (setopt vterm-always-compile-module t))
    
  • Clearing

    Clear both the screen and the scroll-back on C-l and clear.

    (with-eval-after-load 'vterm
      (setopt vterm-clear-scrollback-when-clearing t))
    
  • Larger scroll-back

    Increase the scroll-back range from 103 to 105 lines 77.

    (with-eval-after-load 'vterm
      (setopt vterm-max-scrollback 100000))
    
  • Disable current line highlighting
    (with-eval-after-load 'vterm
      (add-hook 'vterm-mode-hook
                (lambda ()
                  (setq-local global-hl-line-mode nil)))
      ;; Makes active region/selection invisible.
      ;; (add-hook 'vterm-copy-mode-hook
      ;;           (lambda ()
      ;;             (hl-line-mode (if vterm-copy-mode +1 -1))))
      )
    
  • Quoting keys
    (with-eval-after-load 'vterm
      (keymap-set vterm-mode-map "C-q" #'vterm-send-next-key))
    
  • Shell bridge

    Translate from BashEmacs:

    • emacsfind-file
    • manman

    and call vterm-clear-scrollback on clear in Bash.

    (with-eval-after-load 'vterm
      (setopt vterm-eval-cmds
              '(("find-file" find-file)
                ("man" man)
                ("vterm-clear-scrollback" vterm-clear-scrollback))))
    
    # File `~/.bashrc-emacs', sourced from `~/.bashrc'.
    
    if test "$INSIDE_EMACS" == vterm
    then
        VTERM_COMMAND='\e]51;E'
        VTERM_COMMAND_END='\e\\'
    
        # Bridge `emacs' in Bash to 'find-file' in Emacs.
        emacs() {
            FILE="$(cd $(dirname "$1"); pwd)/$(basename "$1")"
            printf "${VTERM_COMMAND}"
            printf "find-file \"${FILE}\""
            printf "${VTERM_COMMAND_END}"
        }
    
        alias e=emacs
    
        # Bridge `man' in Bash to `man' in Emacs.
        man() {
            printf "${VTERM_COMMAND}"
            printf "man \"$1\""
            printf "${VTERM_COMMAND_END}"
        }
    
        # Call 'vterm-clear-scrollback' in Emacs before 'clear' in Bash.
        clear() {
            printf "${VTERM_COMMAND}"
            printf "vterm-clear-scrollback"
            printf "${VTERM_COMMAND_END}"
            tput clear;
        }
    fi
    

Agenda

  • TODO Contribute my-project-vterm

Bug reports

  • TODO Pacify C compiler warning

Bash

A Unix shell.

A GNU project.

… write the definition line … … move Bash config in here …

A portable shebang: #!/usr/bin/env bash

Apple changed the default shell on macOS from Bash to Zsh because the GNU project re-licensed Bash from GPL 2 to GPL 3 that enforces strict ethics and freedom. Apple shipped the old version of Bash for a while and then switched to Zsh.

  1. Install modern Bash with brew install bash
  2. Add /opt/homebrew/bin/bash to /etc/shells.
  3. Run chsh -s /opt/homebrew/bin/bash
  4. check that a FILE exists: test -f FILE

Installation

Tangled to the Homebrew bundle Brewfile:

brew "bash"
brew "bash-completion@2"

Configuration

  • TODO Learn more about configuring Bash and clean up
  • .bash_profile
    # Silence Apple's stupid message:
    # > The default interactive shell is now zsh.
    # > To update your account to use zsh, please run `chsh -s /bin/zsh`.
    # > For more details, please visit https://support.apple.com/kb/HT208050.
    export BASH_SILENCE_DEPRECATION_WARNING=1
    
    # Source the initialization file.
    test -r $HOME/.bashrc && source $HOME/.bashrc
    
  • .profile
    # Shell initialization file.
    
    export BC_ENV_ARGS="$HOME"/.bcrc
    
    # Create temporary directories if needed.
    # (Notmuch and MPD fail to work without these.)
    mkdir -p \
          ~/.cache/notmuch \
          ~/.cache/mpd
    
    mkdir -p ~/org/tmp/torrents
    
    # Initialize Homebrew.
    eval "$(/opt/homebrew/bin/brew shellenv)"
    
    # Find Java automatically.
    export PATH=/opt/homebrew/opt/openjdk/bin:$PATH
    
    # Find Emacs automatically.
    export PATH=~/org/src/emacs/nextstep/Emacs.app/Contents/MacOS/bin:$PATH
    export PATH=~/org/src/emacs/nextstep/Emacs.app/Contents/MacOS/:$PATH
    
    # Use the latest GNU Texinfo, not the ancient one bundled with macOS.
    # NOTE Necessary for building Org Mode.
    export PATH=/opt/homebrew/opt/texinfo/bin:$PATH
    
    # Use the latest GNU Make, not the ancient one bundled with macOS.
    export PATH=/opt/homebrew/opt/make/libexec/gnubin:$PATH
    
    # Configure Locale.
    export LC_ALL=en_US.UTF-8
    
    # Locate the Password Store files.
    export PASSWORD_STORE_DIR=~/org/pass/
    
  • .bashrc
    # GNU Bash initialization file.
    # https://www.gnu.org/software/bash/
    
    DEFAULT_STYLE='\e[0m'
    
    BLACK='\e[30m'
    RED='\e[31m'
    GREEN='\e[32m'
    YELLOW='\e[33m'
    BLUE='\e[34m'
    MAGENTA='\e[35m'
    CYAN='\e[36m'
    GRAY='\e[90m'
    LIGHT_GRAY='\e[37m'
    LIGHT_RED='\e[91m'
    LIGHT_GREEN='\e[92m'
    LIGHT_YELLOW='\e[93m'
    LIGHT_BLUE='\e[94m'
    LIGHT_MAGENTA='\e[95m'
    LIGHT_CYAN='\e[96m'
    WHITE='\e[97m'
    
    BOLD='\e[1m'
    DIM='\e[2m'
    ITALIC='\e[3m'
    UNDERLINED='\e[4m'
    BLINKING='\e[5m'
    REVERSE='\e[7m'
    INVISIBLE='\e[8m'
    
    # Source shell configuration.
    source ~/.profile
    
    # Improve Bash completions.
    source /opt/homebrew/etc/profile.d/bash_completion.sh
    
    # Avoid duplicate entries in the shell history.
    HISTCONTROL=ignoredups
    
    # Avoid storing session histories.
    SHELL_SESSION_HISTORY=0
    
    # Use color to indicate file type.
    # SEE ALSO: The analogous setting in ".inputrc".
    alias ls="ls -Gha"
    
    function _build_prompt() {
        local EXIT=$? # must be first
    
        # NOTE: Every escape sequence in PS1 must be wrapped in '\[' and
        # '\]' or else the GNU Readline library will compute an incorrect
        # visible length of the 'PS1' string resulting in glitchy redraws.
    
        # Clear the prompt.
        PS1=""
    
        # Add exit code, if non-zero.
        if [[ $EXIT -ne 0 ]]; then
            PS1+="\[${LIGHT_RED}\](${EXIT}) "
        fi
    
        # Add username and hostname.
        PS1+="\[${LIGHT_GREEN}\]\[${ITALIC}\]"
        PS1+="\u@\h"
        PS1+="\[${DEFAULT_STYLE}\]"
    
        # Add Git branch.
        PS1+="\[${LIGHT_MAGENTA}\]"
        PS1+="$(__git_ps1) "
        PS1+="\[${DEFAULT_STYLE}\]"
    
        # Add working directory.
        PS1+="\[${UNDERLINED}\]\[${LIGHT_YELLOW}\]"
        PS1+="\W"
        PS1+="\[${DEFAULT_STYLE}\]"
    
        # Add prompt symbol, `$' or `#'.
        PS1+=" \$ "
    }
    
    # Use a fancy prompt, if running in a fancy terminal.
    if [[ "${TERM:0:5}" == "xterm" ]]
    then
        PROMPT_COMMAND=_build_prompt
    else
        PS1="$ "
    fi
    
    # Improve Emacs intergration.
    source ~/.bashrc-emacs
    
    # Activate Python virtual environment.
    PYTHON_ACTIVATE=~/.venv/bin/activate
    if [[ -f $PYTHON_ACTIVATE ]]
    then
        source $PYTHON_ACTIVATE
    fi
    
    # Find programs installed with `cargo install'
    export PATH=/Users/salutis/.cargo/bin:$PATH
    

Parameter expansion

  • Replace
    • first ${string/pattern/replacement}
    • all ${string//pattern/replacement}
  • Prefix (dirname)
    EXAMPLE=dir/file.ext
    dirname $EXAMPLE
    echo ${EXAMPLE%/*}
    
    dir
    dir
    
  • Suffix (basename)
    EXAMPLE=dir/file.ext
    basename $EXAMPLE
    echo ${EXAMPLE##*/}
    
    file.ext
    file.ext
    

Grouping commands

  • No subshell {...}
    echo $BASHPID && { echo $BASHPID; echo $BASHPID; }
    
    52998
    52998
    52998
    
  • Subshell (...)
    echo $BASHPID && ( echo $BASHPID; echo $BASHPID; )
    
    53069
    53070
    53070
    
    • Gotcha with $$

      In a subshell, $$ expands to the PID of the parent shell.

      echo $$ && ( echo $$; echo $$; )
      
      53011
      53011
      53011
      

Control operators

a ; b
execute a and then b
a && b AND list
execute b if a exits with 0
a || b OR list
execute b if a exits with non-0

Fennel

Destructuring

T ↦ P ≡ {K₁ V₁ K₂ V₂ …} ↦ {K₁ P₁ K₂ P₂ … CAPTURE?} syntax
Destructure table T.
Destructure Vᵢ to Pᵢ for every equal Kᵢ.
If Kᵢ is :, interpret it as key "Pᵢ" literally.
If Kᵢ stars with ?, also match nil.
If CAPTURE is &as T*, set T* to T.
Gotcha: {} matches any table, not the empty table.
T ↦ P ≡ [V₁ V₂ … Vₙ] ↦ [P₁ P₂ … Pₘ CAPTURE?] syntax
Destructure a sequence from table T.
Destructure Vᵢ to Pᵢ for every i ≤ m.
If CAPTURE is &as T*, set T* to T.
If CAPTURE is & T*, set T* to [Tₙ₊₁ … Tₘ].
Gotcha: [] matches any table, not the empty table.
V ↦ P ≡ (values V₁ V₂ … Vₙ) ↦ (P₁ P₂ … Pₘ) syntax
Destructure multiple values.
Destructure Pᵢ to Vᵢ for every i ≤ m.
Gotcha: Multiple values cannot be nested.
X ↦ E base case syntax
If E is literal, check X = E.
If E is identifier, bind E to X (†).
(†) If not documented otherwise, such as in match.
X ↦ _ convention
Ignore E.
Also _E for any E.

Type system design

Types

8 Lua types

Schemas

;; Type predicates

string, integer, ...      ;; 8 built-in types in Lua
{:x integer :y integer}   ;; product, map
{integer true}            ;; product, set
[string]                  ;; product, sequence, homogeneous
[string number]           ;; product, sequence, heterogeneous, fixed length
[:a :b :c]                ;; sum, enumeration, sequence
{:a true :b true :c true} ;; sum, enumeration, set
(fn even? [value] ...)    ;; custom
_                         ;; anything

(type point {:x number :y number})       ;; new type, macro
(assert (supertype? vertex module.node)) ;; interoperability check

;; Sub-type checking.

;; New keyword: &in

(number 1)     ;; evaluates to 1
(number :hi)   ;; type error: `hi' is not a number

;; Type checking at function boundaries.

;; Goal: Avoid identifier-type repetition, encourage reuse.

;; New keyword: &to

(point origin)                     ;; check type
{: name : education &in person}    ;; check sub-type, map
[first second &in pair]            ;; check sub-type, sequence

(fn origin? [point &to boolean])                    ;; Automatic types, all
(fn scale [point (number factor) &to point])        ;; Automatic types, some
(fn success? [{: status &in response} &to boolean]) ;; Destructuring, maps
(fn origin? [{: x : y &in point} &to boolean])      ;; Destructuring, maps
(fn first? [head &in list &to boolean])             ;; Destructuring, sequences

;; Automatic typing encourages:
;;
;; "It is better to have 100 functions operate on one data structure than 10
;; functions on 10 data structures.  --- Alan Perlis

My macros

My with-early-exit macro

Inspired by call-with-current-continuation in Scheme.

(macro with-early-exit [[out] body]
  "Execute BODY, exiting early with `...' on (OUT ...)."
  `(let [sentinel# {}]
     (λ ,out [...]
       (error [sentinel# ...] 2))
     (match (pcall #,body)
       (true result#) result#
       (false [sentinel# & rest#]) (table.unpack rest#)
       (false other#) (error other# 2))))

My enum macro

(macro enum [& objects]
  "Create a table mapping OBJECTS to themselves."
  (collect [_ object (ipairs (table.pack objects))]
    object object))

My log macro

(λ log [sexp ?depth]
  "Print the SEXP and its value to DEPTH, then return it."
  (let [depth (+ (or ?depth 0) 2)
        sexp-view (view sexp)]
    `(let [view# (require :fennel-view)
           value# ,sexp
           value-view# (view# value# {:depth ,depth})]
       (print (if (= ,sexp-view value-view#)
                  (string.format "DEBUG: %s" ,sexp-view)
                  (string.format "DEBUG: %s: %s" ,sexp-view value-view#)))
       value#)))
  • TODO Log address (pointer) if sexp evaluates to table
  • TODO Add locus (file/line), perhaps with pcall and error

Scheme

Poem

                       ((I m a g i n e)
                     (shriram@cs.rice.edu)
               (((Imagine there's no FORTRAN)
                   (It's easy if you try)
           (No SML below us) (Above us only Y)
          (Imagine all              the people)
         (Living for                their Chez))
      ((Imagine there's          no memory leaks)
                             (It isn't hard to do)
                              (Nothing to malloc(3)
                                    or free(3) for)
                               (And no (void *) too)
                             (Imagine all the people)
                              (Living in parentheses))
                           ((You may say I'm a Schemer)
                             (But I'm not the only one)
                         (I hope someday you'll join us)
                               (And the world will be as
                        (lambda (f) (lambda (x) (f x)))))
                          ((Imagine those   continuations)
                         (I wonder              if you can)
                   (No need for              C or pointers)
               (A brotherhood                        of Dan)
                (Imagine all                      the people)
                (GCing all                          the world))
           ((You may say                          I'm a Schemer)
          (But I'm not                              the only one)
     (I hope someday                                you'll join us)
    (And the world                                        will be as
(lambda (f)                                     (lambda (x) (f x)))))))

https://schemers.org/Miscellaneous/imagine.txt

Lua

Pattern

A regexp-like78 string used by the Lua functions

defined as

7447b9ac-a872-4cfa-a43d-98c92ad59299.svg
where ^ matches the beginning of the string,
  $ matches the end of the string, and
  RECURSIVE-ITEM is the recursive item.

In-pattern recursive item

A component of the pattern:

c87749b4-a95d-49e8-b51a-836571254b25.svg
where (RECURSIVE-ITEM) captures the content of RECURSIVE-ITEM,
  () captures the position of (, and
  ITEM is the item.

In-pattern item

A component of the recursive item:

6ef4bc15-67a0-46c4-bfca-ff10b13944bd.svg
where CLASS is the class,
  QUANTIFIER is the quantifier, and
  SPECIAL is the special.

In-pattern class

A component of the item:

CLASS Description Complement
c matches character c  
c-d matches characters c through d  
. matches any character 79  
%a matches any letter %A
%c matches any control character %C
%d matches any digit %D
%g matches any printable character but space %G
%l matches any lowercase letter %L
%p matches any punctuation character %P
%s matches any whitespace character %S
%u matches any uppercase letter %U
%w matches any alphanumeric character %W
%x matches any hexadecimal digit %X
[xxx] matches the union of class(es) xxx [^xxx]
%xxx escapes xxx magic character ^$()%.[]*+-?  

In-pattern quantifier

A component of the item:

QUANTIFIER Description
? eagerly matches 0 or 1 times
* eagerly matches 0 or more times
+ eagerly matches 1 or more times
- lazily matches 0 or more times

In-pattern special

A component of the item:

e7041eb0-42cf-4482-ba9b-3a11fcceb090.svg
where REFERENCE is the capture reference,
  BALANCED is the balanced expression, and
  FRONTIER is the frontier expression.

In-pattern capture reference

A kind of special that

matches the DIGIT-th capture.

e92c04b7-aab0-4c5b-9642-e6c15345dd8c.svg

Example

to match ABC and ABCDEF without repeating ABC in the pattern:

return string.match("ABC ABCDEF", "(ABC) (%1DEF)")
ABC, ABCDEF

In-pattern balanced expression

A kind of special that

matches strings surrounded by LEFT and RIGHT.

0d2344e3-3377-4c1a-a72f-6bbcb03ed269.svg

Example

to capture all parenthesized expressions:

return string.match("A (B) C", "%b()")
(B)

In-pattern frontier expression

A kind of special that

matches the empty string S

where

  • the character immediately before S is not in the CLASS and
  • the character immediately after S is the given CLASS.
aff7d3c1-3ae9-4949-b40d-7e3d372ec7e8.svg

where CLASS is the in-pattern class.

Example

to find the index of the first non-letter character:

index, _ = string.find("ABC-CEF", "%f[^%a]")
return index
4

SQLite

SQLite

Propagating changes

  1. Enable PRAGMA foreign_keys = ON
  2. Add k PRIMARY KEY to the parent table t 80
  3. Add FOREIGN KEY(k) REFERENCES t(k) to the child table
  4. Optionally specify

    • FOREIGN KEY … ON DELETE action
    • FOREIGN KEY … ON UPDATE action

    with action being one of:

    • RESTRICT
    • SET NULL
    • SET DEFAULT
    • CASCADE

Example: Propagating deletions

PRAGMA foreign_keys = ON;
CREATE TABLE t(k PRIMARY KEY, x);
CREATE TABLE u(k, x, FOREIGN KEY(k) REFERENCES t(k) ON DELETE CASCADE);
INSERT INTO t(k, x) VALUES (10, 100);
INSERT INTO u(k, x) VALUES (10, 200);
DELETE FROM t WHERE k = 10;
SELECT count(*) from u;
count(*)
0

Example: Propagating updates

PRAGMA foreign_keys = ON;
CREATE TABLE t(k PRIMARY KEY, x);
CREATE TABLE u(k, x, FOREIGN KEY(k) REFERENCES t(k) ON UPDATE CASCADE);
INSERT INTO t(k, x) VALUES (10, 100);
INSERT INTO u(k, x) VALUES (10, 200);
UPDATE t SET k = 20;
SELECT k from u;
k
20

Example: Improving indentation of joins

CREATE TABLE t(a, b, x);
CREATE TABLE u(a, b, y);
INSERT INTO t(a, b, x) VALUES (1, 2, 10);
INSERT INTO u(a, b, y) VALUES (1, 2, 20);
SELECT x, y
  FROM t
       INNER JOIN u ON TRUE
           AND t.a = u.a
           AND t.b = u.b
x y
10 20

Example: Replacing rows

CREATE TABLE t(k INTEGER PRIMARY KEY, v);
INSERT INTO t(k, v) VALUES (1, 10);
INSERT OR REPLACE INTO t VALUES (1, 20);
SELECT * FROM t;
k v
1 20

Example: Constraining tables to exactly one row

CREATE TABLE t(v, singleton BOOL UNIQUE DEFAULT TRUE CHECK(singleton = TRUE));
INSERT INTO t(v) VALUES (10);
INSERT OR REPLACE INTO t(v) VALUES (20);
SELECT * FROM t;
v singleton
20 1

Example: Merging tables column-wise

CREATE TABLE t(a, b);
CREATE TABLE u(c, d);

INSERT INTO t(a, b) VALUES (10, 20);
INSERT INTO u(c, d) VALUES (30, 40);

SELECT a, b, c, d FROM t JOIN u;
a b c d
10 20 30 40

Example: Generating sequences recursively

Notes on using recursive Common Table Expressions (CTEs):

  • WITH RECURSIVE must contain a compound SELECT
  • UNION ALL performs better than UNION

The WITH Clause, SQLite documentation

WITH RECURSIVE seq(n) AS (
  VALUES(1) UNION ALL SELECT n + 1 FROM seq LIMIT 10
)
SELECT * FROM seq;
n
1
2
3
4
5
6
7
8
9
10

Example: Sum identity

CREATE TABLE t(k, a);
CREATE TABLE u(k, b);

INSERT INTO t(k, a) VALUES (1, 10), (2, 20), (3, 30);

SELECT
  ifnull(sum(a), 0) as 'sum(a)',
  ifnull(sum(b), 0) as 'sum(b)'
  FROM t FULL OUTER JOIN u ON u.k = t.k;
sum(a) sum(b)
60 0

Example: Unix epoch timestamp

SELECT unixepoch() as 'now';
now
1756394327

LaTeX

Set builders

When typesetting set builders,

use \mid and not \vert or |

or spacing will be incorrect.

Transposes

Four ways to typeset matrix transpose:

\begin{gather*}
  A^\mathrm{T} =
  A^\top =
  A^\intercal =
  A^\mathsf{T}
  \\[2ex]
  \vec{v}^\mathrm{T} =
  \vec{v}^\top =
  \vec{v}^\intercal =
  \vec{v}^\mathsf{T}
\end{gather*}
\begin{gather*} A^\mathrm{T} = A^\top = A^\intercal = A^\mathsf{T} \\[2ex] \vec{v}^\mathrm{T} = \vec{v}^\top = \vec{v}^\intercal = \vec{v}^\mathsf{T} \end{gather*}

Subjectively, the last one looks best.

Bottom figures

To place a figure at the bottom of a page, write:

\begin{figure*}[!b] \ldots \end{figure*}

Boxing

In equation, use \boxed{...}:

\begin{equation*}
  \boxed{x} = 1
\end{equation*}
\begin{equation*} \boxed{x} = 1 \end{equation*}

but in align, use \Aboxed{...} from mathtools:

\begin{align*}
  x & = 1 \\
  \Aboxed{y & = 2}
\end{align*}
\begin{align*} x & = 1 \\ \Aboxed{y & = 2} \end{align*}

Aligned systems

For best alignment,

use \left\{ and \right. with the aligned environment

instead of cases wrapped in align*.

\begin{equation*} \left\{ \begin{aligned} 2 x_1 + 4 x_2 & \geq 12 \\ 5 x_1 + 3 x_2 & \geq 15 \\ 4 x_1 + x_2 & \geq 8 \end{aligned} \right. \end{equation*}

Division

After \usepackage{physics},

\div is redefined from division to divergence,

and \div is available as \divisionsymbol.

Grouping

Parenthesized expressions are grouped automatically, so

{(AB)}_{m \times n} = (AB)_{m \times n}.

Stacking

N.B. Works only in displayed math.

Relations

\begin{equation*}
  a \stackrel{?}{=} 1
\end{equation*}
\begin{equation*} a \stackrel{?}{=} 1 \end{equation*}

Subscripts

\begin{equation*}
  \max_{\substack{1 \leq i \leq m \\ 1 \leq j \leq n}}
\end{equation*}
\begin{equation*} \max_{\substack{1 \leq i \leq m \\ 1 \leq j \leq n}} \end{equation*}

Python

Python

Installation

  • Interpreter

    Tangled to the Homebrew bundle Brewfile:

    brew "python@3.12"
    

    N.B. We install version 3.12 to make TensorFlow work.

  • Virtual environment
    python3.12 -m venv ~/.venv/
    

    N.B. We use version 3.12 to make TensorFlow work.

    N.B. Avoid the proprietary Anaconda distribution.

    • python-interpreter is for non-interactive use, and
    • python-shell-interpreter is for interactive use.
    (with-eval-after-load 'python
      (let ((interpreter "~/.venv/bin/ipython"))
        (setopt python-interpreter interpreter
                python-shell-interpreter interpreter)))
    
  • Package management
    source ~/.venv/bin/activate
    pip install --upgrade pip
    
  • Language server
    source ~/.venv/bin/activate
    pip install "python-lsp-server[all]"
    

    N.B. [all] installs support for all 3rd-party tooling.

    (with-eval-after-load 'eglot
      (add-to-list 'eglot-server-programs
                   '((python-mode python-ts-mode) .
                     ("~/.venv/bin/pylsp"))))
    
    (with-eval-after-load 'python
      (add-hook 'python-mode-hook #'eglot-ensure)
      (add-hook 'python-mode-hook #'outline-minor-mode))
    
  • Type hints
    source ~/.venv/bin/activate
    pip install pyright pylsp-mypy
    

    Ignore with:

    • # pyright: ignore
    • # mypy: ignore
  • Refactoring
    source ~/.venv/bin/activate
    pip install pylsp-rope
    
  • Linting
    source ~/.venv/bin/activate
    pip install python-lsp-ruff
    
  • Formatting

    https://pypi.org/project/black/

    source ~/.venv/bin/activate
    pip install python-lsp-black # black
    
    (with-eval-after-load 'package
      (add-to-list 'package-selected-packages 'blacken))
    
    (with-eval-after-load 'blacken
      (setopt blacken-executable "~/.venv/bin/black"
              blacken-line-length 'fill))
    
    ;; (add-hook 'python-mode-hook #'blacken-mode)
    
  • Notebooks
    source ~/.venv/bin/activate
    pip install jupyter
    

    N.B. To run Jupyter, execute

    source ~/.venv/bin/activate && jupyter notebook
    

    in a shell or in the Compilation mode.

    • Experimental
      source ~/.venv/bin/activate
      pip install jupytext
      
      (with-eval-after-load 'package
        (add-to-list 'package-selected-packages 'code-cells)
        (add-to-list 'package-selected-packages 'drepl))
      

      Make code-cells use our Python environment.

      (with-eval-after-load 'code-cells
        (dolist (index (list 0 1))
          (setf (nth 0 (nth index code-cells-convert-ipynb-style))
                "~/.venv/bin/jupytext")))
      

Set comprehension

A Python expression that creates a set, using the syntax

{EXPRESSION for ITEM in ITERABLE}
{EXPRESSION for ITEM in ITERABLE if CONDITION}

similar to set-builder notation.

Example

{x * 10 for x in [1, 2, 3, 4, 5]}
{40, 10, 50, 20, 30}

Example with if

{x * 10 for x in [1, 2, 3, 4, 5] if x % 2 == 0}
{40, 20}

Generator comprehension

A Python expression that creates a list, using the syntax

(EXPRESSION for ITEM in ITERABLE)
(EXPRESSION for ITEM in ITERABLE if CONDITION)

similar to set-builder notation.

List comprehension

A Python expression that creates a list, using the syntax

[EXPRESSION for ITEM in ITERABLE]
[EXPRESSION for ITEM in ITERABLE if CONDITION]

similar to set-builder notation.

Example

[x * 10 for x in [1, 2, 3, 4, 5]]
[10, 20, 30, 40, 50]

Example with if

[x * 10 for x in [1, 2, 3, 4, 5] if x % 2 == 0]
[20, 40]

Dictionary comprehension

A Python expression that creates a dictionary, using the syntax

{KEY: VALUE for ITEM in ITERABLE}
{KEY: VALUE for ITEM in ITERABLE if CONDITION}

similar to set-builder notation.

Example

{x: x * 10 for x in [1, 2, 3, 4, 5]}
{1: 10, 2: 20, 3: 30, 4: 40, 5: 50}

Example with if

{x: x * 10 for x in [1, 2, 3, 4, 5] if x % 2 == 0}
{2: 20, 4: 40}

NumPy

NumPy

A Python library for numerical programming with arrays.

Installation

source ~/.venv/bin/activate
pip install numpy numpy.typing

Interface

NumPy follows the standard Array API:

https://data-apis.org/array-api/

n-dimensional array: ndarray

The main data structure of NumPy representing

  • high-performance
  • homogeneous
  • multi-dimensional
  • rectangular
  • fixed-size

array.

Construction from sequences: np.array(SEQUENCE, OPTIONS)

Option Default Description
dtype auto NDARRAY.dtype
copy False whether copy to copy SEQUENCE
ndmin auto the minimum number of dimensions

Define

A function with the signature

numpy.array(SEQUENCE, dtype = dtype)

which

creates a new array from the given SEQUENCE,

like a list, tuple, and similar.

The dtype argument specifies the type.

Example

import numpy as np
a = np.array([[1, 2, 3],
              [4, 5, 6]])
return a
[[1 2 3]
 [4 5 6]]

Explore ndmin

import numpy as np

array = np.array([10, 20, 30], ndmin=2)

return array
[[10 20 30]]

Pre-filling: np.<empty|zeros|ones|full>[_like]

  • filled with garbage (not clearing memory, fastest)
    • np.empty(SHAPE, dtype=float64)
    • np.empty_like(ARRAY)
  • filled with 0s
    • np.zeros(SHAPE, dtype=float64)
    • np.zeros_like(ARRAY)
  • filled with 1s
    • np.ones(SHAPE, dtype=float64)
    • np.ones_like(ARRAY)
  • filled with VALUE
    • np.full(SHAPE, VALUE, dtype=DTYPE)
    • np.full_like(ARRAY, VALUE)
import numpy as np
a = np.zeros((4, 4), dtype = np.int64)
a
array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]])

Example: The _like variants inherit shape and dtype

import numpy as np

a = np.array([10, 20, 30])
b = np.empty_like(a)

(b.shape, b.dtype)
((3,), dtype('int64'))

Example: Set dtype to int to get CPU-native integers

import numpy as np

a = np.empty((3), dtype=int)

a.dtype
int64

Example: np.<empty|zeros|ones> default to dtype=float64

import numpy as np

a = np.zeros(5)

a.dtype
float64

Example: np.full infers DTYPE from VALUE

import numpy as np

a = np.full(3, 10)

a.dtype

Sampling intervals: np.<arange|linspace>

Evenly spaced:

  • numpy.arange(FIRST = 0, LAST, STEP = 1, dtype=DTYPE)

    Values FIRST to (LAST - STEP) increasing/decreasing by STEP:

    import numpy as np
    a = np.arange(10, 50, 10)
    a
    
    array([10, 20, 30, 40])
    
  • numpy.linspace(FIRST, LAST, COUNT = 50, dtype=float)

    Values FIRST to LAST, equally spaced to COUNT instances:

    import numpy as np
    a = np.linspace(10, 50, 5)
    a
    
    array([10., 20., 30., 40., 50.])
    

Explore: arange with float STEP

import numpy as np
a = np.arange(10, 50, 0.1)
a.dtype
float64

Explore: Default dtype of arange is native int or float

import numpy as np
a = np.arange(10, 50, 10)
(a.dtype, a.dtype == int)
(dtype('int64'), True)
import numpy as np
a = np.arange(0.1, 0.5, 0.1)
(a.dtype, a.dtype == float)
(dtype('float64'), True)

Explore: Default dtype of linspace is native float

import numpy as np
a = np.linspace(50, 10, 5)
(a.dtype, a.dtype == float)
(dtype('float64'), True)

Explore: Decreasing arange implies STEP < 0

import numpy as np
a = np.arange(50, 10, -10)
a
array([50, 40, 30, 20])

Explore: Decreasing linspace implies LAST < FIRST

import numpy as np
a = np.linspace(50, 10, 5)
a
array([50., 40., 30., 20., 10.])

Matrix multiplication: linarg.matmul (@)

Example

Compute

\begin{align*} Y = A B = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix} = \begin{pmatrix} \vec{y}_1 & \vec{y}_2 \end{pmatrix} = \begin{pmatrix} 19 & 22 \\ 43 & 50 \end{pmatrix} \end{align*}

with

\begin{align*} \vec{y}_1 & = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 5 \\ 7 \end{pmatrix} = 5 \begin{pmatrix} 1 \\ 3 \end{pmatrix} + 7 \begin{pmatrix} 2 \\ 4 \end{pmatrix} = \begin{pmatrix} 5 \\ 15 \end{pmatrix} + \begin{pmatrix} 14 \\ 28 \end{pmatrix} = \begin{pmatrix} 19 \\ 43 \end{pmatrix} \\[1ex] \vec{y}_2 & = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 6 \\ 8 \end{pmatrix} = 6 \begin{pmatrix} 1 \\ 3 \end{pmatrix} + 8 \begin{pmatrix} 2 \\ 4 \end{pmatrix} = \begin{pmatrix} 6 \\ 18 \end{pmatrix} + \begin{pmatrix} 16 \\ 32 \end{pmatrix} = \begin{pmatrix} 22 \\ 50 \end{pmatrix} \end{align*}

by

import numpy as np

A = np.array([[1, 2],
              [3, 4]])

B = np.array([[5, 6],
              [7, 8]])

A @ B
19 22
43 50

Broadcasting: Implicit outer product

Implict outer product in NumPy, where

dimensions of size 1 are “stretched”

if they need to be larger.

Example

Denote numpy.add by \(\stackrel{?}{+}\) and let

\begin{align*} A = \begin{pmatrix} 100 & 200 \\ 300 & 400 \end{pmatrix}, \quad \vec{v} = \begin{pmatrix} 1 \\ 2 \end{pmatrix}; \end{align*}

then

\begin{align*} A \stackrel{?}{+} \vec{v} & = A + 1 \vec{v}^{\mathsf{T}} \\ & = \begin{pmatrix} 100 & 200 \\ 300 & 400 \end{pmatrix} + \vec{1} {\begin{pmatrix} 1 \\ 2 \end{pmatrix}}^{\!\!\mathsf{T}} \\ & = \begin{pmatrix} 100 & 200 \\ 300 & 400 \end{pmatrix} + \begin{pmatrix} 1 \\ 1 \end{pmatrix} \begin{pmatrix} 1 & 2 \end{pmatrix} \\ & = \begin{pmatrix} 100 & 200 \\ 300 & 400 \end{pmatrix} + \begin{pmatrix} 1 \\ 1 \end{pmatrix} \begin{pmatrix} 1 & 2 \end{pmatrix} \\ & = \begin{pmatrix} 100 & 200 \\ 300 & 400 \end{pmatrix} + \begin{pmatrix} 1 & 2 \\ 1 & 2 \end{pmatrix} \\ & = \begin{pmatrix} 101 & 202 \\ 301 & 402 \end{pmatrix} \end{align*}

and

import numpy as np
a = np.array([[100, 200], [300, 400]])
v = np.array([1, 2])
a + v
array([[101, 202],
       [301, 402]])

Matplotlib

Matplotlib

A plotting library for Python.

Installation

source ~/.venv/bin/activate
pip install matplotlib

Configuration file

import matplotlib as mpl
mpl.matplotlib_fname()
/Users/rudy/.matplotlib/matplotlibrc

Configuration

Stored in ~/.matplotlib/matplotlibrc 81

# Export with transparent background.
savefig.transparent: True

# Use LaTeX by default.
text.usetex: True

# Fix spindly LaTeX font and add support for Physics awesomness.
text.latex.preamble: \usepackage{mlmodern} \usepackage{physics}

# Set colors.
<<matplotlib-style()>>

where <<matplotlib-style>> is

source ~/.venv/bin/activate
STYLE=$(find ${VIRTUAL_ENV} -name dark_background.mplstyle | head)
cat ${STYLE} | grep :
lines.color: white
patch.edgecolor: white
text.color: white
axes.facecolor: black
axes.edgecolor: white
axes.labelcolor: white
axes.prop_cycle: cycler('color', ['8dd3c7', 'feffb3', 'bfbbd9', 'fa8174', '81b1d2', 'fdb462', 'b3de69', 'bc82bd', 'ccebc4', 'ffed6f'])
xtick.color: white
ytick.color: white
grid.color: white
figure.facecolor: black
figure.edgecolor: black
boxplot.boxprops.color: white
boxplot.capprops.color: white
boxplot.flierprops.color: white
boxplot.flierprops.markeredgecolor: white
boxplot.whiskerprops.color: white

Light style

To temporarily enable the default light style, execute

plt.style.use('default')

Graphing single function

To plot a function in Matplotlib:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots()

ax.grid()

ax.set_xlabel(r'\(x\)')
ax.set_xticks([-np.pi, 0, +np.pi])
ax.set_xticklabels([r'\(-\pi\)', '0', r'\(\pi\)'])

ax.set_ylabel(r'\(y = \sin(x)\)')
ax.set_yticks([-1, 0, +1])
ax.set_title('The sine function.')

ax.plot(x, y)

fig
61bd589a-915c-4a34-a540-69f6183e1fe7.svg

Graphing multiple functions

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-np.pi, np.pi, 100)

y_sin = np.sin(x)
y_cos = np.cos(x)

fig, ax = plt.subplots()

ax.set_title('Some trigonometric functions.')

ax.set_xlabel(r'\(x\)')
ax.set_xticks([-np.pi, 0, +np.pi])
ax.set_xticklabels([r'\(-\pi\)', '0', r'\(\pi\)'])

ax.set_ylabel(r'\(y\)')
ax.set_yticks([-1, 0, +1])

ax.plot(x, y_sin, label=r'\(y = \sin(x)\)')
ax.plot(x, y_cos, label=r'\(y = \cos(x)\)')

ax.grid()
ax.legend()

fig
4f242b9a-82bf-4cd2-97bd-c705a1fd4e1a.svg

Deep learning

Universal approximation theorem

A pivotal result in deep learning stating that

feed-forward neural networks are universal estimators,

that is, for every continuous function 82

\begin{equation*} f : \mathbb{R}^n \to \mathbb{R}^m \end{equation*}

there exists a feed-forward neural network \(\hat{f}\) with

  • one hidden layer and
  • non-polynomial activation functions

that approximates \(f\) such that

\begin{equation*} \boxed{| f(x) - \hat{f}(x) | < \epsilon} \end{equation*}

for

  • all \(x\) in any given interval \([a, b] \subset \mathbb{R}^n\)
  • all degrees of accuracy \(\epsilon\).

Input matrix

A matrix with training features 83, as a rank-2 tensor:

\begin{equation*} X = \begin{pmatrix} \vphantom{\Big(} \vec{x}^1 & \cdots & \vec{x}^m \end{pmatrix} = \begin{pmatrix} x_1^1 & \cdots & x_1^m \\ \vdots & \ddots & \vdots \\ x_n^1 & \cdots & x_n^m \end{pmatrix} \in \mathbb{R}^{n \times m} \end{equation*}

where

  • \(x^i\) is the \(i\)-th example
  • \(x^i_j\) is the \(j\)-th feature of the \(i\)-th example

and

  • \(n\) is the number of features
  • \(m\) is the number of examples.

Target matrix

A matrix with training targets 84, as a rank-2 tensor:

\begin{equation*} Y = \begin{pmatrix} \vphantom{\Big(} \vec{y}^1 & \cdots & \vec{y}^m \end{pmatrix} = \begin{pmatrix} y_1^1 & \cdots & y_1^m \\ \vdots & \ddots & \vdots \\ y_n^1 & \cdots & y_n^m \end{pmatrix} \in \mathbb{R}^{n \times m} \end{equation*}

where

  • \(x^i\) is the \(i\)-th target
  • \(x^i_j\) is the \(j\)-th target of the \(i\)-th example

and

  • \(m\) is the number of examples
  • \(n\) is the number of targets.

Synonyms

  • True targets

Weight tensor

A rank-3 tensor with all neuron weights in a neural network:

\begin{equation*} W^{[i]} = \begin{pmatrix} w^{[i](1)}_{1} & \cdots & w^{[i](n)}_{1} \\ \vdots & \ddots & \vdots \\ w^{[i](1)}_{p} & \cdots & w^{[i](n)}_{p} \end{pmatrix} \in \mathbb{R}^{p \times n} \end{equation*}

where

  • \(w^{[i]}\) are all weights in the \(i\)-th layer
  • \(w^{[i]}_j\) are all weights in the \(j\)-th neuron in the \(i\)-th layer
  • \(w^{[i](k)}_j\) is the \(k\)-th weight in the \(j\)-th neuron in the \(i\)-th layer

so

  • \(w^{[i](k)}_j\) is the weight of the path from
    • the \(k\)-th neuron in the \((i - 1)\)-th layer to
    • the \(j\)-th neuron in the \(i\)-th layer

and

  • \(i\) is the layer number
  • \(p\) is the number of neurons in \(i\)-th layer
  • \(n\) is the number of neurons in the \((i - 1)\)-th layer 85

Ranks

The ranks of the weight tensor are:

  • rank-3 tensor \(W\)
    all weights in the neural network
  • rank-2 matrix \(W^{[i]}\)
    all weights of all neurons in the \(i\)-th layer
  • rank-1 row vector86 \(w^{[i]}_{j}\)
    all weights of the \(j\)-th neuron in the \(i\)-th layer
  • rank-0 scalar \(w^{[i](k)}_{j}\)
    the \(k\)-th weight of the \(j\)-th neuron in the \(i\)-th layer.

Drawing of rank-3 network weights

Rank-3 weight tensor \(W\):

607e47c9-1bee-4307-9df7-3e3848ad6a59.svg

Drawing of rank-2 layer weights

Rank-2 matrix \(W^{[i]}\) of a weight tensor \(W\):

42195ce8-027c-4ee4-97d2-35d6cf6b47c2.svg

Drawing of rank-1 neuron weights

Rank-1 row vector \(\vec{w}^{[i]}_{j}\) of a weight tensor \(W\):

e5bbe26a-7006-4f7c-809e-948c9024ea41.svg

Drawing of rank-0 weight

Rank-0 scalar \(w^{[i](k)}_{j}\) of a weight tensor \(W\):

08dbd520-41ce-429d-b77a-5d06458f058c.svg

Bias tensor

A rank-2 tensor with all neuron biases in a neural network:

\begin{equation*} \vec{b}^{[i]} = \begin{pmatrix} b^{[i]}_1 \\ \vdots \\ b^{[i]}_p \end{pmatrix} \in \mathbb{R}^p \end{equation*}

where

  • \(b^{[i]}\) are all biases of the \(i\)-th layer
  • \(b^{[i]}_j\) is the bias of the \(j\)-th neuron in the \(i\)-th layer

and

  • \(i\) is the layer number
  • \(p\) is the number of neurons in the \(i\)-th layer.

Forward propagation

End-to-end, input-to-output transformation in a neural network:

\begin{equation*} \hat{Y} = \hat{f}(X) = A^{[\ell]} \in \mathbb{R}^{p^{\scriptstyle [\ell]} \times p^{\scriptstyle [\ell - 1]}} \end{equation*}

with

\begin{equation*} A^{[i]} = \begin{cases} X \in \mathbb{R}^{n \times m} & i = 0 \\ \boxed{g^{[i]}(W^{[i]} A^{[i - 1]} + \vec{b}^{[i]})} \in \mathbb{R}^{p^{\scriptstyle [i]} \times p^{\scriptstyle [i - 1]}} & i > 0 \end{cases} \end{equation*}

and

\begin{equation*} p^{[1]} = p^{[0]} \end{equation*}

where

  • \(X_{m \times n}\) is the input matrix
  • \(g^{[i]}\) is the activation function used in the \(i\)-th layer
  • \(W^{[i]}\) is a matrix in the weight tensor
  • \(\vec{b}^{[i]}\) is a vector in the bias tensor
  • \(p^{[i]}\) is the number of neurons in the \(i\)-th layer
  • \(\ell\) is the number of layers in the network.

Dimensionality check: Base case

Check the forward propagation dimensions for

  1. the input layer \(A^{[0]}\)
  2. the first hidden layer \(A^{[1]}\)
\begin{align*} \tag{1} A^{[0]}_{p^{[0]} \times n^{[0]}} & = A^{[0]}_{n \times m} = X_{n \times m} \\ \tag{2} \boxed{A^{[1]}_{p^{[1]} \times n^{[1]}}} & \stackrel{?}{=} \boxed{A^{[1]}_{p^{[1]} \times p^{[0]}}} \end{align*}

with

\begin{align*} p^{[0]} & = n^{[0]} \\ p^{[1]} & = n^{[1]} \end{align*}

by

\begin{align*} \boxed{A^{[1]}_{p^{[1]} \times n^{[1]}}} & = g^{[1]} \big( W^{[1]}_{p^{[1]} \times n^{[1]}} A^{[0]}_{p^{[0]} \times n^{[0]}} + \vec{b}^{[1]}_{p^{[1]} \times 1} \big) && \text{by definition of \(A^{[i]}\)} \\ & = g^{[1]} \big( W^{[1]}_{p^{[1]} \times p^{[0]}} A^{[0]}_{p^{[0]} \times n^{[0]}} + \vec{b}^{[1]}_{p^{[1]} \times 1} \big) && \text{substitute \(n^{[1]}\) with \(p^{[0]}\)} \\ & = g^{[1]} \big( T^{[1]}_{p^{[1]} \times n^{[0]}} + \vec{b}^{[1]}_{p^{[1]} \times 1} \big) && \text{multiply \(W^{[1]} A^{[0]}\)} \\ & = g^{[1]} \big( U^{[1]}_{p^{[1]} \times n^{[0]}} \big) && \text{add \(T^{[1]} + \vec{b}^{[1]}\)} \\ & = g^{[1]} \big( U^{[1]}_{p^{[1]} \times p^{[0]}} \big) && \text{substitute \(n^{[0]}\) with \(p^{[0]}\)} \\ & = \boxed{A^{[1]}_{p^{[1]} \times p^{[0]}}} && \text{apply \(g^{[1]}\).} \end{align*}

Dimensionality check: Recursive case

Check the forward propagation dimensions for the hidden layer

\begin{equation*} \boxed{A^{[2]}_{p^{[2]} \times n^{[2]}}} \stackrel{?}{=} \boxed{A^{[2]}_{p^{[2]} \times p^{[1]}}} \end{equation*}

with

\begin{equation*} p^{[1]} = n^{[1]} \end{equation*}

by

\begin{align*} \boxed{A^{[2]}_{p^{[2]} \times n^{[2]}}} & = g^{[2]} \big( W^{[2]}_{p^{[2]} \times n^{[2]}} A^{[1]}_{p^{[1]} \times n^{[1]}} + \vec{b}^{[2]}_{p^{[2]} \times 1} \big) && \text{by definition of \(A^{[i]}\)} \\ & = g^{[2]} \big( W^{[2]}_{p^{[2]} \times p^{[1]}} A^{[1]}_{p^{[1]} \times n^{[1]}} + \vec{b}^{[2]}_{p^{[2]} \times 1} \big) && \text{substitute \(n^{[2]}\) with \(p^{[1]}\)} \\ & = g^{[2]} \big( T^{[2]}_{p^{[2]} \times n^{[1]}} + \vec{b}^{[2]}_{p^{[2]} \times 1} \big) && \text{multiply \(W^{[2]} A^{[1]}\)} \\ & = g^{[2]} \big( U^{[2]}_{p^{[2]} \times n^{[1]}} \big) && \text{add \(T^{[2]} + \vec{b}^{[2]}\)} \\ & = g^{[2]} \big( U^{[2]}_{p^{[2]} \times p^{[1]}} \big) && \text{substitute \(n^{[1]}\) with \(p^{[1]}\)} \\ & = \boxed{A^{[2]}_{p^{[2]} \times p^{[1]}}} && \text{apply \(g^{[2]}\).} \end{align*}

Synonyms

  • Forward prop
  • Forward pass

Automatic differentiation

A way of efficiently

computing partial derivatives

after evaluation, reusing intermediate values.

Method

Differentiate

\begin{equation*} f(x, y) = (x + y) (y + 3) \end{equation*}

at

\begin{align*} x & = 1 \\ y & = 2 \end{align*}

with respect to \(x\) and \(y\).

  • Diagram
    41a8009a-4603-4e44-b48e-94c32f57ba79.svg
  • Forward pass

    Compute forward

    \begin{align*} a & = x + y = 1 + 2 = 3 \\ b & = y + 3 = 2 + 3 = 5 \\ c & = ab = 3 \cdot 5 = 15 \end{align*}

    getting

    \begin{equation*} f(x) = c = 15. \end{equation*}
  • Backward pass

    Using cached \(a\), \(b\), and \(c\), we proceed backward:

    • Compute the gradients of \(c\) with respect to \(a\) and \(b\):
      \begin{align*} \pdv{c}{a} & = \pdv{a} ab = \pdv{a} 5a \eval{}_{b = 5} = 5 \\[1ex] \pdv{c}{b} & = \pdv{b} ab = \pdv{b} 3b \eval{}_{a = 3} = 3 \end{align*}
    • Compute the gradients of \(a\) with respect to \(x\) and \(y\):
      \begin{align*} \pdv{a}{x} & = \pdv{x} (x + y) = \pdv{x} (x + 2) \eval{}_{y = 2} = 1 \\[1ex] \pdv{a}{y} & = \pdv{y} (x + y) = \pdv{x} (1 + y) \eval{}_{x = 1} = 1 \end{align*}
    • Compute the gradients of \(b\) with respect to \(x\) and \(y\):
      \begin{align*} \pdv{b}{x} & = \pdv{x} (y + 3) = \pdv{x} (2 + 3) \eval{}_{y = 2} = 0 \\[1ex] \pdv{b}{y} & = \pdv{y} (y + 3) = \pdv{x} (y + 3) \eval{}_{x = 1} = 1 \end{align*}
    • Use the chain rule to compute the gradients of \(c\) with respect to \(x\) and \(y\):
      \begin{align*} \pdv{c}{x} & = \pdv{c}{a} \pdv{a}{x} + \pdv{c}{b} \pdv{b}{x} = 5 \cdot 1 + 3 \cdot 0 = 5 \\[1ex] \pdv{c}{y} & = \pdv{c}{a} \pdv{a}{y} + \pdv{c}{b} \pdv{b}{y} = 5 \cdot 1 + 3 \cdot 1 = 8 \end{align*}

Pros and cons

Pros:

  • fast execution
  • exact results 87

Cons:

  • requires prior evaluation and caching of intermediate results
  • no symbolic representation of the derivative

Alternatives

symbolic differentiation
slower when derivatives are complex
numerical differentiation
slower when sufficiently precise

Embedding

A real-valued vector whose relation to other members of the vector space represents semantic or contextual similarity.

Relations

Embeddings are usually compared via the dot product.

Synonyms

  • Token embedding
  • Word embedding

ReLU

An activation function defined as

\begin{equation*} \operatorname{relu}(x) = \max(0, x) = \begin{cases} 0 & x \le 0 \\ x & x > 0 \end{cases}. \end{equation*}

Differentiability

ReLU is not differentiable at zero, so automatic differentiation typically takes

\begin{equation*} \dv{x} \operatorname{relu}(x) \stackrel{\mathrm{AD}}{=} \begin{cases} 0 & x \le 0 \\ 1 & x > 0 \end{cases}. \end{equation*}

Implementation and plot

To plot a function in Matplotlib:

import matplotlib.pyplot as plt
import numpy as np

def relu(x):
    return np.maximum(0, x)

x = np.arange(-1, 1, 0.01)
y = relu(x)

fig, ax = plt.subplots()

ax.set_xlabel(r'\(x\)')
ax.set_ylabel(r'\(y = f(x)\)')
ax.plot(x, y)
ax.grid()

fig
b275a7f1-6d6b-40fb-a21d-29e6488e8e6a.svg

Synonyms

  • Rectified linear unit

Ollama

Installation

To install Ollama via Homebrew, tangle the following to Brewfile:

brew "ollama", restart_service: true

Models

Models for Ollama are available in the Ollama Library.

Server

The Ollama server:

  • runs via Homebrew services on MacOS 88
  • runs on TCP port 11434 on localhost (127.0.0.1)

Command line interface

Ollama is controlled via the ollama CLI:

Command Description
ollama pull MODEL Pull the MODEL from Ollama Library
ollama show MODEL Describe the pulled MODEL
ollama run MODEL Run the MODEL 89
ollama stop MODEL Stop running the MODEL
ollama ls List all pulled models
ollama ps List all running models
ollama rm MODEL Remove the pulled MODEL

Curriculum learning

  1. Set the difficulty level to zero.
  2. Sample the training set for the examples of the current difficulty.
  3. Train the model and evaluate it with regard to the curriculum criteria.
  4. If the model passes the curriculum criteria, increase the difficulty.
  5. Go to 2.

TensorFlow

TensorFlow

A FOSS Python library for building

hardware-accelerated computational graphs,

typically used for machine learning.

Installation

Tangled to ~/Brewfile for Homebrew:

source ~/.venv/bin/activate
# pip install tensorflow
pip install tensorflow-metal
pip install tensorboard

N.B. TensorFlow may require older Python.

  • Test
    import tensorflow as tf
    gpus = len(tf.config.list_physical_devices ('GPU'))
    print(f"TensorFlow v{tf.__version__} installed.")
    print(f"{gpus} GPU(s) available.")
    
    TensorFlow v2.16.2 installed.
    1 GPU(s) available.
    

Alternatives

PyTorch
a widely used alternative
JAX
a new kid on the block with functional style

Automatic differentiation with GradientTape

The means of automatic differentiation in TensorFlow.

  1. create: tf.GradientTape()
  2. compute gradients: tape.gradient(y, [x, ...])

Synonyms

  • Autodiff
  • Autograd

Example: Scalar

  1. Evaluate \(y = x^2\) at \(x = 10\).
  2. Find \(\dv*{y}{x}\) at said \(x\).
import tensorflow as tf

x = tf.constant(10.0)

with tf.GradientTape() as tape:
    tape.watch(x)
    y = x ** 2

dx = tape.gradient(y, x)

f"y = {y}, dy/dx = {dx}"
y = 100.0, dy/dx = 20.0

Example: Scalars (multivariate)

  1. Evaluate \(y = a^2 + b\) at \(a = 10\) and \(b = 20\).
  2. Find \(\dv*{y}{a}\) and \(\dv*{y}{b}\) at said \(a\) and \(b\).
import tensorflow as tf

a = tf.constant(10.0)
b = tf.constant(20.0)

with tf.GradientTape() as tape:
    tape.watch([a, b])
    y = a ** 2 + b

da, db = tape.gradient(y, [a, b])

f"y = {y}, dy/da = {da}, dy/db = {db}"
y = 120.0, dy/da = 20.0, dy/db = 1.0

Example: Vector and graph

Given \(y = \sin x\), graph \(y\) and \(\dv*{y}{x}\) at \(x \in [-\pi, \pi]\).

import math
import tensorflow as tf
import matplotlib.pyplot as plt

# Differentiate.

x = tf.linspace(-math.pi, math.pi, 100)

with tf.GradientTape() as tape:
    tape.watch(x)
    y = tf.sin(x)

dy_dx = tape.gradient(y, x)

# Plot.

plt.style.use('dark_background')
plt.rc('legend', facecolor='#111')

fig, ax = plt.subplots()

ax.set_xlabel(r'\(x\)')
ax.set_xticks([-math.pi, 0, +math.pi])
ax.set_xticklabels([r'\(-\pi\)', '0', r'\(\pi\)'])

ax.set_ylabel(r'\(y\)')
ax.set_yticks([-1, 0, +1])

ax.plot(x, y, label=r'\(y = \sin(x)\)')
ax.plot(x, dy_dx, label=r'\(y = \dv*{x} \sin(x) = \cos(x)\)')

ax.grid()
ax.legend()

fig
fb886e3f-7bf8-4ddb-957b-3bf2495ca3fd.svg

Gotcha: Only floats are supported

import tensorflow as tf

x1 = tf.Variable(4)                    # Does dot work.
x2 = tf.Variable(4.)                   # Works.
x3 = tf.Variable(4.0)                  # Works.
x4 = tf.Variable(4, dtype=tf.float32)  # Works.

with tf.GradientTape(persistent=True) as tape:
    y1 = x1 ** 2
    y2 = x2 ** 2
    y3 = x3 ** 2
    y4 = x4 ** 2

dy_dx_1 = tape.gradient(y1, x1)
dy_dx_2 = tape.gradient(y2, x2)
dy_dx_3 = tape.gradient(y3, x3)
dy_dx_4 = tape.gradient(y4, x4)

del tape

f"{dy_dx_1}, {dy_dx_2}, {dy_dx_3}, {dy_dx_4}"
None, 8.0, 8.0, 8.0

Gotcha: Constants are ignored by default

import tensorflow as tf

x1 = tf.constant(1.)
x2 = tf.constant(2.)

with tf.GradientTape(persistent=True) as tape:
    tape.watch(x2)
    y1 = x1 ** 2
    y2 = x2 ** 2

dydx1 = tape.gradient(y1, x1)
dydx2 = tape.gradient(y2, x2)

del tape

f"{dydx1}, {dydx2}"
None, 4.0

Gotcha: Tapes are single-use by default

When GradientTape.gradient is called, all resources are released.

To compute multiple gradients, use multiple tapes or:

import tensorflow as tf

x1 = tf.Variable(1.)
x2 = tf.Variable(2.)

with tf.GradientTape(persistent=True) as tape:
    y1 = x1 ** 2
    y2 = x2 ** 2

dy_dx_1 = tape.gradient(y1, x1)
dy_dx_2 = tape.gradient(y2, x2)

del tape # NOTE Do not forget.

f"{dy_dx_1}, {dy_dx_2}"
2.0, 4.0

Broadcasting

Performance benchmark

TensorFlow performs better than NumPy when

tensors flow multiple times through existing graphs.

Benchmark

import tensorflow as tf
import numpy as np
import timeit

def np_add(a, b):
    y = np.copy(a)
    for i in range(b):
        y += 1
    return y

def tf_add_eager(a, b):
    y = tf.identity(a)
    for i in range(b):
        y += tf.constant(1, dtype=a.dtype)
    return y

tf_add_graph = tf.function(tf_add_eager)

a_shape = (20, 20)
b = 1000 - 1

tf_a, tf_b = (tf.ones(a_shape), b)
np_a, np_b = (np.ones(a_shape), b)

results = [["Runs", "NumPy", "TensorFlow Eager", "TensorFlow Graph"], None]

for runs in [1, 2, 10, 100, 1000]:
    row = [runs]
    for function in [lambda : np_add(np_a, np_b),
                     lambda : tf_add_eager(tf_a, tf_b),
                     lambda : tf_add_graph(tf_a, tf_b)]:
        seconds = timeit.timeit(function, number = runs)
        milliseconds_per_run = (seconds / runs) * 1000
        row.append(f"{milliseconds_per_run:.4f}")
    results.append(row)

results
Runs NumPy TensorFlow Eager TensorFlow Graph
1 1.2050 47.8199 266.5787
2 0.7031 33.2319 0.2714
10 0.6882 32.9146 0.1607
100 0.6879 33.1700 0.1417
1000 0.7048 33.1270 0.1360

Interpretation of numbers

Utilities

Exiftool

View metadata:

exiftool FILE

Set metadata:

exiftool -FIELD=VALUE... FILE...
exiftool -artist="..." -title="..." -year=... FILE

webm editing not yet supported. :(

Fun

Jokes for nerds

UDP

I would tell you a UDP joke,
but you might not get it.

Loop

Why did the for loop stop running?
It took a break.

Programmers as politicians

Why do programmers make good politicians?
Their goto is to switch statements.

Nullary function

Why did the nullary function get along with everyone?
Because it didn’t want any arguments.

SQL query

A SQL query walks into a bar,
walks up to two tables and asks,
“Can I join you?”

— King Mui

Types of programmers

There are three types of programmers:
Those who can count and those who cannot.

Engineers

An optimist will say that the glass is half-full.
A pessimist will say that the glass is half-empty.
An engineer will say that the glass is twice as large as necessary.

Recursion

To understand what recursion is, you must first understand recursion.

Old C programmers

Old C programmers don’t die, they’re just cast into void.

Bytes

Two bytes meet. The first byte asks, “Are you ill?”

The second byte replies, “No, just feeling a bit off.”

Ten kinds of people

There are ten kinds of people in the world, those who understand binary, those who don’t.

Octal jokes

Why don’t jokes work in octal? Because 7 10 11.

English

Useful phrases

  • Take the stairs/elevator

Fun phrases

  • “some sentences are a bit choppy” (DeepSeek R1 Llama Distill 14B)
  • “that sentence is a real tangle” (Gemma 3 12B IT QAT)
  • “gives the reader a concrete anchor before the statistics hit” (Claude)

Fun words/terms

  • pre-fly [something with someone]
  • stretch goal
  • snoozy (snoozier, snooziest)

Fun terms: Mine

  • cook up some patches

Misc

Abbreviation

adj.

Adjective.

AFAICT

As far as I can tell.

AFAIK

As far as I know.

AFAIR

As far as I remember.

AFAIU

As far as I understand.

AFK

Away from keyboard.

AKA, a.k.a.

Also known as.

AMA

Ask me anything.

ASAP

As soon as possible.

ATM

  • At the moment.
  • Automatic teller machine [US] (banking machine).

B2B

Business to business.

B2C

Business to customer.

BBIAB

Be back in a bit.

BDFL

Benevolent dictator for life.

BF

Boyfriend.

BFF

Best friends forever.

BFN

Bye for now.

BRB

Be right back.

BTW

By the way.

CL

Common Lisp.

CLI

Command line interface.

D/L

Download.

DX

Developer experience.

e.g.

For example (exempli gratia).

EOF

End of file.

EOL

End of line.

ETA

Estimated time of arrival.

etc

And the rest (et cetera).

FAQ

Frequently asked questions.

FML

Fuck my life.

FTW

For the win.

FU

Fuck you.

FUD

Fear, uncertainty, doubt — spread of misinformation.

FWIW

For what it’s worth.

FYI

For your information.

GF

Girlfriend.

GTFO

Get the fuck out.

i.e.

That is (id est).

IANAL

I am not a lawyer.

IDE

Integrated development environment.

IDK

I don’t know.

IIRC

If I recall correctly.

IIUC

If I understand correctly.

IMAO

In my arrogant opinion.

IME

In my experience.

IMHO

In my humble opinion.

IMNSHO

In my not so humble opinion.

IMO

  • In my opinion.
  • International Mathematical Olympiad.

IOW

In other words.

KISS

Keep it simple, stupid! — US Navy, 1960s

leet, 1337

Elite.

LHS

Left-hand side.

LMAO

Laughing my ass off.

LMK

Let me know.

LMS

Learning management system.

LOL

Laughing out loud.

LTR

Left-to-right (e.g. English).

MTA

Mail transfer agent.

MUA

Mail user agent.

n.b.

Note well (nota bene).

NBD

No big deal.

NIH

Not invented here [syndrome].

OBTF

One Big Text File

OMG

Oh my god.

OOTB

Out of the box.

OSD

On-screen display.

OTOH

On the other hand.

P2P

Peer-to-peer.

PC

Personal computer.

PITA

Pain in the ass.

pl.

Plural.

POV

Point of view.

QOL

Quality of life.

RAM

Random access [read-write] memory.

RHS

Right-hand side.

ROFL

Rolling on the floor laughing.

ROM

Read-only memory.

RTFM

Read the fucking manual.

RTL

Right-to-left (e.g. Arabic).

SNAFU

Situation normal, all fucked up.

SOTA

State of the art.

TBD

To be decided.

TBH

To be honest.

THX

Thanks.

TIA

Thanks in advance.

TIL

Today I learned.

TLDR, TL;DR

Too long, didn’t read.

TOC

Table of contents.

TRT

The right thing.

TTYL

Talk to you later.

TTYT

Talk to you tomorrow.

TV

Television.

TY

Thank you.

TYVM

Thank you very much.

UI

User interface.

UX

  • User experience.
  • Unix (not used much anymore).

w.r.t.

With regard to.

WDYT?

What do you think?

WIP

Work in progress.

WRT

With respect to.

WTF

What the fuck.

WYSIWYG

What you see is what you get (read: wiziwig).

YMMV

Your mileage may vary.

YOLO

You live only once.

NP

Prefixes

Endo-

“within”

As in endosymbiosis or in endomembrane system.

[1, p. 108]

Prepositions

à la

“in the style or manner of” (à la)

A french preposition.

Heimlich maneuver

A first-aid treatment of choking.

Preparation

Before the maneuver, try the back slaps:

  1. Stand behind the person.
  2. Put your hand on their chest.
  3. Bend them forward as much as possible.
  4. With your other hand, firmly slap their back.
  5. Repeat five times.

Procedure

Do not proceed if the person can cough or talk.

  1. Stand behind the person.
  2. Wrap your arms around their waist.
  3. Place your fist, thumb side in:
    • below their rib cage
    • above their belly button.
  4. Wrap your other hand around the fist.
  5. Pull inward and upward, firmly and quickly.
  6. Repeat five times.

To perform the Heimlich on yourself, perform the steps 3+.

Writing

Writing

Serialization of thought.

As aid to thinking

"If you're thinking without writing, you only think you're thinking."

--- Leslie Lamport

As time machine

"One glance at a book and you're inside the mind of another person,
maybe somebody dead for thousands of years."

--- Carl Sagan, Cosmos, 1980

Topic sentence

The opening sentence of a paragraph that states its main claim.

Purpose

Tells the reader what the paragraph argues before presenting evidence.

Structure

Claim first, then supporting details from most to least important.

In Results sections

Especially useful in scientific writing, where Results sections should document findings from most to least important.

References

[1]
M. A. Clark, J. Choi, and M. Douglas, Biology, 2nd ed. OpenStax, Rice University, 2020. Available: https://openstax.org/details/books/biology-2e
[2]
J. S. Pearson, “Writing in the social and physical sciences,” in Contribute a verse: A guide to first year composition, T. L. Bennett, D. Gessell, and D. Edelman-Young, Eds., University of North Georgia Press, 2015, ch. 10, pp. 146–165. Available: https://ung.edu/university-press/books/introduction-to-first-year-composition/
[3]
B. Peirce, “On quantity, with special reference to collectional and mathematical infinity,” 1895.
[4]
G. Galilei, The assayer. Lynx, 1623.
[5]
G. Hartman, T. Siemers, B. Heinold, and D. Chalishajar, APEX calculus, Version 4.0. Self-published, 2018. Available: http://www.apexcalculus.com
[6]
R. H. Hammack, Book of proof, 3.4. Richard Hammack, 2018. Available: https://richardhammack.github.io/BookOfProof/
[7]
O. Levin, Discrete mathematics: An open introduction, 3th Edition, 5th Printing. Self-published, 2021. Available: http://discrete.openmathbooks.org
[8]
C. Stitz and J. Zeager, Essential precalculus. 2013. Available: https://opentext.uleth.ca
[9]
G. Hartman, Fundamentals of matrix algebra, 3rd ed. Self-published, 2011. Available: http://www.apexcalculus.com/other-texts
[10]
B. Yakir, Introduction to statistical thinking. The Hebrew University of Jerusalem; Self-published, 2011. Available: http://pluto.huji.ac.il/~msby/StatThink
[11]
S. Axler, Linear algebra done right, 4th ed. Springer, 2024. Available: https://linear.axler.net/
[12]
D. L. Poole and A. K. Mackworth, Artificial intelligence: Foundations of computational agents, 2nd ed. University of British Columbia; Cambridge University Press, 2017. Available: https://artint.info/
[13]
J. W. Cutrone, “Linear algebra from elementary to advanced specialization,” Johns Hopkins University; Coursera. Available: https://www.coursera.org/specializations/linear-algebra-elementary-to-advanced
[14]
E. W. Weisstein, “Wolfram MathWorld,” Wolfram Research. Available: https://mathworld.wolfram.com/
[15]
J. Erickson, Algorithms. Self-published, 2019. Available: https://jeffe.cs.illinois.edu/teaching/algorithms/
[16]
S. A. Cook, “The complexity of theorem-proving procedures,” in Proceedings of the third annual ACM symposium on theory of computing, in Stoc’71. Shaker Heights, Ohio, USA: Association for Computing Machinery, 1971, pp. 151–158. doi: 10.1145/800157.805047.
[17]
R. M. Karp, “Reducibility among combinatorial problems,” in Symposium on the complexity of computer computations, 1972. doi: 10.1007/978-1-4684-2001-2_9.
[18]
H. H. Hoos and T. Stützle, “SATLIB: The satisfiability library,” 2011, Darmstadt University of Technology. Available: https://www.cs.ubc.ca/~hoos/SATLIB/
[19]
B. Selman, H. Levesque, and D. Mitchell, “A new method for solving hard satisfiability problems,” in Proceedings of the tenth national conference on artificial intelligence, in Aaai’92. San Jose, California: AAAI Press, 1992, pp. 440–446. Available: https://dl.acm.org/doi/10.5555/1867135.1867203
[20]
I. P. Gent and T. Walsh, “Towards an understanding of hill-climbing procedures for SAT,” in Proceedings of the eleventh national conference on artificial intelligence, in Aaai’93. Washington, D.C.: AAAI Press, 1993, pp. 28–33. Available: https://dl.acm.org/doi/10.5555/1867270.1867275
[21]
B. Selman, H. A. Kautz, and B. Cohen, “Noise strategies for improving local search,” in Proceedings of the twelfth aaai national conference on artificial intelligence, in Aaai’94. Seattle, Washington: AAAI Press, 1994, pp. 337–343. Available: https://dl.acm.org/doi/10.5555/2891730.2891782
[22]
B. Selman and H. Kautz, “Domain-independent extensions to GSAT: Solving large structured satisfiability problems,” in Proceedings of the 13th international joint conference on artifical intelligence - volume 1, in Ijcai’93. Chambery, France: Morgan Kaufmann Publishers Inc., 1993, pp. 290–295.
[23]
B. Mazure, L. Saïs, and É. Grégoire, “Tabu search for SAT,” in Proceedings of the fourteenth national conference on artificial intelligence and ninth conference on innovative applications of artificial intelligence, in Aaai’97/iaai’97. Providence, Rhode Island: AAAI Press, 1997, pp. 281–285. Available: https://cdn.aaai.org/AAAI/1997/AAAI97-044.pdf
[24]
G. Reinelt, “TSPLIB: A library of sample instances for the TSP (and related problems).” Available: http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/
[25]
R. Stallman and contributors, “What is free software?,” Free Software Foundation. Available: https://www.gnu.org/philosophy/free-sw.en.html
[26]
R. Stallman and contributors, “GNU Emacs,” Free Software Foundation. Available: https://www.emacs.org/
[27]
R. Stallman and contributors, GNU Emacs manuals online. Free Software Foundation. Available: https://www.gnu.org/software/emacs/manual/
[28]
P. Stavrou, Available: https://protesilaos.com/
[29]
D. Wilson, “System crafters.” Available: https://systemcrafters.net/
NO_ITEM_DATA:stallman+2022
[31]
D. E. Knuth, “Literate programming,” The computer journal, vol. 27, no. 2, pp. 97–111, 1984, doi: 10.1093/comjnl/27.2.97.
[32]
P. Bruegel, “The tower of Babel,” 1563.
[33]
B. D’Arcus and contributors, “Citar.” Available: https://github.com/bdarcus/citar
[34]
B. D’Arcus et al., “Citation style language.” Accessed: Nov. 17, 2021. [Online]. Available: https://citationstyles.org
[35]
M. Vollmer et al., “It’s Magit! A Git porcelain inside Emacs.” Available: https://magit.vc/
[36]
J. Bernoulli, “Emacsair: Collecting frequent-committer miles.” Available: https://emacsair.me/
[37]
J. A. Donenfeld and contributors, “pass: The standard Unix password manager.” Available: https://www.passwordstore.org/
[38]
S. Sorensen, T. Calancha, and contributors, “Emacs password-store.” Available: https://git.zx2c4.com/password-store/tree/contrib/emacs
[39]
Z. Evgeny, “Telega manual.” Available: https://zevlg.github.io/telega.el/
[40]
“Telegram database library.” Available: https://core.telegram.org/tdlib

Footnotes:

1

As of 2025, my main client is Egghead Games.

2

N.B. This definition encompasses mathematics.

3

As defined in The Magic of Reality by Richard Dawkins.

4

Sometimes folded into the Results section.

5

Sometimes folded into the Discussion section.

6

Also called repeated or iterated conjunction.

7

Also called repeated or iterated disjunction.

8

Simply put, a Boolean-valued function \(f : D \to \mathbb{B}\).

9

Also called repeated or iterated addition.

10

Also called repeated or iterated multiplication.

11

Also denoted with the square brackets,

\begin{equation*} \vec{v} = \begin{bmatrix} v_1 \\ \vdots \\ v_n \end{bmatrix}. \end{equation*}
12

Also denoted

  • with square brackets instead of parentheses, \(A = \begin{bmatrix} \vphantom{\big(} \ldots \end{bmatrix}\), or
  • with comma-separated indices, \(a_{i, j}\).
13

The definition is self-referential on purpose.

14

For all \(p\), \(g(p)\) is tested after adding \(p\) to \(F\) to

  • enable informed variants take into account cost and
  • avoid evaluating computationally expensive \(g\) unnecessarily.
15

Serves as the basis for the implementations of BFS and DFS, which see.

16

No “depth”, to maintain the graph-theoretical perspective of generic search.

17

In practice, often improved backtracking.

18

Based on the implementation of the generic search, which see.

19

Discovered after defining DFS and BFS symmetrically with “some” and “all”.

20

No “depth”, to maintain the graph-theoretical perspective of generic search.

21

Discovered after defining DFS and BFS symmetrically with “some” and “all”.

22

Based on the implementation of the generic search, which see.

23

This makes the definition of time complexity recursive.

24

Per the Cook-Levin theorem, where Levin co-discovered NP-completeness in Soviet Russia.

25

Even enumerated, in optimal solution enumeration.

26

Called a tour.

27

Another notable method is Dantzig–Fulkerson–Johnson (DFJ).

28

Free as in “free speech”, not “free beer”.

29

We cannot use identity because the final / is needed for C-a o.

30

Some parts of Emacs have their own bug trackers; notably the Org mode provides the org-submit-bug-report command.

31

Emacs uses Debbugs from the Debian project.

32

For example, to insert a line break literally, type ~ C-q C-j.

33

If repeated, appends to the last kill. To repeat automatically N times, prefix with C-u /N/.

34

To deactivate the mark with no action, type C-g.

35

For paragraphs, use M-q that respects sentence-end-double-space.

36

Unfill with M-x unfill-paragraph.

37

Disabled by default, so we enable them:

(put #'upcase-region 'disabled nil)
(put #'downcase-region 'disabled nil)
38

Prefix with C-u [0-9]+ or C-[0-9]+ to shrink/enlarge by [0-9]+ lines.

Related commands:

  • window-swap-states swap current window with following window
39

These are hidden in various commands.

40

The search uses Isearch.

41

To cancel recording, type C-g.

42

The bell rings, for example, upon reaching EOF.

43

If the first line is a shebang, the second line is scanned.

45

Milkypostman’s Emacs Lisp Package Archive

46

The “i” in the name stands for “incremental”, not interactive.

47

Thing at point is available with M-n in the minibuffer.

48

RET or any non-Isearch key.

49

Also removes flags, e.g. C for “just copied in”.

50

If needed, the current directory is available in the future history via M-n.

51

With visible-bell set, it becomes visual feedback.

52

The alternative key bindings are

  • M-s h .
  • M-s h r
  • M-s h p
  • M-s h u

respectively.

53

A patch is planned to add support for extra indicators.

54

In Vim, this is easily done by setting colorcolumn=72,79.

55

To see standard output and error output, e.g. NSLog, click “+ Instrument” and pick “stdout/stderr”.

56

'magit-extras can do this but with hard-coded C-x p m.

57

M-w ignores calc-kill-line-numbering.

58

Summing the elements is equal to \(1^\mathsf{T} \vec{x}\).

59

Applies to M-k and C-k, but not M-w.

60

Shared with others on GitHub.

61

We keep the basic style as a backup because some commands need it to work correctly.

62

We do not use package.el because we want the Emacs frontend to match the notmuch binary (that comes with it anyway).

63

Also, the logo does not play well with dark themes, as of Notmuch 0.39.

64

To toggle, use Notmuch Show tagging.

65

The new motion keys correspond to the original motion keys N and P.

66

See man notmuch-insert.

67

“Is empty” as per the notmuch-mua-subject-regexp.

68

“Mentions attachments” as per the notmuch-mua-attachment-regexp.

69

Shown is a typical two-parent (a.k.a. two-way) merge, but octopus merges with N > 2 parents follow the SHA^N pattern.

70

Perhaps on a temporary branch.

71

See also magit-status-here.

72

Why is this not default?

73

More specifically, org-edit-src-code called by org-edit-special.

74

Before, I tried and failed with

  • built-in narrowing, which breaks indentation and more,
  • built-in indirect buffers, which do not support fontification.
75

I do not understand why this helps, as

  • telega-emoji-use-images is nil (default)
  • telega-symbols-emojify says

“List of symbols to emojify if telega-emoji-use-images is non-nil.”

76

With no region, RET copies the line at point.

77

105 is the maximum, as per SB_MAX in vterm-module.h.

78

Due to its small size, Lua does not use POSIX regular expressions.

79

See utf8.charpattern for UTF-8 characters.

80

The parent column must be PRIMARY KEY, or “foreign key mismatch” error is shown.

81

~/.config/matplotlib/ is not searched on MacOS; the file must be in:

import matplotlib as mpl
mpl.get_configdir()
/Users/rudy/.matplotlib
82

Non-continuous functions can be approximated with continuous ones.

83

Features for all examples in the corresponding target matrix.

84

Targets for all examples in the corresponding input matrix.

85

For the input layer \(i = 1\), \(n\) is the number of features in the training data set.

86

\(w^{[i]}_{j}\) is a row vector.

87

Ignoring e.g. floating point precision loss.

88

To serve manually, execute ollama serve.

89

Executes ollama pull $model first, if needed.

Author: Rudolf Adamkovič

Created: 2026-08-01 Sat 02:39