x==y 

James B. Wilson

Colorado State

University

Graphic Design by Ezra Wilson

Lil' Pete & Big Jim had a problem

  • We had a proof the code would work
  • We had an example that didn't work
  • Here is what we found behind it all:
> V1 := ... // A vector space
> V2 := ... // Another vector space
> BaseRing(V1) eq BaseRing(V2);
true
> Dimension(V1)
1
> Dimension(V2);
1
> IsIsomorphic(V1,V2);
false
Done in the flagship Computer Algebra System (CAS) Magma

Isomorphic Vector Spaces is an ancient core  highly tested routine.

How does a bug like this happen? Is it a bug?

Warm up problems:

There are many reasons well-reasoned functions misbehave.

Side-effects

spotted as a problem in the 1960's, today it is being taken seriously in functional programming

Confusion in Java/C families

public double f(double y) {
    return y + Math.random();
}
f(2) != f(2)  // almost always.

"side-effects"  specifically hidden variables.

public int f(int y) {
    if System.in.read() >= 97
    	return y-1;
    else
    	return y+1;
}
f(2) != f(2)  // if user types lower/Upper case

More subtle Confusion

class Database {
  int count;
  public int uses() {
    return ++count;
  }
}
Database db = new Database();
db.uses() != db.uses()

Another "side-effect"  a.k.a. mutate state/hidden outcome.

"In languages with no side-effects, like Haskell, we can substitute equals for equals"
-Referential Transparency

Wikipedia Oct. 22, 2019

module Main (main) where

data Floop = One | Two | Three
instance Eq Floop where
    One == One         = True
    One == Two         = False
    One == Three       = False
    Two == One         = False
    Two == Two         = True
    Two == Three       = True   --- 2=3
    Three == One       = False
    Three == Two       = True   --- 3=2
    Three == Three     = True

shuffle       :: Floop -> Floop
shuffle One   = Two
shuffle Two   = Two    --- fix 2
shuffle Three = One    --- move 3

main = print ( (Two == Three) && (shuffle Two /= shuffle Three) )
--- prints "True" proving Haskell violates Leibniz Law

Haskell does not replace equal for equal

(Should someone correct Wikipedia?)

Types of Confusion

  • SIDE-EFFECTS
    • Hidden Variables: Program looks like \(f:A\to B\) but is actually \(\hat{f}:A\times X\to B\) (sometimes \(\hat{f}:\prod_{a:A}X_a\to B\)).
    • Hidden Outcomes: Looks like \(f:A\to B\) but acts like \(f:A\to B\times Y\).   Often \(\hat{f}(a)=(f(a),y)\) where system returns \(y\) by overwritting original \(a\) - mutated state.  Also could be \(\hat{f}:A\to B+Y\), e.g. errors.
  • HIDDEN PARTITIONS: Looks like \(f:A/_{\sim}\to B\) but runs like \(\hat{f}:A\to B\).

A pinch of computational algebra

Computational Vector Spaces

\(F\in\mathbb{M}_{2a\times 2b}(k)\) is \(K\)-linear if

\(X\in K\to (X\otimes I_{a}) F=F(X\otimes I_b)\)

Suffices to test on generators: 

\(K=\left\langle I_2,\begin{bmatrix} 0 & 1\\ -1 & 0 \end{bmatrix}\right\rangle\)

\((I_2\otimes I_a)F=F(I_2\otimes I_b)\)

\(\left(\begin{bmatrix} 0 & 1 \\ -1 & 0 \end{bmatrix}\otimes I_a\right)F=F\left(\begin{bmatrix} 0 & 1\\ -1 & 0 \end{bmatrix}\otimes I_b\right)\)

Assume \(K/k\) is a quadratic field extension, e.g. \(\mathbb{C}/\mathbb{R}\) or \(\mathbb{F}_9/\mathbb{F}_3\).

Vector Space Isomorphism Algorithm

Given: \(V_1,V_2\) K-vector spaces,

Find: K-linear maps \(V_1\to V_2\)

1. given by a \(2a\times 2b\)-matrix \(F\) over k.

2. where for each generator \(X\) of \(K\), 

\((X\otimes I_a)F=F(X\otimes I_b).\)

