Skip to content

Commit e2990f7

Browse files
Revert "bugfix and updated docs"
This reverts commit f93b10f.
1 parent f93b10f commit e2990f7

File tree

14 files changed

+72
-103
lines changed

14 files changed

+72
-103
lines changed

API_REFERENCE_FOR_CLASSIFICATION.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# APLRClassifier
22

3-
## class aplr.APLRClassifier(m:int=3000, v:float=0.1, random_state:int=0, n_jobs:int=0, cv_folds:int=5, bins:int=300, verbosity:int=0, max_interaction_level:int=1, max_interactions:int=100000, min_observations_in_split:int=20, ineligible_boosting_steps_added:int=10, max_eligible_terms:int=5, boosting_steps_before_interactions_are_allowed: int = 0, monotonic_constraints_ignore_interactions: bool = False, early_stopping_rounds: int = 500)
3+
## class aplr.APLRClassifier(m:int=3000, v:float=0.3, random_state:int=0, n_jobs:int=0, cv_folds:int=5, bins:int=300, verbosity:int=0, max_interaction_level:int=1, max_interactions:int=100000, min_observations_in_split:int=20, ineligible_boosting_steps_added:int=10, max_eligible_terms:int=5, boosting_steps_before_interactions_are_allowed: int = 0, monotonic_constraints_ignore_interactions: bool = False, early_stopping_rounds: int = 500)
44

55
### Constructor parameters
66

77
#### m (default = 3000)
88
The maximum number of boosting steps. If validation error does not flatten out at the end of the ***m***th boosting step, then try increasing it (or alternatively increase the learning rate).
99

10-
#### v (default = 0.1)
11-
The learning rate. Must be greater than zero and not more than one. The higher the faster the algorithm learns and the lower ***m*** is required. However, empirical evidence suggests that ***v <= 0.1*** gives better results. If the algorithm learns too fast (requires few boosting steps to converge) then try lowering the learning rate. Computational costs can be reduced by increasing the learning rate while simultaneously decreasing ***m***, potentially at the expense of predictiveness.
10+
#### v (default = 0.3)
11+
The learning rate. Must be greater than zero and not more than one. The higher the faster the algorithm learns and the lower ***m*** is required. If the algorithm learns too fast (requires few boosting steps to converge) then try lowering the learning rate. Computational costs can be reduced by increasing the learning rate while simultaneously decreasing ***m***, potentially at the expense of predictiveness.
1212

1313
#### random_state (default = 0)
1414
Used to randomly split training observations into cv_folds if ***cv_observations*** is not specified when fitting.

