GithubHelp home page GithubHelp logo

kevinsullivan / cs6501s23 Goto Github PK

View Code? Open in Web Editor NEW
6.0 5.0 6.0 8.95 MB

Formal Mathematics for Software Design

Makefile 0.05% Shell 0.18% Lean 27.08% Python 0.77% Batchfile 0.04% Pascal 5.10% CSS 1.14% TeX 0.37% HTML 52.74% C++ 10.41% JavaScript 2.08% Dafny 0.04%

cs6501s23's Introduction

Abstract Mathematics as Semantics Domains for Complex Computations

TL;DR

This repo supports the development and delivery of notes for an early graduate course in computer science at the University Virginia. This course is intended to take early graduate students from a point of having no knowledge of formal reasoning through basics of logic and proof using the Lean proof assistant, and into the realm of formalized mathematics. The thesis underlying this work is that CS can now benefit greatly by taking advantage of rapid advances in the formalization of abstract and advanced mathemtics to provide deep semantic foundations for critical application areas, including robotics, cyberphysical systems more generally, and AI. This work is thus meant to start to close a significant gap between mathematics and computer science research to improve software design, productivity, and dependability, but not on a path to formal verification. The evolving notes are here. We solicit interest and are open to research community engagement. Feel free to reach out: [email protected].

Motivation

For millenia, to the present day, abstract mathematics has been an analog, paper-and-pencil, pursuit. For most of that time, there was nothing but paper and pencil, of course and that's what it mostly remains.

One downside of this state of affairs is that it keeps it hard to formally relate software (purely digital) to the abstract mathematics of the domain, that it's meant to implement, expressed in the concrete languages of practitioners in these domains (e.g., physics, Einstein notation for tensors, coordinate-free points on smooth manifolds, and charts imposing coordinate systems on them, that the software is meant to implement.

This work has been funded in part by the National Science Foundation as part of our project on the Physical Semantics of Code. In this project we are devising, realizing, and evaluating new concepts and methods for constructing semantic domains for complex computations in statically typechecked and foundationally verified abstract mathematics--the mathematics of any given domain. For now we are working on formalizing aspects of classical physics in the service of robotics and cyber-physical systems more generally.

This Repository

This repository is the course development and delivery platform for CS6501, Special Topics in Software Engineering, Abstract Mathemtical Foundations, being taught in the Spring of 2023.

The rest of this document is entirely for my own use. It presents instructions, mostly for myself, on how to use it.

Some elements of the (not yet fully automated or very flexible) Sphinx-based workflow implemented here was adopted, with many thanks for making it available, from Jeremy Avigad's public site in support of a different project and presentation.

Technical Nonsense

The lean_source directory contains all of the information needed to produce notes for one course. Each major section of a course is a set of chapters, A section is represented by a folder. A chapter by a file within that folder. Within a chapter, subdivisions are by use of _______ section notations.

Follow the naming conventions. When you add file/chapters or folders, it's necessary to manually update the mkall.sh script file.

Beyond a brief introductory chapter, at the time this is being written, there is just one other section, on propositional logic and a verified, nicely designed implementation of this language.

  • Edit .lean files using Avigad-style markup in lean_source
  • From homedir, lean_source/mkall.sh generates .rst files
    • in source. for the book
    • an exercise file and a solution file for each section in ___

Dependencies

This repo uses VSCode remote containers and a customer docker image. All essential dependencies should be installed and you should be nearly ready to start producing content. The require pre-step is to create an aws credentials file in /root/.aws with the aws credentials for the aws user on whose behalf operations will run on the AWS back end. (You need to have in place VSCode with its Remote Containers extension, and Docker Desktop.)

Manual Processes

The following files are maintained by hand:

  • The file source/index.rst should have an entry for each chapter/dicrectory.
  • For each chapter/directory, there should be a .rst file in source. It should include
    • each of the sections.
    • For each section, there should be a .lean file in the appropriate place in lean_source.
  • Each section must have a line in lean_source/mk_all.sh.

Header Style

