(These notes are mainly for my own reference, but also come in handy when I need to something to point to when a colleague or student needs to know how to do this.)
http://www.oracle.com/technetwork/java/javase/downloads/index.html
I’ll assume we’re using Linux on an x64 machine, so we would,
for example, download a file with a name like jdk-9.0.1_linux-x64_bin.tar.gz
.
(As of Dec 6, 2018, the direct link to this file is here.)
I’ll assume we downloaded the tar.gz file into the directory ~/opt/Java
tar xvzf jdk-9.0.1_linux-x64_bin.tar.gz
create the directory /usr/lib/jvm
sudo mkdir -p /usr/lib/jvm
If you already have directory named /usr/lib/jvm/jdk-9.0.1
, move it out of the way:
sudo mv /usr/lib/jvm/jdk-9.0.1{,.orig}
Move your newly unpacked jdk directory to /usr/lib/jvm
then give it the alias jdk1.9.0
:
sudo mv ~/opt/Java/jdk-9.0.1 /usr/lib/jvm/
sudo ln -s /usr/lib/jvm/jdk{-9.0.1,1.9.0}
Use the update-alternatives
program to configure multiple versions of Java
on the same machine. (For more details see: notes on configuring JDK 1.7 on Ubuntu):
This first block of 9 commands can be copy-and-pasted to the command line all at once:
sudo update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.9.0/bin/java" 1;
sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.9.0/bin/javac" 1;
sudo update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/lib/jvm/jdk1.9.0/bin/javaws" 1;
sudo update-alternatives --install "/usr/bin/jcontrol" "jcontrol" "/usr/lib/jvm/jdk1.9.0/bin/jcontrol" 1;
sudo chmod a+x /usr/bin/java;
sudo chmod a+x /usr/bin/javac;
sudo chmod a+x /usr/bin/javaws;
sudo chmod a+x /usr/bin/jcontrol;
sudo chown -R root:root /usr/lib/jvm/jdk1.9.0;
The following commands are interactive and should be invoked individually:
sudo update-alternatives --config java
sudo update-alternatives --config javac
sudo update-alternatives --config javaws
sudo update-alternatives --config jcontrol
Check which version of Java your system is currently using with the command java -version
.
Finally, if you have other versions of Java installed on your system, you should make them
available using the update-alternatives
program by following the same steps as we did above
for jdk version 9.0.1.
[1] Keith Kearnes. Varieties with a difference term. J. Algebra, 177(3):926–960, 1995.
[[1]]: http://dx.doi.org/10.1006/jabr.1995.1334
]]>The UniversalAlgebra/Conferences repository is simply a list of links to the conferences listed.
Here are some other more general conference listings that may be updated more frequently than mine:
]]>Bill Lampe pointed out that the main result here has been known for a long time. However, our proof seems new and simpler to us.
The note also describes closure operators and reviews some useful facts about them. Finally, we recall a theorem from Berman’s thesis that relates congruence lattices of partial algebras with those of total algebras.
The main purpose of the note is to describe some tools that we plan to exploit in our quest to represent every finite lattice as the congruence lattice of a finite algebra.
]]>Consider the relational structure $\mathbf B = \langle \{0, 1\}, R\rangle$ where $R$ is the ternary relation $\{0, 1\}^3 - \{(0,0,0), (1,1,1)\}$. That is,
We now describe the constraint satisfaction problem $\operatorname{CSP}(\mathbf B)$.
By an “instance” of $\operatorname{CSP}(\mathbf B)$, we mean a (finite) relational structure $\mathbf A = \langle A, S \rangle$ with a single ternary relation $S$.
The constraint satisfaction problem $\operatorname{CSP}(\mathbf B)$ is the following:
Given an instance $\mathbf A = \langle A, S \rangle$, does there exist a relational homomorphism from $\mathbf A$ to $\mathbf B$? That is, does there exist a function $f: A \rightarrow \{0,1\}$ such that $(f(a), f(b), f(c)) \in R$ whenever $(a, b, c) \in S$?
The kernel of a function $f$ with codomain $\{0,1\}$ has two equivalence classes, or “blocks”—namely, $f^{-1}\{0\}$ and $f^{-1}\{1\}$.
If one of these classes is empty, then $f$ is constant in which case it cannot be a homomorphism into the relational structure $\langle \{0, 1\}, R\rangle$ (since $(0,0,0)\notin R$ and $(1,1,1)\notin R$).
Therefore, the kernel of every homomorphism $f : \mathbf A \rightarrow \mathbf B$ has two nonempty blocks.
Now, given a partition of $A$ into two blocks, $\pi = |A_1|A_2|$, there are exactly two functions of type $A \rightarrow \{0,1\}$ with kernel $\pi$. One is $f(x) = 0$ iff $x \in A_1$ and the other is $1-f$. It is obvious that either both $f$ and $1-f$ are homomorphisms or neither $f$ nor $1-f$ is a homomorphism.
Indeed, both are homomorphisms if and only if for all tuples $(a,b,c) \in S$ we have $\{a,b,c\} \nsubseteq A_1$ and $\{a,b,c\} \nsubseteq A_2$.
Neither is a homomorphism if and only if there exists $(a,b,c) \in S$ with $\{a,b,c\} \subseteq A_1$ or $\{a,b,c\} \subseteq A_2$.
Now, for each tuple $s = (a,b,c) \in S$, we let $\langle s\rangle$ denote the equivalence relation of $A$ generated by $a$, $b$, and $c$. It is clear that a function $f: A \rightarrow \{0, 1\}$ is a homomorphism from $\mathbf A$ to $\mathbf B$ if and only if for all $s \in S$ the relation $\langle s\rangle$ does not belong to the kernel of $f$. Therefore, a solution to the instance $\mathbf A = \langle A, S \rangle$ of $\operatorname{CSP}(\mathbf B)$ exists if and only if there is at least one coatom in the lattice of equivalence relations of $A$ that is not contained in the union $ \bigcup_{s\in S} \, ^\uparrow\langle s \rangle $ of principal filters.
The Covered Coatoms Problem is the following: Given a set $A$ and a list $s_1 = (a_1, b_1, c_1), s_2=(a_2, b_2, c_2), \dots, s_n =(a_n, b_n, c_n)$ of 3-tuples with elements drawn from $A$, decide whether all of the coatoms of the lattice $\prod_A$ of partitions of $A$ are contained in the union $ \bigcup_{i=1}^n \, ^\uparrow\langle s_i \rangle $ of principal filters.
If we find an algorithm that decides this in polynomial time (in the size of the set $A$), or if we prove that no such algorithm exists, then this would answer the P versus NP question.
]]>Suppose we choose four numbers at random without replacement from the first 20 prime numbers. What is the probability that the sum of these four numbers is odd?
Solution By definition, an even number is an integer multiple of 2; that is, even numbers have the form $2n$, for some integer $n$. An odd number is one more than an even number; that is, odd numbers have the form $2n+1$ for some integer $n$.
If all four numbers are odd, then the sum is even. This follows from the fact that the sum of two odd numbers is even: $(2n+1) + (2k+1) = 2(n+k) + 2 = 2(n+k+1)$. And the sum of two even numbers is again even: $2n+2k = 2(n+k)$. Therefore, in order for the sum of the four primes chosen to be odd, the only even prime number, 2, must be among the four numbers we selected.
Here are two ways to compute the probability that 2 is one of the four numbers selected: First, consider the probability of not selecting 2: $$ P(\text{2 is not among the chosen primes}) = \frac{19}{20} \frac{18}{19} \frac{17}{18} \frac{16}{17} = \frac{16}{20} = \frac{4}{5}. $$
Then the probability that 2 is among the four chosen prime numbers is $1 - \frac{4}{5}$, or $\frac{1}{5}$.
Alternatively, we could count the number of four-tuples that have a 2. This is the same as taking 4 times the number of ways to choose the other three numbers: $19\cdot 18\cdot 17$. (The factor of 4 appears since we could put the 2 in any of four positions). Now divide this by the total number of four-tuples, $20 \cdot 19\cdot 18\cdot 17$, to arrive at the same answer as above:
Suppose you have an 8 sided die with sides labeled 1 to 8. You roll the die once and observe the number $f$ showing on the first roll. Then you have the option of rolling a second time. If you decline the option to roll again, you win $f$ dollars. If instead you exercise the option to roll a second time, and if $s$ shows on the second roll, then you win $f + s$ dollars if $f+s < 10$ and 0 dollars if $f+s \geq 10$. What is the best strategy?
Solution
A strategy is given by specifying what to do in response to each possible first roll, $f$. The best strategy will maximize the expected winnings.
The expected winnings if we roll just once is simply the number that appears, $f$.
Let $S$ be the random variable representing the value showing on the second roll. This is uniformly distributed with $P(S=s) = 1/8$. Therefore, if we observe $f$ and then decide to roll twice, the expected winnings are
where $\chi$ denotes the characteristic function, that is, $\chi_{\mathrm{True}}=1$ and $\chi_{\mathrm{False}}=0$.
If we let $N= \min{8, 10 - f -1}$, then the expected value of rolling twice is
If we observe $f=1$ on the first roll, then
Since this is greater than 1, we should certainly exercise the option to roll twice if 1 shows up on the first roll.
Continuing this way, we have
Since this is larger than 4, we should roll twice when observing 4 on the first roll.
However, if $f = 5$, then
Since this is less than 5, we should not roll a second time when a 5 is observed on the first roll.
General strategy: Exercise the option to roll a second time if the first results in 1, 2, 3, or 4. Otherwise, decline the second roll and take the value shown on the first roll.
(The original question was for 3 coins, with respective probabilities 0.5, 0.3, and 0.2 of coming up heads. This version is a generalization to $n$ coins with arbitrary probabilities that sum to 1.)
Suppose you have a bag with $n$ coins, $C_1, C_2, \dots, C_n$, and coin $C_i$ has probability $p_i$ of coming up heads when flipped. Assume $p_1 + p_2 + \cdots p_n = 1$. Suppose you draw a coin from the bag at random and flip it and it comes up heads. If you flip the same coin it again, what is the probability it comes up heads?
Denote by $H_i$ the event that heads turns up on the $i$-th flip, and by a slight abuse of notation, let $C_i$ denote the event that we are flipping coin $C_i$. Then
Applying Bayes’ Theorem,
Assuming all coins are equally likely to have been drawn from the bag, $P(C_i)=\frac{1}{n}$. Therefore,
whence,
Therefore,
For the special case given in the original problem, we have
Suppose you have two urns that are indistinguishable from the outside.
One of the urns contains 3 one-dollar coins and 7 ten-dollar coins.
The other urn contains 5 one-dollar coins and 5 ten-dollar coins.
Suppose you choose an urn at random and draw a coin from it at random.
You find that it is a $10 coin. Now you are given the option to draw
again (without replacing the first coin) from either the same urn or
the other urn. Should you draw from the same urn or switch?
Solution
The problem is uninteresting if we assume the draws are made with replacement. Clearly we should not switch urns in this case. Assume the second draw is made without replacing the first coin.
Let $X$ be a random variable denoting the value of the second draw. We compute the expected values $E(X | \text{ stay})$ and $E(X | \text{ switch})$ and whichever is higher determines our strategy.
Let us call the urn that starts with 7 ten-dollar coins urn $A$, and the urn that starts with 5 ten-dollar coins urn $B$. Let $A_1$ denote the event that urn $A$ is chosen for the first draw and let $B_1$ denote the event that urn $B$ is chosen for the first draw. Let $T$ denote the event that the first draw produces a ten-dollar coin.
The probability that a ten-dollar coin appears on the first draw is
Given that $T$ occurred, the probability we were drawing from $A$ is, by Bayes’ Theorem,
Similarly the probability we were drawing from $B$, given $T$, is
Now, if we decide to draw again from the same urn and that urn happens to be $A$, there will be 6 ten-dollar coins remaining, so our expected value of drawing again from $A$ is
If we decide to draw from the same urn and it happens to be $B$, there will be 4 ten-dollar coins remaining, so
So, if we don’t switch urns, the expected value of the second draw is
Suppose instead we decided to switch urns. In the event $A_1$ that the first draw was from urn $A$, then urn $B$ will contain its original contents: 5 ten-dollar coins and 5 one-dollar coins. Therefore,
If we switch in the event $B_1$, then we choose from a pristine urn $A$ having 7 ten-dollar coins and 3 one-dollar coins. Thus,
Therefore,
So, the expected value of the second draw if we switch urns is a little more than 6.83.
Conclusion: The best strategy is to switch urns before drawing the second coin.
]]>I made this version for my own reference, while working through the tutorial, making some revisions and additions, and a few corrections. You may prefer the original.
$\def\N{\mathbb N} \def\true{\mathsf{true}} \def\conj{\wedge} \def\disj{\vee}$
Table of Contents
Copyright (c) 2013, Liam O’Connor-Davis
Welcome to Learn You an Agda and Achieve Enlightenment! If you’re reading this, you’re probably curious as to what Agda is, why you want to learn it, and in general what the big deal is about dependently typed, purely functional programming.
Inspired by BONUS, the writer of Learn You a Haskell, I decided that I should write an approachable Agda tutorial that would introduce dependently typed programming to ordinary people rather than Ivory Tower Academics. Of course, seeing as I am one of those Ivory Tower Academics, this might not be easy. I am, however, prepared to give it a try. Learning Agda was a very rewarding but very difficult process for me. It is my hope that, by writing this tutorial, it will become a little bit easier for everyone else.
(The original tutorial suggested that Haskell should be learned before Agda. However, if one already knows some functional programming, then Agda should not be very hard to learn. This should be especially true for those with some background in logic and experience with other dependently typed languages.)
This tutorial is not aimed at those who are completely new to functional programming. Agda is similar on a basic level to typed functional languages such as Haskell and ML, and so knowing a language in the ML family will certainly make learning Agda a great deal easier.
If you don’t know a statically typed functional language, I recommend that you learn Haskell, as Agda has a close relationship with the Haskell ecosystem. If you’re looking for a good Haskell tutorial, look no further than this book’s companion, Learn You a Haskell.
If you don’t know how purely functional programming works, learn a little of it before trying to tackle Agda.
Understanding of imperative and object oriented programming (C, Java, Ruby..) isn’t necessary. In fact, trying to apply skills learned from these languages might even be harmful when you’re trying to learn Agda.
The moral of the story is: keep an open mind. A lot of Agda’s power comes from features that are at first difficult to understand. It took a long time for everything in Agda to fall into place in my head. Agda is hard. After some time, though, Agda’s inherent awesomeness comes to the fore, and it all just clicks. If you encounter obstacles in your Agda learning, don’t be discouraged! Keep working, and eventually you will be a master of Agda fu.
Agda is a programming language, but not a programming language like Java. It’s not even very much like Haskell, although it’s a lot more like Haskell than Java.
Agda is a programming language that uses dependent types. Many of you would be familiar with types from imperative languages such as Java or C++, and if you’re reading up to this point, you should also have a familiarity with types from Haskell.
Types in these languages essentially annotate expressions with a tag. At a simple level,
an expression’s type might just be a concrete type, like Bool
or Int
. Java (through
generics), C++ (through templates) and Haskell all support polymorphic types as well,
such as List a
or Map k v
.
But, if List a
is a type, then what exactly is just List
(without the parameter)?
Haskell calls it a “type constructor”, but really it’s a function at the type
level. List
takes in a type, say Int
, and returns a new type, List
Int
. Haskell (with appropriate extensions) even supports arbitrary functions on
the type level, that don’t necessarily have to construct a type term, and
instead can simply refer to existing ones.
So, Haskell has type-level functions, even type-level types (kinds). It almost seems like an entirely new language, overlaid over Haskell, that operates at compile time, manipulating type terms.
In fact, you could think of any type system this way. In C++, people exploit the Turing-completeness of their type system to perform compile-time analysis and computation. While such type level work is very powerful, I fear that such type machinery is very often difficult to understand and manipulate. Even in Haskell, applications that make extensive use of type-level computation are very often substantially harder to comprehend. The type-level “language” is almost always substantially more complicated to work with than the value-level “language.”
In Agda, the distinction between types and values does not exist. Instead, the language you use to manipulate type terms is exactly the same language that you use to manipulate values.
This means that you can actually include values inside a type. For example, the List
type constructor can be parameterized by both the type of its contents and the length of
the list in question (we’ll be doing this later). This allows the compiler to check for you
to make sure there are no cases where you attempt to call head
on a
potentially empty list, for example. Being able to include values inside a type,
and use all the same value-level operations on them, is what makes Agda
dependently typed - Not only can values have a type, but types can have a value.
In fact, seeing as the language of values and the language of types are the same, any property that you can express about a value can be expressed statically in its type, and machine checked by Agda. We can statically eliminate any error scenario from our program.
If I can come up with a function of type Foo -> Bar
(and Agda says that it’s
type correct) that means that I’ve written not only a program, but also a proof
by construction that, assuming some premise Foo
, the judgment Bar
holds. (We’ll touch more on proofs later; I don’t want to get bogged down in
details just yet.)
Seeing as our Foo
and Bar
can be as expressive as we like, this lets us
prove anything we want about our program simply by exploiting this
correspondence between proofs and programs - called the
Curry-Howard Correspondence,
discovered by two brilliant logicians in the sixties.
The validity of formal verification of software is often hotly contested by programmers who usually have no experience in formal verification. Often testing methodologies are presented as a more viable alternative.
While formal verification is excessive in some situations where bugs are acceptable, I hardly think that testing could replace formal verification completely. Here are three reasons for this:
Of course, proofs are not for every scenario, but I think they should be far more widely used than they currently are.
Thanks to Curry-Howard, Agda can also be used as a proof language, as opposed to a programming language. You can construct a proof not just about your program, but about anything you like.
In fact, Curry-Howard shows us that the fundamentals of functional programming (Lambda Calculus), and the fundamentals of mathematical proof (Logic) are in fact the same thing (isomorphic). This means that we can structure mathematical proofs in Agda as programs, and have Agda check them for us. It’s just as valid as a standard pen-and-paper mathematical proof (probably more so, seeing as Agda doesn’t let us leave anything as “an exercise for the reader” - and Agda can check our proof’s correctness automatically for us. We’ll be doing this later by proving some basic mathematical properties on Peano natural numbers.
So, Agda is a language that really lives the dream of the Curry-Howard correspondence. An Agda program is also a proof of the formula represented in its type.
At the time of writing, it is only really feasible to edit Agda code using Emacs. GNU Emacs or XEmacs are both fine. However, you don’t need a great deal of Emacs proficiency to edit Agda code.
(If you are using Ubuntu Linux, you may wish to skip to the next section, Installing on Ubuntu Linux.)
You’ll need GHC, a Haskell compiler, and an assortment of tools and libraries that make up the Haskell Platform. It is the best way to get started using Haskell, and it’s also the easiest way to get Agda.
Once you have Haskell and Emacs, there are three things you still need to do:
cabal-install
tool to download, compile, and set up Agda.$ cabal install agda
PATH
):$ agda-mode setup
$ agda-mode compile
By then you should be all set. To find out if everything went as well as expected, head on over to the next section.
On a Ubuntu Linux system, instead of installing Agda using cabal as above, one could alternatively use the following two commands, which take a few minutes to run:
sudo apt-get install agda-mode
sudo apt-get install agda-stdlib
That’s it! Now, when you launch Emacs and edit a file with the .agda extension,
it should switch to agda-mode
or agda2-mode
. If not, you can switch manually
by invoking one of the following (in Emacs, of course): M-x agda-mode
or
M-x agda2-mode
or Esc-x agda-mode
. (An easy way to find out which agda
modes are available is to type M-x agda
and then hit tab a couple of times to
see the possible completions.)
Unlike the previous section, this section will actually involve some coding in Agda.
Most language tutorials start with the typical “Hello, World” example, but this is not really appropriate for a first example in Agda. Unlike other languages, which rely on a whole lot of primitive operations and special cases for basic constructs, Agda is very minimal - most of the “language constructs” are actually defined in libraries.
Agda doesn’t even have numbers built in, so the first thing we’re going to do is
define them—specifically natural numbers. Natural numbers are nonnegative
integers, that is, the whole numbers starting with zero and going
up. Mathematics uses the symbol $\N$ to represent natural numbers, so we’re going
to borrow that for our example (Another thing that sets Agda apart from other
languages is its extensive use of unicode to make mathematical constructs more
natural). To enter ℕ into emacs, type \bn
. To enter the unicode arrow (→),
type \->
. I’m going to demonstrate this line by line, so bear with me.
First, open a file named LearnYouAn.agda in Emacs and type the following:
module LearnYouAn where data ℕ : Set where
The data
keyword means we’re defining a type—in this case, ℕ
.
In this example, we’re specifying that the type ℕ
is of type Set
(that’s
what the colon means).
If you recall the introduction, I mentioned that in Agda, types and values are treated the same way. Since values are given types, types are given types as well. Types are merely a special group of language terms, and in Agda, all terms have types.
Even Set
(the type of our type ℕ
) has a type: Set₁
, which has a type
Set₂
, going on all the way up to infinity. We’ll touch more on what these
Set
types mean later, but for now you can think of Set
as the type we give
to all the data types we use in our program.
This infinite hierarchy of types provides an elegant resolution of
Russell’s Paradox.
For example, Set₁
cannot contain Set₁
or Set₂
, only Set
, so Russell’s
problematic set (that contains itself) cannot exist.
Okay, so, we’ve defined our type, but now we need to fill the type with values. While a type with no values does have its uses, a natural numbers type with no values is categorically wrong. So, the first natural number we’ll define is zero:
zero : ℕ
Here we are simply declaring the term zero
to be a member of our new type
ℕ
. We could continue to define more numbers this way:
zero : ℕ one : ℕ two : ℕ
But we’d quickly find our text editor full of definitions and we’d be no closer to defining all the natural numbers than when we started. So, we should instead refer to a strict mathematical definition.
(The notation I’m using here should be familiar to anyone who knows set theory and/or first-order logic. Don’t panic if you don’t know these things, we’ll be developing models for similar things in Agda later, so you will be able to pick it up as we go along.)
This is called an inductive definition of natural numbers. We call it inductive because it consists of a base rule, where we define a fixed starting point, and an inductive rule that, when applied to an element of the set, induces the next element of the set. This is a very elegant way to define infinitely large sets. This way of defining natural numbers was developed by a mathematician named Giuseppe Peano, and so they’re called the Peano numbers.
We will look at inductive proof in the coming sections, which shares a similar structure.
For the base case, we’ve already defined zero to be in $\mathbb{N}$ by saying:
zero : ℕ
. To define the natural numbers inductively, let’s recall the
induction step of first order logic. This can be written as follows:
Given a natural number n
, the constructor suc
will return
another natural number. In other words, suc
could be considered a
function that, when given a natural number, produces the next natural
number. In Agda, we define the constructor suc
like so:
data ℕ : Set where zero : ℕ suc : ℕ → ℕ
Now we can express the number one as suc zero
, and the number two as suc (suc
zero)
, and the number three as suc (suc (suc zero))
, and so on.
Incidentally, this definition of natural numbers corresponds to the Haskell data type:
data Nat = Zero | Suc Nat
If you load that into GHCi and ask it what the type of Suc
is, it
(unsurprisingly) will tell you: Nat -> Nat
. This is a good way to get an
intuition for how to define constructors in Agda.
Also, GHC supports an extension, Generalized Algebraic Data Types or GADTs, which allows you to define data types Agda style:
data Nat :: * where Zero :: Nat Suc :: Nat -> Nat
It’s worth noting that GADTs are not exactly the same as Agda data definitions, and Haskell is still not dependently typed, so much of what you learn in this book won’t carry over directly to extended Haskell.
Now we’re going to define some arithmetic operations on our natural numbers. Let’s try addition, first.
_+_ : ℕ → ℕ → ℕ
Here I’m declaring a function. To start with, I give it a type^{2}—it takes two natural numbers, and returns a natural number.
Unlike Haskell which has only prefix functions (ordinary functions) and infix functions (operators), Agda supports mixfix syntax. This allows you to declare functions where the arguments can appear anywhere within a term. You use underscores to refer to the “holes” where the arguments are meant to go.
So, an if-then-else construct in Agda can be declared with:^{3}
if_then_else_ : ∀ { a } → Bool → a → a → a
This can be used with great flexibility: You can call this function with if a
then b else c
, which Agda interprets as if_then_else_ a b c
. This syntactic
flexibility delivers great expressive power, but be careful about using it too
much, as it can get very confusing!
Now, let’s implement and check the sum function by structural recursion.^{4}
_+_ : ℕ → ℕ → ℕ zero + m = m (suc n) + m = suc (n + m)
Normally we’d run the program at this point to verify that it works, but in Agda we check our code. This checks that all our proof obligations have been met:
Proof obligations of a program can only be machine-checked if the program terminates, but checking that any program terminates is in general undecidable (see The Halting Problem). To circumvent this dilemma, Agda runs its checker only on structural recursion with finite data structures, and warns that it can’t check proof obligations in which non-structural recursion is used. We will discuss this more in later sections, but all of the examples in the early part of this tutorial can be proved by Agda to terminate.
At this point, the contents of the LearnYouAn.agda file should be as follows:
module LearnYouAn where data ℕ : Set where zero : ℕ suc : ℕ → ℕ _+_ : ℕ → ℕ → ℕ zero + m = m (suc n) + m = suc (n + m)
Make sure you get the indentation right! In particular, the constructors
for zero
and suc
start in the 5th
column (below the t in data
), whereas all three lines in the definition of
_+_
begin in column 3.
To check the program, type C-c C-l
in Emacs (or choose Load from the Agda
menu). If your program checks correctly, there will be no error messages, no hole markers
(yellow highlighting) and no orange-highlighted non-terminating sections. Also,
the words (Agda: Checked)
should appear in the Emacs mode line, and you should
notice that the colors of characters have changed.
Right now, our checks aren’t all that meaningful—the only thing they prove is that our addition function does indeed take any natural number and produce a natural number, as the type suggests. Later on, when we encode more information in our types, our checks can mean a lot more—even more than running and testing the program.
To evaluate an expression (just to verify that it truly does work), we can type
C-c C-n
into emacs, or select “Evaluate term to normal form” from the Agda
menu. Then, in the minibuffer, we can type an expression for 3 + 2:
(suc (suc (suc zero))) + (suc (suc zero))
This produces the following representation of the number 5:
(suc (suc (suc (suc (suc zero)))))
In this section we have examined the Peano natural numbers, and defined some basic functions and data types in Agda. In the next section, we’ll look at propositional logic, and how to encode logical proofs in Agda using this system.
If you are having trouble getting the First program to work, make sure you have the indentation right. It might help to download the LearnYouAn.agda file, just to be sure.
Now that we’ve defined the natural numbers, we’re going to do some simple example proofs of some basic mathematical properties. We’ll first discuss logic and logic specification in natural deduction, and as we go, we’ll discuss the application to Agda.
At a fundamental level, a logic is a system of judgments. Judgments are statements in a mathematical language that may be proven, or unproven. A language is usually described as a set of strings, which make up every term in the language, but this is a simplification: a language can be made up of arbitrary data structures. We just use strings to represent these structures because any data structure can be represented in some string form.
For our example, we will define a very simple logic based on the language of natural numbers $\mathbb{N}$ we used earlier.
We’re going to have just one type of judgment, of the form $\mathbb{N}\ \textbf{even}$, which is provable only when the given number is even.
A logic consists of a set of axioms and a set of rules. Axioms are the foundation of the logic: they’re the basic, simple statements that are assumed to be true. Rules describe how to produce new theorems from existing ones. A theorem is a proposition that has been proved. Thus, the rules tell us how to construct proofs of statements using proofs of other statements. We can formally specify these axioms and rules in a meta-logic called natural deduction, by writing them in the form of inference rules, which look like this:
This says that if we can prove all of the premises $P_1 \cdots P_n$, then we can prove the conclusion $C$.
For our purposes, we have just one axiom, that the number zero is even. Axioms are written as inference rules with no premises:
Then, based on the inductive reasoning we used earlier, the rules for our logic should express that if some number $m$ is even, then $m + 2$ is also even. We do this by writing an inference rule schema, which describes a set of rules, by including one or more metavariables in an inference rule.
If we have some metavariable $x$ in a rule schema, we can substitute any term in the language for $x$, and the result is a valid rule. For example,
If we want to show that four is even, we apply this rule twice (once where $x$ is two, and once when $x$ is zero), leaving the obligation $\mathtt{zero}\ \textbf{even}$ which is shown by the axiom ${\rm Z\scriptsize ERO}$.
We can write this proof using natural deduction in a “proof tree” format:
When proving a theorem, we work from the bottom of this tree upwards, applying rules that fit the form of the goal as we go. When reading the proof, we work downwards, reasoning from known axioms to the theorem that we want.
Agda’s types correspond to judgments. If we can construct a value, or “inhabitant,” of a certain type, we have simultaneously constructed a proof that the theorem encoded by that type holds.
As types are judgments, and values are theorems, data constructors for a type correspond to inference rules for the corresponding proposition.
Let’s encode the judgment $\textbf{even}$ in Agda, based on our definition in
natural deduction.
We’ll use the mix-fix name _even
here rather than just even
so that we can
use the judgment in post-fix form. As our judgment is over the language of
natural numbers, we index the type constructor for our judgment by the type ℕ
.
data _even : ℕ → Set where
Then we can define the axiom ${\rm Z\scriptsize ERO}$ as a
constructor for the type zero even
as follows:
ZERO : zero even
The ${\rm S\scriptsize TEP}$ axiom is a little more complicated, due to the presence of the metavariable $x$. If we just write the rule as-is,
STEP : x even → suc (suc x) even
Agda responds with the following:
/home/username/LearnYouAn.agda:14,12-13
Not in scope:
x
at /home/username/LearnYouAn.agda:14,12-13
when scope checking x
To resolve this, we let STEP
depend on a parameter, or variable. We name
this variable $x$, and specify that $x$ must be of type ℕ, as follows:
STEP : (x : ℕ) → x even → suc (suc x) even
This is an example of a dependent type, which provides a means of defining a
rule schema (a collection of rules parameterized by a variable).
Here, for example, STEP zero
refers to the rule zero even → suc (suc zero) even
.
Thus STEP
has the same substitution semantics for metavariables that we
described above.
At this point, our full program looks as follows:
module LearnYouAn where data ℕ : Set where zero : ℕ suc : ℕ → ℕ _+_ : ℕ → ℕ → ℕ zero + n = n (suc n) + m = suc (n + m) data _even : ℕ → Set where ZERO : zero even STEP : (x : ℕ) → x even → suc (suc x) even
You should type this program into your Emacs buffer, save it as the file
LearnYouAn.agda, and check it with C-c C-l
Before we go on to use our program to actually prove something, we make
one more observation. In the current version of our program, the type of x
can be inferred from its context, since we use it with _even
, which takes a
instance of type ℕ
as an argument. Therefore, so we can use the special ∀
symbol to introduce x
and omit the type. So, our final definition of _even
is
data _even : ℕ → Set where ZERO : zero even STEP : ∀ x → x even → suc (suc x) even
(This change will appear in the file LearnYouAn2.agda below.)
Now we use our Agda program to prove that four is even. Add the following lines
to your LearnYouAn.agda file, and then type C-c C-l
(use \_1
to type the subscript symbol ₁):
proof₁ : suc (suc (suc (suc zero))) even proof₁ = ?
Agda will convert the question mark into the symbols { }0
, which is Agda’s
notation for a hole in the program (i.e., a hole in the proof).
proof₁ : suc (suc (suc (suc zero))) even proof₁ = { }0
The following will appear in a separate Emacs buffer:
?0 : suc (suc (suc (suc zero))) even
This tells us that our proof obligation at hole ?0
is
suc (suc (suc (suc zero))) even
.
Next, put your cursor into the hole, and type STEP ? ?
, and then type C-c
C-space
. This splits the hole in two more holes.
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP { }1 { }2
?1 : ℕ
?2 : suc (suc zero) even
The obligation ?1
, corresponding to hole { }1
, is the number we must provide
for x
when applying the STEP
constructor.
Hole { }2
corresponds to proof obligation ?2
, which is the obligation to
show that two is even. In this case, there is only one constructor that fits
the obligation—namely, STEP
Move the cursor inside hole { }2
and type C-c C-r
. Agda splits the hole
into another STEP
call for us, resulting in two more holes, { }3
and { }4
.
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP { }1 (STEP { }3 { }4)
?1 : ℕ
?3 : ℕ
?4 : zero even
Another C-c C-r
in hole { }4
will fill it with ZERO
.
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP { } (STEP { } ZERO)
?1 : ℕ
?3 : ℕ
The remaining obligations, ?1
and ?3
, can also be fulfilled automatically based
on the surrounding context. We can see what constraints on the holes are known
to Agda by using the “Show Constraints” option, or by typing
C-c C-=
. For the present example, this prints the following:
?1 := suc (suc zero)
?3 := zero
Agda will fill in the holes for us if we use the “Solve Constraints” option, or
C-c C-s
, and our final proof becomes
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP (suc (suc zero)) (STEP zero ZERO)
Another feature worth mentioning is Agsy, an automatic proof searcher for
Agda. Simply type C-c C-a
in any hole and Agsy will search for an appropriate
term to fill it. It’s not guaranteed to find anything, but it can be useful.
(In the example above, it works well.)
It can be annoying, though, to have to pass in those numbers to STEP
explicitly, when Agda already knows from surrounding context exactly what they
are. In these situations, you can use a single underscore (_
) to indicate that
you wish Agda to infer the value in this position during type-checking.
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP _ (STEP _ ZERO)
If Agda cannot infer the value of the implicit underscore, an “unsolved metavariable” will be shown in the goals window, and the underscore will be highlighted in yellow.
For this particular judgment, however, it is almost always obvious from known constraints what the value of those numbers should be. In these cases, it’s common to use an implicit parameter when declaring the type:
data _even : ℕ → Set where ZERO : zero even STEP : ∀ {x} → x even → suc (suc x) even -- long form { x : ℕ } is also fine
Note that here we have added braces around the variable x
in our definition of
STEP
. This lets us omit the number entirely when writing our proof:
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP (STEP ZERO)
If Agda cannot, for whatever reason, infer the value of the implicit parameter, yellow highlighting and an unsolved metavariable will be added, as before. In those scenarios, you can manually specify the value of the implicit parameter by using braces:
proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP {suc (suc zero)} (STEP {zero} ZERO)
For _even
, this is not usually necessary, but if you find yourself specifying
implicit parameters frequently, you may wish to consider making it explicit.
In Agda, an implication proposition corresponds to a function type.
When we prove an implication, say A ⇒ B
, we assume the premise A
, and
derive the conclusion B
. In terms of proof by construction, this
is the same as implementing a function—a function that, when given an argument
of type A
, constructs an return value of type B
.
In other words, given a proof (by construction) of some proposition
A
, our function produces a proof of proposition B
.
By writing such a function (and having Agda check it), we have constructed a
proof of A ⇒ B
.
Consider the simple tautology A ⇒ A
.
Let’s prove this in Agda!
First, we prove it for the proposition that natural numbers exist, that is, “if natural numbers exist, then natural numbers exist.”
proof₂ : ℕ → ℕ proof₂ ν = ν -- type \nu for ν.
So, proof of this tautology corresponds to the identity function. The reason for
this is fairly clear: Given a proof of A
, there’s one obvious way to produce
a proof of A
: present the same proof you were just given!
It would be nice though, to make the above proof about all propositions, not merely the proposition that natural numbers exist—after all, the proof is the same regardless of the proposition involved!
To do this, we have to exploit Agda’s flexible type system a little. We make our identity function take an additional parameter—a type. Given a type, we then return an identity function, instantiated for that type.
proof₂′ : (A : Set) → A → A proof₂′ _ x = x
The new type signature here means: Given some value of type Set
(i.e., a type),
called A
, return a function from A
to A
. We take this to be equivalent to
the logical statement, “For any proposition A
, A ⇒ A
.”
In logic, the universal quantifier symbol ∀
means “for any” or “for all”.
So, the above type signature could be stated as, $(\forall A) (A \Rightarrow A)$.
Making propositions about all members of a set (or universe) is called
universal quantification, and it corresponds to parametric polymorphism
(including Java generics and C++ templates) in type system lingo.
Now we can implement our special case proof, proof₂
, in terms of the more
general proof₂′
, as follows:
proof₂ : ℕ → ℕ proof₂ = proof₂′ ℕ
In other words, “to prove ℕ → ℕ, apply proof₂′
to type ℕ.”
In the next section we will start a new Agda program so, before moving on, we list the full contents of our first Agda program, which resides in the file LearnYouAn2.agda.
module LearnYouAn2 where data ℕ : Set where zero : ℕ suc : ℕ → ℕ _+_ : ℕ → ℕ → ℕ zero + n = n (suc n) + m = suc (n + m) data _even : ℕ → Set where ZERO : zero even STEP : ∀ x → x even → suc (suc x) even proof₁ : suc (suc (suc (suc zero))) even proof₁ = STEP (suc (suc zero)) (STEP zero ZERO) proof₂ : ℕ → ℕ proof₂ ν = ν proof₂′ : (A : Set) → A → A proof₂′ _ x = x
Unlike universal quantification or implication, conjunction and disjunction do not correspond to built-in types in Agda, however they are fairly straightforward to define.
The conjunction of the propositions $P$ and $Q$ is denoted by $P\conj Q$. Informally, to prove the conjunction $P\conj Q$, we prove each of its components. If we have a proof each component, then we automatically have a proof of their conjunction. Thus conjunction corresponds to a pair or a tuple (more formally known as a product type) in Agda.
More formally, to give meaning to conjunction, we must say how to introduce the judgment $P \conj Q\ \true$. A verification of $P \conj Q$ requires a proof of $P$ and a proof of $Q$. Thus, the introduction rule for conjunction is,
Let’s introduce conjunction in Agda. Create a file called IPL.agda
containing the following (and check it with C-c C-l
):
module IPL where data _∧_ (P : Set) (Q : Set) : Set where ∧-intro : P → Q → (P ∧ Q)
(The symbol ∧
is produced by typing \and.)
Here we’ve defined a new data type, this time it is parameterized by two propositions, which make up the components of the conjunction. Conjunction itself is also a proposition, and we give it the type Set.
Notice how the ∧-intro
constructor can only produce a proof of P ∧ Q
if it
is passed both a proof of P
and a proof of Q
. This is how conjunction is
demonstrated by construction—it is impossible to construct a conjunction that
is not supported by proofs of each component.
We now prove some simple properties about conjunctions, such as P ∧ Q ⇒ P
.
proof₁ : {P Q : Set} → (P ∧ Q) → P proof₁ (∧-intro p q) = p
This is the elimination rule sometimes called “and-elim-1” or “and-elim-left.”
If we define a similar rule for “and-elim-2”, we have the following program:
module IPL where data _∧_ (P : Set) (Q : Set) : Set where ∧-intro : P → Q → (P ∧ Q) proof₁ : {P Q : Set} → (P ∧ Q) → P proof₁ (∧-intro p q) = p proof₂ : {P Q : Set} → (P ∧ Q) → Q proof₂ (∧-intro p q) = q
Now that we have defined conjunction and implication, we can define a notion of
logical equivalence. Two propositions are equivalent if both propositions
can be considered to be the same. This is defined as: if one is true, the other
is also true. In logic, this is called bijection and is written as
A ⇔ B
. Bijection can be expressed simply as a conjunction of two implications:
If A is true then B is true, and if B is true then A is true.
_⇔_ : (P : Set) → (Q : Set) → Set a ⇔ b = (a → b) ∧ (b → a)
(The symbol ⇔
is produced by typing <=>.)
We can write programs (i.e. proofs) of the algebraic properties of conjunction. The commutative property says that $A \conj B \Leftrightarrow B \conj A$. Let’s prove it:
∧-comm′ : {P Q : Set} → (P ∧ Q) → (Q ∧ P) ∧-comm′ (∧-intro p q) = ∧-intro q p ∧-comm : {P Q : Set} → (P ∧ Q) ⇔ (Q ∧ P) ∧-comm = ∧-intro (∧-comm′ {P} {Q}) (∧-comm′ {Q} {P}) -- implicits provided for clarity only.
Remove the implicits and have Agda check the following:
∧-comm′ : {P Q : Set} → (P ∧ Q) → (Q ∧ P) ∧-comm′ (∧-intro p q) = ∧-intro q p ∧-comm : {P Q : Set} → (P ∧ Q) ⇔ (Q ∧ P) ∧-comm = ∧-intro ∧-comm′ ∧-comm′
Let’s also prove associativity.
∧-assoc₁ : { P Q R : Set } → ((P ∧ Q) ∧ R) → (P ∧ (Q ∧ R)) ∧-assoc₁ (∧-intro (∧-intro p q) r) = ∧-intro p (∧-intro q r) ∧-assoc₂ : { P Q R : Set } → (P ∧ (Q ∧ R)) → ((P ∧ Q) ∧ R) ∧-assoc₂ (∧-intro p (∧-intro q r)) = ∧-intro (∧-intro p q) r ∧-assoc : { P Q R : Set } → ((P ∧ Q) ∧ R) ⇔ (P ∧ (Q ∧ R)) ∧-assoc = ∧-intro ∧-assoc₁ ∧-assoc₂
If conjunction is a pair, because it requires both proofs to hold, then
disjunction is a sum type (also known as an Either
type), because it only
requires one proof in order to hold. In order to model this in Agda, we add
two constructors to the type, one for each possible component of the
disjunction.
data _∨_ (P Q : Set) : Set where ∨-intro₁ : P → P ∨ Q ∨-intro₂ : Q → P ∨ Q
Using this, we can come up with some interesting proofs. The simplest one to
prove is disjunction elimination, which is the rule
∀A B C ⇒ ((A ⇒ C) ∧ (B ⇒ C) ∧ (A ∨ B))⇒ C
.
In other symbols,
In words, if $A$ implies $C$ and $B$ implies $C$, and $A$ is true or $B$ is true, then if follows that $C$ is true.
∨-elim : {A B C : Set} → (A → C) → (B → C) → (A ∨ B) → C ∨-elim ac bc (∨-intro₁ a) = ac a ∨-elim ac bc (∨-intro₂ b) = bc b
We can also prove the algebraic properties of disjunction, such as commutativity:
∨-comm′ : {P Q : Set} → (P ∨ Q) → (Q ∨ P) ∨-comm′ (∨-intro₁ p) = ∨-intro₂ p ∨-comm′ (∨-intro₂ q) = ∨-intro₁ q ∨-comm : {P Q : Set} → (P ∨ Q) ⇔ (Q ∨ P) ∨-comm = ∧-intro ∨-comm′ ∨-comm′
The associativity proof is left as an exercise for the reader.
You have probably noticed if you’re familiar with boolean logic that I’ve avoided mentioning false throughout this entire section. Unlike boolean logic, Agda’s intuitionistic logic does not have a well-defined notion of “false”. In classical and boolean logics, all propositions are considered to be either true or false. Intuitionistic logic, by contrast, is purely constructive. You can either construct a proof for a proposition, making it true, or you can fail to construct a proof, making you feel bad.
The only “false” values that exist in intuitionistic logic, therefore, are
values for which there can exist no proof. In Agda, this corresponds to a type
that contains no values. We call this type ⊥
, pronounced “bottom”. We define
it like so:
data ⊥ : Set where
That’s right. No, it’s not a mistake. There are no constructors for ⊥
. It is a
type for which it is impossible to produce a value. Having such a value allows
us to define negation (¬A
) as true if A
being true would mean bottom is true
(which is impossible). Or, in more formal terms: ¬A ⇔ (A ⇒ ⊥)
¬ : Set → Set -- for ¬ type \neg ¬ A = A → ⊥
This section has taught you how to encode propositional logic into Agda’s type system. The correspondences discussed here between disjunction and sum types, conjunction and product types, functions and implication, and propositions and types are the fundamentals behind the Curry-Howard Correspondence. Using these tools, you can encode any constructive proof in Agda, which covers a vast range of possible proofs, including the vast majority of proofs encountered in program verification.
Future sections will introduce relational equality, and begin proving some theorems about the Peano numbers we introduced in the previous section.
(This section did not appear in the original version of the tutorial. It is adapted from Stephen Diehl’s tutorial, is essentially independent of the rest of the tutorial, and could be read immediately after Section 1.)
Let’s get some practice creating some “much needed” gaps in our proofs and then filling them in. As we learned above, Agda calls such gaps “holes”.
Open a file called HoleFilling.agda and put the following code in it:
module HoleFilling where data Bool : Set where false : Bool true : Bool
Above we saw how to implement a general conjunction, but here we will implement a simpler
(post-fix) conjunction for the Bool
type in order to demonstrate the creation and removal of holes.
To implement conjunction for Bool
, we merely have to give the value of the conjunction for each
of the four possible pairs of Bool
values. We begin by entering the following
(put this outside the indentation block of data Bool
):
∧ : Bool → Bool → Bool ∧ a b = ?
Note that Bool → Bool → Bool
indicates the types of arguments ∧
should expect
(namely, ∧
is a function from Bool
type to Bool → Bool
type). Since Agda knows what to expect,
it can write much of the function for us. As usual, use C-c C-l
to type-check the program, and
Agda creates a hole where we had a question mark. Our program should now look like this
module HoleFilling where data Bool : Set where false : Bool true : Bool ∧ : Bool → Bool → Bool ∧ a b = {!!}
Now, with the cursor in the hole (i.e., at the braces { }), and with the hole highlighted,
type C-c C-,
and Agda should respond with a list of goals that we must accomplish in order to give a
valid definition of ∧
for type Bool
.
Goal: Bool
————————————————————————————————————————————————————————————
b : Bool
a : Bool
With the cursor still in the hole, hit C-c C-c
and Agda responds with
“pattern variables to case”, which prompts us to introduce a case for the first variable.
Next to the phrase enter the variable b
, to which Agda responds with
∧ : Bool → Bool → Bool ∧ a false = {!!} ∧ a true = {!!}
Put the cursor in the first of the two new holes and again type C-c C-c
but this time
enter a in response to the “pattern variables to case” prompt.
∧ : Bool → Bool → Bool ∧ false false = {!!} ∧ true false = {!!} ∧ a true = {!!}
With the cursor in the hole, type C-c C-c
and again enter a.
∧ : Bool → Bool → Bool ∧ false false = {!!} ∧ true false = {!!} ∧ false true = {!!} ∧ true true = {!!}
Finally, we fill in the right hand sides to comport with conjunction:
∧ : Bool → Bool → Bool ∧ false false = false ∧ true false = false ∧ false true = false ∧ true true = true
Instead of filling in the holes in the end manually, you could
place the cursor in each of the holes and hit C-c C-a
.
The result will be four false
’s, which wouldn’t be a very
useful definition of conjunction… so change the last false
to true
!
Below is a list of the Emacs commands we encountered above. For a more complete list, visit the Agda Wiki.
Command | Key binding |
---|---|
Load | C-c C-l |
Evaluate term | C-c C-n |
Show goals | C-c C-? |
Next goal | C-c C-f |
Split obligation | C-c C-space |
Split again | C-c C-r |
Show Constraints | C-c C-= |
Solve Constraints | C-c C-s |
Agsy find proof term | C-c C-a |
Copyright (c) 2013, Liam O’Connor-Davis
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of Liam O’Connor-Davis nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
suc
standing for successor. ↩
Unlike Haskell, type declarations are mandatory. ↩
Don’t worry if you’re scared by that ∀
sign, all will be explained in time. ↩
Don’t be scared by the term - structural recursion is when a recursive function follows the structure of a recursive data type - it occurs very frequently in functional programs.↩
Consider two finite algebras that are the same except for a relabeling of the operations. Our intuition tells us that these are simply different names for the same mathematical structure. Formally, however, not only are these distinct mathematical objects, but also they are nonisomorphic.
Isotopy is another well known notion of “sameness” that is broader than isomorphism. However, I recently proved a result that shows why isotopy also fails to fully capture algebraic equivalence. $\def\bA{\bf A} \def\bB{\bf B} \def\bC{\bf C}$
This post provides a little bit of background and motivation, with a couple of examples of the problems that arise when trying to decide whether two algebraic objects are “equivalent.”
Let us consider an example of two algebras that are the same except for the naming of their operations. (This is an elaboration on Exercises 5.1 in Algebras, Lattices, Varieties.)
Let $\bA$ be an algebra of size 4 and label the elements of the universe $\{1,2,3,4\}$. Suppose $\bA$ has two unary operations, $f^\bA$ and $g^\bA$, where $f^\bA$ is the permutation $(1,2)(3,4)$ and $g^\bA$ is the permutation $(1,3)(2,4)$.
Thus, the operations permute the elements as follows:
The congruence lattice of $\bA$ is
where, by abuse of notation, we identify the congruences with their corresponding partitions as follows:
Now consider the algebras $\mathbf{A}/\theta_1$, $\mathbf{A}/\theta_2$, and $\mathbf{A}/\theta_3$ which have respective universes
It is clear that
Also, no two factors are isomorphic. This is because of the naming of their respective operations.
The algebra $\mathbf{A}/\theta_1$ has operations $f^{\bA/\theta_1}$ and $g^{\bA/\theta_1}$, where $g^{\bA/\theta_1}$ permutes the blocks $\{1, 2\}\longleftrightarrow \{3, 4\}$ and $f^{\bA/\theta_1}$ leaves them fixed.
On the other hand, the algebra $\bA/\theta_2$ has operations $f^{\bA/\theta_2}$ and $g^{\bA/\theta_2}$, where $f^{\bA/\theta_2}$ permutes the blocks $\{1, 3\} \longleftrightarrow \{2, 4\}$ and $g^{\bA/\theta_2}$ leaves them fixed.
Thus, the operation in $\bA/\theta_1$ corresponding to the symbol $f$ is the identity, while in $\bA/\theta_2$ the $f$ symbol corresponds to the nonidentity permutation. But apart from this trivial relabeling, these two algebraic structures are the same.
This may be viewed as a minor technical issue that could be overcome by simply viewing the two algebras as nonindexed algebras (see, e.g., Polák), and we can certainly say that up to a relabeling of the operation symbols, the two algebras are isomorphic.
However, it would be preferable to have a notion of algebraic equivalence that did not hinge on operation alignment.
The example above was a simple, almost trivial illustration of how the notion of isomorphic (indexed) algebras fails to capture our intuitive understanding of what it means for two algebras to be structurally the same. But there are examples that go deeper and are not as easy to side step.
Consider the Oates-Powell Theorem 1 which states that the variety generated by a finite group has a finite equational basis.
In a 1982 paper Roger Bryant proved that if we merely single out an element of the group and giving it a label, then the resulting algebra (called a pointed group) generates a variety that need not be finitely based.
Any reasonable notion of algebraic equivalence should at least respect congruences lattice structure. The congruence relations of algebras are the kernels of homomorphisms, and the congruence lattice is a basic “algebraic” feature of an algebra. Certainly we cannot accept a notion of “sameness” that equates two algebras with different congruence lattices (puting aside for now what it means for two congruence lattices to be “different”).
Isotopy provides a notion of “sameness” of algebras that is less demanding than isomorphism, so there is some hope that it provides a reasonably intuitive notion of algebraic equivalence.
But by the foregoing remark, if isotopy is to serve as a notion of algebraic equivalence, we should at least demand that two isotopic algebras have the “same” congruence lattices, and for two lattices to be the “same” in any sense, they must have the same number of elements so that we can at least find a bijection between them (even if it is not a “structure preserving” bijection).
However, I recently proved that, for any number $N \in \mathbb{N}$, it’s easy to construct a pair of isotopic algebras with congruence lattices that differ in size by more than $N$ elements. We see from this that there is no hope for isotopy as a reasonable notion of algebraic equivalence. (A link to my short article describing the construction appears below.)
Later, we will explore Voevodsky’s univalence principle and try to write down the definitions of universal algebras as higher types so that we can exploit the ideas of homotopy type theory in order to possibly arrive at a more useful, intuitionistic definition of algebraic equivalence.
The Isotopy GitHub repository contains my article in which I present isotopic algebras with congruence lattices that are vastly different in size.
Claim: If the three $\alpha_i$’s pairwise permute, then all pairs in the lattice permute.
Whether or not this claim is true is a simplified version of a question left open by Pálfy and Saxl at the end of their 1990 paper. Below is a more formal statement of the problem, and a link to my notes describing a proposed method of solution. There remains one gap in the proof, that I’m not yet sure how to fill, but I am hopeful that the overall strategy will work.
In an attempt to prove the claim above and its generalization, I apply an idea described in Heinrich Werner’s paper called graphical composition.
Before giving a more precise statement of the problem, let us recall a couple of basic definitions. Given two equivalence relations $\alpha$ and $\beta$ on a set $X$, the relation
is called the composition of $\alpha$ and $\beta$, and if $\alpha \circ \beta = \beta \circ \alpha$ then $\alpha$ and $\beta$ are said to permute.
Problem. $\def\bA{\bf A} \def\bB{\bf B}$ Let $\bA$ be a finite algebra with $\operatorname{Con} \bA$ isomorphic to $M_n$, for some $n\geq 4$. If three nontrivial congruences of $\bA$ pairwise permute, does it follow that every pair of congruences of $\bA$ permute?
My GitHub repository contains the following:
]]>At this point, the TypeFunc repository is mostly a collection of links, but it also includes notes and software resources that I have found helpful.
]]>The published version of the paper is in the file DeMeo-Expansions-AU-2013.pdf, and is also available at springer.com.
In the file gap2uacalc.g is a GAP program that can be used on its own to convert GAP groups and G-sets into UACalc .ua files, which can then be imported into the Universal Algebra Calculator. See universalalgebra.org for more information about gap2uacalc.g.
For questions, comments, or suggestions please submit an issue.
Thanks for your interest in this work!
]]>The paper has been accepted and is in the final review stage. It should appear in print sometime in 2014. The original submission is available in the file DeMeo-IEProps-rev1.pdf. The latest revision is in the tex directory.
For questions, comments, or suggestions please submit an issue.
Thanks for your interest in this work!
]]>The GroupSound project is about harmonic analysis on finite groups. Classical dsp filtering algorithms can be implemented as operations involving functions (e.g., audio signals) defined on a finite group. That is, the group serves as the domain, or “index set,” of the functions. In this project, we explore the idea of using the finite group as an adjustable parameter of a digital audio filter.
Underlying many digital signal processing (dsp) algorithms, in particular those used for digital audio filters, is the convolution operation, which is a weighted sum of translations $f(x-y)$. Most classical results of dsp are easily and elegantly derived if we define our functions on $\mathbb{Z}/n\mathbb{Z}$, the abelian group of integers modulo n. If we replace this underlying “index set” with a nonabelian group, then translation may be written $f(y^{-1}x)$, and the resulting audio filters arising from convolution naturally produce different effects than those obtained with ordinary (abelian group) convolution.
By listening to samples produced using various nonabelian groups, we try to get a sense of the “acoustical characters” of finite groups.
Please visit the GroupSound project webpage for more details.
]]>Updates were rejected
problem that is described on this page.
Based on the information provided on that page, combined with a few other
commands required for Ubuntu machines, this post provides instructions for
cloning an Octopress repository.(This works on Ubuntu 13.10 and probably other variants.)
Make sure ruby1.9.1-dev and gem are installd:
sudo apt-get install ruby1.9.1-dev gem
gem install bundler
rbenv rehash
Get the source and initialize empty _deploy directory:
git clone git@github.com:williamdemeo/williamdemeo.github.io.git
cd williamdemeo.github.io/
git checkout source
mkdir _deploy
cd _deploy
git init
git remote add -t master -f origin git@github.com:williamdemeo/williamdemeo.github.io.git
cd ..
Use bundler to check that all the dependencies in GemFile are met:
bundle install
Generate the pages and deploy:
rake generate
rake deploy
Troubleshooting.
If rake generate
produces “invalid byte sequence in US-ASCII” then try the following:
# from the command line
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
bundle
Make some changes, e.g., create a new post:
rake new_post["title"]
Posts live in source/_posts
. Edit one and push the changes to source
branch either directly with Magit or with
git add .
git commit -am "explanation of changes"
git push origin source
Now preview the changes: rake preview
and load http://localhost:4000
in browser.
When satisfied, rake deploy
.
This post describes the proof as it was presented by Bauer. These notes are rough and intended for my own reference. Please see François Dorais’ blog post for a nice discussion of this topic.
The Axiom of Choice (AC) states that if $\mathcal{S}$ is a collection of nonempty sets, then there is a choice function $f$ that can be used to select an element from each set in $\mathcal{S}$.
Law of the Excluded Middle (LEM) states that $P$ is a proposition, then $P \bigvee \neg P$.
Diaconescu’s Theorem: AC $\rightarrow$ LEM.
Proof: Assume AC. Let $P$ be any proposition. We will prove $P \bigvee \neg P$.
Define the set $\mathbf{2} = \{0, 1\} = \{x \mid x = 0 \bigvee x= 1\}$.
Define the following sets:
Note that $P \Rightarrow A = B = \mathbf{2}$. Therefore, $A \neq B \Rightarrow \neg P$.
Both of the sets $A$ and $B$ are nonempty, since 0 belongs to $A$ and 1 belongs to $B$.
Therefore, $\{A, B\}$ is a set of nonempty sets, so by AC we have a choice function,
Now, because equality on $\mathbb{N}$ is decidabile (which can be proved by induction on $\mathbb{N}$), we can consider cases:
If $f(A) = 0 = f(B)$, then $0 \in B$, so $P$.
If $f(A) = 1 = f(B)$, then $1 \in A$, so $P$.
If $f(A) \neq f(B)$, then $A \neq B$ so $P$ cannot hold. (Recall, $P \Rightarrow A = B = \mathbf{2}$.)
We have covered all cases and found that $P \bigvee \neg P$ holds. ∎
Proofs of Diaconescu’s Theorem in Coq