├── .gitignore ├── .ruby-version ├── README.md ├── artificial_neural_networks ├── Elixir.NeuralNetwork.Neuron.beam ├── Elixir.NeuralNetwork.beam └── neural_network.ex ├── bfs.exs ├── concurrent_search.exs ├── data └── investment │ └── options.txt ├── generate_and_test.exs ├── random_people_generator_script.rb └── ucs.exs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | data/people/.DS_Store 3 | data/people/*.txt 4 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ai_algorithms_in_elixir 2 | These are all the coding scripts associated with my A.I. blog at http://automatingthefuture.com 3 | -------------------------------------------------------------------------------- /artificial_neural_networks/Elixir.NeuralNetwork.Neuron.beam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheQuengineer/ai_algorithms_in_elixir/89808dd4eb54b53fc97f7c98b28ae16547fadda0/artificial_neural_networks/Elixir.NeuralNetwork.Neuron.beam -------------------------------------------------------------------------------- /artificial_neural_networks/Elixir.NeuralNetwork.beam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheQuengineer/ai_algorithms_in_elixir/89808dd4eb54b53fc97f7c98b28ae16547fadda0/artificial_neural_networks/Elixir.NeuralNetwork.beam -------------------------------------------------------------------------------- /artificial_neural_networks/neural_network.ex: -------------------------------------------------------------------------------- 1 | defmodule NeuralNetwork do 2 | @moduledoc """ 3 | The Code that illustrates how to calculate the output of a neuron utilizing several 4 | Transfer functions. What you will see below is the Elixir based implementation 5 | of these various activation functions. This code accompanies the 2nd post in the 6 | Artificial Neural Network blog posts on Automating The future called 7 | `Activating The Artificial Neuron`. 8 | It also has code present that calculates the errors of a neurons output. 9 | The local error and the global error formulas can be seen in this file as well. 10 | """ 11 | defmodule Neuron do 12 | defstruct [inputs: [], weights: [], bias: nil] 13 | end 14 | 15 | def calculate_output(:linear, neuron) when is_map(neuron) do 16 | summation(neuron.inputs, neuron.weights) 17 | |> add_bias(neuron) 18 | |> linear 19 | end 20 | 21 | def calculate_output(:saturating_linear, neuron) when is_map(neuron) do 22 | summation(neuron.inputs, neuron.weights) 23 | |> add_bias(neuron) 24 | |> saturating_linear 25 | end 26 | 27 | def calculate_output(:symmetric_saturating_linear, neuron) when is_map(neuron) do 28 | summation(neuron.inputs, neuron.weights) 29 | |> add_bias(neuron) 30 | |> symmetric_saturating_linear 31 | end 32 | 33 | def calculate_output(:hard_limit, neuron) when is_map(neuron) do 34 | summation(neuron.inputs, neuron.weights) 35 | |> add_bias(neuron) 36 | |> hard_limit 37 | end 38 | 39 | def calculate_output(:positive_linear, neuron) when is_map(neuron) do 40 | summation(neuron.inputs, neuron.weights) 41 | |> add_bias(neuron) 42 | |> positive_linear 43 | end 44 | 45 | def calculate_output(:sigmoid, neuron) when is_map(neuron) do 46 | summation(neuron.inputs, neuron.weights) 47 | |> add_bias(neuron) 48 | |> sigmoid 49 | end 50 | 51 | def calculate_output(:symmetrical_hard_limit, neuron) when is_map(neuron) do 52 | summation(neuron.inputs, neuron.weights) 53 | |> add_bias(neuron) 54 | |> symmetrical_hard_limit 55 | end 56 | 57 | def calculate_output(:hyperbolic_tangent, neuron) when is_map(neuron) do 58 | summation(neuron.inputs, neuron.weights) 59 | |> add_bias(neuron) 60 | |> :math.tanh 61 | end 62 | 63 | @doc """ 64 | The Linear Error Function used for calculating the error for a single 65 | Neuron. 66 | """ 67 | def calculate_local_error(actual, ideal), do: ideal - actual 68 | 69 | @doc """ 70 | The Global Error Function used for calculating the error of the entire 71 | Neural network. 72 | `MSE` aka Mean Square Error which is the most commonly used in Neural Networks 73 | """ 74 | def calculate_global_error(:mse, error_calculations) when is_list(error_calculations) do 75 | actual = 76 | Enum.map(error_calculations, &:math.pow(&1, 2)) 77 | |> Enum.sum 78 | error = actual / length(error_calculations) 79 | "MSE ERROR: #{error}%" 80 | end 81 | 82 | @doc """ 83 | The Global Error Function used for calculating the eror of the entire 84 | Neural Network. This is usually used in the LMA Neural Network Architecture. 85 | `ESS` is the shortname for Sum of Squares Error. 86 | """ 87 | def calculate_global_error(:ess, error_calculations) when is_list(error_calculations) do 88 | errors = 89 | Enum.map(error_calculations, &:math.pow(&1, 2)) 90 | |> Enum.sum 91 | total_errors = errors / 2 92 | "ESS ERROR TOTAL: #{total_errors}" 93 | end 94 | 95 | def summation([], []), do: 0 96 | 97 | def summation(inputs, weights) when is_list(inputs) and is_list(weights) do 98 | [i1 | i_tail] = inputs 99 | [w1 | w_tail] = weights 100 | (i1 * w1) + summation(i_tail, w_tail) 101 | end 102 | 103 | def add_bias(calc, neuron), do: calc + neuron.bias 104 | 105 | defp sigmoid(calculation), do: 1 / (1 + :math.exp(-calculation)) 106 | 107 | defp positive_linear(calc) when calc < 0 , do: 0 108 | 109 | defp positive_linear(calc) when 0 <= calc, do: calc 110 | 111 | defp hard_limit(calc) when calc < 0, do: 0 112 | 113 | defp hard_limit(calc) when calc >= 0, do: 1 114 | 115 | defp symmetrical_hard_limit(calc) when calc < 0, do: -1 116 | 117 | defp symmetrical_hard_limit(calc) when calc >= 0, do: 1 118 | 119 | defp symmetric_saturating_linear(calc) when calc < -1, do: -1 120 | 121 | defp symmetric_saturating_linear(calc) when -1 <= calc and calc <= 1, do: calc 122 | 123 | defp symmetric_saturating_linear(calc) when calc > 1, do: 1 124 | 125 | defp saturating_linear(calc) when calc < 0, do: 0 126 | 127 | defp saturating_linear(calc) when calc <= 0 and calc <= 1, do: calc 128 | 129 | defp saturating_linear(calc) when calc > 1, do: 1 130 | 131 | defp linear(calc) when is_float(calc), do: calc 132 | end 133 | -------------------------------------------------------------------------------- /bfs.exs: -------------------------------------------------------------------------------- 1 | defmodule BreadthFirst do 2 | @moduledoc """ 3 | This is a Graph based Search Algorithm that is very popular in A.I.. 4 | The concept is to find the quickest path to a specific destination. 5 | This should only be used if there is a possible solution to be found, and if 6 | the depth of the search doesn't go that deep. Really deep levels of search 7 | can potentially make this algorithm search run for a very long time simply because 8 | it checks every single parent node and its children one by one. 9 | # Examples of real world usages 10 | - Finding quickest root to a place 11 | - Finding a specific item that is known to be present in a group of other items 12 | - Finding a anomaly in a group of related objects 13 | """ 14 | 15 | def search({room, item}) do 16 | start_unexplored_rooms_list 17 | if room in unexplored_rooms do 18 | start_exploring_rooms 19 | expand(room) 20 | find_in_room(item) 21 | else 22 | IO.puts("I don't understand.") 23 | end 24 | end 25 | 26 | defp house do 27 | %{ 28 | kids_room: ["Crib", "Teddy Bear", "Blanket", "Pacifier", "Tent", 29 | "Book Bag", "Piggy Bank", "Toy Box", "Cat In the Hat Book", 30 | "Pirate Ship", "LapTop"], 31 | laundry_room: ["Basket", "detergent", "Lysol", "Matches", 32 | "Clothes Line", "Broom", "Dust Pan", "Doggie Bed", "Mop"], 33 | back_yard: ["Tonka Truck", "Cheerios", "Basketball", "Plants", 34 | "Hammock", "Pool", "Wet bar", "Wet Suit", "Grill", "Hose", 35 | "Sun Glasses", "Gnome Statue"], 36 | game_room: ["Playstation 3", "Xbox", "Wallet","Jumanji Board Game", 37 | "TV", "pool table"], 38 | bath_room: ["Hair Spray", "Tooth brush", "soap", "Sleeping Pills", 39 | "Tooth Paste", "Bath Towels", "Plunger", "Bathroom rug", 40 | "Shower Curtain"], 41 | master_bedroom: ["Picture", "Cat", "Pillow", "Armoire", "Night Stand", 42 | "Plant Container", "Drapes", "Headboard", "Blinds", "Vases", "Photos", 43 | "Clothing", "Telephone", "Jewlery Boxes", "DVD Player"], 44 | green_house: ["Spray Bottle", "Shovel", "Light Hose", "Rain Boots"], 45 | garage: ["Audi", "Tool Box", "Rake"], 46 | guest_room: ["Car Keys", "TV", "Remote Control", "Lamp", "Book"], 47 | kitchen: ["Pots", "Spatula", "Knives", "Plates", "Refridgerator", 48 | "Deep Freezer", "Apron", "Coffee Machine", "Fruit Bowl", "Juicer"], 49 | study: ["Book Case", "Reading Lamp", "Office Chairs", "Computer", 50 | "Message Table", "Water Station", "World Globe", "Boat Model"], 51 | dining_room: ["Wine Glasses", "Table Leaf Bags", "Bench, Settee", 52 | "Wall Shelves", "Lazy Susan", "Valances", " China Cabinets", 53 | "Host Chairs", "Wall Clock", "Artwork", "Posters", "Candle Holders", 54 | "Boxes", "Napkins", "NapkinRings", "Buffet Lamps", "Water Vases", 55 | "Trays", "Crystal Glassware"], 56 | living_room: ["Moroccan Rug", "Coffee Table", "Bold Drapes", 57 | "Curved TV", "Comfy Pillows", "Throw Blankets"] 58 | } 59 | end 60 | 61 | defp explored_rooms do 62 | Agent.get(ExploredRooms, &(&1)) 63 | end 64 | 65 | defp unexplored_rooms do 66 | Agent.get(UnExploredRooms, &(&1)) 67 | end 68 | 69 | defp room_check_count do 70 | Agent.get(RoomCheckCount, &(&1)) 71 | end 72 | 73 | defp start_exploring_rooms do 74 | Agent.start(fn() -> [] end, [name: ExploredRooms]) 75 | start_room_check_count 76 | end 77 | 78 | defp start_unexplored_rooms_list do 79 | Agent.start(fn() -> Map.keys(house) end, [name: UnExploredRooms]) 80 | end 81 | 82 | defp start_room_check_count do 83 | Agent.start(fn() -> 0 end, [name: RoomCheckCount]) 84 | end 85 | 86 | defp expand(room) when is_atom(room) do 87 | Agent.update(UnExploredRooms, fn(list) -> List.delete_at(list, -1) end) 88 | Agent.update(ExploredRooms, fn(list) -> List.insert_at(list, 0, room) end) 89 | Agent.update(RoomCheckCount, fn(current_count) -> current_count + 1 end) 90 | end 91 | 92 | defp find_in_room(item) do 93 | Enum.map(unexplored_rooms, &look_for(item, &1)) 94 | end 95 | 96 | defp look_for(item, room) do 97 | room_items = Map.get(house, room) 98 | if item in room_items do 99 | IO.puts("Found the #{item} in the #{Atom.to_string(room)}") 100 | IO.puts("My search path before the item was found..") 101 | Enum.each(Enum.reverse(explored_rooms), &IO.puts(&1)) 102 | IO.puts("It took a total of #{inspect(room_check_count)} room checks before item was found!") 103 | else 104 | expand(room) 105 | end 106 | end 107 | end 108 | 109 | BreadthFirst.search({:master_bedroom, "Wallet"}) 110 | -------------------------------------------------------------------------------- /concurrent_search.exs: -------------------------------------------------------------------------------- 1 | defmodule ConcurrentSearch do 2 | @moduledoc """ 3 | This uses the Binary search algo which is the easiest search algorithm in computer science. 4 | This particular implementation simultanously searches every player file in the 5 | data directory for the target supplied by the user. It portrays the scalability 6 | and flexibility of elixir. 7 | 8 | The entry point is the `find/1` function. This module accompanies the third blog 9 | post about performing concurrent searches with elixir on http://automatingthefuture.com. 10 | """ 11 | 12 | def find(target) when is_number(target) do 13 | start_results_list 14 | files = fetch_files() 15 | IO.puts("Searching for #{target} in all #{IO.inspect(length(files))} files") 16 | {time, _} = :timer.tc(fn -> search_in(files, target) end) 17 | IO.inspect(Agent.get(__MODULE__, &(&1))) 18 | IO.puts("Search completed in #{IO.inspect(time)} MicroSeconds!") 19 | end 20 | 21 | defp search_in(files, target) do 22 | current_caller = self 23 | files 24 | |> Enum.map(fn(file) -> 25 | spawn_link(fn() -> 26 | send(current_caller, {self, find_in(file, target), file}) 27 | end) 28 | end) 29 | |> Enum.map(fn(pid) -> get_result_for(pid) end) 30 | end 31 | 32 | def find_in(file, target) when is_bitstring(file) and is_integer(target) do 33 | {:ok, data} = File.read(file) 34 | data 35 | |> prepare 36 | |> Enum.sort(&(&1 < &2)) 37 | |> divide_and_conquer_for(target) 38 | end 39 | 40 | defp fetch_files do 41 | Path.wildcard("data/people/*.txt") 42 | end 43 | 44 | defp divide_and_conquer_for(list, target) when is_list(list) do 45 | highest_boundry = length(list) - 1 46 | lowest_boundry = 0 47 | reduce_search(list, target, lowest_boundry, highest_boundry) 48 | end 49 | 50 | defp reduce_search(list, target, lowest_boundry, highest_boundry) do 51 | middleIndex = div(highest_boundry + lowest_boundry, 2) 52 | middleValue = Enum.at(list, middleIndex, "Ouch.. Nothing here!") 53 | 54 | cond do 55 | highest_boundry < lowest_boundry -> "Number not found." 56 | target < middleValue -> reduce_search(list, target, lowest_boundry, middleIndex - 1) 57 | target > middleValue -> reduce_search(list, target, middleIndex + 1, highest_boundry) 58 | target == middleValue -> middleValue 59 | end 60 | end 61 | 62 | defp get_result_for(pid) do 63 | receive do 64 | {^pid, "Number not found.", file} -> 65 | response = "#{"\u274C"} Nope. that number is not in file #{file}" 66 | Agent.update(__MODULE__, fn(list) -> [response | list] end) 67 | {^pid, number, file} -> 68 | response = "#{"\u2705"} Yes, #{number} is present in file #{file}." 69 | Agent.update(__MODULE__, fn(list) -> [response | list] end) 70 | end 71 | end 72 | 73 | defp prepare(data) do 74 | list = String.replace(data, "\n", " ") 75 | str_numbers_list = Regex.scan(~r/\d+/, list) 76 | str_numbers_list 77 | |> List.flatten 78 | |> Enum.map(&String.to_integer(&1)) 79 | end 80 | 81 | defp start_results_list do 82 | Agent.start(fn() -> [] end, [name: __MODULE__]) 83 | end 84 | end 85 | 86 | ConcurrentSearch.find(777) 87 | -------------------------------------------------------------------------------- /data/investment/options.txt: -------------------------------------------------------------------------------- 1 | Grocery Outlet, 3.39 2 | Target, 3.28 3 | Under Armor, 5.38 4 | Chipotle, 2.21 5 | AMC Theatres, 1.32 6 | Gold Nugget Makers, 3.32 7 | Marinas, 2.10 8 | Tesla Model 3, 1.19 9 | La Croix, 3.21 10 | Essex Properties, 1.39 11 | Comcast, 1.23 12 | AT&T, 4.21 13 | Pete's Coffee, 6.38 14 | HP, 11.12 15 | Apple, 2.17 16 | Wells Fargo, 1.20 17 | Macco, 2.99 18 | Firestone, 1.22 19 | Spoon, 1.33 20 | Microsoft, 6.21 21 | Bank Of America, 0.23 22 | Jabber, 0.11 23 | ZenDesk, 3.45 24 | StarBucks, 2.33 25 | NBA, 2.12 26 | Nike, 3.21 27 | California Pizza Kitchen, 2.73 28 | Weed Maps, 5.32 29 | eero, 1.98 30 | Vimeo, 3.12 31 | Baxter, 0.02 32 | Balloon Inc, 0.12 33 | GNC, 1.12 34 | Gas Inc, 0.11 35 | Sony, 9.21 36 | Naughty Dog, 8.21 37 | Riot Games, 2.88 38 | Bleacher Report, 5.32 39 | Lehman Brothers, 0.01 40 | AIG, 1.01 41 | Eylus, 0.21 42 | Kygeo, 0.11 43 | GymBot, 0.22 44 | Oreo, 3.11 45 | Rubbermaid, 0.92 46 | Subway, 1.03 47 | palm, 1.99 48 | Canon, 1.45 49 | IBM, 5.33 50 | Ask, 0.13 51 | Kodak, 3.21 52 | Oral-B, 2.28 53 | Hershey's, 0.03 54 | Dell, 2.19 55 | John Deere, 1.23 56 | CNN, 3.27 57 | Youtube, 2.28 58 | Costco, 2.92 59 | Kellog's, 0.44 60 | Chilis, 0.33 61 | KFC, 0.00 62 | KMart, -5.32 63 | Zippo, -2.99 64 | Fedex, -1.09 65 | Wendy's, 1.39 66 | USPS, -9.32 67 | Lego, 3.22 68 | NASA, 4.21 69 | LandRover, -7.12 70 | Colgate, 2.19 71 | Gatorade, 3.55 72 | Chevron, -0.89 73 | DHL, 0.00 74 | The Motion Man, 0.87 75 | Latby Industries, 0.99 76 | Pizza Hut, 6.00 77 | Crest, 0.99 78 | Ebay, 2.21 79 | Virgin, 3.25 80 | WooCoo, 0.76 81 | WellPixeled, 2.32 82 | VISA, 0.89 83 | FLyder, 3.01 84 | Itchy Pig, -3.21 85 | Hot Wheels, 0.13 86 | Cisco, 6.32 87 | Oracle, -3.21 88 | FirePup, -8.99 89 | NFL, 4.22 90 | PEPSI, 2.77 91 | Coke, 5.33 92 | Google, 6.19 93 | Facebook, 4.97 94 | Instagram, -33.22 95 | Twitter, -3.21 96 | ABC, 1.11 97 | Fox, -2.19 98 | Hulu, 2.37 99 | Boeing, 4.55 100 | Subaru, 2.43 101 | SEGA, -0.24 102 | Samsung, 2.09 103 | Bic, 0.03 104 | Adobe, 4.88 105 | Sun Microsystems, 5.32 106 | Plataformatec, 7.02 107 | Burger King, 6.39 108 | Compaq, 3.01 109 | ESPN, 7.21 110 | 7 up, -9.33 111 | Blue Bottle, 9.01 112 | Taco Bell, -14.22 113 | CAT, 3.67 114 | PopTarts, -2.77 115 | Wink, 5.03 116 | oyogi, -0.88 117 | WeWork, 2.97 118 | McDonald's, -3.23 119 | Shipster, 0.93 120 | Sprint, 0.92 121 | Nabisco, 2.10 122 | -------------------------------------------------------------------------------- /generate_and_test.exs: -------------------------------------------------------------------------------- 1 | defmodule GenerateAndTest do 2 | @moduledoc """ 3 | This script is designed to show the functionality of the AI Algorithm 4 | that is known as Generate and Test. It will produce the results to a 5 | addition question and answer whether or not the answer is even. 6 | 7 | For Context go and read the associated blog post at 8 | http://www.automatingthefuture.com/blog/2016/3/21/ai-generate-and-test-elixir 9 | """ 10 | 11 | require Integer 12 | 13 | def start do 14 | start_list 15 | generate(20) 16 | Enum.each(questions, &display_answer_for(&1)) 17 | end 18 | 19 | defp display_answer_for(question) do 20 | IO.puts(question) 21 | IO.puts(answer_to(question)) 22 | end 23 | 24 | defp answer_to(question) do 25 | Regex.scan(~r/(\d+) \+ (\d+)/, question, [capture: :all_but_first]) 26 | |> List.flatten 27 | |> Enum.map(&String.to_integer(&1)) 28 | |> Enum.reduce(0, fn(n, acc) -> n + acc end) 29 | |> even? 30 | |> say_answer 31 | end 32 | 33 | defp addition_question(first_number, second_number) do 34 | "Is the result of #{first_number} + #{second_number} even?" 35 | end 36 | 37 | defp even?(number) when is_number(number) do 38 | if Integer.is_even(number) do: true, else: false 39 | end 40 | 41 | defp say_answer(true) do 42 | "YES!" 43 | end 44 | 45 | defp say_answer(false) do 46 | "NOPE." 47 | end 48 | 49 | def start_list do 50 | Agent.start(fn() -> [] end, [name: __MODULE__]) 51 | end 52 | 53 | defp build_list(question) do 54 | Agent.update(__MODULE__, fn(list) -> [question | list] end) 55 | end 56 | 57 | def questions do 58 | Agent.get(__MODULE__, &(&1)) 59 | end 60 | 61 | def generate(amount_of_questions) when is_number(amount_of_questions) do 62 | generate(0, amount_of_questions) 63 | end 64 | 65 | def generate(accumulator, amount_of_questions) when amount_of_questions >= 1 do 66 | question = addition_question(Enum.random(1..100_000), Enum.random(1..100_000)) 67 | build_list(question) 68 | generate(accumulator + 1, amount_of_questions - 1) 69 | end 70 | 71 | def generate(total, 0) do 72 | IO.puts("#{total} addition questions generated.") 73 | :ok 74 | end 75 | end 76 | 77 | GenerateAndTest.start 78 | -------------------------------------------------------------------------------- /random_people_generator_script.rb: -------------------------------------------------------------------------------- 1 | require "faker" 2 | 3 | def generate(people_count) 4 | people_files = [] 5 | people_count.times do 6 | people_files.push("#{Faker::Name.first_name.downcase}-#{Faker::Name.last_name.downcase}-id-#{rand(20000)}.txt") 7 | end 8 | people_files.each do | file | 9 | File.open("data/people/#{file}", 'w') do | file | 10 | 5000.times do 11 | file.write(rand(1..8000)) 12 | file.write("\n") 13 | end 14 | end 15 | end 16 | puts "Generated #{people_count} files in data/people/" 17 | end 18 | 19 | generate(20000) 20 | -------------------------------------------------------------------------------- /ucs.exs: -------------------------------------------------------------------------------- 1 | defmodule UniformCostSearch do 2 | @moduledoc """ 3 | The Uniform Cost Search Is a search Technique that is used to determine what 4 | action would have the least amount of path cost. It is alot like the Breadth 5 | First Search algorithm, only It differs in when the goal test is applied. 6 | This module will illustrate an example of how it can be used in the real world. 7 | It accompanies the Uniform Cost Search blog post on http://automatingthefuture.com 8 | """ 9 | @investment_file "data/investment/options.txt" 10 | 11 | def fetch_projections do 12 | start_possible_investments_list 13 | data = File.read!(@investment_file) 14 | data 15 | |> String.rstrip(?\n) 16 | |> String.split("\n") 17 | |> Enum.each(&gather(&1)) 18 | IO.inspect(Agent.get(PossibleInvestments, &(&1))) 19 | end 20 | 21 | defp gather(investment) do 22 | data_list = String.split(investment, ",") 23 | {company, price} = {List.first(data_list), String.lstrip(List.last(data_list))} 24 | build_possible_investments(company, price) 25 | end 26 | 27 | defp start_possible_investments_list do 28 | Agent.start(fn -> [] end, [name: PossibleInvestments]) 29 | end 30 | 31 | defp build_possible_investments(company, price) do 32 | data = [company_name: company, projected_price: String.to_float(price)] 33 | Agent.update(PossibleInvestments, fn(list) -> [data | list] end) 34 | end 35 | end 36 | 37 | 38 | UniformCostSearch.fetch_projections 39 | --------------------------------------------------------------------------------