Numbering in outputs is determined by sphinx labels

  • # with overline, for parts
  • * with overline, for chapters (individual lean file)
  • =, for sections
  • -, for subsections
  • ^, for subsubsections
  • \“, for paragraphs

Build Script

From the top-level directory of the cloned repo do this:

  • lean_source/mkall.sh
  • make html
  • make latexpdf
  • deploy.sh (TODO: needs to be fixed)
  • instead follow instructions in UPLOAD.md

The script deploy.sh KS: FIX deploy everythings (textbook and user version of the example and solution files) to an arbitrary repository, set up to use the gh-pages branch to display the html. Note: Avigad uses the following here: ./deploy.sh leanprover-community mathematics_in_lean

Implementation

The lean markup processing program is in the lean_sphinx.py file.

cs6501s23's People

Contributors

avigad avatar kevinsullivan avatar patrickmassot avatar robertylewis avatar bartoszpiotrowski avatar kbuzzard avatar gebner avatar hmonroe avatar lcw avatar bryangingechen avatar ericrbg avatar guilhermehas avatar jcommelin avatar mcol avatar

Stargazers

ivanhb avatar  avatar Neha avatar  avatar  avatar 鐘天楽 avatar

Watchers

James Cloos avatar Valeria de Paiva avatar  avatar Kostas Georgiou avatar  avatar

cs6501s23's Issues

Section 2.6 Suggestions

2.6.2.1

Typo here with "sucg" instead of "such"

The standard notation for sucg a “meaning-of” operator is a pair of denotation or Scott brackets.

2.6.2.3

Might be helpful to first show the proof without the repeat and then show how long it is followed by showing how using repeat makes it much shorter. That would likely help student s get a better sense of why repeat is useful here and what is going on in the proof here as they can't step through each loop of the repeat

2.6.2. Commutativity - Typo

"The first two axioms that that the and and or operators (∧, ∨) are commutative."
I believe the sentence should be "The first two axioms are that the and and or operators (∧, ∨) are commutative.". Not sure if this is the intended meaning.

Chapter 2.1.3 - Suggestions

Possible terms to remove or de-emphasize?

  • context-free grammars

Writing Clarification Suggestions

2.1.3

Clarifying this paragraph

We’ll start by defining separate data types (each with just one value) to represent left and right parentheses, respectively. The names of the types are lparen and rparen. Each has a single value that we will call mk. We can use qualified names to distinguish these values: lparen.mk and rparen.mk.

To something more like

We’ll start by defining two data types. The names of the types are lparen and rparen. We use these to represent left and right parentheses, respectively. Each holds a single value inside it that we will label as mk. We can use qualified names to distinguish between the two types' differing mk values: lparen.mk and rparen.mk.


Clarifying this sentence

Second, if b is any balanced string, then the term mk_nonempty l b r is also (that is also represents) a balanced string, namely (b)

to something along the lines of

Second, if b is any balanced string, then the term mk_nonempty l b r is also a balanced string. What that represents is taking the existing string b and combining it with a left parenthesis l and a right parenthesis r. In other words, it represents turning b into (b)

Ch 4 - Suggestions

4.2.2. Operations:
"When proving function or other data type, however, the particular value of the type that you construct is usually important. "
I believe it should be something like "When proving a function or another data type, however, the particular value of the type that you construct is usually important. "

4.3.1. Data Type
"The list data type is surprising similar to the nat data type. Where as a larger nat is constructed from only a smaller nat, a larger list is constructed from a new first element (the head of the new list) and a smaller list (the tail of the new list)."
Should be "The list data type is surprisingly similar to the nat data type. Whereas a larger nat is constructed from only a smaller nat, a larger list is constructed from a new first element (the head of the new list) and a smaller list (the tail of the new list)."

4.3.4. Partial Functions
"Let’s see some fo the solutions that are available."
Should be "Let’s see some of the solutions that are available."

4.3.4.2. Option values
"The next solution changes the type of the function, so that return value is in the form of a variant type, a value of which is either none or some valid return value."
Should be something like "The next solution changes the type of the function, so that the return value is in the form of a variant type, a value of which is either none or some valid return value."