3. Sets up a system of k-linear equations to solve for \(F\)'s.

4. Isomorphism selects \(F\) invertible (Ronyai, Ivanyos, Brooksbank-Luks).

Computational Modules

Given a ring K generated by X, the K-module V is input by the image of X in \({\rm End}(V)\).

 

Concretely, \(V=k^a\) and we give

\(X_1,\ldots,X_m\in\mathbb{M}_a(k)\)

Module Isomorphism Problem

(a la 50 years of history)

 

Given: \(X_1,\ldots,X_m\in\mathbb{M}_{a}(k)\) and \(Y_1,\ldots,Y_m\in\mathbb{M}_{b}(k)\)

Find: F invertible and \((\forall i)(X_i F=FY_i)\)

Magma Vector Space Problem Diagnosis

Same algebra, different generators.

\(K_1=\left\langle I_2,\begin{bmatrix} 0 & 1\\ -1 & 0 \end{bmatrix}\right\rangle\)

\(K_2=\left\langle I_2,\begin{bmatrix} 1 & 1\\ -1 & 1 \end{bmatrix}\right\rangle\)

So \(K_1=K_2\) and every \(K_1\)-vector space is a \(K_2\)-vector space, but 

\(\left(\begin{bmatrix} 0 & 1\\ -1 & 0\end{bmatrix}\otimes I_a\right)F=F\left(\begin{bmatrix} 1 & 1\\-1 & 1\end{bmatrix}\otimes I_b\right)\)

has no solutions!

(A form of Hidden Partition Problem.)

Consequence

\(K_1=K_2\not\rightarrow {_{K_1} K_1}={_{K_2} K_2}\)

In fact in this model of modules these are not even comparable by isomorphism.

 

Further problems with == from the trenches

Equality tests are brittle. Why?

\(A,B: \mathbb{M}_{a\times b}(\mathbb{Z}/2[t])\)

  • \(A=_{\mathbb{M}_{3\times 4}(\mathbb{Z}/2[t])}B\Leftrightarrow A_{ij}=_{\mathbb{Z}/2[t]}B_{ij}\)
  • \(\sum_k A_{ijk} t^k=_{\mathbb{Z}/2[t]}\sum_k B_{ijk} t^k\Leftrightarrow A_{ijk}=_{\mathbb{Z}/2}B_{ijk}\)
  • \(A_{ijk}=_{\mathbb{Z}/2} B_{ijk}\Leftrightarrow (\exists q_{ijk})(A_{ijk}-B_{ijk}=q_{ijk}*n)\)

Misunderstanding == at any level destroys test, e.g.:

  • A package changes, returns numbers unreduced now requires explicit reduction.
  • A generic type parameter gets used that has a finer equality than the ones tested.

Equality is tiered, often the mistake/limits were made deep down inside someone thing you don't control.

Order of basis matters. Both spaces were designed to use "lex-least", but one was right-to-left the other left-to-right.

(Real bug was with \(\mathbb{F}^n\wedge \mathbb{F}^n\) and the skew matrices.)

Fix? have to trace maps from domain -- slower, awkward, easy to do wrong.

By fixing coordinate bases \(\mathbb{F}^a\otimes \mathbb{F}^b=\langle e_i\otimes e_j \mid i\in [a], j\in [b]\rangle\) and \(\mathbb{M}_{a\times b}(\mathbb{F})=\langle E_{ij}\mid i\in [a],j\in [b]\rangle\) we identify these two maps.

 

But it failed!  Why?

\mathbb{F}^a\times \mathbb{F}^b\rightarrowtail \mathbb{F}^a\otimes\mathbb{F}^b: (u,v)\mapsto u\otimes v
\mathbb{F}^a\times \mathbb{F}^b\rightarrowtail \mathbb{M}_{a\times b}(\mathbb{F}): (u,v)\mapsto u^{\dagger}v

These are equal 1-dimensional vector spaces, but still non-isomorphic?! How!

