Class: Chain
Direct Known Subclasses
Constant Summary collapse
- @@default_tail_theory =
nil
Instance Attribute Summary collapse
-
#chain_mapping ⇒ Object
readonly
TODO This access is proably temporary.
Class Method Summary collapse
Instance Method Summary collapse
-
#<<(node) ⇒ Object
TODO I want to drop allot of these quick methods.
- #[](index) ⇒ Object
-
#add_link_to(theory, position, value_mapping) ⇒ Object
Returns any number of new chains after adding this link to the position specified.
-
#broken_link_count ⇒ Object
Returns the number of breaks in the chain - this is essential the number of unmet depements.
- #collect(&block) ⇒ Object
-
#complete? ⇒ Boolean
Returns true if the chain can be connected from the head to the tail.
- #copy ⇒ Object
- #describe(tab = 0) ⇒ Object
-
#duplicate_component_ids? ⇒ Boolean
DEVELOPMENT Because I’m using Marshal to restore some of the theories I just need to check that none of the components have duplicate ids.
- #each(&block) ⇒ Object
-
#each_theory_progression(initial_runtime_method, test_cases) ⇒ Object
Returns an array of implemented theories that have variable syntax that can be mapped to literal syntax(runtime_method,0,…) and also contain real values for each of the variables.
- #empty? ⇒ Boolean
-
#extension_permutaions(theory, value_mapping = nil) ⇒ Object
Attempts to add a new link to form a complete chain between the head and tail.
- #first ⇒ Object
-
#highlight_broken_links ⇒ Object
Writes out the chain but highlights any of links in the chain weren’t connected to a dependent.
-
#implement ⇒ Object
Implements a new the chain where all the theories are implemented.
-
#implementable? ⇒ Boolean
Return true if the chain can be implemented - so all the theories have literal values associated with them.
-
#initialize ⇒ Chain
constructor
A new instance of Chain.
- #last ⇒ Object
- #length ⇒ Object
- #pop ⇒ Object
- #reverse ⇒ Object
- #shift ⇒ Object
-
#solvable?(finish, theories) ⇒ Boolean
This uses the supplied theories and checks whether the dependents of any of theories that might be used are reachable.
- #theories_sequence ⇒ Object
-
#theory_variables ⇒ Object
Returns an array of all the theory variables used in the chain.
-
#unify_chain ⇒ Object
Returns a new chain where they are all using the same respective theory variables.
-
#uniq_theory_variable_ids ⇒ Object
Returns an array of all the unique theory variables used within the chain.
-
#unmet_dependents ⇒ Object
Returns an array of all the depdendents in the chain that haven’t been met by the front of the chain.
-
#unmet_dependents_and_link ⇒ Object
Returns a hash of all the unmet dependents down the chain (excluding the head) and the link/theory that it belongs to.
- #unmet_dependents_ids ⇒ Object
-
#variables_creation(runtime_method, test_cases) ⇒ Object
TODO Maybe just move this method to the UnfiedChain class Returns a set containing the intrinsic statements that created the variables.
- #write(tab = 0) ⇒ Object
Constructor Details
#initialize ⇒ Chain
Returns a new instance of Chain.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/Chain.rb', line 10 def initialize() @values, @nodes = {}, [] # Create an array of possible ids for any theories added @uniq_theory_instance_ids = ('A'...'Z').to_a # Create a result version of 'finish' # TODO Drop @tail_theory as a instance variable #@tail_theory = Theory.new([tail_dependent],nil,[]) @tail_theory = Chain.default_tail_theory # NOTE: Now the head and tail are using the same ids for their variables @chain_mapping = ChainMapping.new() # Get the value mappings for the chain tc = Parser.run('test_cases') real_method = Parser.run('runtime_method') # Add the tail to the nodes list #add_link(@tail_theory,{1=>real_method,2=>tc}) extension_permutaions(@tail_theory,{1=>real_method,2=>tc}) end |
Instance Attribute Details
#chain_mapping ⇒ Object (readonly)
TODO This access is proably temporary
4 5 6 |
# File 'lib/Chain.rb', line 4 def chain_mapping @chain_mapping end |
Class Method Details
.default_tail_theory ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/Chain.rb', line 35 def self.default_tail_theory if @@default_tail_theory.nil? finish = Parser.run("if(runtime_method.all_pass?(test_cases))\nreturn true\nend") tail_theory = finish.write tail_theory.gsub!('runtime_method','var1') tail_theory.gsub!('test_cases','var2') tail_dependent = TheoryDependent.new(StringToTheory.run(tail_theory)) @@default_tail_theory = Theory.new([tail_dependent],nil,[]) @@default_tail_theory.copy else @@default_tail_theory.copy end end |
Instance Method Details
#<<(node) ⇒ Object
TODO I want to drop allot of these quick methods
50 51 52 |
# File 'lib/Chain.rb', line 50 def <<(node) @nodes << node end |
#[](index) ⇒ Object
62 63 64 |
# File 'lib/Chain.rb', line 62 def [](index) return @nodes[index] end |
#add_link_to(theory, position, value_mapping) ⇒ Object
Returns any number of new chains after adding this link to the position specified.
TODO SHould I raise an error if it doesn’t connect to anything?
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/Chain.rb', line 275 def add_link_to(theory,position,value_mapping) # TODO Down and up are quite similar I should consider refactoring # Do through each of the dependents and then find a result with the same structure mappings = [@chain_mapping.copy] upward_links = @nodes[0...position] theory.copy.dependents.each do |dependent| each_result(position,upward_links) do |index,link,result| if result.same_structure?(dependent) new_mappings = [] mappings.each do |x| new_mappings << x.apply_mapping_update(theory,dependent.theory_component_id,link,result.theory_component_id) end mappings = new_mappings end end end # Go down the rest of the chain looking for exisitng links with unmet dependents downward_links = @nodes[position...@nodes.length] theory.copy.results.each do |result| each_unmet(:dependents,position,downward_links) do |index,link,dependent| if dependent.same_structure?(result) new_mappings = [] mappings.each do |x| new_mappings << x.apply_mapping_update(link,dependent.theory_component_id,theory,result.theory_component_id) end mappings = new_mappings end end end # Strip out any mappings that are identical to original they are # links that haven't been connected with anything # (don't include the head since it only has one thing to connect to) # => TODO Do I like this? It means I'm including some forced connections unless @nodes.length < 2 # Identify the mappings that are the same mappings = mappings.select {|x| !@chain_mapping.same?(x) } # => TODO Should inlcude error/warning and exit when there are no new mappings end # Identify any orphan variables in the action and give them uniq global ids mappings.each do |mapping| theory.orphan_action_variables.each do |x| mapping.add_mapping( mapping.next_free_global_id, theory.theory_instance_id, x.theory_variable_id ) end end # Create a new nodes array with the new theory in place updated_nodes = @nodes.copy updated_nodes.insert(position,theory) # Create a new chain for each of the possible mappings extended_chains = [] mappings.each do |x| c = self.copy new_chain = c.create!(updated_nodes,x,@uniq_theory_instance_ids.copy,@values.copy) extended_chains << new_chain end return extended_chains end |
#broken_link_count ⇒ Object
Returns the number of breaks in the chain - this is essential the number of unmet depements.
160 161 162 163 164 165 166 167 168 |
# File 'lib/Chain.rb', line 160 def broken_link_count collection = TheoryCollection.new(@nodes) return collection.dependents.inject(0) do |total,dependent| unless @chain_mapping.connected_component_ids.include?(dependent.theory_component_id) total += 1 end total end end |
#collect(&block) ⇒ Object
95 96 97 |
# File 'lib/Chain.rb', line 95 def collect(&block) return @nodes.collect(&block) end |
#complete? ⇒ Boolean
Returns true if the chain can be connected from the head to the tail.
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/Chain.rb', line 223 def complete? # Check that all the variables in all the theories have been globally mapped, as well as the head @nodes.each do |theory| # Find the global variable for each of the theory variables theory.all_theory_variables.each do |theory_variable| begin to_global_id(@chain_mapping,theory.theory_instance_id,theory_variable.theory_variable_id) rescue StandardError => e StandardLogger.instance.error(e) return false end end end return dependents_met? end |
#copy ⇒ Object
82 83 84 85 |
# File 'lib/Chain.rb', line 82 def copy #return Chain.new(@nodes.copy) return Marshal.load(Marshal.dump(self)) end |
#describe(tab = 0) ⇒ Object
103 104 105 |
# File 'lib/Chain.rb', line 103 def describe(tab=0) return @nodes.inject('') {|total,x| total += x.describe} end |
#duplicate_component_ids? ⇒ Boolean
DEVELOPMENT Because I’m using Marshal to restore some of the theories I just need to check that none of the components have duplicate ids.
151 152 153 154 155 |
# File 'lib/Chain.rb', line 151 def duplicate_component_ids? collection = TheoryCollection.new(@nodes) component_ids = collection.components.inject([]) {|total,x| total << x.theory_component_id } return (component_ids.length != component_ids.uniq.length) end |
#each(&block) ⇒ Object
58 59 60 |
# File 'lib/Chain.rb', line 58 def each(&block) @nodes.each(&block) end |
#each_theory_progression(initial_runtime_method, test_cases) ⇒ Object
Returns an array of implemented theories that have variable syntax that can
be mapped to literal syntax(runtime_method,0,...) and also contain
real values for each of the variables. So the runtime method will
include all statements added to it.
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/Chain.rb', line 363 def each_theory_progression(initial_runtime_method,test_cases) raise StandardError.new('Only complete theories can be watched') unless self.implementable? theories = [] previous_runtime_method = initial_runtime_method.copy chain_validator = TheoryChainValidator.new progressive_chain do |chain,theory| initial_runtime_method.copy runtime_method = chain_validator.build_method_from_chain( chain.implement, initial_runtime_method.copy, test_cases) # Create the intrinsic mapping for the theory unified_chain = chain.unify_chain # * Go through each variable and find the intrinsic value theory_intrinsic_mapping = Mapping.new values = {:before=>{},:after=>{}} unified_chain.theory_variables.each do |var| intrinsic_value = global_id_intrinsic_value(var.theory_variable_id) theory_intrinsic_mapping[var.theory_variable_id] = intrinsic_value # Add the actual varlue of the intrinsic value if intrinsic_value.kind_of?(IntrinsicRuntimeMethod) values[:before][var.theory_variable_id] = previous_runtime_method.copy values[:after][var.theory_variable_id] = previous_runtime_method.copy next end if intrinsic_value.kind_of?(IntrinsicTestCases) values[:before][var.theory_variable_id] = test_cases.copy values[:after][var.theory_variable_id] = test_cases.copy next else values[:before][var.theory_variable_id] = intrinsic_value.literal values[:after][var.theory_variable_id] = intrinsic_value.literal end end # Generate a unified version of the theory mapping = generate_theory_mapping(theory.theory_instance_id) unified_theory = theory.map_to(mapping) yield theory.map_to(theory_intrinsic_mapping,values), unified_theory # Save the previous method for the dependents previous_runtime_method = runtime_method end return theories end |
#extension_permutaions(theory, value_mapping = nil) ⇒ Object
Attempts to add a new link to form a complete chain between the head and tail. It starts by linking to the tail and then head.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/Chain.rb', line 189 def extension_permutaions(theory,value_mapping=nil) # Give the theory an instance id theory = theory.copy raise StandardError.new('Reached limit to chains length') if @uniq_theory_instance_ids.empty? theory.theory_instance_id = @uniq_theory_instance_ids.shift # Save the values for use later during the global mapping @values[theory.theory_instance_id] = value_mapping # The first link added should be the tail if @nodes.empty? add_tail(theory,value_mapping) return end # The second link should be the head # (this doesn't necessarily need to be be case but I'll keep this convention if @nodes.length == 1 return add_link_to(theory,0,value_mapping) end # Attempt to add a link to all the possible locations (head and tail are exempt) extended_chains = [] (1...@nodes.length).each do |position| res = self.copy.add_link_to(theory,position,value_mapping) extended_chains += res end return extended_chains end |
#first ⇒ Object
66 67 68 |
# File 'lib/Chain.rb', line 66 def first return @nodes.first end |
#highlight_broken_links ⇒ Object
Writes out the chain but highlights any of links in the chain weren’t connected to a dependent.
175 176 177 178 179 180 181 182 183 184 |
# File 'lib/Chain.rb', line 175 def highlight_broken_links if duplicate_component_ids? StandardLogger.instance.warning('You are using theories that share the same component_id') end unmet = [] each_unmet(:dependents,0,@nodes) do |index,theory,dependent| unmet << dependent.theory_component_id end return @nodes.inject('') {|msg,x| msg += x.highlight(unmet)} end |
#implement ⇒ Object
Implements a new the chain where all the theories are implemented. It can only be implemented if the chain is complete e.g. all the dependents and results are matched up.
348 349 350 351 352 353 354 355 356 |
# File 'lib/Chain.rb', line 348 def implement raise StandardError.new('This chain is not complete') unless complete? # Generate a global value mapping - so global id to real value unifyied_chain = unify_chain return unifyied_chain.implement(intrinsic_mapping) end |
#implementable? ⇒ Boolean
Return true if the chain can be implemented - so all the theories have literal values associated with them. This could be directly via when the theory was added to the chain or indirectly via connecting two theories.
419 420 421 422 423 424 425 |
# File 'lib/Chain.rb', line 419 def implementable? self.implement return true rescue StandardError => e StandardLogger.instance.info e return false end |
#last ⇒ Object
70 71 72 |
# File 'lib/Chain.rb', line 70 def last return @nodes.last end |
#length ⇒ Object
54 55 56 |
# File 'lib/Chain.rb', line 54 def length return @nodes.length end |
#pop ⇒ Object
91 92 93 |
# File 'lib/Chain.rb', line 91 def pop return @nodes.pop end |
#reverse ⇒ Object
74 75 76 |
# File 'lib/Chain.rb', line 74 def reverse return Chain.new(@nodes.reverse) end |
#shift ⇒ Object
78 79 80 |
# File 'lib/Chain.rb', line 78 def shift return @nodes.shift end |
#solvable?(finish, theories) ⇒ Boolean
This uses the supplied theories and checks whether the dependents of any of theories that might be used are reachable.
NOTE: This is only a rough assessment of whether the dependents are available
it doesn't mean that it is solvable but does identify that it definitely
isn't.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/Chain.rb', line 248 def solvable?(finish,theories) # Get the finish and check all the dependents can be met by the theories all_results = theories.inject([]) {|total,x| total += x.results} return false unless all_results.any? {|x| x.same_structure?(finish)} # Find the theories that can solve the problem and check their dependents can be met # TODO Should probably include the head theory in here too. collection = TheoryCollection.new(theories.copy) finishing_theories = [] collection.each_with_result_structure(finish) do |t,r| finishing_theories.push(t) end finishing_theories.each do |x| end res = finishing_theories.any? do |x| dependency_tree_met?(x,theories+[self.first.copy]) end return res end |
#theories_sequence ⇒ Object
126 127 128 |
# File 'lib/Chain.rb', line 126 def theories_sequence @nodes.collect {|x| x.theory_id} end |
#theory_variables ⇒ Object
Returns an array of all the theory variables used in the chain
429 430 431 |
# File 'lib/Chain.rb', line 429 def theory_variables return TheoryCollection.new(@nodes).theory_variables end |
#unify_chain ⇒ Object
Returns a new chain where they are all using the same respective theory variables.
TODO This seems to create implemented theories - I’m not sure this is what I want
135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/Chain.rb', line 135 def unify_chain unless complete? raise StandardError.new('Only complete chains can be unified') end unified_theories = @nodes.inject([]) do |total,theory| mapping = generate_theory_mapping(theory.theory_instance_id) mapped_theory = theory.map_to(mapping) total.push(mapped_theory) end return UnifiedChain.new(unified_theories) end |
#uniq_theory_variable_ids ⇒ Object
Returns an array of all the unique theory variables used within the chain.
e.g. [0,6,7]
112 113 114 115 116 |
# File 'lib/Chain.rb', line 112 def uniq_theory_variable_ids return @nodes.inject([]) do |total,theory| total = total | theory.all_theory_variables.collect {|x| x.theory_variable_id} end end |
#unmet_dependents ⇒ Object
Returns an array of all the depdendents in the chain that haven’t been met by the front of the chain.
436 437 438 439 440 441 442 443 444 445 |
# File 'lib/Chain.rb', line 436 def unmet_dependents results = [] return results if self.empty? duplicate_chain = self.copy last_link = duplicate_chain.pop last_link.dependents.each do |dependent| results.push dependent unless dependent_met?(dependent,duplicate_chain.copy) end return results+duplicate_chain.unmet_dependents end |
#unmet_dependents_and_link ⇒ Object
Returns a hash of all the unmet dependents down the chain (excluding the head) and the link/theory that it belongs to.
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/Chain.rb', line 532 def unmet_dependents_and_link results = [] return results if self.empty? duplicate_chain = self.copy # Remove the head link from the chain duplicate_chain.shift duplicate_chain.length.times do last_link = duplicate_chain.pop last_link.dependents.each do |d| next if dependent_met?(d,duplicate_chain.copy) results.push({:dependent=>d.copy,:theory=>last_link.copy}) end end return results end |
#unmet_dependents_ids ⇒ Object
118 119 120 121 122 123 124 |
# File 'lib/Chain.rb', line 118 def unmet_dependents_ids collection = TheoryCollection.new(@nodes) return collection.dependents.inject([]) do |total,dependent| total << dependent.theory_component_id unless @chain_mapping.connected_component_ids.include?(dependent.theory_component_id) total end end |
#variables_creation(runtime_method, test_cases) ⇒ Object
TODO Maybe just move this method to the UnfiedChain class Returns a set containing the intrinsic statements that created the variables. It should look something like the following.
[:global_variable_id=>0,:intrinsic_statement=>runtime_method,:written_statement=>‘var1’,
{:global_variable_id=>3,:intrinsic_statement=>runtime_method.params[0],:written_statement=>'var1.params[var3]'},
...]
Currently the only way variables are created are for array access.
There should not be the case where there are duplicate intrinsic_statements but different global_variable_ids - for now at least.
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
# File 'lib/Chain.rb', line 460 def variables_creation(runtime_method,test_cases) raise StandardError.new('This should only be called on complete chains') unless complete? res = [] unify_chain.theory_variables.each do |var| intrinsic_value = global_id_intrinsic_value(var.theory_variable_id) if intrinsic_value.kind_of?(IntrinsicRuntimeMethod) res << { :global_variable_id=>var.theory_variable_id, :intrinsic_statement=>Parser.run('runtime_method'), :written_statement=>'var'+var.theory_variable_id.to_s, :variable_value=>runtime_method.copy } elsif intrinsic_value.kind_of?(IntrinsicTestCases) res << { :global_variable_id=>var.theory_variable_id, :intrinsic_statement=>Parser.run('test_cases'), :written_statement=>'var'+var.theory_variable_id.to_s, :variable_value=>test_cases.copy } else # TODO This whole bit is just really rushed------------------ write tests! # Find all the statements that include that variable unified_components = unify_chain.components # TODO This is an extremely clumsy approach # Collect all the written components matching the delcaration written_components = unified_components.collect {|x| x.write} written_components = written_components.select {|x| x.include?('var'+var.theory_variable_id.to_s)} # => DEV temp_component = written_components.first # => MATCH var0.params[var2] in runtime_method.add_statement_at(OpenStatement.new(TheoryStatement.new(If.new, Container.new(var0.params[var2], Equivalent.new, var1[var4][:params][var3]))),var0.statement_id) reg = eval('/[\w\d\.\[\]:]*\['+var.write+'\]/') # TODO Should use the Parser to find the statement with the varx in #var_match = /\s.*\[var[\d]\]/ #var_match = /^[\s|\(]+(.*\[var[\d]\])/ var_match = reg usage_statements = written_components.inject([]) do |total,x| #unless x.write.match(/\s.*\[var[\d]\]/).nil? unless x.write.match(var_match).nil? match = x.write.match(var_match)[0] total << TheoryStatement.new(StringToTheory.run(match)) end total end intrinsic_statements = usage_statements.collect {|x| x.map_to(intrinsic_mapping)} unless intrinsic_statements.collect {|x| x.write}.uniq.length == 1 raise StandardError.new('Unable to create intrinsic variable create for variable '+var.theory_variable_id.to_s) end res << { :global_variable_id=>var.theory_variable_id, :intrinsic_statement=>intrinsic_statements.first, :written_statement=>usage_statements.first.write, :variable_value=>intrinsic_mapping[var.theory_variable_id].copy } end end return res end |
#write(tab = 0) ⇒ Object
99 100 101 |
# File 'lib/Chain.rb', line 99 def write(tab=0) return @nodes.inject('') {|total,x| total += x.write} end |