4.3.4.3. Precondition
"Let’s see how e might first write the function using a tactic script, to take advantage of your familiarity with using it to build proofs."
Should be "Let’s see how be might first write the function using a tactic script, to take advantage of your familiarity with using it to build proofs."

4.4.3. Map
"Of course well run into exactly the same sort of problem, of having to engage in error-prone cloning and editing of code, if we want to now map lists of Boolean values to lists of strings (e.g., mapping each tt to “T” and each ff to “F”).
And you can imagine many other examples: mapping lists of employees to list of their corresponding salaries, or mapping lists of Boolean values to lists of their negations, etc. "
Should be "Of course we'll run into exactly the same sort of problem, of having to engage in error-prone cloning and editing of code, if we want to now map lists of Boolean values to lists of strings (e.g., mapping each tt to “T” and each ff to “F”).
And you can imagine many other examples: mapping lists of employees to lists of their corresponding salaries, or mapping lists of Boolean values to lists of their negations, etc.
"

"The answer should now we pretty clear:"
Should be "The answer should now be pretty clear:"

4.5. Recursive Proofs
"provide a concrete and pecific example of this reasoning and how we can automate it using tools we already have, concluding what is called the induction axiom for natural numbers (arguments to P);"
Should be "provide a concrete and specific example of this reasoning and how we can automate it using tools we already have, concluding what is called the induction axiom for natural numbers (arguments to P);"

4.5.1. The Idea by Example
"The simp tactict tries to find, and if found applies, rules/axioms from the definition of the listed functions: here from just nat.add."
Should be "The simp tactic tries to find, and if found applies, rules/axioms from the definition of the listed functions: here from just nat.add."

4.5.1.2. A Solution
"From a proof that 0 is a left identity for 0 can we build a proof that 0 is a left identity for one!"
Should be "From a proof that 0 is a left identity for 0 we can build a proof that 0 is a left identity for one!"

4.5.1.8. Monoids and Foldr
"We can now define a general structure that we can instantiate to formally represent either and additive or a multiplicative monoid on the natural numbers."
Should be "We can now define a general structure that we can instantiate to formally represent either an additive or a multiplicative monoid on the natural numbers."

4.5.2.2. Induction Axiom for Lists
"We see again that there’s nothing myseterious about proof by induction."
Should be "We see again that there’s nothing mysterious about proof by induction."

"Every inductive axiom can be understood as a proof of universal generalization that asserts that every value of some input type has a corresponging result value of type."
Should be "Every inductive axiom can be understood as a proof of universal generalization that asserts that every value of some input type has a corresponding result value of type."

Ch 5 - Suggestions

  1. Typeclasses
    "Key ideas include the following: (1) we will define a safe version of foldr that wil works for any monoid; we’ll see how to associate monoid data (a binary operation, identity element, property proofs) with types, whose values are taken as monoid elements; (4) how to tell Lean to automaticall (implicitly) find and pass monoid data structures to functions depending on the types of other arguments; (5) and ways to use this approach in useful ways."

Should be something like: "Key ideas include the following: (1) we will define a safe version of foldr that will work for any monoid; (2) we’ll see how to associate monoid data (a binary operation, identity element, property proofs) with types, whose values are taken as monoid elements; (3) how to tell Lean to automatically (implicitly) find and pass monoid data structures to functions depending on the types of other arguments; (4) and ways to use this approach in useful ways."

5.1.1. Algebraic Structures
"This chapter will explain how to formalize such structures in Lean, settings patterns for more abstract mathematics as well as for important generalized programming abstractions, as well. For example, we ‘ll see that applicative functor objects extend function application to multiple arguments, and that monads extend function composition to add useful behaviors to it, in turn enabling apparently imperative styles of programming in pure functional languages."

Should be something like: "This chapter will explain how to formalize such structures in Lean, setting patterns for more abstract mathematics as well as for important generalized programming abstractions, as well. For example, we‘ll see that applicative functor objects extend function application to multiple arguments, and that monads extend function composition to add useful behaviors to it, in turn enabling apparently imperative styles of programming in pure functional languages."
I think "settings patterns" is supposed to be "setting patterns", but not 100% sure.

