<commit_before>/*
  Copyright (c) by respective owners including Yahoo!, Microsoft, and
  individual contributors. All rights reserved.  Released under a BSD (revised)
  license as described in the file LICENSE.
*/
#include <float.h>

#include "vw.h"
#include "reductions.h"
#include "vw_exception.h"
#include "gen_cs_example.h"

namespace GEN_CS
{
using namespace LEARNER;
using namespace CB_ALGS;
using namespace CB;

inline bool observed_cost(cb_class* cl)
{ //cost observed for this action if it has non zero probability and cost != FLT_MAX
  return (cl != nullptr && cl->cost != FLT_MAX && cl->probability > .0);
}

cb_class* get_observed_cost(CB::label& ld)
{ for (auto& cl : ld.costs)
    if (observed_cost(&cl))
      return &cl;
  return nullptr;
}

//Multiline version
void gen_cs_example_ips(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { CB::label ld = examples[i]->l.cb;

    COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    if (ld.costs.size() == 1 && ld.costs[0].cost != FLT_MAX)
      wc.x = ld.costs[0].cost / ld.costs[0].probability;
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//Multiline version
void gen_cs_example_dm(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { CB::label ld = examples[i]->l.cb;

    COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    if (ld.costs.size() == 1 && ld.costs[0].cost != FLT_MAX)
      wc.x = ld.costs[0].cost;
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//Multiline version
void gen_cs_test_example(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { COST_SENSITIVE::wclass wc = {FLT_MAX,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//single line version
void gen_cs_example_ips(cb_to_cs& c, CB::label& ld, COST_SENSITIVE::label& cs_ld)
{ //this implements the inverse propensity score method, where cost are importance weighted by the probability of the chosen action
  //generate cost-sensitive example
  cs_ld.costs.erase();
  if (ld.costs.size() == 1 && !is_test_label(ld))   //this is a typical example where we can perform all actions
  { //in this case generate cost-sensitive example with all actions
    for (uint32_t i = 1; i <= c.num_actions; i++)
    { COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
      if (c.known_cost != nullptr && i == c.known_cost->action)
      { wc.x = c.known_cost->cost / c.known_cost->probability; //use importance weighted cost for observed action, 0 otherwise
        //ips can be thought as the doubly robust method with a fixed regressor that predicts 0 costs for everything
        //update the loss of this regressor
        c.nb_ex_regressors++;
        c.avg_loss_regressors += (1.0f / c.nb_ex_regressors)*((c.known_cost->cost)*(c.known_cost->cost) - c.avg_loss_regressors);
        c.last_pred_reg = 0;
        c.last_correct_cost = c.known_cost->cost;
      }

      cs_ld.costs.push_back(wc);
    }
  }
  else   //this is an example where we can only perform a subset of the actions
  { //in this case generate cost-sensitive example with only allowed actions
    for (auto& cl : ld.costs)
    { COST_SENSITIVE::wclass wc = {0., cl.action, 0., 0.};
      if (c.known_cost != nullptr && cl.action == c.known_cost->action)
      { wc.x = c.known_cost->cost / c.known_cost->probability; //use importance weighted cost for observed action, 0 otherwise

        //ips can be thought as the doubly robust method with a fixed regressor that predicts 0 costs for everything
        //update the loss of this regressor
        c.nb_ex_regressors++;
        c.avg_loss_regressors += (1.0f / c.nb_ex_regressors)*((c.known_cost->cost)*(c.known_cost->cost) - c.avg_loss_regressors);
        c.last_pred_reg = 0;<commit_msg>safe probability minimums<commit_after>/*
  Copyright (c) by respective owners including Yahoo!, Microsoft, and
  individual contributors. All rights reserved.  Released under a BSD (revised)
  license as described in the file LICENSE.
*/
#include <float.h>

#include "vw.h"
#include "reductions.h"
#include "vw_exception.h"
#include "gen_cs_example.h"

namespace GEN_CS
{
using namespace LEARNER;
using namespace CB_ALGS;
using namespace CB;

inline bool observed_cost(cb_class* cl)
{ //cost observed for this action if it has non zero probability and cost != FLT_MAX
  return (cl != nullptr && cl->cost != FLT_MAX && cl->probability > .0);
}

cb_class* get_observed_cost(CB::label& ld)
{ for (auto& cl : ld.costs)
    if (observed_cost(&cl))
      return &cl;
  return nullptr;
}

  float safe_probability(float prob)
  {
    if (prob <= 0.)
      {
	std::cout << "Probability " << prob << " is not possible, replacing with 1e-3.  Fix your dataset. " << std::endl;
	return 1e-3;
      }
    else
      return prob;
  }
  
//Multiline version
void gen_cs_example_ips(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { CB::label ld = examples[i]->l.cb;

    COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    if (ld.costs.size() == 1 && ld.costs[0].cost != FLT_MAX)
      wc.x = ld.costs[0].cost / safe_probability(ld.costs[0].probability);
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//Multiline version
void gen_cs_example_dm(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { CB::label ld = examples[i]->l.cb;

    COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    if (ld.costs.size() == 1 && ld.costs[0].cost != FLT_MAX)
      wc.x = ld.costs[0].cost;
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//Multiline version
void gen_cs_test_example(v_array<example*> examples, COST_SENSITIVE::label& cs_labels)
{ cs_labels.costs.erase();
  bool shared = CB::ec_is_example_header(*examples[0]);
  for (uint32_t i = 0; i < examples.size()-1; i++)
  { COST_SENSITIVE::wclass wc = {FLT_MAX,i,0.,0.};
    if (shared && i > 0)
      wc.class_index = (uint32_t)i-1;
    cs_labels.costs.push_back(wc);
  }

  if (shared)//take care of shared examples
  { cs_labels.costs[0].class_index = 0;
    cs_labels.costs[0].x = -FLT_MAX;
  }
}

//single line version
void gen_cs_example_ips(cb_to_cs& c, CB::label& ld, COST_SENSITIVE::label& cs_ld)
{ //this implements the inverse propensity score method, where cost are importance weighted by the probability of the chosen action
  //generate cost-sensitive example
  cs_ld.costs.erase();
  if (ld.costs.size() == 1 && !is_test_label(ld))   //this is a typical example where we can perform all actions
  { //in this case generate cost-sensitive example with all actions
    for (uint32_t i = 1; i <= c.num_actions; i++)
    { COST_SENSITIVE::wclass wc = {0.,i,0.,0.};
      if (c.known_cost != nullptr && i == c.known_cost->action)
      {
	wc.x = c.known_cost->cost / safe_probability(c.known_cost->probability); //use importance weighted cost for observed action, 0 otherwise
        //ips can be thought as the doubly robust method with a fixed regressor that predicts 0 costs for everything
        //update the loss of this regressor
        c.nb_ex_regressors++;
        c.avg_loss_regressors += (1.0f / c.nb_ex_regressors)*((c.known_cost->cost)*(c.known_cost->cost) - c.avg_loss_regressors);
        c.last_pred_reg = 0;
        c.last_correct_cost = c.known_cost->cost;
      }

      cs_ld.costs.push_back(wc);
    }
  }
  else   //this is an example where we can only perform a subset of the actions
  { //in this case generate cost-sensitive example with only allowed actions
    for (auto& cl : ld.costs)
    { COST_SENSITIVE::wclass wc = {0., cl.action, 0., 0.};
      if (c.known_cost != nullptr && cl.action == c.known_cost->action)
	{ wc.x = c.known_cost->cost / safe_probability(c.known_cost->probability); //use importance weighted cost for observed action, 0 otherwise

        //ips can be thought as the doubly robust method with a fixed regressor that predicts 0 costs for everything
        //update the loss of this regressor
        c.nb_ex_regressors++;
        c.avg_loss_regressors += (1.0f / c.nb_ex_regressors)*((c.known_cost->cost)*(c.known_cost->cost) - c.avg_loss_regressors);
        c.last_pred_reg = 0;
{'fraction_non_alphanumeric': 0.14052526943601548, 'fraction_numerical': 0.015590666527152873, 'mean_word_length': 3.445581395348837, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 49, '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': '643606', 'n_tokens_mistral': 3616, 'n_tokens_neox': 3464, 'n_words': 873}