Skip to content

Commit

Permalink
Merge branch 'conditional_contextual_bandit' into jagerrit/python_json
Browse files Browse the repository at this point in the history
  • Loading branch information
jackgerrits committed Mar 26, 2019
2 parents f4dfbac + de4603b commit 3ac3827
Show file tree
Hide file tree
Showing 25 changed files with 1,496 additions and 121 deletions.
10 changes: 5 additions & 5 deletions cs/unittest/TestJson.cs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion test/unit_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_executable(vw-unit-test.out
main.cc options_test.cc options_boost_po_test.cc cb_explore_adf_test.cc explore_test.cc
stable_unique_tests.cc test_common.h object_pool_test.cc
stable_unique_tests.cc test_common.h object_pool_test.cc ccb_parser_test.cc json_parser_test.cc
dsjson_parser_test.cc
)

# Add the include directories from vw target for testing
Expand Down
143 changes: 143 additions & 0 deletions test/unit_test/ccb_parser_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <boost/test/test_tools.hpp>

#include "test_common.h"

#include <vector>
#include "conditional_contextual_bandit.h"
#include "parser.h"

CCB::label parse_label(parser* p, std::string label)
{
auto lp = CCB::ccb_label_parser;
tokenize(' ', { const_cast<char*>(label.c_str()), const_cast<char*>(label.c_str()) + strlen(label.c_str()) }, p->words);
CCB::label l;
lp.default_label(&l);
lp.parse_label(p, nullptr, &l, p->words);

return l;
}

BOOST_AUTO_TEST_CASE(ccb_parse_label)
{
parser p;
p.words = v_init<substring>();
p.parse_name = v_init<substring>();

auto label = parse_label(&p, "ccb shared");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 0);
BOOST_CHECK(label.outcome == nullptr);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::shared);

label = parse_label(&p, "ccb action");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 0);
BOOST_CHECK(label.outcome == nullptr);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::action);

label = parse_label(&p, "ccb decision");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 0);
BOOST_CHECK(label.outcome == nullptr);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::decision);

label = parse_label(&p, "ccb decision 1,3,4");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 3);
BOOST_CHECK_EQUAL(label.explicit_included_actions[0], 1);
BOOST_CHECK_EQUAL(label.explicit_included_actions[1], 3);
BOOST_CHECK_EQUAL(label.explicit_included_actions[2], 4);
BOOST_CHECK(label.outcome == nullptr);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::decision);

label = parse_label(&p, "ccb decision 1:1.0:0.5 3");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 1);
BOOST_CHECK_EQUAL(label.explicit_included_actions[0], 3);
BOOST_CHECK_CLOSE(label.outcome->cost, 1.0f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.outcome->probabilities.size(), 1);
BOOST_CHECK_EQUAL(label.outcome->probabilities[0].action, 1);
BOOST_CHECK_CLOSE(label.outcome->probabilities[0].score, .5f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::decision);

label = parse_label(&p, "ccb decision 1:-2.0:0.5,2:0.25,3:0.25 3,4");
BOOST_CHECK_EQUAL(label.explicit_included_actions.size(), 2);
BOOST_CHECK_EQUAL(label.explicit_included_actions[0], 3);
BOOST_CHECK_EQUAL(label.explicit_included_actions[1], 4);
BOOST_CHECK_CLOSE(label.outcome->cost, -2.0f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.outcome->probabilities.size(), 3);
BOOST_CHECK_EQUAL(label.outcome->probabilities[0].action, 1);
BOOST_CHECK_CLOSE(label.outcome->probabilities[0].score, .5f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.outcome->probabilities[1].action, 2);
BOOST_CHECK_CLOSE(label.outcome->probabilities[1].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.outcome->probabilities[2].action, 3);
BOOST_CHECK_CLOSE(label.outcome->probabilities[2].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(label.type, CCB::example_type::decision);

BOOST_REQUIRE_THROW(parse_label(&p, "shared"), VW::vw_exception);
BOOST_REQUIRE_THROW(parse_label(&p, "other shared"), VW::vw_exception);
BOOST_REQUIRE_THROW(parse_label(&p, "other"), VW::vw_exception);
BOOST_REQUIRE_THROW(parse_label(&p, "ccb unknown"), VW::vw_exception);
BOOST_REQUIRE_THROW(parse_label(&p, "ccb decision 1:1.0:0.5,4:0.7"), VW::vw_exception);
}

