Date post: | 14-Apr-2017 |
Category: |
Engineering |
Upload: | carin-meier |
View: | 926 times |
Download: | 0 times |
Genetic Programming and Beyond with clojure.specCarin Meier(@gigasquid)- Cognitect
I'm so excited to be here!
PerspectiveSeeing things in a different way
Java Developermany years
Everything is an object
Different way of thinkingModeling the world in a functional way
Four Tribes of MathematiciansDavid Mumford Math and Beauty and Brain Areas
Four Tribes of Programmers» Explorers
» Alchemists
» Wrestlers
» Detectives
Four Tribes of ProgrammersExplorers
Those who get excited about discovering a new algorithm or tool.
Four Tribes of ProgrammersExplorersGem Collectors
Bring beautiful things back from their discoveries
Four Tribes of ProgrammersExplorersMappers
Describe and chart the new lands
Four Tribes of ProgrammersAlchemists
Those who get excited about finding connections between different areas of programming or other fields that no one had seen before.
Pour one into another and explode!
Four Tribes of ProgrammersWrestlers
Those who enjoy the scale of programming.
They thrive on true big data, speed, and handling things at scale that would make other people faint
Four Tribes of ProgrammersDetectives
Those who find enjoyment in diving into the deep, detailed aspects of programming.
Understanding things at a 200X magnification.
Belong to One or Many
» Explorers
» Alchemists
» Wrestlers
» Detectives
Alchemist
Seek IntegrationI am a big “integration always proponent"
Integration
“Take risks at the edge of scholarship and seek integration.Most forays won’t yield anything, but some certainly will.”M Gazzaniga (Split Brain) leader of cognitive neuroscience
Combine clojure.spec with things I'm interested in
Clojure.specGenetic Programming
Why Clojure.spec?» Allows you to describe data and create a spec
» Allows you to validate data against the spec
» Allows you to generate data from the spec
Describe Data and Create Spec
foo is a integer
(require '[clojure.spec :as s]);; => nil(s/def ::foo int?);; => :user/foo
Validate Data against a Spec
Is this data a valid foo ?
(s/valid? ::foo 1);; => true
(s/valid? ::foo "hi");; => false
Validate Data against a Spec
Is this data a valid foo ?Why Not?
(s/explain ::foo "hi");;=> val: "hi" fails spec: :user/foo predicate: int?
Generate Data from a Spec
Can you give me some example data?
(s/exercise ::foo 1);; => ([-1 -1])
What is Genetic Programming?Evolving creatures through breeding and mutating
What is Genetic Programming?The creatures are PROGRAMS!
Our ExperimentGiven some dataUse genetic programming to evolve a spec program to describe it
Spec Creatures Eat Data["hi" true 5 10 "boo"]
Spec Creatures» program = spec
» score
{:program (s/cat :0 int? :1 string?) :score 0}
How to Score a CreatureHow much of the data can the spec consume without errors?
Perfect Score(s/explain-data (s/cat :0 int? :1 string?) [1 "hi"]);=> nil
Not Perfect ScoreUse explain-data to figure it out
(s/explain-data (s/cat :0 int? :1 string?) [1 true])
;=> #:clojure.spec{:problems [{:path [:1] :pred string? :val true :via [] :in [1]}]}
How to Score a Creature» Perfect match = 100
» Anything less is how far in the sequence it was valid
Creating a Creature
Creating a CreatureUse the power of Clojure!Code is Data
Creature Creation Controls» All start with s/cat
» Allowed preds
» integer?
» string?
» boolean?
» even?
» odd?
Creature Creation Controls» Allow composition
» s/+
» s/*
» s/and
» s/or
Creature Creation Controlsprobability knobs
(def seq-prob 0.3)(def nest-prob 0.00)(def max-depth 4)(def and-or-prob 0.85)
Creature Create!(make-random-cat 3);=> (clojure.spec/cat :0 (s/and integer? odd?) :1 integer? :2 boolean?)
Creature Create!Make an initial population
(defn initial-population [popsize max-cat-length] (for [i (range popsize)] {:program (make-random-cat (inc (rand-int max-cat-length)))}))
So Now What?How do they evolve?
Mutate
Mutate a Creature
» Walk the creatures program
» Choose a program segment with a probability
» Swap out the piece for newly generated segment
Mutate Creature!(mutate {:program '(clojure.spec/cat :0 (s/and integer? odd?) :1 integer?)})
;=> {:program (clojure.spec/cat :0 (s/or (s/and integer? even?)) :1 integer?)}
What about Breeding?
CrossoverTakes two creatures and swaps a node from one creature to another
Crossover» Use the walk function to select at a random
probability the crossover node from the first creature
» Insert it into the second creature's program at another random spot
Crosover!(crossover {:program '(clojure.spec/cat :0 (s/and integer? odd?) :1 integer?)} {:program '(clojure.spec/cat :0 string? :1 boolean?)})
;=> {:program (clojure.spec/cat :0 (s/and integer? odd?) :1 boolean?)}
We can now» Create Creatures
» Score Creatures
» Change Creatures with Mutation
» Breed Creatures with other Creatures
Evolution Process
Evolution Process» Create initial population
» Rank them
» Take the top two best ones and carry them over (this is known as elitism)
» Create the next generation from selecting creatures for crossover and mutation
» Repeat!
How do you select them?
How do you select them?Good Question
How do you select them?Tournament selectionbut there are many other ways too
Tournament Selection» Pick n creatures from the whole population
» Among those, pick the best scored one
This will allow diversity in our population that is needed for proper evolution
Ready to EvolveMake a function that has inputs
population size how many generations tournament size test data
Ends with creature that is perfect fit or max generations
(defn perfect-fit [creatures] (first (filter #(= 100 (:score %)) creatures)))
Moment of Truth
Evolve(def creature-specs (evolve 100 100 7 ["hi" true 5 10 "boo"]))
Evolve(def creature-specs (evolve 100 100 7 ["hi" true 5 10 "boo"]))
(perfect-fit creature-specs)
Evolve(def creature-specs (evolve 100 100 7 ["hi" true 5 10 "boo"]))
(perfect-fit creature-specs)
;=>{:program (clojure.spec/cat :0 string? ; :1 boolean? ; :2 (s/and integer? odd?) ; :3 integer? ; :4 string?) ; :score 100}
Yay!We Did it - but wait...
Our generated creature is a spec
Specs can generate data
Specs can generate data (s/exercise (eval (:program (perfect-fit creature-specs))) 5);; ([("" true -1 -1 "") {:0 "", :1 true, :2 -1, :3 -1, :4 ""}];; [("D" false -1 -1 "G") {:0 "D", :1 false, :2 -1, :3 -1, :4 "G"}];; [("12" false -1 0 "l0") {:0 "12", :1 false, :2 -1, :3 0, :4 "l0"}];; [("" false -1 -2 "") {:0 "", :1 false, :2 -1, :3 -2, :4 ""}];; [("2" false 1 0 "Jro") {:0 "2", :1 false, :2 1, :3 0, :4 "Jro"}])
We can use that generated data to generate more specs!
Genetic Programming Summary
Genetic Programming Summary» Start with a way to generate random creatures
» Have a way to evaluate their fitness
» Create a way to change them for the next generations using
» Mutation
» Crossover
Genetic Programming Summary» Have an evolution process
» Create an initial population
» Rank them
» Create the next generation using selection techniques and mutation/ crossovers
» Don’t forget about diversity
Clojure.spec is really coolWhat other things can it do?
Could We Use it to Make Code Smarter?
Wouldn't it be great if a program could recover from an error and heal itself
This code would be able to rise above the mistakes of its humble programmer and make itself better
Self Healing Code
Clojure.specSelf Healing Code
Self Healing Code IngredientsThe paper Towards Design for Self-healing outlines a few main ingredients that we will need
Self Healing Code Ingredients
» Failure Detection - This one is pretty straight forward. We need to detect the problem in order to fix it.
» Fault Diagnosis - Once the failure has been detected, we need to be able to figure out exactly what the problem was so that we can find a solution.
Self Healing Code Ingredients
» Fault Healing - This involves the act of finding a solution and fixing the problem.
» Validation - Some sort of testing that the solution does indeed solve the problem.
Now we have ingredientsHow do we self heal?
Horizontal Donor Code Transfer
MIT developed a system called CodePhage
CodePhage» Inspired from the biological world with horizontal
gene transfer of genetic material between different organisms.
» In it they use a horizontal code transfer system that fixes software errors by transferring correct code from a set of donor application
Cool! Can we do that in Clojure?
» Clojure - has macros that let code modify itself
» clojure.spec - gives code the ability to describe itself
» clojure.spec - let's code share these specs with other code with its global registry
» clojure.spec - gives us the ability to generate example data from the specs
Self Healing Clojure Experiment
» write a small report program
» function called report
» made up of three helper functions.
» takes in a list of earnings and outputs a string summary of the average.
Self Healing Clojure Experiment
(defn report [earnings] (-> earnings (clean-bad-data) (calc-average) (display-report)))
Self Healing Clojure Experiment
(defn report [earnings] (-> earnings (clean-bad-data) (calc-average) (display-report)))
Divide by zero error waiting in calc-average!!!
Our Goal» (should we choose to accept the mission)
On the error, use clojure.spec to find a matching replacement function from a set of donor candidates.
The Setupreport function - dive in
(defn report [earnings] (-> earnings (clean-bad-data) (calc-average) (display-report)))
clean-bad-data
It takes in a vector of anything and filters out only those elements that are numbers.
(defn clean-bad-data [earnings] (filter number? earnings))
clean-bad-data specsspec the input
earnings will be a vector, (for the params) with another vector of anything.
(s/def ::earnings (s/cat :elements (s/coll-of any?)))
clean-bad-data specsspec the output
Custom generator - which will constrain the generator to just returning the value [[1 2 3 4 5]] as its example data* will explain why later
(s/def ::cleaned-earnings (s/with-gen (s/cat :clean-elements (s/coll-of number?)) #(gen/return [[1 2 3 4 5]]))
clean-bad-data specsexample of running the function with specs
(clean-bad-data [1 2 "cat" 3]);=>(1 2 3)
clean-bad-data specsexample of running exercise on it
(s/exercise ::cleaned-earnings 1);=> ([[[1 2 3 4 5]] {:clean-elements [1 2 3 4 5]}])
spec calc-average function with vital/fatal flaw
(defn calc-average [earnings] (/ (apply + earnings) (count earnings)))
(s/def ::average number?)
(s/fdef calc-average :args ::cleaned-earnings :ret ::average)
display-report function
(s/def ::report-format string?)
(defn display-report [avg] (str "The average is " avg))
(s/fdef display-report :args ::average :ret ::report-format)
report function
(defn report [earnings] (-> earnings (clean-bad-data) (calc-average) (display-report)))
(s/fdef report :args ::earnings :ret ::report-format)
Ready for Test Drive
Ready for Test Drive(report [1 2 3 4 5]);=> "The average is 3"
And the fatal flaw:
(report []);=> EXCEPTION! Divide by zero
We have our problem setupNow... the Donor Candidates
Donor CandidatesIn another namespace there will be a number of functions all speced out
Donor CandidatesSome will match the spec for our failing function, some will not
Donor CandidatesBad Matches
» bad-calc-average - matches spec but wrong answer value
» bad-calc-average2 - good value but returns string
» adder - not spec match
Donor CandidatesGood Match better calc average
(s/def ::numbers (s/cat :elements (s/coll-of number?)))(s/def ::result number?)
(defn better-calc-average [earnings] (if (empty? earnings) 0 (/ (apply + earnings) (count earnings))))
We have the setupWe have the donor candidatesNow what is the process?
The Self Healing Process
The Self Healing Process» Try the report function and catch any exceptions.
» If we get an exception, look through the stack trace and find the failing function name.
» Retrieve the failing function's spec from the spec registry
The Self Healing Process» Look for potential replacement matches in the
donor candidates
» Check the orig function's and the donor's :args spec and make sure that they are both valid for the failing input
The Self Healing Process» Check the orig function's and the donor's :ret
spec and make sure that they are both valid for the failing input
» Call spec exercise for the original function and get a seed value. Check that the candidate function's result when called with the seed value is the same result when called with the original function.
The Self Healing Process» If a donor match is found, then redefine the
failing function as new function. Then call the top level report form again, this time using the healed good function.
» Return the result!
Code it up in a function with-healing
Try it out!!
First we call the report function with a non-empty vector.
(healing/with-healing (report [1 2 3 4 5 "a" "b"]));=>"The average is 3"
Now the big test...
(healing/with-healing (report []))
; ERROR in function self-healing.core/calc-average ; Divide by zero -- looking for replacement; Retreiving spec information for function ; {:args :self-healing.core/cleaned-earnings; :ret :self-healing.core/average, :fn nil}; Checking candidates better-calc-average ; adder bad-calc-average bad-calc-average2); ----------
; **Looking at candidate better-calc-average; ****Comparing args :numbers :cleaned-earnings with input [[]]; ****Comparing seed [[1 2 3 4 5]] with new function; ****Result: old 3 new 3; Found a matching candidate replacement;; Replacing with candidate match better-calc-average; ----------; Calling function again; Healed function result is: The average is 0;=>"The average is 0"
Since the function is now healed we can call it again and it won't have the same issue.
(healing/with-healing (report []));=>"The average is 0"
Success!
Self Healing Summary» Very simple - didn't do validation on fn of spec
» Only checked one seed value from exercise but could have done much more - 10 or 100
» We neglected to use the built in feature of spec to check functions that would have found the divide by zero before it happened
Self Healing Summary» clojure.spec rocks
» clojure.spec adds another dimension to how we can solve problems in self-healing
Code is out on Github
» https://github.com/gigasquid/self-healing
» https://github.com/gigasquid/genetic-programming-spec
Taking a Step Back
We've just scratched the surface on how clojure.spec can be used
To All My Fellow Explorers, Alchemists, Wrestlers, and Detectives
Clojure.spec has opened the doors wide to new and exciting areas
Challenge to youClojure.spec + ? = win
Thank you!
Pic Credits
Cardboard Box City
Cat in box
Forest
heart
tribe
evolution
mad scientist
gold
dna
programmer
test drive
cooking