aplr/aplr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class APLRClassifier:
300300
def __init__(
301301
self,
302302
m: int = 3000,
303-
v: float = 0.1,
303+
v: float = 0.3,
304304
random_state: int = 0,
305305
n_jobs: int = 0,
306306
cv_folds: int = 5,

cpp/APLRClassifier.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class APLRClassifier
4646
bool monotonic_constraints_ignore_interactions;
4747
size_t early_stopping_rounds;
4848

49-
APLRClassifier(size_t m = 3000, double v = 0.1, uint_fast32_t random_state = std::numeric_limits<uint_fast32_t>::lowest(), size_t n_jobs = 0,
49+
APLRClassifier(size_t m = 3000, double v = 0.3, uint_fast32_t random_state = std::numeric_limits<uint_fast32_t>::lowest(), size_t n_jobs = 0,
5050
size_t cv_folds = 5, size_t reserved_terms_times_num_x = 100, size_t bins = 300, size_t verbosity = 0, size_t max_interaction_level = 1,
5151
size_t max_interactions = 100000, size_t min_observations_in_split = 20, size_t ineligible_boosting_steps_added = 10, size_t max_eligible_terms = 5,
5252
size_t boosting_steps_before_interactions_are_allowed = 0, bool monotonic_constraints_ignore_interactions = false,

cpp/APLRRegressor.h

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,14 @@ void APLRRegressor::throw_error_if_dispersion_parameter_is_invalid()
435435
if (loss_function == "tweedie")
436436
{
437437
bool dispersion_parameter_equals_invalid_poits{is_approximately_equal(dispersion_parameter, 1.0) || is_approximately_equal(dispersion_parameter, 2.0)};
438-
bool dispersion_parameter_is_in_invalid_range{is_less(dispersion_parameter, 1.0)};
438+
bool dispersion_parameter_is_in_invalid_range{std::isless(dispersion_parameter, 1.0)};
439439
bool dispersion_parameter_is_invalid{dispersion_parameter_equals_invalid_poits || dispersion_parameter_is_in_invalid_range};
440440
if (dispersion_parameter_is_invalid)
441441
throw std::runtime_error("Invalid dispersion_parameter (variance power). It must not equal 1.0 or 2.0 and cannot be below 1.0.");
442442
}
443443
else if (loss_function == "negative_binomial" || loss_function == "cauchy" || loss_function == "weibull")
444444
{
445-
bool dispersion_parameter_is_in_invalid{is_less_equal(dispersion_parameter, 0.0)};
445+
bool dispersion_parameter_is_in_invalid{std::islessequal(dispersion_parameter, 0.0)};
446446
if (dispersion_parameter_is_in_invalid)
447447
throw std::runtime_error("Invalid dispersion_parameter. It must be greater than zero.");
448448
}
@@ -551,7 +551,7 @@ void APLRRegressor::throw_error_if_response_contains_invalid_values(const Vector
551551
std::string error_message{"Response values for the logit link function or binomial loss_function cannot be less than zero or greater than one."};
552552
throw_error_if_response_is_not_between_0_and_1(y, error_message);
553553
}
554-
else if (loss_function == "gamma" || (loss_function == "tweedie" && is_greater(dispersion_parameter, 2.0)))
554+
else if (loss_function == "gamma" || (loss_function == "tweedie" && std::isgreater(dispersion_parameter, 2)))
555555
{
556556
std::string error_message;
557557
if (loss_function == "tweedie")
@@ -560,7 +560,7 @@ void APLRRegressor::throw_error_if_response_contains_invalid_values(const Vector
560560
error_message = "Response values for the " + loss_function + " loss_function must be greater than zero.";
561561
throw_error_if_vector_contains_non_positive_values(y, error_message);
562562
}
563-
else if (link_function == "log" || loss_function == "poisson" || loss_function == "negative_binomial" || loss_function == "weibull" || (loss_function == "tweedie" && is_less(dispersion_parameter, 2.0) && is_greater(dispersion_parameter, 1.0)))
563+
else if (link_function == "log" || loss_function == "poisson" || loss_function == "negative_binomial" || loss_function == "weibull" || (loss_function == "tweedie" && std::isless(dispersion_parameter, 2) && std::isgreater(dispersion_parameter, 1)))
564564
{
565565
std::string error_message{"Response values for the log link function or poisson loss_function or negative binomial loss function or weibull loss function or tweedie loss_function when dispersion_parameter<2 cannot be less than zero."};
566566
throw_error_if_vector_contains_negative_values(y, error_message);
@@ -1098,7 +1098,7 @@ void APLRRegressor::update_intercept(size_t boosting_step)
10981098
else
10991099
intercept_update = v * (neg_gradient_current.array() * sample_weight_train.array()).sum() / sample_weight_train.array().sum();
11001100
if (model_has_changed_in_this_boosting_step == false)
1101-
model_has_changed_in_this_boosting_step = !is_approximately_zero(intercept_update);
1101+
model_has_changed_in_this_boosting_step = !is_approximately_equal(intercept_update, 0.0);
11021102
linear_predictor_update = VectorXd::Constant(neg_gradient_current.size(), intercept_update);
11031103
linear_predictor_update_validation = VectorXd::Constant(y_validation.size(), intercept_update);
11041104
update_linear_predictor_and_predictions();
@@ -1159,7 +1159,7 @@ size_t APLRRegressor::find_best_term_index(std::vector<Term> &terms, std::vector
11591159
bool term_is_eligible{terms[term_index].ineligible_boosting_steps == 0};
11601160
if (term_is_eligible)
11611161
{
1162-
if (is_less(terms[term_index].split_point_search_errors_sum, lowest_errors_sum))
1162+
if (std::isless(terms[term_index].split_point_search_errors_sum, lowest_errors_sum))
11631163
{
11641164
best_term_index = term_index;
11651165
lowest_errors_sum = terms[term_index].split_point_search_errors_sum;
@@ -1365,9 +1365,9 @@ void APLRRegressor::add_promising_interactions_and_select_the_best_one()
13651365
if (allowed_to_add_one_interaction)
13661366
{
13671367
if (best_term_before_interactions_was_not_selected)
1368-
error_is_less_than_for_best_term_before_interactions = is_less(interactions_to_consider[sorted_indexes_of_errors_for_interactions_to_consider[j]].split_point_search_errors_sum, neg_gradient_nullmodel_errors_sum);
1368+
error_is_less_than_for_best_term_before_interactions = std::isless(interactions_to_consider[sorted_indexes_of_errors_for_interactions_to_consider[j]].split_point_search_errors_sum, neg_gradient_nullmodel_errors_sum);
13691369
else
1370-
error_is_less_than_for_best_term_before_interactions = is_less(interactions_to_consider[sorted_indexes_of_errors_for_interactions_to_consider[j]].split_point_search_errors_sum, terms_eligible_current[best_term_before_interactions].split_point_search_errors_sum);
1370+
error_is_less_than_for_best_term_before_interactions = std::isless(interactions_to_consider[sorted_indexes_of_errors_for_interactions_to_consider[j]].split_point_search_errors_sum, terms_eligible_current[best_term_before_interactions].split_point_search_errors_sum);
13711371

13721372
if (error_is_less_than_for_best_term_before_interactions)
13731373
{
@@ -1390,7 +1390,7 @@ void APLRRegressor::select_the_best_term_and_update_errors(size_t boosting_step)
13901390
return;
13911391

13921392
if (model_has_changed_in_this_boosting_step == false)
1393-
model_has_changed_in_this_boosting_step = !is_approximately_zero(terms_eligible_current[best_term_index].coefficient);
1393+
model_has_changed_in_this_boosting_step = !is_approximately_equal(terms_eligible_current[best_term_index].coefficient, 0.0);
13941394
linear_predictor_update = terms_eligible_current[best_term_index].calculate_contribution_to_linear_predictor(X_train);
13951395
linear_predictor_update_validation = terms_eligible_current[best_term_index].calculate_contribution_to_linear_predictor(X_validation);
13961396
update_linear_predictor_and_predictions();
@@ -1578,7 +1578,7 @@ void APLRRegressor::print_summary_after_boosting_step(size_t boosting_step, Eige
15781578

15791579
void APLRRegressor::abort_boosting_when_no_validation_error_improvement_in_the_last_early_stopping_rounds(size_t boosting_step)
15801580
{
1581-
bool validation_error_is_better{is_less(validation_error_steps.col(0)[boosting_step], best_validation_error_so_far)};
1581+
bool validation_error_is_better{std::isless(validation_error_steps.col(0)[boosting_step], best_validation_error_so_far)};
15821582
if (validation_error_is_better)
15831583
{
15841584
best_validation_error_so_far = validation_error_steps.col(0)[boosting_step];
@@ -1744,7 +1744,7 @@ std::string APLRRegressor::compute_raw_base_term_name(const Term &term, const st
17441744
{
17451745
double temp_split_point{term.split_point};
17461746
std::string sign{"-"};
1747-
if (is_less(temp_split_point, 0.0))
1747+
if (std::isless(temp_split_point, 0))
17481748
{
17491749
temp_split_point = -temp_split_point;
17501750
sign = "+";
@@ -1902,11 +1902,11 @@ void APLRRegressor::check_term_integrity()
19021902
bool given_term_has_incorrect_split_point;
19031903
if (term.direction_right)
19041904
{
1905-
given_term_has_incorrect_split_point = is_less_equal(given_term.split_point, term.split_point);
1905+
given_term_has_incorrect_split_point = std::islessequal(given_term.split_point, term.split_point);
19061906
}
19071907
else
19081908
{
1909-
given_term_has_incorrect_split_point = is_greater_equal(given_term.split_point, term.split_point);
1909+
given_term_has_incorrect_split_point = std::isgreaterequal(given_term.split_point, term.split_point);
19101910
}
19111911
if (given_term_has_no_split_point)
19121912
throw std::runtime_error("Bug: Interaction in term " + term.name + " has no split point.");
@@ -1992,7 +1992,7 @@ void APLRRegressor::sort_terms()
19921992
{ return a.estimated_term_importance > b.estimated_term_importance ||
19931993
(is_approximately_equal(a.estimated_term_importance, b.estimated_term_importance) && (a.base_term < b.base_term)) ||
19941994
(is_approximately_equal(a.estimated_term_importance, b.estimated_term_importance) && (a.base_term == b.base_term) &&
1995-
is_less(a.coefficient, b.coefficient)); });
1995+
std::isless(a.coefficient, b.coefficient)); });
19961996

19971997
for (size_t i = 0; i < terms.size(); ++i)
19981998
{
@@ -2104,9 +2104,9 @@ void APLRRegressor::cap_predictions_to_minmax_in_training(VectorXd &predictions)
21042104
{
21052105
for (Eigen::Index i = 0; i < predictions.rows(); ++i)
21062106
{
2107-
if (is_greater(predictions[i], max_training_prediction_or_response))
2107+
if (std::isgreater(predictions[i], max_training_prediction_or_response))
21082108
predictions[i] = max_training_prediction_or_response;
2109-
else if (is_less(predictions[i], min_training_prediction_or_response))
2109+
else if (std::isless(predictions[i], min_training_prediction_or_response))
21102110
predictions[i] = min_training_prediction_or_response;
21112111
}
21122112
}
@@ -2265,13 +2265,13 @@ std::map<double, double> APLRRegressor::get_coefficient_shape_function(size_t pr
22652265
{
22662266
for (auto &key : coefficient_shape_function)
22672267
{
2268-
bool key_split_point_is_higher{is_greater(key.first, terms[relevant_term_indexes[i]].split_point)};
2268+
bool key_split_point_is_higher{std::isgreater(key.first, terms[relevant_term_indexes[i]].split_point)};
22692269
bool key_split_point_is_not_too_high{true};
22702270
for (auto &given_term : terms[relevant_term_indexes[i]].given_terms)
22712271
{
22722272
if (given_term.direction_right != terms[relevant_term_indexes[i]].direction_right)
22732273
{
2274-
if (is_greater(key.first, given_term.split_point))
2274+
if (std::isgreater(key.first, given_term.split_point))
22752275
{
22762276
key_split_point_is_not_too_high = false;
22772277
break;
@@ -2288,13 +2288,13 @@ std::map<double, double> APLRRegressor::get_coefficient_shape_function(size_t pr
22882288
{
22892289
for (auto &key : coefficient_shape_function)
22902290
{
2291-
bool key_split_point_is_lower{is_less(key.first, terms[relevant_term_indexes[i]].split_point)};
2291+
bool key_split_point_is_lower{std::isless(key.first, terms[relevant_term_indexes[i]].split_point)};
22922292
bool key_split_point_is_not_too_low{true};
22932293
for (auto &given_term : terms[relevant_term_indexes[i]].given_terms)
22942294
{
22952295
if (given_term.direction_right != terms[relevant_term_indexes[i]].direction_right)
22962296
{
2297-
if (is_less(key.first, given_term.split_point))
2297+
if (std::isless(key.first, given_term.split_point))
22982298
{
22992299
key_split_point_is_not_too_low = false;
23002300
break;

cpp/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ project(APLR VERSION 0.1.0)
44
include(CTest)
55
enable_testing()
66
set(CMAKE_CXX_FLAGS -pthread)
7-
set(CMAKE_CXX_FLAGS -fopenmp)
87

98
add_executable(APLR main.cpp)
109

cpp/functions.h

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,44 +13,28 @@
1313

1414
using namespace Eigen;
1515

16-
bool is_approximately_equal(double a, double b, double tolerance = std::numeric_limits<double>::epsilon())
16+
template <typename TReal>
17+
static bool is_approximately_equal(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
1718
{
18-
if (std::isinf(a) && std::isinf(b))
19-
{
20-
if (std::signbit(a) == std::signbit(b))
21-
return true;
22-
else
23-
return false;
24-
}
25-
26-
double relative_tolerance{(fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * tolerance};
27-
double absolute_tolerance{std::fmax(relative_tolerance, tolerance)};
28-
return fabs(a - b) <= absolute_tolerance;
29-
}
30-
31-
bool is_approximately_zero(double a, double tolerance = std::numeric_limits<double>::epsilon())
32-
{
33-
return is_approximately_equal(a, 0.0, tolerance);
34-
}
19+
if (std::isinf(a) && std::isinf(b) && std::signbit(a) == std::signbit(b))
20+
return true;
3521

36-
bool is_greater(double a, double b, double tolerance = std::numeric_limits<double>::epsilon())
37-
{
38-
return std::isgreater(a, b) && !is_approximately_equal(a, b, tolerance);
39-
}
22+
TReal diff = std::fabs(a - b);
23+
if (diff <= tolerance)
24+
return true;
4025

41-
bool is_greater_equal(double a, double b, double tolerance = std::numeric_limits<double>::epsilon())
42-
{
43-
return std::isgreater(a, b) || is_approximately_equal(a, b, tolerance);
44-
}
26+
if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
27+
return true;
4528

46-
bool is_less(double a, double b, double tolerance = std::numeric_limits<double>::epsilon())
47-
{
48-
return std::isless(a, b) && !is_approximately_equal(a, b, tolerance);
29+
return false;
4930
}
5031

51-
bool is_less_equal(double a, double b, double tolerance = std::numeric_limits<double>::epsilon())
32+
template <typename TReal>
33+
static bool is_approximately_zero(TReal a, TReal tolerance = std::numeric_limits<TReal>::epsilon())
5234
{
53-
return std::isless(a, b) || is_approximately_equal(a, b, tolerance);
35+
if (std::fabs(a) <= tolerance)
36+
return true;
37+
return false;
5438
}
5539

5640
std::set<std::string> get_unique_strings(const std::vector<std::string> &string_vector)
@@ -288,14 +272,14 @@ VectorXd calculate_exp_of_linear_predictor_adjusted_for_numerical_problems(const
288272
double max_exp_of_linear_predictor{std::exp(max_exponent)};
289273
for (Eigen::Index i = 0; i < linear_predictor.rows(); ++i)
290274
{
291-
bool linear_predictor_is_too_small{is_less(linear_predictor[i], min_exponent)};
275+
bool linear_predictor_is_too_small{std::isless(linear_predictor[i], min_exponent)};
292276
if (linear_predictor_is_too_small)
293277
{
294278
exp_of_linear_predictor[i] = min_exp_of_linear_predictor;
295279
continue;
296280
}
297281

298-
bool linear_predictor_is_too_large{is_greater(linear_predictor[i], max_exponent)};
282+
bool linear_predictor_is_too_large{std::isgreater(linear_predictor[i], max_exponent)};
299283
if (linear_predictor_is_too_large)
300284
{
301285
exp_of_linear_predictor[i] = max_exp_of_linear_predictor;

cpp/main.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ int main()
4646
// Saving results
4747
save_as_csv_file("output.csv", predictions);
4848
std::cout << "min validation_error " << model.validation_error_steps.minCoeff() << "\n\n";
49-
std::cout << is_approximately_equal(model.validation_error_steps.minCoeff(), 6.82063, 0.00001) << "\n";
49+
std::cout << is_approximately_equal(model.validation_error_steps.minCoeff(), 4.97628, 0.00001) << "\n";
5050

5151
std::cout << "mean prediction " << predictions.mean() << "\n\n";
52-
std::cout << is_approximately_equal(predictions.mean(), 23.6126, 0.0001) << "\n";
52+
std::cout << is_approximately_equal(predictions.mean(), 23.6053, 0.0001) << "\n";
5353

5454
std::cout << "best_m: " << model.m << "\n";
5555

cpp/pythonbinding.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ PYBIND11_MODULE(aplr_cpp, m)
233233

234234
py::class_<APLRClassifier>(m, "APLRClassifier", py::module_local())
235235
.def(py::init<int &, double &, int &, int &, int &, int &, int &, int &, int &, int &, int &, int &, int &, int &, bool &, int &>(),
236-
py::arg("m") = 3000, py::arg("v") = 0.1, py::arg("random_state") = 0, py::arg("n_jobs") = 0, py::arg("cv_folds") = 5,
236+
py::arg("m") = 3000, py::arg("v") = 0.3, py::arg("random_state") = 0, py::arg("n_jobs") = 0, py::arg("cv_folds") = 5,
237237
py::arg("reserved_terms_times_num_x") = 100, py::arg("bins") = 300, py::arg("verbosity") = 0,
238238
py::arg("max_interaction_level") = 1, py::arg("max_interactions") = 100000, py::arg("min_observations_in_split") = 20,
239239
py::arg("ineligible_boosting_steps_added") = 10, py::arg("max_eligible_terms") = 5,

0 commit comments

Comments
 (0)