BOOST_AUTO_TEST_CASE(ccb_cache_label)
{
io_buf io;
io.init();
io.space.resize(1000);
io.space.end() = io.space.begin() + 1000;

parser p;
p.words = v_init<substring>();
p.parse_name = v_init<substring>();

auto lp = CCB::ccb_label_parser;
auto label = parse_label(&p, "ccb decision 1:-2.0:0.5,2:0.25,3:0.25 3,4");

lp.cache_label(&label, io);
io.head = io.space.begin();

CCB::label uncached_label;
lp.default_label(&uncached_label);
lp.read_cached_label(nullptr, &uncached_label, io);

BOOST_CHECK_EQUAL(uncached_label.explicit_included_actions.size(), 2);
BOOST_CHECK_EQUAL(uncached_label.explicit_included_actions[0], 3);
BOOST_CHECK_EQUAL(uncached_label.explicit_included_actions[1], 4);
BOOST_CHECK_CLOSE(uncached_label.outcome->cost, -2.0f, FLOAT_TOL);
BOOST_CHECK_EQUAL(uncached_label.outcome->probabilities.size(), 3);
BOOST_CHECK_EQUAL(uncached_label.outcome->probabilities[0].action, 1);
BOOST_CHECK_CLOSE(uncached_label.outcome->probabilities[0].score, .5f, FLOAT_TOL);
BOOST_CHECK_EQUAL(uncached_label.outcome->probabilities[1].action, 2);
BOOST_CHECK_CLOSE(uncached_label.outcome->probabilities[1].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(uncached_label.outcome->probabilities[2].action, 3);
BOOST_CHECK_CLOSE(uncached_label.outcome->probabilities[2].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(uncached_label.type, CCB::example_type::decision);
}

BOOST_AUTO_TEST_CASE(ccb_copy_label)
{
parser p;
p.words = v_init<substring>();
p.parse_name = v_init<substring>();
auto lp = CCB::ccb_label_parser;

auto label = parse_label(&p, "ccb decision 1:-2.0:0.5,2:0.25,3:0.25 3,4");

CCB::label copied_to;
lp.default_label(&copied_to);

lp.copy_label(&copied_to, &label);

BOOST_CHECK_EQUAL(copied_to.explicit_included_actions.size(), 2);
BOOST_CHECK_EQUAL(copied_to.explicit_included_actions[0], 3);
BOOST_CHECK_EQUAL(copied_to.explicit_included_actions[1], 4);
BOOST_CHECK_CLOSE(copied_to.outcome->cost, -2.0f, FLOAT_TOL);
BOOST_CHECK_EQUAL(copied_to.outcome->probabilities.size(), 3);
BOOST_CHECK_EQUAL(copied_to.outcome->probabilities[0].action, 1);
BOOST_CHECK_CLOSE(copied_to.outcome->probabilities[0].score, .5f, FLOAT_TOL);
BOOST_CHECK_EQUAL(copied_to.outcome->probabilities[1].action, 2);
BOOST_CHECK_CLOSE(copied_to.outcome->probabilities[1].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(copied_to.outcome->probabilities[2].action, 3);
BOOST_CHECK_CLOSE(copied_to.outcome->probabilities[2].score, .25f, FLOAT_TOL);
BOOST_CHECK_EQUAL(copied_to.type, CCB::example_type::decision);
}
186 changes: 186 additions & 0 deletions test/unit_test/dsjson_parser_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <boost/test/test_tools.hpp>

#include "test_common.h"

#include <vector>
#include "conditional_contextual_bandit.h"
#include "parse_example_json.h"

v_array<example*> parse_dsjson(vw& all, std::string line)
{
auto examples = v_init<example*>();
examples.push_back(&VW::get_unused_example(&all));
DecisionServiceInteraction interaction;

VW::read_line_decision_service_json<true>(all, examples, (char*)line.c_str(), line.size(), false,
(VW::example_factory_t)&VW::get_unused_example, (void*)&all, &interaction);

return examples;
}

// TODO: Make unit test dig out and verify features.
BOOST_AUTO_TEST_CASE(parse_dsjson_cb)
{
std::string json_text = R"(
{
"_label_cost": -1,
"_label_probability": 0.8166667,
"_label_Action": 2,
"_labelIndex": 1,
"Version": "1",
"EventId": "0074434d3a3a46529f65de8a59631939",
"a": [
2,
1,
3
],
"c": {
"shared_ns": {
"shared_feature": 0
},
"_multi": [
{
"_tag": "tag",
"ns1": {
"f1": 1,
"f2": "strng"
},
"ns2": [
{
"f3": "value1"
},
{
"ns3": {
"f4": 0.994963765
}
}
]
},
{
"_tag": "tag",
"ns1": {
"f1": 1,
"f2": "strng"
}
},
{
"_tag": "tag",
"ns1": {
"f1": 1,
"f2": "strng"
}
}
]
},
"p": [
0.816666663,
0.183333333,
0.183333333
],
"VWState": {
"m": "096200c6c41e42bbb879c12830247637/0639c12bea464192828b250ffc389657"
}
}
)";
auto vw = VW::initialize("--dsjson --cb_adf --no_stdin", nullptr, false, nullptr, nullptr);
auto examples = parse_dsjson(*vw, json_text);

BOOST_CHECK_EQUAL(examples.size(), 4);

// Shared example
BOOST_CHECK_EQUAL(examples[0]->l.cb.costs.size(), 1);
BOOST_CHECK_CLOSE(examples[0]->l.cb.costs[0].probability, -1.f, FLOAT_TOL);
BOOST_CHECK_CLOSE(examples[0]->l.cb.costs[0].cost, FLT_MAX, FLOAT_TOL);

// Action examples
BOOST_CHECK_EQUAL(examples[1]->l.cb.costs.size(), 0);
BOOST_CHECK_EQUAL(examples[2]->l.cb.costs.size(), 1);
BOOST_CHECK_EQUAL(examples[3]->l.cb.costs.size(), 0);

BOOST_CHECK_CLOSE(examples[2]->l.cb.costs[0].probability, 0.8166667, FLOAT_TOL);
BOOST_CHECK_CLOSE(examples[2]->l.cb.costs[0].cost, -1.0, FLOAT_TOL);
BOOST_CHECK_EQUAL(examples[2]->l.cb.costs[0].action, 2);
}

// TODO: Make unit test dig out and verify features.
BOOST_AUTO_TEST_CASE(parse_dsjson_ccb)
{
std::string json_text = R"(
{
"Timestamp":"timestamp_utc",
"Version": "1",
"c":{
"_multi": [
{
"b_": "1",
"c_": "1",
"d_": "1"
},
{
"b_": "2",
"c_": "2",
"d_": "2"
}
],
"_df":[
{
"_id": "00eef1eb-2205-4f47",
"_inc": [1,2],
"test": 4
},
{
"_id": "set_id",
"other": 6
}
]
},
"_decisions":[{
"_label_cost": 2,
"_o": [],
"_a": 1,
"_p": 0.25
},
{
"_label_cost": 4,
"_o":[],
"_a": [2, 1],
"_p": [0.75, 0.25]
}
],
"VWState": {
"m": "096200c6c41e42bbb879c12830247637/0639c12bea464192828b250ffc389657"
}
}
)";

auto vw = VW::initialize("--ccb_explore_adf --dsjson --no_stdin", nullptr, false, nullptr, nullptr);
auto examples = parse_dsjson(*vw, json_text);

BOOST_CHECK_EQUAL(examples.size(), 5);
BOOST_CHECK_EQUAL(examples[0]->l.conditional_contextual_bandit.type, CCB::example_type::shared);
BOOST_CHECK_EQUAL(examples[1]->l.conditional_contextual_bandit.type, CCB::example_type::action);
BOOST_CHECK_EQUAL(examples[2]->l.conditional_contextual_bandit.type, CCB::example_type::action);
BOOST_CHECK_EQUAL(examples[3]->l.conditional_contextual_bandit.type, CCB::example_type::decision);
BOOST_CHECK_EQUAL(examples[4]->l.conditional_contextual_bandit.type, CCB::example_type::decision);

auto label1 = examples[3]->l.conditional_contextual_bandit;
BOOST_CHECK_EQUAL(label1.explicit_included_actions.size(), 2);
BOOST_CHECK_EQUAL(label1.explicit_included_actions[0], 1);
BOOST_CHECK_EQUAL(label1.explicit_included_actions[1], 2);
BOOST_CHECK_CLOSE(label1.outcome->cost, 2.f, .0001f);
BOOST_CHECK_EQUAL(label1.outcome->probabilities.size(), 1);
BOOST_CHECK_EQUAL(label1.outcome->probabilities[0].action, 1);
BOOST_CHECK_CLOSE(label1.outcome->probabilities[0].score, .25f, .0001f);

auto label2 = examples[4]->l.conditional_contextual_bandit;
BOOST_CHECK_EQUAL(label2.explicit_included_actions.size(), 0);
BOOST_CHECK_CLOSE(label2.outcome->cost, 4.f, .0001f);
BOOST_CHECK_EQUAL(label2.outcome->probabilities.size(), 2);
BOOST_CHECK_EQUAL(label2.outcome->probabilities[0].action, 2);
BOOST_CHECK_CLOSE(label2.outcome->probabilities[0].score, .75f, .0001f);
BOOST_CHECK_EQUAL(label2.outcome->probabilities[1].action, 1);
BOOST_CHECK_CLOSE(label2.outcome->probabilities[1].score, .25f, .0001f);
}
Loading

0 comments on commit 3ac3827

Please sign in to comment.