\mathbb{C}_1 = \left\langle\begin{bmatrix} 1 & 0 \\ 0 & 1\end{bmatrix},\begin{bmatrix} 0 & 1 \\ -1 & 0 \end{bmatrix}\right\rangle
\mathbb{C}_2 = \left\langle\begin{bmatrix} 1 & 0 \\ 0 & 1\end{bmatrix},\begin{bmatrix} 1 & 1 \\ -1 & 1 \end{bmatrix}\right\rangle
Magma System
> C1 := ...; C2 := ...;
> C1 eq C2;
true
> IsIsomorphic(C1, C2);
false

Possible Fixes

  • Refine the equality \(K_1=K_2\)
  • Make new type something like \(\prod_{K:Ring} X_K\) and only define modules over this.
  • Throw out `==`
  • Something more creative.

Refine the equality on rings

  • Solves one problem for one function on \({\sf Ring}\), what about the next?
  • Breaks many properly working things in \({\sf Ring}\).

Make a new Type for coefficients of modules

  • Similar to how pointed sets used for homotopy groups.
  • But inclusion of generators is a programmer's need, not a math requirement.
  • Delays the problem: user still has a ring with two generating sets and wants to compare modules.

Throw out ==?

  • Similar to functional program, e.g. Haskell, just decided not to allow side-effects.
  • Huge problems: since Dijkstra, equality has been key to flow control, and many other uses.
  • Overriding == is how we create certain types, quotients. Your logic will be far less expressive.
  • Mathematically this would lead to programming with \(\infty\)-groupoids and higher topos theory.  Very subtle.

A pinch of logic
(paper thin covering of a much tougher, richer, philosophical inquiry)

Leibniz' Law

\(x=y \Leftrightarrow (\forall f)(f(x)=f(y))\)

Indistinguishable Identicals

\(x=y \Rightarrow (\forall f)(f(x)=f(y))\)

Identical Indistinguishables

\(x=y \Leftarrow (\forall f)(f(x)=f(y))\)

Essential: Lets us substitute reliably

