<commit_before># frozen_string_literal: true
require 'matrix'

class MetaModel < ApplicationRecord
  belongs_to :discipline
  belongs_to :operation

  has_many :surrogates, dependent: :destroy

  validates :discipline, presence: true

  after_initialize :_set_defaults

  MATRIX_FORMAT = 'matrix'
  FORMATS = [MATRIX_FORMAT]

  class PredictionError < StandardError; end

  def analysis
    discipline.analysis  # a metamodel a no existence out of analysis context
  end

  def build_surrogates
    analysis.response_variables.each do |v|
      (0...v.dim).each do |index|
        surrogates.build(variable: v, coord_index: index-1, kind: default_surrogate_kind)
      end
    end
  end

  def train(force: true)
    surrogates.each do |surr|
      surr.train if force || !surr.trained?
    end
  end

  def predict(values)
    res = []
    surrogates.each do |surr|
      yvals = surr.predict(values)
      if res.empty?
        res = yvals.map{|y| [y]}
      else
        yvals.each_with_index do |y, i|
          res[i] << y
        end
      end
    end
    res
  rescue => e
    raise PredictionError.new("Cannot make prediction for '#{values}'")
  end

  def training_input_values
    @training_inputs ||= Matrix.columns(operation.input_cases.map(&:values)).to_a
  end

  def training_output_values(varname, coord_index)
    operation.cases.where(coord_index: coord_index).joins(:variable).where(variables: {name: varname}).take.values
  end

  def qualification
    surrogates.map(&:qualify)
  end

private

  def _set_defaults
    self.default_surrogate_kind = Surrogate::SURROGATES[0] if self.default_surrogate_kind.blank? 
  end
  
end<commit_msg>fix bug related to inputs/outputs variable order for surrogates<commit_after># frozen_string_literal: true
require 'matrix'

class MetaModel < ApplicationRecord
  belongs_to :discipline
  belongs_to :operation

  has_many :surrogates, dependent: :destroy

  validates :discipline, presence: true

  after_initialize :_set_defaults

  MATRIX_FORMAT = 'matrix'
  FORMATS = [MATRIX_FORMAT]

  class PredictionError < StandardError; end

  def analysis
    discipline.analysis  # a metamodel a no existence out of analysis context
  end

  def build_surrogates
    analysis.response_variables.each do |v|
      (0...v.dim).each do |index|
        surrogates.build(variable: v, coord_index: index-1, kind: default_surrogate_kind)
      end
    end
  end

  def train(force: true)
    surrogates.each do |surr|
      surr.train if force || !surr.trained?
    end
  end

  def predict(values)
    res = []
    # Convention: values in res corresponds to var names alphabetically sorted
    sorted = surrogates.sort_by { |surr| surr.variable.name }
    names = sorted.map { |surr| surr.variable.name }
    Rails.logger.info "Predict with surrogates of : #{names}"
    sorted.each do |surr|
      yvals = surr.predict(values)
      if res.empty?
        res = yvals.map{|y| [y]}
      else
        yvals.each_with_index do |y, i|
          res[i] << y
        end
      end
    end
    res
  rescue => e
    raise PredictionError.new("Cannot make prediction for '#{values}'")
  end

  def training_input_values
    @training_inputs ||= Matrix.columns(operation.input_cases.sort_by{|c| c.variable.name }.map(&:values)).to_a
  end

  def training_output_values(varname, coord_index)
    operation.cases.where(coord_index: coord_index).joins(:variable).where(variables: {name: varname}).take.values
  end

  def qualification
    surrogates.map(&:qualify)
  end

private

  def _set_defaults
    self.default_surrogate_kind = Surrogate::SURROGATES[0] if self.default_surrogate_kind.blank? 
  end
  
end
{'fraction_non_alphanumeric': 0.09325287986834888, 'fraction_numerical': 0.0016456390565002743, 'mean_word_length': 3.3159763313609467, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7214189', 'n_tokens_mistral': 1225, 'n_tokens_neox': 1179, 'n_words': 295}