5.1.2. Monoids so far
"Here are examples using these constructs. .First we apply foldr to a monoid α and a list α."

Should be "Here are examples using these constructs. First, we apply foldr to a monoid α and a list α."

5.2. Associating values with types
"We can then use the *+ and * notations to denote whathever operators are recorded in the op field of any given monoid object."

Should be "We can then use the + and * notations to denote whatever operators are recorded in the op field of any given monoid object."

"For this to work (and for some other reasons) we’’ define separate additive and multiplicative monoid types."

Should be: "For this to work (and for some other reasons) we’ll define separate additive and multiplicative monoid types."

5.2.1. Foldr over any monoid
"Finally, given these constraints, we note an real opportunity. "

Should be: "Finally, given these constraints, we note a real opportunity. "

1.2.4. This class - Typo

We will mainly use Lean 3, nothwithstaning that Lean 4 is garnering real attention and effort.

The typo "nothwithstaning" should be changed to "notwithstanding".

Chapter 3 - Overall notes:

Chapter 3

Overall

Overall notes, the chapter moves through concepts very quickly. I think it needs some deeper explications of each concept before going forward

For something about type universes that may help a bit, one thing I remember about type universes that confused me at the time I learned them was that unlike OOP typing, an object cannot be a member of multiple types

3.4.4.1. Introduction Rules

Lambda expressions are not explained sufficiently before being used here. Would be helpful to describe the syntax here and what it means

2.7 Suggestions

For 2.7.1, it might be helpful to take some of the theorems in section 2.7.1 and have a checkpoint exercise just to make sure students understand the material by leaving blanks or chunks of some of the latter theorems to fill out. By leaving different parts of a theorem for students to fill out, students can ask for help on specific parts of the theorems based on what they got wrong/incorrect.

1.2.3. What is the point? - Typo

"This is is relevant because it suggests that fully formalizing the fully developed mathematical language of the given domain, we will be well on our way to having reference specifications and with computable implementations."

There are two "is" at the beginning of the sentence.
Also a "by" could be added between "that" and "fully".

Example of Automated Reasoning

"This means, among other things, that one can add points to points in tf without receiving any type errors from the programming language system, even though addition of points to points makes no physical sense and is inconsistent with the abstract mathematics of the domain. In an affine space, there is no operation for adding points to point."

This is more of a request for clarification. Does automating this rule of preventing the addition of a point to a point count as automated reasoning?

2.2.1. Syntax - Wording

"To begin, we define a datatype the values of which will represent our the variables."

I believe removing "the" before variables would help the wording

"To begin, we define a datatype the values of which will represent our variables."

1.2.3. What is the point? - Typo

"This is the of abstract specifictions from which concrete implementations are derived."

The typo could be changed by removing "of" and changing it to, "These are the abstract specifications from which concrete implementations are derived.", or something along those lines.

2.1.3. Formal Syntax - Typo

"We Open the bal namespace so that we don’t have to write bal. before each constructor name."

"Open" should not be capitalized and "bal." doesn't need the "."

Section 2.4.3 - Suggestions

  • It might be helpful to provide an example use of pEval as students likely don't have lean syntax quite down at this point
  • Worth defining what a unary operator is (that it only takes one input rather than two)
  • Worth clarifying what is mean by "Add end-to-end support for logical nand (↑) and nor (↓) expression-building operators"

Chapter 2.2 - Adding and Updating Comments Here

Chapter 2.2

2.2.2

There's a typo here, the comment here should read "return false (ff in Lean)"

def all_false : prop_var → bool
| _ := ff   -- for any argument return true (tt in Lean)

This prop_eval code is a bit confusing potentially. Maybe some more explanation like this would be good

--This function takes in a proposition expression *and* a function called i representing the interpretation of all variables
def prop_eval : prop_expr → (prop_var → bool) → bool
| (var_expr v) i := i v --a variable expression evaluates to just the interpretation of that variable
| (and_expr e1 e2) i := band (prop_eval e1 i) (prop_eval e2 i) --evaluate each part of the and expression individually and then do a boolean and (band) on those values
| (or_expr e1 e2) i := bor (prop_eval e1 i) (prop_eval e2 i) --evaluate each part of the or expression individually and then do a boolean or (bor) on those values

