Class: MarkovTwitter::MarkovBuilder::Node
- Inherits:
-
Object
- Object
- MarkovTwitter::MarkovBuilder::Node
- Defined in:
- lib/markov_twitter/markov_builder/node.rb
Overview
Represents a single node in a Markov chain.
Instance Attribute Summary collapse
-
#linkages ⇒ Hash<Symbol, Hash<String, Float>>
The :next and :previous linkages.
-
#nodes ⇒ Hash<String,Node>
readonly
A reference to the attr of the parent MarkovBuilder.
-
#total_num_inputs ⇒ Hash<Symbol>, Integer
The total number of inputs added in each direction.
-
#value ⇒ String
readonly
A single token, such as a word.
Instance Method Summary collapse
-
#add_and_adjust_probabilities(direction, other_node, mirror_change = true) ⇒ void
Adds a single node to the :next linkages and updates probabilities.
-
#add_linkage!(direction, other_node, probability, mirror_change = true) ⇒ void
Force-adds a linkage at a specific probability.
-
#add_next_linkage(child_node, mirror_change = true) ⇒ void
Adds another node to the :next linkages, updating probabilities.
-
#add_prev_linkage(parent_node, mirror_change = true) ⇒ void
Adds another node to the :prev linkages, updating probabilities.
-
#delete_linkage!(direction, other_node, mirror_change = true) ⇒ void
Force-removes a linkage, re-adjusting other probabilities but potentially breaking their proportionality.
-
#get_probability_unit(direction) ⇒ Float
Determines the weight of a single insertion by looking up the total number of insertions in that direction.
-
#initialize(value:, nodes:) ⇒ Node
constructor
A new instance of Node.
-
#remove_and_adjust_probabilities(direction, other_node, mirror_change = true) ⇒ void
Removes a single node from the :prev linkages and updates probabilities.
-
#remove_next_linkage(child_node, mirror_change = true) ⇒ void
Removes a node from the :next linkages, updating probabilities.
-
#remove_prev_linkage(parent_node, mirror_change = true) ⇒ void
Removes a node from the :prev linkages, updating probabilities.
-
#update_opposite_direction(direction, other_node, method_name, *other_args) ⇒ void
Calls given method_name on other_node, passing the opposite direction to the one given as an argument.
Constructor Details
#initialize(value:, nodes:) ⇒ Node
Returns a new instance of Node.
28 29 30 31 32 33 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 28 def initialize(value:, nodes:) @value = value @linkages = { next: Hash.new(0), prev: Hash.new(0) } @total_num_inputs = { next: 0, prev: 0 } @nodes = nodes end |
Instance Attribute Details
#linkages ⇒ Hash<Symbol, Hash<String, Float>>
Returns the :next and :previous linkages.
-
Outer hash is keyed by the direction (:next, :prev).
-
Inner hash represents possible traversals - keyed by string value, its values are probabilities representing the likelihood of choosing that route.
15 16 17 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 15 def linkages @linkages end |
#nodes ⇒ Hash<String,Node> (readonly)
Returns a reference to the attr of the parent MarkovBuilder.
24 25 26 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 24 def nodes @nodes end |
#total_num_inputs ⇒ Hash<Symbol>, Integer
Returns the total number of inputs added in each direction.
-
also used to re-calculate probabilities.
20 21 22 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 20 def total_num_inputs @total_num_inputs end |
#value ⇒ String (readonly)
Returns a single token, such as a word.
7 8 9 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 7 def value @value end |
Instance Method Details
#add_and_adjust_probabilities(direction, other_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Adds a single node to the :next linkages and updates probabilities. Also updates the opposite direction, e.g. :prev will be updated if something is added to :next.
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 43 def add_and_adjust_probabilities(direction, other_node, mirror_change=true) @nodes[other_node.value] ||= other_node total_num_inputs[direction] += 1 unit = get_probability_unit(direction) probability_multiplier = (total_num_inputs[direction] - 1) * unit linkages[direction].each_key do |node_key| linkages[direction][node_key] *= probability_multiplier end linkages[direction][other_node.value] += unit # Add a linkage in the opposite direction to keep :next and :prev in sync update_opposite_direction(direction, other_node, __method__) if mirror_change end |
#add_linkage!(direction, other_node, probability, mirror_change = true) ⇒ void
This method returns an undefined value.
Force-adds a linkage at a specific probability. Readjusts other probabilities but may break their proportionality. Updates the opposite direction to keep :next and :prev in sync
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 127 def add_linkage!(direction, other_node, probability, mirror_change=true) raise ArgumentError, "invalid probability" unless probability.between?(0,1) # first remove any existing node there and distribute the probability. delete_linkage!(direction, other_node) # Re-adjust each probability to account for the added value linkages[direction].each_key do |key| linkages[direction][key] *= (1 - probability) # remove the linkage if it's probability is zero if linkages[direction][key].zero? delete_linkage!(direction, @nodes[key]) end end # Add the new value and set its probability binding.pry if other_node.value == "dog" linkages[direction][other_node.value] = probability # increment the total count total_num_inputs[direction] += 1 # Add a linkage in the opposite direction to keep :next and :prev in sync if mirror_change update_opposite_direction(direction, other_node, __method__, probability) end end |
#add_next_linkage(child_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Adds another node to the :next linkages, updating probabilities.
166 167 168 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 166 def add_next_linkage(child_node, mirror_change=true) add_and_adjust_probabilities(:next, child_node) end |
#add_prev_linkage(parent_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Adds another node to the :prev linkages, updating probabilities.
174 175 176 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 174 def add_prev_linkage(parent_node, mirror_change=true) add_and_adjust_probabilities(:prev, parent_node) end |
#delete_linkage!(direction, other_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Force-removes a linkage, re-adjusting other probabilities but potentially breaking their proportionality. Can be safely run for non-existing nodes. Adjusts the linkages in the opposite direction accordingly.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 103 def delete_linkage!(direction, other_node, mirror_change=true) return unless linkages[direction].has_key? other_node.value probability = linkages[direction][other_node.value] # delete the linkage linkages[direction].delete other_node.value # distribute the probability evenly among the other options. amt_to_add = probability / linkages[direction].keys.length linkages[direction].each_key do |key| linkages[direction][key] += amt_to_add end # decrement the total count total_num_inputs[direction] -= 1 # Add a linkage in the opposite direction to keep :next and :prev in sync update_opposite_direction(direction, other_node, __method__) if mirror_change end |
#get_probability_unit(direction) ⇒ Float
Determines the weight of a single insertion by looking up the total number of insertions in that direction.
60 61 62 63 64 65 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 60 def get_probability_unit(direction) unless total_num_inputs[direction] > 0 raise ArgumentError, "no inputs were added in <direction>" end 1.0 / total_num_inputs[direction] end |
#remove_and_adjust_probabilities(direction, other_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Removes a single node from the :prev linkages and updates probabilities. Safe to run if the other_node is not actually present in the linkages. Mirrors the change in the opposite direction, to keep :prev and :next in sync.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 75 def remove_and_adjust_probabilities(direction, other_node, mirror_change=true) return unless linkages[direction].has_key? other_node.value unit = get_probability_unit(direction) if linkages[direction][other_node.value] - unit <= 0 delete_linkage!(direction, other_node) else linkages[direction][other_node.value] -= unit num_per_direction = total_num_inputs[direction] linkages[direction].each_key do |node_key| linkages[direction][node_key] *= ( num_per_direction / (num_per_direction - 1.0) ) end total_num_inputs[direction] -= 1 end # Add a linkage in the opposite direction to keep :next and :prev in sync update_opposite_direction(direction, other_node, __method__) if mirror_change end |
#remove_next_linkage(child_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Removes a node from the :next linkages, updating probabilities.
182 183 184 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 182 def remove_next_linkage(child_node, mirror_change=true) remove_and_adjust_probabilities(:next, child_node) end |
#remove_prev_linkage(parent_node, mirror_change = true) ⇒ void
This method returns an undefined value.
Removes a node from the :prev linkages, updating probabilities.
190 191 192 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 190 def remove_prev_linkage(parent_node, mirror_change=true) remove_and_adjust_probabilities(:prev, parent_node) end |
#update_opposite_direction(direction, other_node, method_name, *other_args) ⇒ void
This method returns an undefined value.
Calls given method_name on other_node, passing the opposite direction to the one given as an argument. This keeps :next and :prev in sync.
157 158 159 160 |
# File 'lib/markov_twitter/markov_builder/node.rb', line 157 def update_opposite_direction(direction, other_node, method_name, *other_args) other_direction = (%i{next prev} - [direction])[0] other_node.send(method_name, other_direction, self, *other_args, false) end |