# -*- coding: utf-8 -*-

#   Copyright (c) 2010-2014, MIT Probabilistic Computing Project
#
#   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.

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import Imputer

from bdbcontrib.foreign import predictor

class SatOrbitalMechanics(predictor.IForeignPredictor):
    """
    A "foreign predictor" trained on the satellites.csv dataset. The
    SatOrbitalMechanics is trained to model
        `Period_minutes`
    conditioned on
        'Apogee_km', 'Perigee_km',
    Using Kepler's Third Law.

    Attributes
    ----------
    Please do not mess around with any (exploring is ok).

    Methods
    -------
    simulate(n_samples, apogee_km, perigee_km)
        Simulate Period_minutes|Apogee_km,Perigee_km
    probability(period_mins, apogee_km, perigee_km)
        Compute P(period_mins|kwargs).
    """
    conditionals =['Apogee_km', 'Perigee_km']
    target = ['Period_minutes']

    @staticmethod
    def _compute_period(apogee_km, perigee_km):
        """Computes the period of the satellite in seconds given the apogee_km
        and perigee_km of the satellite.
        """
        GM = 398600.4418
        EARTH_RADIUS = 6378
        a = 0.5*(apogee_km + perigee_km) + EARTH_RADIUS
        return 2 * np.pi * np.sqrt(a**3/GM)

    def __init__(self, sat_df):
        """Initializes SatOrbitalMechanics, and learns a noise model for the
        period.
        """
        self.dataset = sat_df[self.conditionals + self.target].dropna()
        X = self.dataset[self.conditionals].as_matrix()
        actual_period = self.dataset[self.target].as_matrix().ravel()

        theoretical_period = self._compute_period(X[:,0], X[:,1])/60.
        errors = np.abs(actual_period-theoretical_period)
        errors = np.mean(np.select(
            [errors<np.percentile(errors, 95)], [errors]))
        self.noise = np.sqrt(np.mean(errors**2))

    def get_targets(self):
        return self.target

    def get_conditions(self):
        return self.conditionals

    def simulate(self, n_samples, **kwargs):
        """Simulates period_val|kwargs under Kepler's Third Law and Gaussian
        noise model. kwargs must be of the form apogee_km=a, perigee_km=p.
        """
        period = self._compute_period(kwargs[self.conditionals[0]],
            kwargs[self.conditionals[1]])
        return period/60. + np.random.normal(scale=self.noise,
            size=n_samples)

    def probability(self, period_val, **kwargs):
        """Computes the probability density P(period_val|kwargs) under
        Kepler's Third Law and Gaussian noise model.
        kwargs must be of the form Apogee_km=a, Perigee_km=p.
        """
        period = self._compute_period(kwargs[self.conditionals[0]],
            kwargs[self.conditionals[1]]) / 60.
        return 1./(self.noise * np.sqrt(2*np.pi)) * \
            np.exp(-(period_val-period)**2 / (2*self.noise**2))

@@ -1436,18 +1436,16 @@
 ondition
-al
 s =%5B'Apo
@@ -1467,16 +1467,16 @@
 ee_km'%5D%0A
+
     targ
@@ -2033,18 +2033,16 @@
 ondition
-al
 s + self
@@ -2098,18 +2098,16 @@
 ondition
-al
 s%5D.as_ma
@@ -2571,18 +2571,16 @@
 ondition
-al
 s%0A%0A    d
@@ -2834,34 +2834,32 @@
 s%5Bself.condition
-al
 s%5B0%5D%5D,%0A         
@@ -2878,26 +2878,24 @@
 lf.condition
-al
 s%5B1%5D%5D)%0A     
@@ -3287,18 +3287,16 @@
 ondition
-al
 s%5B0%5D%5D,%0A 
@@ -3327,18 +3327,16 @@
 ondition
-al
 s%5B1%5D%5D) /

Rename conditionals to conditions.