/*
 * Copyright (C) 2015 Holmes Team at HUAWEI Noah's Ark Lab.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "Logisticregression.h"
#include "../../../Common.h"
#include <cmath>

REGISTER_CLASS(LogisticRegression);
REGISTER_COMMAND_LINE_PARAMETER(
		LogisticRegression,
		"{\"type\":\"Learner\","
		"\"name\":\"LogisticRegression\","
		"\"parameter\":{"
		"\"-r\":\"LearningRatio\","
		"\"-a\":\"Lambda\""
		"}}"
		"");

LogisticRegression::LogisticRegression() : Perceptron() {
	mLambda = 0.0001;
}

LogisticRegression::~LogisticRegression() {
}

void LogisticRegression::doSetParams() {
    mLearningRatio = getParam("LearningRatio", 0.01);
	mLambda = getParam("Lambda", 0.0001);
}

double LogisticRegression::prediction(int actualClass, vector<double>& value, int label) {
	double z = dotProd(value, label);
	z += getWeightAttributes(label, numberInputAttributes);
	double ret;
	if (z <= 0) {
		ret = 1.0 - (1.0 / (1.0 + exp(z)));
	} else {
		ret = 1.0 / (1.0 + exp(-z));
	}
	return ret;
}

double LogisticRegression::getInstanceMultiplier(int actualClass, vector<double>& value, int classLabel) {
	double y = (int) actualClass != classLabel ? -1.0 : 1.0;
	double wx = dotProd(value, classLabel);
	double z = y * (wx + getWeightAttributes(classLabel, numberInputAttributes));
	double factor = y * mLearningRatio * dloss(z);
	return factor;
}

double LogisticRegression::getWeightMultiplier(const Instance& instance, int classLabel) {
	double multiplier = 1.0 - (mLearningRatio * mLambda) / ((double)instancesSeen + 1);
	return multiplier;
}

double LogisticRegression::dloss(double z) {
	double ret;
	if (z < 0) {
		ret = 1.0 / (1.0 + exp(z));
	} else {
		double t = exp(-z);
		ret = t / (t + 1.0);	
	}
	return ret;
}

void LogisticRegression::initWeightAttributes() {
	for (int i = 0; i < numberClasses; i++) {
		weightAttributes[i].resize(numberInputAttributes + 1);
		for (int j = 0; j < numberInputAttributes+1; j++) {
			setWeightAttributes(0, i, j);
		}
	}
}

bool LogisticRegression::exportToJson(Json::Value& jv) {
    if (!Perceptron::exportToJson(jv)) {
        return false;
    }

    jv["lambda"] = mLambda;

	return true;
}

bool LogisticRegression::importFromJson(const Json::Value& jv) {
    if (!Perceptron::importFromJson(jv)) {
        return false;
    }

	mLambda = jv["lambda"].asDouble();

	return true;
}
