EHadoux/aptimizer

View on GitHub
lib/aptimizer/pomdpx/transitionbuilder.rb

Summary

Maintainability
F
5 days
Test Coverage
module Aptimizer
  module POMDPX
    class TransitionBuilder
      extend Helpers
      class << self
        # Need to optimize all those methods as several things are evaluated multiple times (like compatibility).
        # In a further version obviously.

        def build_transitions(xml, pomdp)
          #build_flags_transitions(xml, pomdp.opponent)
          modified_by = get_modifying_rules(pomdp.agent, pomdp.opponent)
          build_non_modified_predicates(xml, pomdp, modified_by)
          build_agent_private_transitions(xml, pomdp.agent, modified_by)
          build_public_argument_transitions(xml, pomdp.agent, pomdp.opponent, modified_by)
        end

        #def build_flags_transitions(xml, opponent)
        #  opponent.flags.each do |f|
        #    name    = "_nr#{f+1}"
        #    parents = "action"
        #    state   = "* -"
        #    prob    = opponent.rules[f].alternatives.map(&:probability).join(" ")
        #    build_cond_prob(xml, name, parents, state, prob)
        #  end
        #end

        def build_agent_private_transitions(xml, agent, modification_hash)
          private_args_selector = lambda {|p| p[0].type == :priv && p[0].owner == 1 && !p[1][0].empty? }
          modification_hash.lazy.select(&private_args_selector).each do |pred, actions_arr|
            build_agent_side_transitions(xml, agent, pred, actions_arr[0])
            actions_arr[0] = []
            modification_hash[pred] = actions_arr
          end
        end

        def build_agent_side_transitions(xml, agent, predicate, modifying_actions)
          prem_set = Set.new
          prem_set.add(predicate.unsided)
          modifying_actions.each do |act_i, _|
            prem_set.merge(agent.actions[act_i].premises.map(&:unsided))
          end
          premisses   = prem_set.to_a
          transitions = [] << "* - " + ("* " * (premisses.size - 1)) + "-" << "identity" << nil
          modifying_actions.each do |act_i, mod|
            instance  = [agent.action_names[act_i]] + (["*"] * premisses.size) + ["-"]
            agent.actions[act_i].premises.each do |prem|
              instance[premisses.find_index(prem.unsided)+1] = prem.positive ? "s1" : "s0"
            end
            transitions << instance.join(" ") << (mod == :add ? "0 1.0" : "1.0 0") << nil
          end
          name     = "n#{convert_string(predicate)}"
          prem_str = premisses.map(&method(:convert_string)).join(" ")
          parents  = "action " + prem_str
          build_cond_prob(xml, name, parents, *transitions)
        end

        def build_public_argument_transitions(xml, agent, opponent, modification_hash)
          public_or_atks = lambda {|p| p[0].type == :pub || p[0].type == :atk}
          modification_hash.lazy.select(&public_or_atks).each do |pred, modif_arrays|
            if modif_arrays[1].empty? && !modif_arrays[0].empty? # No modification of the predicate by the opponent
              build_agent_side_transitions(xml, agent, pred, modif_arrays[0])
            else
              flags_set  = Set.new
              pairs      = Array.new(agent.actions.size) { [] }
              prem_set   = Set.new
              prem_set.add(pred.unsided)
              agent.actions.each_with_index do |act, act_i|
                prem_set.merge(act.premises.map(&:unsided))
                premisses = prem_set.to_a
                instance  = [agent.action_names[act_i]] + (["*"] * premisses.size) + ["-"]
                act.alternatives[0].modifiers.each do |mod|
                  index = premisses.find_index(mod.predicate)
                  instance[index+1] = mod.type == :add ? "s1" : "s0" unless index.nil?
                end
                opponent.rules.each_with_index do |rule, rule_i|
                  if compatible?(rule, instance, premisses)
                    prem_set.merge(rule.premises.map(&:unsided))
                    pairs[act_i] << rule_i
                    flags_set.add(rule_i) if opponent.rules[rule_i].alternatives.size > 1
                  end
                end
              end
              premisses   = prem_set.to_a
              transitions = [] << "* - " + ("* " * (premisses.size - 1)) + ("* " * flags_set.size) + "-" << "identity" << nil
              pairs.each_with_index do |rules_i, act_i|
                instance    = (["*"] * (prem_set.size + 1)) + (flags_set.map{|i| "r#{i}"}) + ["-"]
                instance[0] = agent.action_names[act_i]
                instance[1] = "-"
                agent.actions[act_i].premises.each do |prem|
                  instance[premisses.find_index(prem.unsided)+1] = prem.positive ? "s1" : "s0"
                end
                modified_instance = Array.new(instance)
                agent.actions[act_i].alternatives[0].modifiers.each do |mod|
                  index = premisses.find_index(mod.predicate)
                  modified_instance[index+1] = mod.type == :add ? "s1" : "s0" unless index.nil?
                end
                [false, true].repeated_permutation(rules_i.size).sort_by { |p| p.count(true) }.each do |perm|
                  interm_state    = Array.new(instance)
                  modified_interm_state = Array.new(modified_instance)
                  cumulated_prems = Set.new(agent.actions[act_i].premises.map(&:unsided))
                  cumulated_prems.merge(agent.actions[act_i].alternatives[0].modifiers.map(&:predicate))
                  skip            = false
                  perm.each_with_index do |val, ind|
                    next unless val
                    if compatible?(opponent.rules[rules_i[ind]], modified_interm_state, premisses)
                      prem_to_loop = prem_set - cumulated_prems
                      rule_prem    = opponent.rules[rules_i[ind]].premises
                      rule_prem.each do |prem|
                        if prem_to_loop.include?(prem.unsided)
                          index = premisses.find_index(prem.unsided)+1
                          interm_state[index] = prem.positive ? "s1" : "s0"
                          modified_interm_state[index] = prem.positive ? "s1" : "s0"
                        end
                      end
                      cumulated_prems.merge(rule_prem.map(&:unsided))
                    else
                      skip = true
                      break
                    end
                  end
                  next if skip
                  alt_lists = flags_set.to_a.map do |f|
                    s = opponent.rules[f].alternatives.size
                    s.times.map{|i| "alt#{i+1}"} if perm[f]
                  end.compact
                  cross_flags = [""].product(*alt_lists)
                  cross_flags = [nil] if cross_flags.empty?
                  cross_flags.each do |f|
                    flags                   = Array.new(f)
                    mod_sides               = [[], [], []]
                    interm_state_with_flags = Array.new(interm_state)
                    unless flags.nil?
                      flags.shift
                      #flags.reverse_each.with_index { |flag, flag_i| interm_state_with_flags[-flag_i-2] = flag}
                    end

                    modified = false
                    rules_i.each_with_index do |rule_i, ind|
                      rule = opponent.rules[rule_i]
                      next unless perm[ind]
                      alt  = rule.alternatives.size > 1 ? (flags.shift[-1].to_i - 1) : 0
                      index = interm_state_with_flags.find_index("r#{rule_i}")
                      interm_state_with_flags[index] = "alt#{alt+1}" if rule.alternatives.size > 1
                      if rule.alternatives[alt].modifies?(pred)
                        modified = true
                        mod = rule.alternatives[alt].modifiers.select { |m| m.predicate == pred }
                        raise "Bug" if mod.size != 1
                        mod = mod[0]
                        mod_sides[mod.type == :rem ? 0 : 1] << rule_i
                      else
                        mod_sides[2] << rule_i
                      end
                    end
                    unless modified
                      mod_sides = [[], [], []]
                      if agent.actions[act_i].alternatives[0].modifies?(pred)
                        modified = true
                        mod = agent.actions[act_i].alternatives[0].modifiers.select { |m| m.predicate == pred }
                        raise "Bug" if mod.size != 1
                        mod = mod[0]
                        mod_sides[mod.type == :rem ? 0 : 1] = act_i
                      else
                        mod_sides[2] = act_i
                      end
                    end
                    next unless modified

                    pred_instance_value = interm_state_with_flags[1]
                    if pred_instance_value == "-"
                      probas    = Array.new(4)
                      probas[0] = mod_sides[0].size + mod_sides[2].size
                      probas[1] = mod_sides[1].size
                      probas[2] = mod_sides[0].size
                      probas[3] = mod_sides[1].size + mod_sides[2].size
                      probas.map!{ |p| p.to_f / mod_sides.lazy.map(&:size).reduce(:+)}
                      probas_str = probas.join(" ")
                    else
                      mod_sides[pred_instance_value[1].to_i] += mod_sides[2]
                      sides_sum  = mod_sides[0].size + mod_sides[1].size
                      s0_proba   = mod_sides[0].size.to_f / sides_sum
                      s1_proba   = mod_sides[1].size.to_f / sides_sum
                      probas_str = "#{s0_proba} #{s1_proba}"
                    end
                    interm_state_with_flags.map!{|i| i.start_with?("r") ? "*" : i}
                    transitions << interm_state_with_flags.join(" ") << probas_str << rules_i.map{|r| "r#{r+1}=#{perm[r] ? "1" : "0"}"}.join(" ")
                  end
                end
              end
              prem_str = premisses.map {|p| convert_string(p)}
              build_cond_prob(xml, "n#{convert_string(pred)}", "action " + prem_str.join(" ") + " " + flags_set.to_a.reverse.map{|f| "_r#{f+1}"}.join(" "), *transitions)
            end
          end
        end

        def build_non_modified_predicates(xml, pomdpx, modification_hash)
          private_init   = lambda { |arg, owner| Predicate.new(:priv, arg, owner: owner) }
          public_init    = lambda { |arg, _| Predicate.new(:pub, arg) }
          atk_init       = lambda { |arg, _| arg }
          private_empty  = lambda { |pred, owner| modification_hash.has_key?(pred) && !modification_hash[pred][owner-1].empty? }
          public_empty   = lambda { |pred, _| modification_hash.has_key?(pred) }
          build = lambda do |set, owner, init, tester|
            set.each do |arg|
              pred     = init.call(arg, owner)
              str_name = convert_string(pred)
              parents  = "action #{str_name}"
              state    = "* - -"
              prob     = "identity"
              build_cond_prob(xml, "n#{str_name}", parents, state, prob) unless tester.call(pred, owner)
            end
          end

          build.call(pomdpx.agent.arguments, 1, private_init, private_empty)
          build.call(pomdpx.opponent.arguments, 2, private_init, private_empty)
          build.call(pomdpx.public_space.arguments, 0, public_init, public_empty)
          #build.call(pomdpx.public_space.attacks, 0, :itself.to_proc, public_empty)
          build.call(pomdpx.public_space.attacks, 0, atk_init, public_empty)
        end
      end

      private_class_method :build_agent_private_transitions, #:build_flags_transitions,
                           :build_public_argument_transitions, :build_non_modified_predicates,
                           :build_agent_side_transitions
    end
  end
end