Chapter 4 - Overall Notes

Chapter 4

4.3.2

Infix operators/notation not explained before being used at

-- notation, :: is infix for cons

4.3.5. Exercises

Phrasing it as "values greater than 0" may make this proof trickier since Lean's contradiction tactic does not automatically detect 0 > 0 as a contradiction whereas it does detect 0 ≠ 0

Need to use something like gt_irrefl for the function what I can tell which is not something we've talked about . Likewise creating proofs to give the function requires something like nat.le_succ_of_le

4.4.8. Exercises

The map-reduce is a substantial step up from the other exercises. I think it might be better to either remove this one or change it to something that's a little easier to do

4.5. Recursive Proofs

Typo in this paragraph

provide a concrete and pecific example of this reasoning and how we can automate it using tools we already have, concluding what is called the induction axiom for natural numbers (arguments to P);

4.5.1.3. Summary: Proof by Induction

I think the code about factorials was mistakenly placed here or missing an explanation around it

4.5

Proofs about (+) and (*) are often hard to follow in lean due to using not using the kinds of infix operators that we're used to seeing. It might be very helpful to provide comments on each line to show the goal in terms of infix operators

I think it would be good to give some guidance for the mul_distrib_add_nat_left exercise as this was fairly tricky to figure out especially since it's not clear at first what the simp tactic recognizes for nat.add and what it does not

For the foldr' definition here, I think it might make more sense to use id instead of e here for the pattern matching. Using e is explained later in chapter 5, but isn't yet explained here

def foldr' {α : Type} : @monoid α → list α → α
| (monoid.mk op e _ _) l := foldr op e l

4.5.3. Inductive Families

Still shows the coming soon text here

2.5 - Typo And Suggestions

Typo on "in" at this sentence:

To illustrate the point, n this chapter we add a fourth section


Involute was not defined in 2.5.5 before the example was given there, so it would be good to add a definition before that

2.2.2. Semantics - Typo

"def all_false : prop_var → bool
| _ := ff -- for any argument return true (tt in Lean)"

I believe the comment should be, "for any argument return false (ff in Lean)"

2.8. Inference Rules Validation - Typo

"This chapter pulls together in one place a formal validation of the claim that our model of propositional logic satisfies all of the =inference rules of that logic."

Extra "=" that shouldn't be there

1.1.2. Costs of Concreteness - Typo

"First, as mentioned, it substitues concrete representations for abstract, adding inessesntial complexity to models and computations."
Substitues and inessesntial should be substitutes and inessential.

1.1.1. Abstract Mathematics/ 1.1.2. Costs of Concreteness - Clarity

When discussing affine spaces, tensor fields, and topological manifolds, the examples go over my head. I don't know if I should know these terms or if I haven't taken classes yet that go into these terms, but affine spaces are used again in 1.1.2. and I realized that I wasn't familiar with the term affine spaces. Maybe a small, quick definition in the beginning would help? Not completely sure.

3.2.2. Logical Types - Suggestions

"In Lean, types are terms, too, and so they have types, as we have already seen. So what is the type of 1 = 1. It’s Prop."

I believe a question mark would work better for the "So what is the type of 1=1" sentence, like so: "In Lean, types are terms, too, and so they have types, as we have already seen. So what is the type of 1 = 1? It’s Prop."

"We we have the following picture of the type hierarchy for the terms we’ve just constructed."
One of the "we" should be deleted.

"This is usually shortened to X, Y ⊢ X ∧ Y based on the assumption that everything to the left of the turnstile is assumed to have already been judged to be true. Such a rule can be pronounced as follows: in a context in which you have already judged X and Y to be true you can always conclude that X ∧ Y is true." --> Does this mean that just stating X or just stating Y means that those are true, or does it mean that X is just equal to X? Is it circular, and will the meaning always be this way for inference rules?

Chapter 5 Notes

chapter 5

