Rudolf Adamkovič
Personal Website
Welcome
Welcome to my website! ☺
About me
| Name | Rudolf Adamkovič |
| Nick | Rudy |
| Web | https://adamkovic.org |
| 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
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
My notes
Science
- Science
- Falsifiable
- Formal science
- Formal
- Empirical science
- Empirical
- Scientific method
- Hypothesis
- Scientific fact
- Scientific theory
- Theorem
- Objective reality
- Induction
- Deduction
- Experimental variable
- Experimental group
- Control group
- Serendipity
- Basic science
- Applied science
- Natural science
- Life science
- Physical science
- Social science
- Behavioral science
- Order of magnitude
- Metric prefix
- Nobel prize
- Research paper
- Title
- Abstract
- Introduction
- Research aim
- Research question (RQ)
- Research objective
- Methodology
- Results
- Discussion
- Conclusion
- Poster
Science
The study of the falsifiable. 2
By proof/evidence
By goal
By understanding
- “hard” or “real” science
- behavioral science
Falsifiable
That which is
testable either empirically or formally.
Formal
That which
can be proved absolutely
from given axioms.
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
Scientific theory
interrelated scientific facts.
Objective reality
Everything that is. 3
Induction
Reasoning
from the particular to the general.
Terminology
Not to be confused with mathematical induction.
Opposite
See deductive reasoning.
Example
Observing pea plants to discover Mendelian inheritance.
Deduction
Reasoning
from the general to the particular.
Example
Predicting phenotypes of pea plants using Mendelian inheritance.
Opposite
See inductive 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
See applied science.
Applied science
A science that
pursues knowledge to solve real-world problems.
Opposite
See basic science.
Natural science
An empirical science concerned with
natural phenomena.
Kinds
Also cross-disciplines, e.g. environmental science.
Life science
See biology.
Social science
- history
- economics
Behavioral science
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\)
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.
Other fields
Research paper
The standard format for communicating scientific findings [2].
Structure
Each paper has a title and the sections
- Abstract
- Introduction (with Research aim and Research questions)
- Methodology
- Results and Discussion
- 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
See research objectives.
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].
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.
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.
Intuition
Nobel prize of mathematics, like the Turing award in computer science.
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
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
- Propositional logic
- Statement
- Propositional variable
- Tautology
- Atomic statement
- Molecular statement
- Truth table
- Logical connective
- Negation
- Conjunction
- Disjunction
- Law of non-contradiction (LNC)
- Law of excluded middle (LEM)
- De Morgan’s laws
- Conditional
- Antecedent
- Consequent
- Contrapositive
- Converse
- Biconditional
- Exclusive disjunction
- Mutual exclusivity
- Argument
- Premise
- Conclusion
- Rule of inference
- Modus ponens
- Fallacy
- Predicate logic
- Predicate
- Quantifier
- Universal quantifier
- Existential quantifier
- Unique existential quantifier
Logic
A branch of discrete mathematics concerned with
formal reasoning.
Orders
| Order | Name | Focus |
|---|---|---|
| 0th | propositional logic | statements and arguments |
| 1st | predicate logic | predicates and quantifiers |
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.
Statement
A declaration that is
either true or false,
so Boolean-valued.
By the number of logical connectives, either
[7, Sec. 0.2], [12, Sec. 5.1.1]
Referenced by a propositional variable.
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
FALSEto 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.
Molecular statement
A statement with
at least one logical connective.
The opposite of the atomic statement, conceptually.
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
- Implement
- 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
- Implement
- Function
scheme/org-truth-table
Generate a truth table for presentation in Org documents.
- Implement
Builds on
scheme/truth-table.Note.
HEADERSare 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
- Implement
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\) |
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\).
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.
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\}\).
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.
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\}\).
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.
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.
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.
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 |
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\).
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.
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*}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*}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*}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.
Conjunctive normal form
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\)”, |
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.
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. |
Premise
other than the conclusion \(Q\).
Conclusion
Rule of inference
A structure of a valid argument, that is one
whose conclusion is true
if all of its premises are true.
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.
Predicate logic
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
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.
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.
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.
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:
- a special set with a standard definition,
- a set derived from another using set operations,
- a set defined in-place, typically by enumeration or set builder notation.
Cardinality
Notation
The cardinality of any given set \(S\) is denoted
\begin{equation*} |S|. \end{equation*}Example
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.
Disjoint sets
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
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,
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*}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 (the default)
- directed graph
- weighted graph.
Undirected graph
Directed graph
A graph \(G = (V, E)\) whose
set of edges \(E\) comprises ordered pairs of vertices from \(V\).
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
- Linear equation
- Linear system
- Solution set
- Solution
- Consistent system
- Inconsistent system
- Equivalent system
- Field
- Scalar
- Addition
- Multiplication
- Vector space
- Vector
- Zero vector
- Unit vector
- Vector addition
- Vector subtraction
- Vector scaling
- Vector length
- Vector equation
- Dot product
- Outer product
- Euclidean distance
- Matrix
- Lower triangular matrix
- Upper triangular matrix
- Triangular matrix
- Diagonal matrix
- Matrix equality
- Matrix addition
- Matrix scaling
- Matrix-vector product
- Matrix multiplication
- Matrix equation
- Homogeneous equation
- Root
- Trivial root
- Non-trivial root
- Identity matrix
- Real space
- Subspace
- Zero subspace
- Trivial subspace
- Real line
- Real plane
- Real three-dimensional space
- Origin
- Basis
- Standard basis
- Standard vector
- Dimension
- Coefficient matrix
- Augmented matrix
- Row echelon form
- Reduced row echelon form
- Elementary row operation
- Row reduction
- Row equivalence
- Leading entry
- Leading one
- Pivot column
- Pivot position
Linear algebra
A branch of mathematics concerned with
References
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
Homogeneity
Every linear system is
either homogeneous or not.
Solution set
The set of all variable assignments that satisfy a linear system.
Solution
A member of the solution set.
Consistent system
with at least one solution.
Opposite
The consistent system is the opposite of the inconsistent system.
Inconsistent 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.
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
commutes, so
\(a + b = b + a\)
associates, so
\((a + b) + c = a + (b + c)\),
has the exactly one additive identity: \(0\), so
\(a + 0 = a\),
has the exactly one additive inverse: \(-a\), so
\(a + (-a) = 0\).
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\}\).
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
commutes, so
\(ab = ba\),
associates, so
\((ab)c = a(bc)\),
distributes over addition, so
\(c(a + b) = ca + cb\),
has the exactly one multiplicative identity: \(1\), so
\(1a = a\),
has the exactly one multiplicative inverse: \(1 / a\), so
\(\frac{1}{a} a = a^{-1} = 1\).
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\}\).
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.
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. |
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*}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. |
Unit vector
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:
- Draw \(\vec{u}\) and \(\vec{v}\) head to tail.
- Draw \(\vec{u} + \vec{v}\) from the free tail to the free head.
Properties
For all vectors \(\{\vec{u}, \vec{v}, \vec{w}\} \subseteq V\), it
commutes, so
\(\vec{u} + \vec{v} = \vec{v} + \vec{u}\),
associates, so
\((\vec{u} + \vec{v}) + \vec{w} = \vec{u} + (\vec{v} + \vec{w})\),
has exactly one additive identity: the zero vector \(\vec{0}\), so
\(\vec{v} + \vec{0} = \vec{v}\),
has exactly one additive inverse: \(-\vec{v}\), so
\(\vec{v} + (-\vec{v}) = \vec{0}\).
Implement
… in Scheme as
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.
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:
commutes, so
\(a \vec{v} = \vec{v} a\);
associates, so
\((a b) \vec{v} = a (b \vec{v})\);
distributes over vector addition, so
\(a(\vec{u} + \vec{v}) = a \vec{u} + a \vec{v}\); and
has exactly one multiplicative identity: \(1\), so
\(1 \vec{v} = \vec{v}\).
Implement
In Scheme:
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*}Intuition
In the real plane \(\mathbb{R}^2\):
The correspondence with Pythagorean theorem:
- Recall that \(c^2 = a^2 + b^2\) and so \(c = \sqrt{a^2 + b^2}\).
- Set \(a = v_1\), \(b = v_2\), and \(c = \|\vec{v}\|\).
- 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}\).
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:
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*}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
Euclidean distance
The length of the vector difference
\begin{equation*} \| \vec{u} - \vec{v} \|. \end{equation*}Intuit
In the real plane \(\mathbb{R}^2\):
- Draw \(\vec{u}, \vec{v}\) as points \(P\) and \(Q\).
- 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
- by vector,
- by vector length,
- by dot product,
- by exponentiation, and
- 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
- by vector,
- by vector length,
- by the dot product in terms of the matrix transpose,
- by the matrix transpose,
- by matrix multiplication, and
- by exponentiation.
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-lengthandmy-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
A triangular matrix that is both lower triangular and upper triangular.
Matrix equality
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
commutes, so
\(A + B = B + A\),
associates, so
\((A + B) + C = A + (B + C)\),
has exactly one additive identity: the zero matrix, so
\(A + 0_{m \times n} = A\),
has exactly one additive inverse: \(-A\), so
\(A + (-A) = 0_{m \times n}\).
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. |
Properties
For all matrices \(A, B\) and scalars \(c, d\), it
commutes, so
\(c A = A c\),
associates, so
\(c (d A) = (c d) A\),
distributes over matrix addition, so
\(c (A + B) = c A + c B\), and
has exactly one multiplicative identity: \(1\), so
\(1 A = A\).
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
Properties
For all matrices \(A, B, C\), vectors \(\vec{u}, \vec{v}\), and scalars \(c\), it
associates, so
\((A B) C = A (B C)\),
associates with respect to matrix scaling and vector scaling, so
\(c ( A B ) = ( c A ) B = A ( c B )\) \(\qand\) \(c ( A \vec{u} ) = ( c A ) \vec{u} = A ( c \vec{u} )\),
left-distributes over matrix addition and vector addition, so
\(A ( B + C ) = A B + A C\) \(\qand\) \(A ( \vec{u} + \vec{v} ) = A \vec{u} + A \vec{v}\)
and its multiplicative identity is the identity matrix, so
\(I A = A I = A\) \(\qand\) \(I \vec{u} = \vec{u}\).
Anti-properties
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]]
Matrix equation
A linear system written as a matrix multiplication,
\begin{equation*} A \vec{x} = \vec{b}. \end{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*}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*}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*}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*}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*}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*}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*}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*}Trivial root
The zero vector as the root of the homogeneous equation.
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
The identity matrix is
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.
Examples
The simplest real spaces are
- the real line \(\mathbb{R}\)
- the real plane \(\mathbb{R}^2\)
- the real three-dimensional space \(\mathbb{R}^3\)
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*}
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
Real plane
The real space, denoted
\begin{equation*} \mathbb{R}^2, \end{equation*}and read “R two”, containing
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
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
A linearly independent 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
A member of the standard basis.
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
- each non-zero row is above all zero rows
- each leading entry is to the right of all leading entries above it
- each entry in the column below the leading entry is zero,
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
- each leading entry is 1
- each entry in the column above the leading one is 0.
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.
- Write an augmented matrix \(M\) for the system.
- Move all zero rows to the bottom.
- For each row \(R\), from top to bottom:
- Scale \(R\) so that its leading entry is 1.
- For each row \(R'\) below \(R\):
- Replace \(R'\) so that the column below the leading one in \(R\) is 0.
Phase 2. Towards reduced row echelon form.
- Is the last column is a pivot column?
- If so, abort execution, for the system is inconsistent.
- For each row \(R\), bottom to top:
- For each row \(R'\) above \(R\):
- Replace \(R'\) so that the column above the leading one in \(R\) is 0.
- For each row \(R'\) above \(R\):
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.
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*}Move all zero rows.
Nothing to do.
Enumerating rows, top to bottom:
For \(M_1\):
Scale the selected row.
Nothing to do.
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\):
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*}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\):
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*}Replace the rows below the selected row.
Nothing to do.
Phase 2.
Is the last column is a pivot column?
The system is consistent.
Enumerating rows, bottom to top:
For \(M_3\):
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\):
- 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\):
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 one
The leading entry of a non-zero row of a matrix in reduced row echelon form.
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
of \(A\).
Probability theory
Probability theory
A branch of mathematics
concerned with probability,
Foundations
Probability theory is built on the probability axioms.
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
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
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”. |
Probability axioms
The facts that the probability
of an event is a non-negative real number,
\[ \forall A, \> \Pr(A) \geq 0 \]
of the sample space is the unit,
\[ \Pr(\Omega) = 1 \]
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).\]
History
Introduced by Russian mathematician Andrey Kolmogorov in the 1930s.
Descriptive statistics
- Descriptive statistics
- Individual
- Variable
- Observation
- Normalization
- Population
- Parameter
- Population size
- Sample
- Sample size
- Statistic
- Robust statistic
- Central tendency
- Population mean
- Sample mean
- Law of large numbers
- Sampling distribution
- Sampling distribution of the sample mean
- Central limit theorem
- Frequency (distribution)
- Relative frequency (distribution)
Individual
An object distinct from any other object.
Variable
A function of an individual.
Population
A partially observable set of individuals.
Intuition
The set whose parameters are estimated with statistics.
Population size
A parameter representing
the cardinality of a population.
Notation
Often denoted \(n\) for the “number” of members.
Sample size
The statistic representing
the cardinality of a sample.
Notation
Commonly denoted
- \(n\) for the “number” of members
- \(|X|\) given the sample \(X\) using set cardinality notation
Robust statistic
Examples
- robust: sample median
- not robust: sample mean
- robust: interquartile range
- not robust: sample standard deviation
Central tendency
A measure of
the center of a distribution.
Intuition
Represents a kind of “typical value”.
Examples
- the sample mean
- the population median
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
The sample mean is not a robust statistic.
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
Random variables
Drawing multiple samples from a population
makes the statistic into a random variable.
Example
Pertaining to the central limit theorem,
Sampling distribution of the sample mean
A random variable representing
the 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.
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
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)
Variants
Relative frequency (distribution)
A statistic representing
absolute frequency normalized to the sample size.
Significance
Relative frequency estimates probability and
connects descriptive statistics to probability theory
under frequentist interpretation.
Computer science
- Computer science (CS)
- Decision problem
- Divide and conquer
- Recursion
- Base case
- Recursive case
- Merge sort
- Master theorem
- Generic search
- Depth-first search (DFS)
- Breadth-first search (BFS)
- Backtracking
- Pure function
- Memoization
- Dynamic programming
- Computational complexity
- Time complexity
- Elementary operation
- Space complexity
- Asymptotic complexity
- Big O
- Order of growth
- Hardness
- Complexity class
- P
- NP
- NP-complete
- NP-hard
- Graph coloring (COL)
- 3-coloring (3COL)
- Boolean satisfiability problem (SAT)
- Conjunctive normal form (CNF)
- 3-satisfiability (3SAT)
- SATLIB
- GSAT
- HSAT
- GWSAT
- TSAT
- WalkSAT (WSAT)
- Unsat
- Flip
- Optimization problem
- Maximization problem
- Minimization problem
- Optimal solution
- Optimal solution enumeration
- Set cover
- Set packing
- Zebra puzzle
- Traveling salesman problem (TSP)
- N-queens
- SAT solver
- Standard linear form
- Conversions to the standard linear form
- Linear constraint
- Big-M trick
- Unordered remove
- Turing award
- FLOPS
- Floating-point number (FP) – Generalize the base + small binary/decimal notes
- Brain float
- Significand
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.
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
- one or more base cases
- one or more recursive cases.
Base case
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
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))))) - 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
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
are:
- start with the frontier \(F = \{(v_1)\}\) and seen vertices \(S = \{v_1\}\)
- if \(|F| = 0\), return empty-handed (\(\bot\))
- remove some path \(p = (v_1, \ldots, v_k)\) from \(F\)
- when the goal predicate \(g(p)\) is true 14, return \(p\) as the solution
- select \(N = \{(v_1, v_2, \ldots, v_k, v^*) : v^* \notin S \land (v_k, v^*) \in E\}\)
- for each \(n \in N\), insert \(n\) to \(F\)
- for each \((v_1, v_2, \ldots, v_k, v^*) \in N\), insert \(v^*\) to \(S\)
- go to step 2.
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.
(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
Depth-first search is analogous to the existential quantifier 19.
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
- \(|V|\) is the cardinality of the vertex set and
- \(|E|\) is the cardinality of the edge set.
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
Breadth-first search is analogous to the universal quantifier 21.
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.
(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
- memoization
- parallelization
- composition
- testability
Relation to mathematics
Pure functions are equivalent to mathematical function.
Memoization
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
Computational complexity
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'\)
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.
NP
The complexity class of decision problems whose
all solutions are verifiable in polynomial time.
NP-complete
A complexity class of problems that are
Symbolic definition
Proofs
To prove a problem \(L\) is NP-complete,
reduce a known NP-complete problem to \(L\).
in polynomial time.
Discovery
NP-hard
Confusion
NP-hard is not a subset of NP, so its problems are
- not necessarily decision problems
- not necessarily verifiable in polynomial time.
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
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.
- Add three interconnected vertices, colored 1, 2, and 3.
- For each variable, add two uncolored vertices connected to 2.
- 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
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
Check
… that Or gadget implements the 3-way disjunction
\begin{equation*} \ell_1 \lor \ell_2 \lor \ell_3. \end{equation*}The Or gadget:
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.
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)
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\)
- Proof
- 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\)
- Proof
- 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 1A: \(\ell_1 = 1\) or \(\ell_2 = 1\)
- 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\)
- Case 2A: \(y_1 = 0\)
- Case 1: \(c\) is satisfiable
- Proof
SATLIB
Citation
@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.
Name
A short for “greedy SAT”.
Algorithm
- Start with a random variable assignment.
- If the formula is satisfied, return the current assignment.
- Flip a variable that maximizes the number of satisfied clauses. †
- If more flips are allowed, go to step 2.
- If more tries are allowed, go to step 1.
- Return a failure.
† Ties are broken randomly.
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.
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.
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
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.
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
Example use
- “# of unsat clauses”
- “a clause goes unsat”
Optimization problem
A computational problem where
solutions are assigned objective values
and optimal solutions are sought25, either as
Maximization problem
Minimization problem
Optimal solution
A solution whose objective value in
- minimal in minimization problems
- maximal in maximization problems.
Optimal solution enumeration
A classical way to
compute all optimal solutions to an optimization problem
by re-parameterizing it as a constraint satisfaction problem:
- find an optimal solution \(y^* = \max y\)
- remove the objective function \(y\)
- add the constraint \(y = y^*\)
- instruct the solver to print all solutions.
Set cover
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
Computation
Assign all parts to suppliers, the fewer the better.
| 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) A1 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
Set packing
Intuition
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
Computation
Assign as many tasks as possible, at most one 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) A1 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
Zebra puzzle
There are five houses.
- The Englishman lives in the red house.
- The Spaniard owns the dog.
- Coffee is drunk in the green house.
- The Ukrainian drinks tea.
- The green house is immediately to the right of the ivory house.
- The Old Gold smoker owns snails.
- Kools are smoked in the yellow house.
- Milk is drunk in the middle house.
- The Norwegian lives in the first house.
- The man who smokes Chesterfields lives in the house next to the man with the fox.
- Kools are smoked in the house next to the house where the horse is kept.
- The Lucky Strike smoker drinks orange juice.
- The Japanese smokes Parliaments.
- 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
\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*}l, m, n = 5, 5, 5x = { (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
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()) - the left-most house (\(i = 1\)) cannot be green
- 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?
- \(i\)-th house such that \(x_{2, i, 5} = 1\)
- \(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 spNone
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 instanceNone
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_distancestraveling_salesman.distance
def distance(tour, distances): destinations = np.roll(tour, 1) distances_ = distances[tour, destinations] distance = np.sum(distances_) return distancetraveling_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, distance1traveling_salesman.solution
def solution(tour, instance): """Convert tour indices to city identifiers.""" cities = instance[:, 0] solution = cities[tour] return solutiontraveling_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:
- load the instance
pr76from TSPLIB [24] - measure the Euclidean distances between all cities
- 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 PR76from 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 - load the instance
- 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:
- load the instance
pr76from TSPLIB [24] - measure the Euclidean distances between all cities
- 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 PR76from 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 - load the instance
- 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*}- Indexing
dwithx[i]must be done withadd_element. - Indexing
dwithadd_elementrequires flattening ofd.
# Set the objective function. model.minimize(sum(t)) - Indexing
- 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:
- load the instance
pr76from TSPLIB [24] - measure the Euclidean distances between all cities
- 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 PR76from 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 - load the instance
- 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:
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*}Linearize the implication with the Big-M trick.
\begin{equation*} t_j \geq t_i + 1 - M \cdot (1 - x_{ij}) \end{equation*}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*}Find the smallest \(M\) that works for all \(t_i\) and \(t_j\).
\begin{equation*} M = n \end{equation*}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:
- load the instance
pr76from TSPLIB [24] - measure the Euclidean distances between all cities
- 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 PR76from 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 - load the instance
- 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:
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*}Replace the implication with a “Big-M constant”.
\begin{equation*} t_j \geq t_i + 1 - M \cdot (1 - x_{ij}) \end{equation*}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*}Find the smallest \(M\) that works for all \(t_i\) and \(t_j\).
\begin{equation*} M = n \end{equation*}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))\(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))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))
SAT solver
An algorithm that solves the Boolean satisfiability problem.
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
- depth-first search
- branching strategy
;;; 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
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
- \(\lambda\) is an indicator variable in the Boolean domain
- \(ax \leq b\) and \(ax \geq b\) are linear constraints
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:
- \(\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).
Brain float
A float used in ANNs.
Format
Like IEEE 754 binary16 but with
- \(E = 7\)-bit exponent \(e\)
- \(8\)-bit significand \(s\)
Freedom
Four essential freedoms
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}
}
Emacs
- Emacs
- Shell
- Global keymap
- TODO SPLIT SPLIT SPLIT Configuration
- Display time
- Frame
- Window
- Selected frame
- Selected window
- TODO Window configuration
- Winner mode
- Buffer
- Current buffer
- Echo area
- Minibuffer
- Minibuffer mode
- Completion list
- Keyboard macro
- Register
- Function
- Command
- Interactive
- Universal argument
- Variable
- Custom variable
- Point, mark, [active] region
- Mode line
- Hook
- Input method
- Kill ring (kill, yank)
- Major mode
- Minor mode
- Global minor mode
- Fundamental mode
- Text mode
- Programming mode
- C
- Lisp
- Column number
- Calendar
- Package
- Isearch
- Query replace
- Dired
- TRAMP
- Debugger
- Bell
- Save Hist
- Hi Lock
- Auto revert
- Fill
- Fill column
- Display fill column indicator
- Display display line numbers
- Hl Line
- Browse URL
- Eldoc
- Outline
- Show Paren
- Flymake
- Smerge
- Hl Todo
- SQLite
- SQL
- Imenu
- Eglot
- Abbrev
- Dabbrev
- Man
- Woman
- NS
- View
- Transient
- Bookmark
- Project
- Xref
.dir-localsfile- File-local variables
- Make
- Fennel
- Paredit
- BibTeX
- PlantUML
- Help mode
- Dictionary
- Calc
- SQLite
- World clock
- Comint
- Compilation
- Compilation Shell
- Diff
- Tab bar
- Electric pair
- Rcirc
- LaTeX
- Graphviz
- Face
- Theme
- Modus Themes
- Doric Themes
- Org
- Org Python
- Org Agenda
- Org Babel
- Org Publish
- Org Cite & Citar
- Org Invoice
- Fortune
- Minad Stack
- Vertico
- Corfu
- Marginalia
- Orderless
- Jinx
- TempEl
- Notmuch
- Notmuch Hello
- Notmuch Search
- Notmuch Show
- Notmuch Message
- Simple HTML renderer (SHR)
- Insidious Big Brother Database (BBDB)
- Avy
- Magit
- Password Store
- GPTel
- Mentor
- Edit Indirect
- Telega
- PDF Tools
- Other modes :)
- Fun modes
- AUCTeX
- Agent shell
- Image
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, andLC_ALLvariables 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 findingemacs,fortunenot findingstrfile,geiser-modenot findingguile,emacsnot findinggccfor native compilation, as well as, spuriouslocalewarnings.(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
~/.emacsto avoidorg-babel-merge-params: No attachment directory for element (add :ID: or :DIR: property)
(setopt org-attach-use-inheritance t) - Custom paths
Use
IDfor directory name 29, with no splitting magic.Tangled directly to
~/.emacsto avoidorg-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
loadthe 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-directorycannot 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 foundbrew reinstall libgccjit
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
References
- Official
[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/ - Community
- Packages
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
- Members
- Reporting bugs
- EmacsConf
- DONE EmacsConf 2025
- https://emacsconf.org/2025/
#emacsconfonirc.libera.chatmpv https://live0.emacsconf.org/gen.webmmpv https://live0.emacsconf.org/dev.webm
- DONE EmacsConf 2025
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
- TODO Review bug reports
List all the bugs from:
https://debbugs.gnu.org/cgi/pkgreport.cgi?which=submitter&data=salutis%40me.com
- DONE Update my contact information (bug)
- DONE Report
xwidget.ccompilation warnings (bug) - DONE Report broken
global-text-scale-adjuston macOS
- DONE Report compilation warnings with Clang
- DONE Make
repunctuate-sentencesoperate on the active region
- WAIT Scrolling aborts
repunctuate-sentences
- WAIT Fix
refill-mode“mangles” text
- DONE Fix
org-fill-paragraphnot filling multiple plain lists
A bug in Org but filled against Emacs.
- WAIT Wrong block header/footer background in Org
- WAIT Confusing behavior of
org-timer-set
- WAIT Emoji makes lines taller
- WAIT Report
mode-line-compactbreaking mouse interactions
- WAIT Emacs: Fix off-by-one highlighting
Patches
- TODO Write and contribute
fortune-modeto 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-pbefore 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
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-regionreverse the order of lines in regionsort-linessort lines in region or file; reverse withC-udelete-duplicate-linesdelete duplicate lines in region (no need to sort); keep blanks withC-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-sentencesfix end-of-sentence punctuation forward
Case and capitalization
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-sentencesM-x transpose-paragraphsM-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-blinksset 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
Termvariant 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/~/cas~/cand/a/b//cas/cin 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
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
Body and decorations
Left to right:
- left scroll bar
- left margin
- left fringe
- body
- right fringe
- right margin
- right scroll bar
Top to bottom:
- tab line
- header line
- body
- horizontal scroll bar
- mode line
- 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 framedefault-frame-alist
parameters applied to all newly created frames
Normal hooks
before-make-frame-hookafter-make-frame-hookafter-setting-font-hook
Abnormal hooks
after-make-frame-functions
called with the newly createdFRAME
Parameterization
Identification:
nametitle
Geometry:
lefttopwidthheightmin-widthmin-heightfullscreen
Decorations:
left-fringeright-fringeright-divider-widthbottom-divider-width
Content:
foreground-colorfontline-spacing
Background:
background-modebackground-coloralpha-background
Cursor:
cursor-colormouse-color
Misc:
alphaminibufferundecorateddisplay
NeXTSTEP (now “macOS”):
ns-appearancens-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
messagefunction, saved to the*Messages*buffer, andspecial purposes, such as by the
Minibuffer, Isearch, Vertico, Eldoc modes.
Related
messagefunction*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, callingyank-from-kill-ring) - using
query-replace(M-%) on minibuffer content.
(setopt enable-recursive-minibuffers t) (add-hook 'after-init-hook #'minibuffer-depth-indicate-mode) - yanking with selection/editing with
- 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-hookminibuffer-exit-hookminibuffer-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
Basic workflow
To record and execute a keyboard macro:
- Begin recording:
C-x ( - Type some keys 41
- End recording:
C-x ) - Execute:
C-x e [e ...]
Automatic repeating
To re-execute a keyboard macro:
- until the bell rings 42: type
C-0before executing N > 0times: typeC-Nbefore 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-xthat 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
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
...-hooktakes no arguments - abnormal hook: named
...-functionstakes 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):
texfor TeX mathematics,emojifor Emoji pictograms,slovakorslovak-quertyfor Slovak diacritics.
Use the TeX input method to enter non-ASCII characters, such as ellipsis (…)
written as \ldots in TeX.
| 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
Adding the “propline”
- Set the major mode, if needed.
- 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:
- First line43 is scanned for
-*- mode: NAME -*-string. - File name is matched against
auto-mode-alistin.dir-locals.el. - Shebang (
#!) is matched againstinterpreter-mode-alist. - Beginning of the buffer content is matched against
magic-mode-alist. - File name is matched against
auto-mode-alist. - Beginning of the buffer content is matched against
magic-fallback-mode-alist.
Symbols
NAME-modecommandmajor-modevariableNAME-mode-hookvariable
Global minor mode
A minor mode associated with more than one buffer.
Fundamental mode
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.
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
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)
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:
- Query string
- history: previous query-replacement pairs
- future history:
- the word at point, if any
- the last successful
isearchstring - previous query and replacement strings
- Replacement string
- history:
- the query string
- previous query and replacement strings
- future history:
- the query string
- history:
(Search history with C-r and C-s, and navigate it with M-p and M-n.)
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 stoggle sort by date prefix with C-uto sort by size* Nshow count and size numbers =diff file at point against… - Navigation
Key Description nnext item pprevious item ^parent directory - Marks
- Editing
Key Description Rrename or move Ccopy Ddelete Ysymlink via relative path Ssymlink via absolute path Mset mode (access bits) +create directory - Direct editing
- Opening and shelling out
Key Description RETopen file at point with Emacs Wopen file at point externally !execute shell command on file(s) &execute shell command on file(s) asynchronously - Search and replace
Key Description Qquery and replace in files - save changes after
QwithC-x sand! - to
grepa directory, usefind-grep-dired
- save changes after
- Compression
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
lscommand 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
Programmatic control
dired-get-marked-files
TRAMP
Performance
Prefer scp over ssh.
Debugger
A built-in major mode in Emacs used for
debugging Emacs Lisp code.
Entry points
toggle-debug-on-errordebug-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
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
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-functionandrevert-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-regionauto-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.
(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.”
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.
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.”
Limit all lines to a maximum of 79 characters.
(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.
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)
Hl Line
(add-hook 'after-init-hook #'global-hl-line-mode)
Browse URL
A typical call stack:
browse-url-at-pointis called (maybe)browse-urlis calledbrowse-url-handlersis consulted- if no match,
browse-url-default-handlersis consulted - if no match,
browse-url-browser-functionis 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
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-diagnosticsflymake-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
sqlite-mode-open-filecommand
SQL
A built-in major mode for
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))
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-reconnecteglot-shutdowneglot-shutdown-alleglot-events-buffereglot-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 "★"))
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))
Dabbrev
to match surrounding words …
M-/ |
dabbrev-expand |
expand word |
C-M-/ |
dabbrev-completion |
complete word |
Man
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.
| 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
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))
Agenda
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 - Local
Selected Dired key bindings defined in the
xref--xref-buffer-mode-map.Key Description Note <go to first line or M-<C-ngo to next line C-pgo to previous line >go to last line or M->pshow previous visible reference nshow next visible reference Pshow first visible reference in previous group Nshow first visible reference in next group TABvisit reference and quit RETvisit reference rquery-replace all matches grevert (refresh) buffer qquit Also see the Outline minor mode key bindings, notably
C-TABto see the overview.
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-MODEkey equalsnil, then thePAIRapplies to all major modes. - If the
VARIABLE-NAMEkey equalseval, then theVARIABLE-VALUEevaluates 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
- Author: Taylor R. Campbell
- Official website: http://paredit.org/
- Official sources: https://mumble.net/~campbell/emacs/paredit/
- Git mirror: https://github.com/paredit/paredit.el
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).
Configuration
- Installation
Install the package.
(with-eval-after-load 'package (add-to-list 'package-selected-packages 'paredit)) - Activation
Enable Paredit for
- all Lisp-based major modes
- Emacs Lisp expressions in
M-:(eval-expression).
What happens is:
submit-emacs-patchcallslm-maintainersonNEWSlm-maintainersactivatesemacs-lisp-modeemacs-lisp-modeactivatesparedit-modeparedit-modecallscheck-parenscheck-parenserrors out and leaves cursor somewhere- 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 (andC-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
- TODO Suggest a better new-line behavior
\followed byC-jinserts newline after\why not
\followed byRET? - TODO Suggest a better un-commenting
Consider
;; <point>(foo)Then
C-M-@to mark the expression(foo)andM-;(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.
| 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 |
calccommand
Key bindings
Selected Calc key bindings defined in the calc-mode-map.
- Stack
Key Description Mnemonic TABswap two topmost items RETduplicate topmost item DELdelete topmost item delete C-/undo yinsert topmost item to other buffer C-kkill topmost item or item at point kill M-kcopy topmost item or item at point 57 kill qhide 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 npop a value, push its additive inverse negation - Statistics
Key Description Mnemonic u +pop a vector, push the sum of its scalars 58 +u Mpop a row vector, push its arithmetic mean mean u Spop a row vector, push its standard deviation standard deviation u Cpop 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))
SQLite
A built-in major mode in Emacs that provides for editing SQL code.
TODO Update the list of keywords
sql-mode-sqlite-font-lock-keywordsis outdated and does not includeRETURNING- Get the keywords from https://sqlite.org/lang_keywords.html
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.
- The (IANA) time zone database (a.k.a.
tzorzoneinfo): file:///usr/share/zoneinfo/zone.tab https://www.iana.org/time-zones - Online time zone search with
zoneinfo: https://time.is/
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/Perthand notAWST (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 RETproject-compile(C-x p c) for projects.M-x recompile RET
Interactive mode
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
if the selected window hosts a buffer in Compilation major mode,
its compilation process is replaced;
else, if any window hosts a buffer named
*compilation*,its compilation process is replaced;
else,
a new window and buffer named
*compilation*is createdand 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
SPCclear 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,
nandpjump by errors, not all matches, e.g. stack frames.(with-eval-after-load 'compile (setopt compilation-skip-threshold 0))
Patches
- WAIT Make the Compilation mode recognize non-legacy Kotlin/Gradle errors
- DONE Make the Compilation mode recognize Lua errors
- my bug: 60830
- my patch: cd0eb055fd4ebc3f7f7f5f5617549f963fe8ecff
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
pandn- Move to the previous hunk or next hunk, respectively.
Commands:diff-hunk-prevanddiff-hunk-next - [
C-u]RET - Jump the corresponding source line.
WithC-u, jump the old version.
Structural editing
scustomSplit 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
vcustomCommit the diff.
Command:vc-next-actionOriginally
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-NUMBERlike old Linuxtty-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)
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,
- set the
rcirc-debug-flagvariable totand - 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.
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
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:
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
Commands
modus-themes-selectmodus-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
Doric Themes
Patches
- TODO Create Doric themes
- Phosphor (green terminal)
- Amber (amber terminal)
- Mono (monochrome terminal)
- DONE Add ERT (Emacs Regression Testing) faces
- DONE Italicize Org quotes and bold Org check boxes
- DONE Make the
org-latex-and-relatedface “stand out” a bit (issue) - DONE Differentiate today and scheduled day in calendar (issue)
Bug reports
Installation
(with-eval-after-load 'package
(add-to-list 'package-selected-packages 'doric-themes))
Org
A built-in major mode in Emacs that implements
a universal information organizer.
Recipes
Key bindings
- Visibility and narrowing
Key Functionality Note TABcycle visibility of subtree w/ C-u C-u, restore initialS-TABcycle visibility of buffer w/ C-<NUM>, to levelNUMC-c TABshow just children C-c C-kshow just branches C-x n snarrow to subtree widen with C-x n wPoint stays in place after
S-TAB, so to fold everything but the heading at point, typeS-TABfollowed byTAB.Indirect buffers and sparse trees:
Key Functionality Note C-c C-x bnarrow to subtree in indirect buffer w/ C-uanother oneC-c /create sparse tree … quit with C-cC-c / tcreate TODOsparse treequit with C-cUse 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 emphasisC-c C-x C-f ~add code emphasisC-c C-x C-f +add strike-through emphasis - Plain lists
Key binding Description Mnemonic Note M-RETinsert item C-c C-x rpromote item right same as headings C-c C-x Rpromote subtree Right same as headings C-c C-x ldemote item left same as headings C-c C-x Ldemote subtree Left same as headings C-c C-ctoggle checkbox DWIM (Do What I Mean) C-c C-crenumber 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-sSet [or remove] SCHEDULEDtimestampschedule [C-u] C-c C-dSet [or remove] DEADLINEtimestampdeadline 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-vnext 3 months in the calendar M-vprevious 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, enteryyyy-mm-ddfor yearyyyy, monthmm, and daydd, as per ISO 8601. To specify a date relative to the selected date, enter a query per the table below.Query Description yyyy-mm-ddon day yyyy-mm-ddhh:mmat time hh:mmhh:mm-hh:mmfrom hh:mmtohh:mmhh:mm+tfrom hh:mmtothours afterhh:mm+tufrom hh:mmtotunitsuafterdclosest future day dm-dclosest future day din monthmmonclosest mon-day in the future-monclosest mon-day in the pastNmonN-th closestmon-day in the future-NmonN-th closestmon-day in the past+NNdays in the future-NNdays in the past+NwNweeks in the future-NwNweeks in the pastOrg outputs the matching date and indicates the future with
(=>F). To specify the time, appendh:mforhhours andmminutes.To repeat, write
<YYYY-MM-DD ddd [" " HH ":" MM ["-" HH ":" MM]] RNU>
where Rdenotes the repeater type, Ndenotes the number of units, and Udenotes 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 unitU,yrepresents a year,ma month,wa week,da day, andhan 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-nnext heading next like C-nC-c C-fnext heading, same level forward like C-fM-{previous element, same kind C-c C-pprevious heading previous like C-pC-c C-bprevious heading, same level back like C-bC-c C-uparent heading up C-c C-jjump to heading jump - Structural editing
Key Description Mnemonic Note M-RETinsert heading at point TODOwithS-C-RETinsert heading at end of subtree TODOwithS-C-c C-qtoggle tag C-c C-x pset property property C-c C-x otoggle ORDEREDpropertyordered M-<up>move subtree(s) backward M-<down>move subtree(s) forward M-<right>promote heading(s) or C-c C-x rM-S-<right>promote subtree or C-c C-x RM-<left>demote heading(s) or C-c C-x lM-S-<left>demote subtree or C-c C-x LC-c C-wrefile subtree like C-wC-c M-wrefile subtree copy like M-wM-hmark (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-wkill (cut) subtree like C-wC-c C-x M-wcopy subtree like M-wC-c C-x C-yyank (paste) subtree like C-y, w/C-ubelow, w/C-u C-uas childC-c C-x DELdelete subtree like DEL, customC-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-olike open line/rowM-S-<up>delete row TABgo to next column S-TABgo 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-creformat table DWIM (Do What I Mean) Useful functions:
org-table-transpose-table-at-point(swap rows and columns)org-lint
- Tasks
Key Description Notes C-S-RETinsert TODOitemextends C-RET, w/C-ubelowC-c C-ttoggle or advance TODOstateon S-<left>andS-<right>on headingC-c ,set priority C-c C-cupdate [/]or[%]progress indicatorC-c C-x oToggle ordered property - Misc
Key Description C-c C-esave export region or document prefix with C-uto re-exportC-c C-x C-vtoggle inline images
Key Description M-;toggle COMMENT(heading or code)C-c ;toggle COMMENT(heading)C-^sort headings, lists, tables, etc. (
COMMENTheadings 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-lworks as follows:Context Description fragment at point toggle preview for fragment no fragment at point show previews for section or region with C-uhide previews for section with C-u C-ushow previews for buffer with C-u C-u C-uhide previews for buffer - Links
- Selected keybindings
Key Description RETorC-c C-ofollow link at point C-c &go back C-c C-linsert link C-c M-linsert last stored link C-c C-loffers the last link copied withorg-store-link(C-c l) but alsofile:links with completion.To follow links in tables, one must use
C-c C-oinstead ofRET. - Contribute!
- TODO Auto-add
//to e.g.http:links in Org onC-c C-l - DONE Report malfunctioning
org-next-visible-heading
- TODO Echo a helpful message after
org-toggle-link-display
e.g. Showing literal links, showing descriptive links
- TODO Fix pluralization
1 files scanned, 1 files contains IDs, and 1586 IDs found.
- TODO Remove unnecessary
setq uniqueinorg-id-new
- TODO Auto-add
- Org ID
- Complete ID links
Add minimal completion for ID links.
- Type
C-c C-l(org-insert-link). - Type
id RET. - Select the target heading.
- 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))))) - Type
- Complete ID links
- Custom global keybindings
(keymap-global-set "C-c l" #'org-store-link) - Use
RETto 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 withimagemagick, 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"))))
- Selected keybindings
- Clocks
Key Context Description Mnemonic C-c C-x C-iheading Clock in in C-c C-x C-oheading Clock out out C-c C-x C-janywhere Find last clock jump C-c C-cclocked time Recalculate duration DWIM Related to clocking (timers):
C-c C-x ;set timer Tips:
- The timer expects
H:MM:SSbut acceptsMandMMas 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 xand chooseclocktable, or executeM-x org-clock-report RET. To update a clock table, typeC-c C-con its+BEGINline. - 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_TOTALproperty, or equivalently theorg-clock-mode-line-totalvariable. The interesting values arecurrent, to see the time of the current clock, andtoday, to see the total time clocked today. - Contribute
- Configure!
Remember the running clock and the clock history across Emacs sessions.
(Org suffers from NIH, and so saving
org-clock-historyororg-clock-stored-historydoes not work with thesavehistmode.)(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))
- The timer expects
- Footnotes
To define and reference a footnote, type
[fn:NAME]as inTo print "Hello, World!" [fn:1] … … [fn:1] Per the tradition, started by Brian Kernighan in 1978.Key Description C-c C-x fJump to, add, sort, normalize, or delete footnote(s) † C-c C-cJump 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 prefixC-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-subtreewithC-uto paste below.
- Enable
- Disable archiving
I keep archiving by mistake, so disable
C-c C-x C-aorg-archive-subtree-defaultC-c C-x C-sorg-archive-subtreeby
(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-nandM-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
LOGGINGproperty. Further, when explicitly enabled, log intoLOGBOOKdrawers 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
DONEfontification
Do not fontify the entire
DONEheadline.(setopt org-fontify-done-headline nil) - Fix capitalization of
EFFORTproperty
Strangely, the
EFFORTproperty is capitalized asEffort, unlike any other property.;; NOTE Not `custom'-settable. (setq org-effort-property "EFFORT") - ??? (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-RETinsert 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
quoteandverseblocks.
(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-relateddoes not includescript, as it causednondeterministic fontification issues
with verbatim, code, or links containing underscores.
- ???
Show inline images and respect
#+attr_orgwhen sizing them.(with-eval-after-load 'org (setopt org-startup-with-link-previews t)) - ???
Move the point with
C-aandC-eintelligently. 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, typeC-aorC-eagain.(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 nGo to next heading next like C-c C-npGo to previous heading previous like C-c C-pfGo to next sibling forward like C-c C-fbGo to previous sibling backward like C-c C-buGo to parent heading up like C-c C-ujJump to heading … jump like C-c C-joOpen link … open like C-c C-oFGo to next block repeatable with fBGo to previous block repeatable with bDMove subtree down UMove subtree up RPromote subtree Right like C-c C-x RrPromote heading right like C-c C-x rLDemote subtree Left like C-c C-x LlDemote heading left like C-c C-x lkKill subtree wCopy subtree as if killed custom, prefix with count yYank killed subtree custom, C-ubelowiInsert heading tAdvance to-do state ,Set priority sSet scheduled date custom, unset w/ C-udSet deadline custom, unset w/ C-ueSet effort time like C-c C-x e:Set tags /Create sparse tree ^Sort children IClock in In OClock out Out &Jump to previous position custom, like C-c &1Set priority 1, so #A2=Set priority 2, so #B3Set priority 3, so #C0Unset priority
- Learn!
- ???
Do not warn about upcoming deadlines.
(The agenda sees the future just fine.)
(setopt org-deadline-warning-days 0) - ???
Make
imenuindex 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-attributesis ignored andtbodyHTML 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
varresolution 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
TODOheadings by default - Set the
:COOKIE_DATA:propertycheckboxto count plain- [X] Task-s.
Note: Plain tasks do not block.
Patches
Note. Do not forget to prefix the mail subject with [PATCH].
- ONGO [PATCH] Improve Org Attach user interface
- ONGO Idea: Make Org Babel work OOTB (idea)
- TODO Fix
org-todo(C-c C-t) temporarily disrupting layout
- Reproduction steps:
- Reset
org-use-fast-todo-selectionto'auto - Split the frame horizontally with
C-x 3 - Type
C-c C-ton a heading.
- Reset
Expected results:
The split layout is not temporarily disrupted.
Actual results:
The split layout is temporarily disrupted.
Notes:
The difference between
'autoand'expertis insignificant.
- Reproduction steps:
- TODO Make
org-gotoinoutlinestyle render markup - TODO Make
org-lintcomplain about duplicate radio targets - TODO Make
org-next-linkfind radio links - TODO Fix date-time input
- Reproduction steps:
- Create a heading.
- Schedule the heading to some date and time.
- Type
C-c C-sto reschedule. - Press the left arrow 35+ times.
Expected results:
Same as
find-file(C-x C-f): bell rings.Actual results:
Moving
funcall-interactively: Beginning of buffermessage.
- Reproduction steps:
- DONE
ob-scheme: Fix errors when the result is a cons cell (mail, patch) - TODO Idea: Enable Speed Commands OOTB
- TODO Fontify
org-insert-structure-template(C-c C-,.) - TODO Fix evaluation of commented-out Python blocks
# Nothing - DONE Pre-populate the description for BBDB links (mail, patch)
- DONE Harmonize wording and capitalization of menus (mail, patch 1, patch 2, patch 3)
- TODO Make
org-priority-downgo from nothing to#C
Currently, it goes from nothing to
B.N.B. Bound to
S-<down>. - TODO Rename functions
Old name New name org-shifttaborg-shiftcontroldownorg-shiftcontrolleftorg-shiftcontrolrightorg-shiftcontroluporg-shiftmetadownorg-shiftmetaleftorg-shiftmetarightorg-shiftmetauporg-metadownorg-metaleftorg-meta-returnorg-metarightorg-metauporg-shiftdownorg-shifttaborg-shiftleftorg-shiftrightorg-shifttaborg-shiftuporg-shifttaborg-meta-return - TODO Make citations “reflow friendly”
notmuch-show:m2k04ti5si.fsf@me.com
Thoughts. What about re-flowing such that the colon can trigger a line-break? That could work also for links.
- TODO Make Org fuzzy links case-fold before matching headlines
notmuch-show:m2o7t3w4xh.fsf@me.com
org-link-search-must-match-exact-headline'query-to-create'case-foldor'fold-caseSymbols (
symbolp) already work file-local.Add a couple of tests and done.
Enables to link
[[foo]]to the headline Foo (the most practical links). - TODO Expose additional
org-agenda-*-leadersvariables
- TODO Make broken link markers customizable
notmuch-show:m2wn7cou6t.fsf@me.com
Do not mark broken links.
(with-eval-after-load 'org (setopt org-export-broken-link-marker "%s")) - TODO Make
C-c C-x e(org-set-effort) capitalize properly
Capitalize the property as
EFFORTand notEffort. - TODO Export BBDB links correctly
e.g. “nil” exports to HTML as “nil”.
- TODO Do not forget the last export on
C-g
C-u C-c C-erepeats the lastC-c C-eexport, butC-c C-e C-gmakes it forget the last export. It should not. - TODO Better compare and contrast LaTeX and MathJax in the manual
Fix the following nonsense:
“Org mode supports embedding LaTeX code into its files, because many academics are used to writing and reading LaTeX source code, and because it can be readily processed to produce pretty output for a number of export back-ends.”
Instead compare and contrast LaTeX and MathJax, listing their pros and cons, perhaps in a table.
- TODO Handle
M->andM-<in the export dialog - TODO Fix the spelling of “colors” in Org
Search for “colours”. Run
checkdoconox-latex.el. - TODO Make Org highlight date on entry in real time
- TODO Make
org-prioritywork on the selected region
(… like
org-tododoes.)- Mark 3 tasks with
C-h C-h C-h. - Set the priority with
C-, a.
- Mark 3 tasks with
- TODO Make it possible to cut multiple headings
- Reproduction steps:
M-h M-h M-hto mark multiple headingsC-c C-x C-wto cut the headings
Expected results:
Org cuts all marked headings.
Actual results:
Org cuts the first marked heading.
- Reproduction steps:
- TODO Org: Fix sorting with LaTeX fragments
Org ignores emphasis when sorting headings but not LaTeX. For example, “\(p\)-hacking” sorts before “absolute complement”.
- TODO Fix undo during capture
It reveals the placeholder.
- TODO Make
M-fandM-bto work inside links - TODO Improve smart quotes
- parse “foo
x” correctly - do not put smart quotes to source, verbatim, and LaTeX blocks
Enable smart double quotes.
;; (with-eval-after-load 'electric ;; (setopt electric-quote-replace-double t));; (electric-quote-mode)Disable smart quotes in source, verbatim, and LaTeX blocks.
(with-eval-after-load 'org (add-to-list 'electric-quote-inhibit-functions 'org-in-src-block-p) (add-to-list 'electric-quote-inhibit-functions 'org-in-verbatim-emphasis) (add-to-list 'electric-quote-inhibit-functions 'org-inside-LaTeX-fragment-p)) - parse “foo
- TODO Automatically translate Unicode to entities
For example, treat “…” the same way as
\dots.This idea seems too obvious, so ask about it first.
- TODO Fix radio links working only in small numbers
- DONE
ox-texinfo: Support numeric values oftocin#+OPTIONS - DONE
ox-texinfo: Fix automatic@reftitles - DONE
ox-texinfo: Support links in headings - DONE
ox-texinfo: Check for math support without warnings and quietly - DONE
ox-texinfo: Sanitize links in headings to avoid broken Texinfo menus (patch) (mail) - DONE Include LaTeX in Texinfo exports
- DONE Add a trailing space to the mode line string of Org Clock
- DONE Spell BibTeX/LaTeX correctly
- BibTeX
- LaTeX
- DONE Fix incorrect clipping of LaTeX/SVG graphics
- DONE Upgrade from MathJax 2 to MathJax 3+
- DONE Test DWIM update of days
- DONE Propertize keys in the export UI
- DONE Make tests work with both GNU and BSD
sed
- DONE Fix shadowed radio targets
- SKIP Adjust baselines when exporting LaTeX to SVG
notmuch-show:m2h77ap1ll.fsf@me.com
This will be solved with the upcoming LaTeX preview system.
- SKIP Fix a possibly problematic string comparison
Waiting for review via email.
- SKIP Improve the naming of Org Src buffers
Patches (some of these are Emacs)
- ONGO Fix eaten escaping backslash in `org-use-speed-commands’ docstring (mail, patch 1, patch 2)
(with-eval-after-load 'org (setopt org-fontify-quote-and-verse-blocks t org-highlight-latex-and-related '(native entities))) - WAIT Control tangling code blocks in commented headline or one with archive tag
- TODO Re: [PATCH] Turn org-mouse features into minor-modes
- TODO Fix
&in Dired showing “1 guesses” for one guess - TODO Bind
qto quitorg-entities-help - TODO Harmonize
org-entities-helpterminology
*Org Entity Help*vs*Org Entities Help* - TODO Strip Org links from
#+titlewhen exporting to HTML - TODO Fontify links properly in
#+titleand#+subtitle - TODO Add a face for the highest priority cookie
This would allow the user to highlight it more visibly.
- TODO Indent
org-insert-structure-template(C-c C-,) on region
Add
org-edit-src-content-indentationif needed. - TODO Fix a link in the
org-export-with-broken-linksdocstring
The symbol
markis presented as a link to the functionmarkinM-x describe-variable RET org-export-with-broken-links RET. - TODO Fix a warning about
@itemize
[fn:56] - 5x10 ml Max VG BOOSTER VG100 (20mg/ml) Imperia - https://www.e-smoke.sk/ - free delivery
exports as
@tab @footnote{@itemize @item 5x10 ml Max VG BOOSTER VG100 (20mg/ml) Imperia @item @uref{https://www.e-smoke.sk/} @item free delivery @end itemize }causing warning
warning: @itemize should only appear at the beginning of a line
- DONE Fix a typo in Org lint (mail, patch)
- SKIP Ring the bell when no next/previous block is found (mail)
- DONE
org-lint: Fix a typo in a checker summary (mail, patch) - ONGO Add support for syntax highlighting in Texinfo exports
Add the
srclanguage as the first argument of@example.See: https://www.gnu.org/software/texinfo/manual/texinfo/html_node/Syntax-Highlighting.html
WIP on the
texinfo-highlightingbranch in~/org/src/org-mode/. - DONE Fall back to Fundamental mode when editing code (commit, thread)
- DONE Use Graphviz Dot mode if available (commit, thread)
- DONE [PATCH] doc/org-manual.org (The Export Dispatcher): Fix a broken link
- DONE [PATCH] Capitalize JavaScript correctly
- SKIP [PATCH] Preview LaTeX recursively
- DONE Re: Syntax highlighting issues
- DONE Fix the use of a deprecated function in the manual (talk, patch)
- DONE
org-manual: Fix a preposition and capitalization (patch, mail)
Contributions
- Patches
- A question about “spacious” citations
- Broken Bash in Org
- DONE Support arrow keys and mouse wheel in export dispatcher (mail, patch)
- TODO Fix lowercase “id”
org-id-track-globally
“with the id is in the same file as the link.”
- TODO Fix
org-next-visible-headingdoc-string
The
argargument is required. - WAIT Remove
#+attr_orgin favor of automatic sizing
Waiting for merging into the Emacs’
masterbranch. - Agenda
- A question about “spacious” citations
- Org mode: My code reviews, takeovers, and merges
- DONE ob-plantuml: var definition not working if @start<…> @end<…> lines are already existing in the babel code block
- DONE Fix typos and improve readability (mail, patch)
- DONE Fix typos in
org-guideandorg-manual(mail, patch) - DONE Fix typo in
org-guide(mail, patch) - DONE Show completions when inserting ID links (mail, patch 1, patch 2, patch 3, patch 4)
- DONE Improve
ol-infodocumentation (mail, patch)
- DONE ob-plantuml: var definition not working if @start<…> @end<…> lines are already existing in the babel code block
- Org mode: Bugs reports
- DONE Do not spellcheck
fnin footnotes (patch, mail) - WAIT Incorrect rendering of
\deg
org-pretty-entitiesprettifies\degas the degree symbol, which it is not in LaTeX. I opened https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50082 to track the issue. - Questions about Org links
- WAIT Attachment previews went from semi-broken to broken
- DONE Spellcheck link descriptions (mail, patch)
- DONE Fix off-by-one fontification of entities and inline LaTeX fragments (mail)
- DONE Fontify the last line of LaTeX environments correctly (issue, mail)
- TODO Report Org Element bugs
org-forward-elementdoes not work in lists butorg-backward-elementdoes.- one
- two
- three
- SKIP Speed Commands break macros if Flyspell is active
Switched from Flyspell to Jinx.
- WAIT LaTeX export is broken with
num:nilandALT_TITLE - WAIT Fix
org-priorityscrewing up links - WAIT Do not start the spellchecker when not needed
- WAIT Report the failing test
test-org-table/sort-lines
- WAIT Do not spell-check file-local variables
- WAIT Fix
org-fill-paragraph
Plain lists: https://list.orgmode.org/87ee5z6wa7.fsf@kyleam.com/
Source blocks: https://list.orgmode.org/e4f858e8-4ab7-655a-264b-f885e5ceef18@posteo.eu/
Fixed in notmuch-show:87bkqqalp1.fsf@localhost?
- WAIT Org folks fix
org-sbe(also tripsorg-linthere)
https://mail.gnu.org/archive/html/emacs-orgmode/2022-10/msg00570.html
(org-sbe "class-path") - SKIP org-babel: Cannot get `:pp’ to work (duplicate)
- DONE Allow spaces in link abbreviations
- DONE Report slow export times
- DONE Fix broken Info JS link
- DONE Fix broken links in The Org Manual
- DONE Make structure templates case-aware
- DONE Do not spellcheck
- TODO Contribute better
org-speed-commands
The list currently contains
org-cut-subtreebut noorg-copy-subtreeandorg-paste-subtree. - TODO Make
C-c C-x fmore practical with space after[fn:N]
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) tos,C-d(org-agenda-deadline) tod,C-c C-x C-i(org-agenda-clock-in) toi, andC-c C-x C-o(org-agenda-clock-out) too.
(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:
- Mark some item in
org-agenda
- Mark some item in
Expected results:
“1 entry marked for bulk action” in the echo area.
Actual result:
“1 entries marked for bulk action” in the echo area.
- Reproduction steps:
- 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
- existing:
- 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].
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-argsdo not apply. - By default
:exportsis set toresults.
- Document-wide
- The
evalheader argument
Determines when to evaluate the
SRCblock.Value Evaluate Example no-exportmanually ‡ literate configuration nonever † syntax definition † Matches the legacy
:eval neverand:noeval.
‡ Matches the legacy:eval never-export. - The
asyncheader argument
To run code asynchronously, set
:session NAMEand:async yes. - The
tangleheader argument
Value Tangle nonot ever yesalways † file always to file † Tangles to a file that matches the document name.
- The
tangle-modeheader argument
Value Description oXXXuse the octal value XXXrwxrwxrwxuse the lsstyle+xuse the chmodstyle - The
padlineheader argument
Useful when tangling multiple blocks into the same file.
Value Newline Note nonone yesbefore/after each code block default - The
commentsheader argument
Combined with
:tangle, determines the kind of commentary added to the tangled file.Value Commentary nonone linkfile and heading orgpreceding text bothsame as linkplusorg - The
resultsheader argument
The
:resultsheader argument takes four classes of values:Class Value Results Note handling nonediscarded handling silentin echo area handling replacein Org file default handling appendin Org file after old handling prependin Org file before old collection valueas value default collection outputas standard output type verbatimas text type tableas table type listas list type filein file see :file,:file-ext,:file-desc,:file-modetype vectorsame as tabletype scalarsame as verbatimformat codeas code format ppas code, pretty-printed format orgas code, Org markup format graphicsas image often combined with fileformat linkas resource see filetypeformat rawraw Org † format drawerraw Org a drawer format htmlraw HTML format latexraw LaTeX format graphicssame as link† For non-block elements,
rawimpliesappendbecause the end of output is impossible to recognize reliably. To avoid appending, add:wrap raw. - The
exportsheader argument
The
:exportsheader defines what to export:Value Exports Note codejust code default resultsjust results bothboth code and results nonenothing If a
SRCblock exists just to produce side effects, such as running a program written in C and OpenGL, use:results silent :eval no-export. - The
cacheheader argument
To cache the
RESULTS, use the:cache yesargument. Org Babel will jot down the checksum of theSRCblock under theRESULTSheader and re-execute theSRCblock just when its content changes. The:cacheargument enables fast exporting and publishing, but it does not work with the:sessionheader argument because sessions hold state. - The
colnamesheader argument
nil- Drop the column names from the input, if tabular.
- Run the code block, possibly producing results.
- 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- Drop the first row from the input, if tabular.
- Run the code block, possibly producing results.
- Re-add the row, if any, to the results, if tabular.
- no
- Do not pre-process tabular inputs.
- The
nowebheader argument
Add
:noweb yesto 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-refheader argument tonameinstead 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
nowebexpansions withC-c C-v vorC-c C-v C-v. - To export nothing in place of
- The
prologueandepilogueheader arguments
Side-effecting setup and teardown code.
- The
dirandmkdirheader argument
dir- working directory
if'attach(or stringattach), the(org-attach-id-dir)is used mkdir- if
t,diris 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.
- show standard 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
- TODO Support tangling without explicit sentinels
See Mulder/Ream update algorithm used in Leo, as mentioned by Ihor.
- TODO Support tangling into the attachment directory
- TODO Set the default directory when editing code
Set the
default-directorywhen editing withC-c 'to that specified in:dir. - TODO Is raw output from Common Lisp and Scheme broken?
- TODO Add support for executing Make source blocks
https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-makefile.html
(For use in my notes on Make.)
- TODO
ob-lua: Add support for naked Lua values
Reproduction steps:
1Expected results:
1
Actual results:
lua: stdin:3: unexpected symbol near '1' [ Babel evaluation exited with code 0 ]
- TODO
ob-lua: Auto-detect tables
Currently, it requires
pp:t = {1, 2, 3} table.insert(t, 4) return t - TODO Org Babel: Contribute an
executefunction for (GNU/BSD) Make
(with-eval-after-load 'org (defun org-babel-execute:makefile-gmake (body params) "Execute BODY with GNU Makefile code using Org Babel. PARAMS is a plist of src block parameters. This function is called by `org-babel-execute-src-block'." (let ((input-file (let ((file (org-babel-temp-file "make-input"))) (with-temp-file file (insert body)) file))) (when-let* ((results (with-temp-buffer ;; TODO use process-lines (call-process "gmake" nil (current-buffer) nil "-f" input-file) (buffer-substring-no-properties (point-min) (point-max))))) (let ((clean-results (string-replace input-file "Makefile" results))) (org-babel-reassemble-table (org-babel-result-cond (cdr (assq :result-params params)) clean-results (org-babel-import-elisp-from-file (let ((output-file (org-babel-temp-file "make-output-"))) (with-temp-file output-file (insert clean-results)) output-file))) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))))) (add-to-list 'org-babel-load-languages '(makefile-gmake t)) - DONE
ob-tangle: Tangle BibTeX files with the standard.bibfile extension
- DONE
ob-lua: Escape double quote characters in results
- DONE
ob-lua: Sort tabular results
- DONE
ob-lua: Remove half-baked session support
- DONE
ob-lua: Improve documentation strings and commentary
- DONE
ob-lua: Do not confuse list-like strings with lists
- DONE
ob-sqlite: Use a transient in-memory database by default
- DONE
ob-lua: Support all types and multiple values in results
- DONE
org-link: Improve UX of ’org-insert-link’
- DONE
ob-core: Remove the confusing “result silenced” message
- DONE Fix incorrect clipping of LaTeX/SVG graphics
- DONE Never throw away standard error
- DONE Test indentation of multiline text in list output
- DONE Fix linting of Java header arguments
Bugs
- WAIT Make
org-babel-detanglemore 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-begorg-babel-tangle-comment-format-end
org-tangle-modeminor 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:asyncbreakscolnames
asyncmakes thecolnamesdisappear:x = [[1, 2, 3], [4, 5, 6]] x1 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-10import 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
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.
| 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.extensionorkey~variant.extensionfor anyvariant.
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_BIBLIOGRAPHYwith a trailing colon
Contribute to Citar
- TODO Investigate why Citar makes Org slow
- Reproduction steps:
- Open a large Org file.
- Run
profiler-start. - Hold
C-v(scroll-up-command). - Run
profiler-report.
Citar, re-parsing its bibliography, uses most of the time.
- Reproduction steps:
- DONE bug Too much bold?
- DONE bug Fix vanilla Emacs completions
- DONE bug Do not rebind
C-dandC-k
Org Invoice
A simple invoice generator for the Org mode.
TODO Make undo work after capturing
Example
- Input
Upon
M-x org-capture RETfollowed byifor “invoice”, the program lets the user interactively find a work log, which is a heading withINVOICE-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.orgthat 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
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.orgfile
* %(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.texfile
\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
"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)))
Minad Stack
Vertico
Author
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
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))))
Corfu
Author
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))
Marginalia
Author
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
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
Custom camel-case processing
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
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:
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
Installation
Website
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.
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
- Installation
- Configuration
The
~/.mbsyncrcconfiguration 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
My disabled archiving
(with-eval-after-load 'notmuch
(setopt notmuch-archive-tags nil))
My patches
- WAIT Fix saved-search buffer titles (thread)
- WAIT Make
notmuch-hellotake into account line numbers
- Skipping until Notmuch drops support for Emacs 25.
- See the discussion and this related message.
- Skipping until Notmuch drops support for Emacs 25.
- WAIT Re-layout
notmuch-helloon window width changes
- Skipping until Notmuch drops support for Emacs 25.
- See the discussion and this related message.
- Skipping until Notmuch drops support for Emacs 25.
- TODO Make the logo work with dark themes
- Emacs now uses proper colors for SVGs (bug).
- 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")))))
- Banks
- 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"))))))
- Citar
- 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")))))
- Egghead Games (unused)
- 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")))))
- All Drafts
- Misc
- Area
- 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,
- a child of Notmuch Hello mode
- a parent of Notmuch Show mode.
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))
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
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
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
Key bindings
- Entry point
C-x gFile dispatch follow up with gfor statusC-x p vProject status - File dispatch
Let us see first if it works well…
- Navigation and selection
Key Description Mnemonic / Note ngo to next item next pgo to previous item previous ^go to parent up j njump to untracked new j ujump to unstaged unstaged j sjump to staged staged j f ujump to fetched unmerged fetch unmerged j p ujump to push unmerged push unmerged j zjump to stashes M-ngo to next sibling next M-pgo to previous sibling previous C-SPCbegin/end selection Imenu works too.
- ???
kkill selection or thing at point commit, stash, etc. - Staging
Key Description / Command Mnemonic / Note sstage selected stage magit-stageSstage all changed “big” stage magit-stage-modifiedwith C-u, include untrackeduunstage selected unstage magit-unstage-fileUunstage all “big” unstage magit-unstage-all - Commits
In the status buffer:
Key Description / Command Mnemonic / Note c ccommit staged changes commit magit-commit-createc wreword last commit message word magit-commit-rewordrewrites history c eadd staged changes to last commit extend magit-commit-extendrewrites history c Fadd staged changes to existing commit fix magit-commit-instant-fixuprewrites history In the commit buffer:
Key Description Mnemonic C-c C-cconfirm commit confirm C-c C-kcancel commit kill M-pshow previous message previous M-nshow next message next - Logs
Key Context Description Mnemonic l lstatus log log l ostatus log for revision, branch, tag log, other l astatus log for all log, all l rstatus reflog log, reflog d dlog show diff Currently, no way exists to copy messages from the log en masse. For a workaround, use the
vcmode: (1) typeM-x vc-dirand then (2) typelfor the log. (bug) - Checkouts and branches
Key Context Description Mnemonic b bstatus, log checkout revision, branch, or tag b cstatus, log create and checkout new branch branch, create b kstatus, log delete branch branch, kill - Reverting
Key Description Note V Vrevert commit to revert current in status, prefix with C-SPC - Rebasing
r wreword commit message reword r e main RETrebase branch onto main - Stashing
Key Description Mnemonic z zstash everything z istash staged index z xstash all but staged excluding index z papply and drop stash pop z aapply stash apply z kdrop stash kill Alternatively, to kill a stash, move the point to it and press
k. - Remotes
Key Description Mnemonic M aadd remote add M kremove remote kill M rrename remote rename f afetch from all remotes fetch F ppull (fetch & merge) rebase local commits with -rP ppush force-push with -f - Patches
Key Description Note W c ccreate patch at point or region w wapply patch (Git) adds commits w aapply patch (Unix) changes files - Resets
X sMove HEAD, keep changessoft reset X hMove HEAD, discard changeshard 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 jjump to/between files/changes M-g ifor ImenuRETvisit file before/after change in other window w/ C-u[C-u] C-RETvisit file in working tree in other window w/ C-u+show more context -show less context vreverse change file, hunk, region, etc. Cadd 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 sourceIn Magit, the former is
dr RET(Diff, Range, accept default). - How to compare the working tree with some commit
- Open
magit-statuswithC-x p m. - If the commit is not in sight, type
llto see the log. - Move the point over commit.
- Type
dformagit-diffandrfor range. - Retrieve the commit hash from future history by typing
M-ntwice. - Type
RETto confirm.
- Open
- How to reverse a committed file, hunk, or region
- Select the file, hunk, or region.
- Type
vformagit-reverse.
Note.
magit-reversebound tovis not to be confused withmagit-revertbound toV. - 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:- Fetch (
f p) the remote changes. - Commit the local changes as normal (
c c). - Pull (
F p) to merge the fetched changes. - Push (
P p).
- Fetch (
- How to compare current commit to some other
- Open
magit-statuswithC-x p m. - Soft-reset to the other commit with
X s. - 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 statuswithC-x p mand pull withF p. - Open
- How to create a branch with unmerged commits
Committed to
master, or some other branch, by mistake?- Open
magit-status. - Type
b Sto branch out as a spin-out.
- Open
- How to cherry-pick commits from other branches
- Open
magit-status. - Switch to the destination branch, if needed.
- Type
l oto see a log of some other branch (orl ato see all) - Select the source branch.
- Select one or more commits.
- Type
A Ato apply the cherry picks.
- Open
- How to find the history of specific region
- Mark the region.
- Execute
magit-file-dispatchbound toC-x V l - Type
lto see the log.
- How to find the history of specific files
For a single file, execute
magit-file-dispatchbound toC-c M-gper- Open
magit-status. - Type
lto see the log operations. - Type
--to limit the log to certain files. - Select one or more files.
- Type
lagain to see the log.
- Open
- How to modify or split existing commits
- Open
magit-status. - Move the point to the commit.
- Type
rto rebase andmto modify the commit. - Type
xto reset. - Type
head~orhead~1to reset to 1 commit older thanhead. - Make commits, accessing the previous commit messages with
M-p. - Type
rto rebase andrto continue (finish) it.
- Open
- How to resolve merge conflicts
To resolve a conflict directly in Magit, type
kon the conflicting file and select which side to keep:Key Description k okeep ours k tkeep theirs This works on hunks too.
Alternatively, type
RETto open a file with the Smerge mode enabled.Actually, Smerge key bindings work on hunks.
- How to diff endpoints of a commit range
- Mark the commit range.
- Type
dd.
- How to generate changelog for existing commit
With a clean
magit-status70:- Move the point to the commit.
- Revert changes with
r. - Initiate the commit with
c c. ExecuteNowM-x magit-generate-changelog RET.C-c C-ifollowed byl g(git-commit-insert-changelog-gnu)- Copy the generate changelog.
- Cancel the commit with
C-c C-k. - 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
gitversion control system (VCS) withmagit
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
gitdiffs 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
gitdiffs.(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))))) - Show
gitstatus in the fringe withdiff-hl
Install the
diff-hlpackage.(with-eval-after-load 'package (add-to-list 'package-selected-packages 'diff-hl))Show uncommitted
gitchanges 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-hlin lockstep withmagit.(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-TABkey 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 isrelevant 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))
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:
-badd buffer to context-fadd 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
Installation
- Backend: CLI
rtorrentclient
- 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
Edit Indirect
A 3rd-party Emacs package for
editing regions in separate buffers
Install
(with-eval-after-load 'package
(add-to-list 'package-selected-packages 'edit-indirect))
Customize key bindings
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
- Customize current line highlighting
Highlight the current line.
(with-eval-after-load 'telega (add-hook 'telega-root-mode-hook #'hl-line-mode))
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?
- Open
dired. - Mark some PDF files.
- Execute
pdf-occur-dired-do-search. - Enter a search query.
- Type
gto 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-modeis 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
Add support for editing BNF (Backus-Naur Form) grammars.
(with-eval-after-load 'package
(add-to-list 'package-selected-packages 'ebnf-mode))
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
(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
- Viewport edit mode
Key Description C-c C-cSend buffer C-c C-kKill buffer C-c C-pLast view C-c C-oToggle 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, useproject-prefixed-buffer-name. Seeproject-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
- DONE Add simple blinking circle as busy indicator
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-fileor 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
podman machine init(download Linux VM)podman machine start [MACHINE](start Linux VM)podman machine set --rootful(if root is required in VM, e.g. ports < 1024)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
Learn how to copy text
To copy text,
- type
C-c C-tto enter thevterm-copy-mode - mark the region76 that is to be copied as a kill
- type
RETto copy the marked text and exit thevterm-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-landclear.(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
emacs→find-fileman→man
and call
vterm-clear-scrollbackonclearin 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
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.
- Install modern Bash with
brew install bash - Add
/opt/homebrew/bin/bashto/etc/shells. - Run
chsh -s /opt/homebrew/bin/bash - check that a
FILEexists:test -f FILE
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
Grouping commands
Control operators
a ; b- execute
aand thenb a && bAND list- execute
bifaexits with0 a || bOR list- execute
bifaexits with non-0
Fennel
Destructuring
T ↦ P ≡ {K₁ V₁ K₂ V₂ …} ↦ {K₁ P₁ K₂ P₂ … CAPTURE?}syntax- Destructure table
T.
DestructureVᵢtoPᵢfor every equalKᵢ.
IfKᵢis:, interpret it as key"Pᵢ"literally.
IfKᵢstars with?, also matchnil.
IfCAPTUREis&as T*, setT*toT.
Gotcha:{}matches any table, not the empty table. T ↦ P ≡ [V₁ V₂ … Vₙ] ↦ [P₁ P₂ … Pₘ CAPTURE?]syntax- Destructure a sequence from table
T.
DestructureVᵢtoPᵢfor everyi ≤ m.
IfCAPTUREis&as T*, setT*toT.
IfCAPTUREis& T*, setT*to[Tₙ₊₁ … Tₘ].
Gotcha:[]matches any table, not the empty table. V ↦ P ≡ (values V₁ V₂ … Vₙ) ↦ (P₁ P₂ … Pₘ)syntax- Destructure multiple values.
DestructurePᵢtoVᵢfor everyi ≤ m.
Gotcha: Multiple values cannot be nested. X ↦ Ebase case syntax- If
Eis literal, checkX = E.
IfEis identifier, bindEtoX(†).
(†) If not documented otherwise, such as inmatch. X ↦ _convention- Ignore
E.
Also_Efor anyE.
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#)))
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)))))))
Lua
Pattern
A regexp-like78 string used by the Lua functions
defined as
| where | ^ |
matches the beginning of the string, |
$ |
matches the end of the string, and | |
RECURSIVE-ITEM |
is the recursive item. |
In-pattern recursive item
In-pattern item
A component of the recursive item:
| 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:
| where | REFERENCE |
is the capture reference, |
BALANCED |
is the balanced expression, and | |
FRONTIER |
is the frontier expression. |
In-pattern capture reference
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
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
CLASSand - the character immediately after S is the given
CLASS.
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
- Example: Propagating deletions
- Example: Propagating updates
- Example: Improving indentation of joins
- Example: Replacing rows
- Example: Constraining tables to exactly one row
- Example: Merging tables column-wise
- Example: Generating sequences recursively
- Example: Sum identity
- Example: Unix epoch timestamp
SQLite
Propagating changes
- Enable
PRAGMA foreign_keys = ON - Add
k PRIMARY KEYto the parent tablet80 - Add
FOREIGN KEY(k) REFERENCES t(k)to the child table Optionally specify
FOREIGN KEY … ON DELETE actionFOREIGN KEY … ON UPDATE action
with
actionbeing one of:RESTRICTSET NULLSET DEFAULTCASCADE
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 RECURSIVEmust contain a compoundSELECTUNION ALLperforms better thanUNION
— 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
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*}
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*}
but in align, use \Aboxed{...} from mathtools:
\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*.
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*}
Subscripts
\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-interpreteris for non-interactive use, andpython-shell-interpreteris 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-mypyIgnore 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 jupyterN.B. To run Jupyter, execute
source ~/.venv/bin/activate && jupyter notebookin 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-cellsuse 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")))
- Experimental
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:
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
VALUEnp.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
FIRSTto(LAST - STEP)increasing/decreasing bySTEP:import numpy as np a = np.arange(10, 50, 10) aarray([10, 20, 30, 40])
numpy.linspace(FIRST, LAST, COUNT = 50, dtype=float)Values
FIRSTtoLAST, equally spaced toCOUNTinstances:import numpy as np a = np.linspace(10, 50, 5) aarray([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 (@)
A matrix multiplication of ndarray:
A @ B.
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
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
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
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.
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\):
Drawing of rank-2 layer weights
Rank-2 matrix \(W^{[i]}\) of a weight tensor \(W\):
Drawing of rank-1 neuron weights
Rank-1 row vector \(\vec{w}^{[i]}_{j}\) of a weight tensor \(W\):
Drawing of rank-0 weight
Rank-0 scalar \(w^{[i](k)}_{j}\) of a weight tensor \(W\):
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
- the input layer \(A^{[0]}\)
- the first hidden layer \(A^{[1]}\)
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*}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
- 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*}
- Compute the gradients of \(c\) with respect to \(a\) and \(b\):
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
Sources
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.
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
Ollama
FOSS server of large language models.
Installation
Models
Models for Ollama are available in the Ollama Library.
- Gemma3 12B by Google
ollama pull gemma3:12b-it-qat - DeepSeek R1 14B distillation to Llama by DeepSeek
DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning
ollama pull deepseek-r1:14bpolitical bias: China
- Llama 3.1 8B by Meta
ollama pull llama3.1:8bpolitical bias: United States
- Phi 4 14B by Microsoft (United States)
- Check
ollama listNAME ID SIZE MODIFIED gemma3:12b-it-qat 5d4fa005e7bb 8.9 GB 6 minutes ago phi4:14b ac896e5b8b34 9.1 GB 7 weeks ago llama3.1:8b 46e0c10c039e 4.9 GB 7 weeks ago deepseek-r1:14b ea35dfe18182 9.0 GB 7 weeks ago
Server
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
- Set the difficulty level to zero.
- Sample the training set for the examples of the current difficulty.
- Train the model and evaluate it with regard to the curriculum criteria.
- If the model passes the curriculum criteria, increase the difficulty.
- 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.
Automatic differentiation with GradientTape
The means of automatic differentiation in TensorFlow.
- create:
tf.GradientTape() - compute gradients:
tape.gradient(y, [x, ...])
Example: Scalar
- Evaluate \(y = x^2\) at \(x = 10\).
- 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)
- Evaluate \(y = a^2 + b\) at \(a = 10\) and \(b = 20\).
- 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
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
TensorFlow copies broadcasting from NumPy.
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
- NumPy is best for one run.
- TensorFlow graph is best for multiple runs.
- TensorFlow eager is for … development. ☺
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.
Funny videos for nerds
They Have Played Us Programmers For Absolute Fools (Lisp, C, Rust)
WAT (Gary Bernhardt, 2012)
Funny videos for normies
English
Fun phrases
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.
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
LHS
Left-hand side.
LMAO
Laughing my ass off.
LMK
Let me know.
LMS
Learning management system.
LOL
Laughing out loud.
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.
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.
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
- No problem, after TY.
- Nondeterministic polynomial-time.
Heimlich maneuver
A first-aid treatment of choking.
Preparation
Before the maneuver, try the back slaps:
- Stand behind the person.
- Put your hand on their chest.
- Bend them forward as much as possible.
- With your other hand, firmly slap their back.
- Repeat five times.
Procedure
Do not proceed if the person can cough or talk.
- Stand behind the person.
- Wrap your arms around their waist.
- Place your fist, thumb side in:
- below their rib cage
- above their belly button.
- Wrap your other hand around the fist.
- Pull inward and upward, firmly and quickly.
- 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
Footnotes:
As of 2025, my main client is Egghead Games.
N.B. This definition encompasses mathematics.
As defined in The Magic of Reality by Richard Dawkins.
Sometimes folded into the Discussion section.
Also called repeated or iterated conjunction.
Also called repeated or iterated disjunction.
Also called repeated or iterated multiplication.
Also denoted with the square brackets,
\begin{equation*} \vec{v} = \begin{bmatrix} v_1 \\ \vdots \\ v_n \end{bmatrix}. \end{equation*}Also denoted
- with square brackets instead of parentheses, \(A = \begin{bmatrix} \vphantom{\big(} \ldots \end{bmatrix}\), or
- with comma-separated indices, \(a_{i, j}\).
The definition is self-referential on purpose.
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.
No “depth”, to maintain the graph-theoretical perspective of generic search.
In practice, often improved backtracking.
Based on the implementation of the generic search, which see.
No “depth”, to maintain the graph-theoretical perspective of generic search.
Based on the implementation of the generic search, which see.
This makes the definition of time complexity recursive.
Per the Cook-Levin theorem, where Levin co-discovered NP-completeness in Soviet Russia.
Even enumerated, in optimal solution enumeration.
Called a tour.
Another notable method is Dantzig–Fulkerson–Johnson (DFJ).
Free as in “free speech”, not “free beer”.
We cannot use identity because the final / is needed for C-a o.
Some parts of Emacs have their own bug trackers; notably the Org mode provides
the org-submit-bug-report command.
For example, to insert a line break literally, type ~ C-q C-j.
If repeated, appends to the last kill. To repeat automatically N times, prefix with C-u /N/.
To deactivate the mark with no action, type C-g.
For paragraphs, use M-q that respects sentence-end-double-space.
Unfill with M-x unfill-paragraph.
Disabled by default, so we enable them:
(put #'upcase-region 'disabled nil)
(put #'downcase-region 'disabled nil)
Prefix with C-u [0-9]+ or C-[0-9]+ to shrink/enlarge by [0-9]+ lines.
Related commands:
window-swap-statesswap current window with following window
These are hidden in various commands.
To cancel recording, type C-g.
If the first line is a shebang, the second line is scanned.
See global minor mode.
Milkypostman’s Emacs Lisp Package Archive
The “i” in the name stands for “incremental”, not interactive.
Thing at point is available with M-n in the minibuffer.
If needed, the current directory is available in the future history via M-n.
With visible-bell set, it becomes visual feedback.
The alternative key bindings are
M-s h .M-s h rM-s h pM-s h u
respectively.
A patch is planned to add support for extra indicators.
In Vim, this is easily done by setting colorcolumn=72,79.
To see standard output and error output, e.g. NSLog, click “+ Instrument” and pick “stdout/stderr”.
'magit-extras can do this but with hard-coded C-x p m.
M-w ignores calc-kill-line-numbering.
Summing the elements is equal to \(1^\mathsf{T} \vec{x}\).
Applies to M-k and C-k, but not M-w.
We keep the basic style as a backup
because some commands need it to work correctly.
We do not use package.el because we want the Emacs frontend to match
the notmuch binary (that comes with it anyway).
Also, the logo does not play well with dark themes, as of Notmuch 0.39.
To toggle, use Notmuch Show tagging.
The new motion keys correspond to the original motion keys N and P.
See man notmuch-insert.
“Is empty” as per the notmuch-mua-subject-regexp.
“Mentions attachments” as per the notmuch-mua-attachment-regexp.
Shown is a typical two-parent (a.k.a. two-way) merge, but octopus merges with N > 2 parents follow the SHA^N pattern.
Perhaps on a temporary branch.
See also magit-status-here.
Why is this not default?
More specifically, org-edit-src-code called by org-edit-special.
Before, I tried and failed with
- built-in narrowing, which breaks indentation and more,
- built-in indirect buffers, which do not support fontification.
I do not understand why this helps, as
telega-emoji-use-imagesis nil (default)telega-symbols-emojifysays
“List of symbols to emojify if
telega-emoji-use-imagesis non-nil.”
With no region, RET copies the line at point.
105 is the maximum, as per SB_MAX in vterm-module.h.
Due to its small size, Lua does not use POSIX regular expressions.
See utf8.charpattern for UTF-8 characters.
The parent column must be PRIMARY KEY, or “foreign key mismatch” error is shown.
~/.config/matplotlib/ is not searched on MacOS; the file must be in:
import matplotlib as mpl
mpl.get_configdir()
/Users/rudy/.matplotlib
Non-continuous functions can be approximated with continuous ones.
Features for all examples in the corresponding target matrix.
Targets for all examples in the corresponding input matrix.
For the input layer \(i = 1\), \(n\) is the number of features in the training data set.
\(w^{[i]}_{j}\) is a row vector.
To serve manually, execute ollama serve.
Executes ollama pull $model first, if needed.