Motivated by contrapositive: \(x\neq y\) must have proof: \((\exists f)(f(x)\neq f(y)\).  Constructivist need to be careful here.

Referential Transparency? See Below.

Referential Transparency?

When serious ask real experts - Philosophers, start here:

https://www.iep.utm.edu/referential-opacity/
 

  • Credited to Quine in the 1960's, actually appears in Frege and later Russell-Whitehead.
  • Books & blogs struggle to state it consistently, resulting in claims that are untestable, or are simply giving Leibniz' Law by another name.
  • Common Version 1: \(y=f(x)\to (\forall g)(g(y)=g(f(x))\)   Use \(f=1\) recover "Indistinguishable Identicals".
  • Common Version 2: if \(f\) is referentially transparent and \(y=f(x)\to (g(y)=g(f(y))\), then \(g\) is referentially transparent.  Again \(f=1\) recovers a "Constructivist Indistinguishable Identicals".

...and of course all this is predicated on understanding what a function is.

 

Lets assume \(\lambda\)-calculus or Combinators.  In particular not Set Theory functions as we will need higher-order soon.

What?!

\(\lambda\)-calculus

Functions are substitution rules.

\(\lambda x.M\) indicates what letter in \(M\) is to be substituted.

Some drift to notation \(x\mapsto M\)

\(M[5/x]\) result of substituting \(x\) with 5.

Care taken to avoid confusion with reuse of variable name.

No Domains, No codomains.

Combinators

Fixed list of axiomatic primitive functions, usually

  • Identity I
  • Constant \(K_a\)
  • Graph Composition \(S\)

Functions are strings of letters from the primitive list.

No Domains, No codomains.

No formulas, instead the model for primitives gives meaning.

\(\lambda\)-calculus

Functions are substitution rules.

Computation: \(\beta\)-reduction, i.e.

given a list of substitution rules,

rewrite into a normal form, e.g. left-most, outer-most.

Combinators

Functions are strings of letters from the primitive list.

Computation: Given a model for the primitives, evaluate the primitives.

 

Canonical model: I, K, S can be given with \(\lambda\)-calculus.

 

Practical model: list of Intel/AMD etc.  Chipset instructions.

Key Point

Functions as substitutions/abstractions make sense even in high-order logic, e.g. functors between categories that are not small.

...and we will need types.

 

Simple Types (polymorphic Curry style):

 

  1. Two list of variables/constants (lower/Upper case will do), later there needs to be a universe tower also.
  2. Type Annotation \(a:A\) historically \(a\in A\)
  3. "Function" Type Builder   \(f:A\to B\)
  4. "Function" Elimination     \(\Gamma,a:A,f:A\to B\vdash f(a):B\), i.e. given an \(a:A\) you get \(f(a):B\) concept of domain/codomain

Just a meta-theory of rules to impose on consistent type annotation.  A model of these rules, say in \(\lambda\)-calculus, gives semantic meaning to "function".

Many other types needed

 

\(\prod_{a:A}B_a\)

\(A+B\)

W-types

More detail on the module problem

Problem is the function \(\Gamma:K\mapsto {_K Mod}\) disobey's Leibniz

\(\Gamma:{\sf Field}\to {\sf Cat}\), i.e. a map of a category to a 2-category...a higher order function.

 

This universe problem remains somewhat unsettled, but we will ignore it for now.

Indistinguishable identicals with Martin-Lof Identity types

x==y is a test

== : (a,b:type) -> (x:a) -> (y:a) -> bool

x == x = true

_ == _  = false

Semantics: true if equal, false if not

x==y is a type

== : {a,b:type} -> (x:a) -> (y:b ) -> type

MakeEq : (x:a) -> x==x

Semanitcs: x==y is the class of evidence accepted to believe x=y

x==y is a test

== : {a,b:type} -> (x:a) -> (y:a) -> bool

  • must be done at run time.
  • forced to claim non-equal

x==y is a type

== : {a,b:type} -> (x:a) -> (y:b ) -> type

  • can be checked at compile time
  • the types always exist, maybe without inhabitants.

x==y is a test

Usage

if ( x == y ) ...

so at run time decide where to go

x==y is a type

Usage

f : (K1:Ring) -> (K2:Ring) -> (proof:K1==K2) -> Module

can't even call f without equality, and the proof could be used to fashion the module consistantly

Martin-Lof's Identity Elimination Rule J

Need to transfer a proof:K1==K2 to a proof \({_{K1} K1}={_{K2} K2}\)

 

The relevant type family is 

\(hom(_{K1} V1,_{K2} V2):type\)

which really means

\(hom:\prod_{K1,K2:Field}\prod_{p:K1=K2} type^{K-Vect\times K-Vect}\)

And we know

\(\hom(_K V_1, _K V_2)=\{F:\mathbb{M}_{a\times b}(k)\mid (\forall i)(X_iF=FX_i)\}\)

Martin-Lof's Identity Elimination Rule J

I.e. We have 

\(C:\prod_{x,y:A}\prod_{p:x=y} type\)

and we know

     \(c:C(x,x,Reflexive(x))\)

 

Martin-Lof Identity type says this inductively defines the whole type family, namely

   \(\hom(_{K1} V1,_{K2} V2)=C(K1,K2,p:K1=K2)\)

is determined so as to agree when \(p=refl(K)\).

It is clear what it will do

  \(\hom(_{K1} V1,_{K2} V2)=C(K1,K2,p:K1=K2)= \{ F\mid XF=Fp(X)\)

where \(p:K_1\to K_2\) is an explicit mapping.

 

Fixing hidden partitions

 

  1. Use identity types instead of equality tests.
  2. Fact: with identity types \(x==_ : Any-> type\) becomes a type family.  Its image is \(\sum_{y:A}x==y\).
  3. So we get a map \(f:A\to A/==\), specifically \(f(a) = \sum_{y:A} x==y\).  
  4. Create the pushout of \(A/_{==}\leftarrow A\to B\)
  5. \(A/_{==}\to Out(f,g)\) replaces g and now respects equality.

In general

  • Hidden Partitions are everywhere you can override ==, e.g. in all of Java.
  • The pushout solution applies in general and factors through the hidden partition.

General Isomorphism

Identity types are generalize to any equivalence relation, isomorphism.

 

Generalized Module Iso Problem.

Given: \(X_1,\ldots,X_m\in \mathbb{M}_a(k)\) and \(Y_1,\ldots,Y_n\in\mathbb{M}_b(k)\)

Find: F, \(F^{-1}\langle X_1,\ldots,X_m\rangle F=\langle Y_1,\ldots,Y_n\rangle\)

Thm. (Brooksbank-W.) Solved Module isomorphism with no distinguished generating set for rings that are cyclic.  I.e. \(K\cong k[t]/(a(t))\).

 

Builds on work of Kayal, Lenstra, Kantor, and Brooksbank-Luks.

 

Coro. (Brooksbank-W.) Can now properly solve isomorphism of finite-dimensional vector spaces over finite fields and number fields.

What of other contexts, like Lie?

Thm. (Grochow) Conjugacy of Lie matrix algebras is Graph Isomophism hard.

 

Even the associative case is limited to cyclic rings and nontrivial.

 

 

Implication to Univalence

  • Veovodsky (inspired by Hofman, Streicher, Awodey and others with similar ideas) made this Axiom of "Univalence"

\(x=y\to x\cong y\) is an isomorphism.

I.e. why make some isos "=" and other not?  Seems arbitrary.

 

One reason: no great models of this yet, possibly one called "cubical" but very slow.

Digression: Polynomial Heirarchy

  • \(\Sigma_0:=P\) polynomial time
  • \(\Sigma_1:=NP\) nondeterministic p-time
  • \(\Sigma_2:=NP^{NP}=\Sigma_1^{\Sigma_1}\)
  • \(\Sigma_{i+1}:= \Sigma_1^{\Sigma_{i}}\)

Implication to Univalence

Axiom of "Univalence"

\(x=y\to x\cong y\) is an isomorphism.

 

Perhaps a distinction between the two that may matter: complexity!  

 

Consider instead hierarchy.  Start with judgemental =

\(x=y\hookrightarrow_P x\cong_P y\hookrightarrow_{NP} x\cong_{NP} y\hookrightarrow_{\Sigma_2} x\cong_{\Sigma_2} y... \)

This could axiomatically imply:

\(x\cong_{\Sigma_2} y\rightarrow_{\Sigma_2} x\cong_{NP} y\rightarrow x\cong_{P} y\rightarrow x=y\)

I.e. you have an axiom schema filtered through complexity

What does this look like say in Java?

  • x==y as references (h=-1)
  • x==y as Object.equals override
  • ADD Object.equals2, Object.equals3, etc. to get x==2 y, x==3 y etc. 
  • Pushout functions over the appropriate level of equality without explicit type conversion.

The point is:

With a better type system the problem never exists, even Haskell's problem would vanish.

 

And this should all be done by the compiler, 

but something else is missing.

But why stop there?

We should want an identity package because...
  • Tests for x==y are brittle
  • Modularity hides key details of equality
  • Fast tests are complex algorithms
  • And we know how to build it.

What's expected as packages today?

Collections

  • Lists
  • Arrays
  • Hash tables
  • Vectors
  • Trees
  • Sorted
  • Balanced (Red-black)
  • Concurrent
  • Serializable

IO

  • Buffered
  • Reader/Writer
  • Random Access
  • Encoded/Decoded
  • System
  • Files
  • Network Sockets

Sys/Util

  • ToString
  • ToInt
  • Printf's
  • Resources
  • Time
  • Regexp

Concurrancy

  • Threads
  • Thread Groups
  • Queues
  • Futures
  • Actors
  • Events

Higher-kinded

  • Functors
  • Option
  • Either
  • Monoids
  • (co-)Monads
  • Applicatives
  • Trampolines
  • Lens
  • StateMonads

Meta-methods

  • Annotations
  • Macros
  • Templates
  • Generics
  • Native
  • Reflection

Have we forgotten support for ==?

An Identity Package for the JVM? (scala.identity)

  • Inductive Identity Types
  • Automatic typing of equals
  • Flow control & Filters on Identity types
  • Color Refinement
  • Weisefeller-Leman
  • Endo-Hom
  • Adjoint-Tensor

x==y

By James Wilson

x==y

The bedrock of computational math is linear algebra, so it may surprise you to learn we still cannot tell if two 1-dimensional vector spaces are the same. The problem though is logic, specifically the reality that in programming x=y does not imply f(x)=f(y), even in cases where the programming language insists otherwise.

  • 747