Think the * needs to be escaped here on this line since it's missing inside of the brakets

and * as a notation for op in a multiplicative monoid, such as ⟨nat, , 1⟩.

Looks like there's a typo on this line:

In practce, Lean provides mechanisms for writing one definition and then cloning it automatically to produce the code for the other.

5.3.6. Example: default typeclass

The solution is already given for the exercise on the notes here. I think that was a mistake?

5.3.8. Typeclass inheritance

Might be good to copy paste the source code definition if this is going to be left in.
The actually types themselves are just Type u -> Type u

2.8.2. Proofs - Typo

"In the style of the preceding examples, give formal proofs of that the remaining inference rules are valid in our own model of propositional logic."

I believe that the "of" after "formal proofs" can be eliminated.

"To get you started, the following proof shosws that the false elimination inference rule is valid in our logic. "
"shosws" should be "shows"

Chapter 2.1.5 - Clarifying Writing

2.1.5

Total functions and natural numbers are not defined before they are used. Perhaps this can be rewritten:

In this case, there is total function from terms of type bal to ℕ, so we can specify the semantics as a function in Lean. (All functions in Lean represent total functions in mathematics.)

to be more like this:

In this case, this is a function from terms of type bal to ℕ (positive whole numbers). This is also total because all inputs (the strings) have a defined output (their depth). We can use that to specify the semantics as a function in Lean. (All functions in Lean represent total functions in mathematics)


This paragraph is a little tricky to read without background in lean. This may also be a good place for a visual. I could help create one here if you would like that

The function is defined by case analysis on the argument. If it is the empty string, mk_empty, the function returns 0. Otherwise (the only remaining possibility) is that the value to which sem is applied is of the form (mk_nonempty l b r) where l and r are values representing left and right parenthesis, and where b is some smaller string/value of type bal. In this case, the nesting depth of the argument is one more than the nesting depth of b, which we compute by applying bal recursively to b.

Perhaps something along the lines of:

The function is defined by giving values base on the possible ways the given string could be have been constructed. This is called case analysis. If it is the empty string, mk_empty, the function returns 0 since the empty string has no depth. Otherwise (the only remaining possibility) is that the value was constructed from combined some other string with parenthesis. In our code that means using mk_nonempty l b r where l and r are values representing left and right parenthesis, and where b is some smaller string/value of type bal. In this case, the nesting depth of the argument is one more than the nesting depth of b, so we can compute the depth by applying bal recursively to `b.

Change "model" to "representation" in many places, as in "model of propositional logic" to "representation thereof," provably satisfying all its essential axioms and and algebraic properties. One thing we haven't done yet is to formalize and verify the property of a propositional logic formula (a "proposition") as valid, satisfiable, unsat. It takes a bit of programming. I can help with one way to do it. But then you have both a (brute force) SAT solver as well as logically formal as well as computable satisfiability, validity, and unsatisfiability predicates. Your solution should provide fully abstract notation, unlimited variables, a full set of operators and their algebraic properties, axioms for formal reasoning, and theorems ultimately derived from them. [Kevin non-stationary foundations.]

1.1.3. A Path Forward - Clarity

"Perhaps the most fundamental reason is that math has up until recently been a quasi-formal, paper-and-pencil exercise, making it, hard even impossible, to connect code to such mathematics."

Sentence structure might be changed to "Perhaps the most fundamental reason is that math has up until recently been a quasi-formal, paper-and-pencil exercise making it hard, even impossible, to connect code to such mathematics." for clarity.

2.1.4. Inductive Datatype Definitions - Typo

"The langauge they define contains all of the strings constructible by any finite number of applications of the defined constructors and no other terms."

"langauge" should be "language"

2.2. Simplified Propositional Logic - Wording/Typo

"Our next step toward formalizing abstract mathematics for software engineering, we will specify the syntax and semantics of a simple but important mathatical language, namely propositional logic."

Mathatical should be mathematical, and I believe adding "For" would improve the wording.

"In our next step toward formalizing abstract mathematics for software engineering, we will specify the syntax and semantics of a simple, but important, mathematical language, namely propositional logic."

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.