import numpy as np
import pandas as pd
from pandas import Series, DataFrame

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.image as mpimg
from matplotlib import rcParams
import seaborn as sb

"""
Explanation: Two Topics Coupled example
Import Python built-in functions we need to run and plot the game
End of explanation
"""


%matplotlib inline
rcParams['figure.figsize'] = 5, 4
sb.set_style('whitegrid')

"""
Explanation: Set up inline matplotlib
End of explanation
"""


import sys
# search path for modules
sys.path.append('/Users/hn/Documents/GitHub/PyOpinionGame/')

import opiniongame.config as og_cfg
import opiniongame.IO as og_io
import opiniongame.coupling as og_coupling
import opiniongame.state as og_state
import opiniongame.adjacency as og_adj
import opiniongame.selection as og_select
import opiniongame.potentials as og_pot
import opiniongame.core as og_core
import opiniongame.stopping as og_stop
import opiniongame.opinions as og_opinions

"""
Explanation: Import Game Modules From a Given Path
User have to edit the path and put the correct one on his/her machine.
End of explanation
"""


config = og_cfg.staticParameters()

path = '/Users/hn/Documents/GitHub/PyOpinionGame/'  # path to the 'staticParameters.cfg'
staticParameters = path + 'staticParameters.cfg'

config.readFromFile(staticParameters) # Read static parameters
config.threshold = 0.0001
config.Kthreshold = 0.00001
config.startingseed = 10
config.learning_rate = 0.1
tau = 0.62 #tip of the tent potential function
config.printOut()

"""
Explanation: Setting Up Game Parameters
End of explanation
"""


print("SEEDING PRNG: "+str(config.startingseed))
np.random.seed(config.startingseed)

"""
Explanation: seed PRNG: must do this before any random numbers are ever sampled during default generation
End of explanation
"""


# These are the default matrices for the state of the system:
# If you want to change them, you can generate a new one in the following cell
default_weights = og_coupling.weights_no_coupling(config.popSize, config.ntopics)
default_initialOpinions = og_opinions.initialize_opinions(config.popSize, config.ntopics)
default_adj = og_adj.make_adj(config.popSize, 'full')

state = og_state.WorldState(adj=default_adj, 
                            couplingWeights=default_weights, 
                            initialOpinions=default_initialOpinions, 
                            initialHistorySize=100, 
                            historyGrowthScale=2)
state.validate()

"""
Explanation: Set up the state of the system
State of the system includes:

Weight Matrix (Matrix of the coupling wieghts between topic)
Initial Opinions of agents
Adjacency matrix of the network

 This is just initialization of the state, later we update some elements of it.
End of explanation
"""


numberOfCommunities = 3
communityPopSize    = 25
config.popSize = numberOfCommunities * communityPopSize

# List of upper bound probability of interaction between communities
uppBound_list = [0.0]

# List of uniqueness Strength parameter
individStrength = [0.0]

config.learning_rate = 0.1
config.iterationMax = 10000
tau = 0.62
config.printOut()
#
# functions for use by the simulation engine
#
ufuncs = og_cfg.UserFunctions(og_select.PickTwoWeighted,
                              og_stop.iterationStop,
                              og_pot.createTent(tau))
                              

# Number of different initial opinions, 
# i.e. number of different games with different initials.
noInitials = np.arange(1)
noGames = np.arange(1)    # Number of different game orders.

# Run experiments with different adjacencies, different initials, and different order of games.
for uniqForce in individStrength:
    config.uniqstrength = uniqForce
    for upperBound in uppBound_list:
        # Generate different adjacency matrix with different prob. of interaction
        # between different communities
        state.adj = og_adj.CommunitiesMatrix(communityPopSize, numberOfCommunities, upperBound)
                
        for countInitials in noInitials:
            # Pick three communities with similar opinions to begin with!
            state.initialOpinions = np.zeros((config.popSize, 1))
            state.initialOpinions[0:25]  = np.random.uniform(low=0.0, high=.25, size=(25,1))
            state.initialOpinions[25:50] = np.random.uniform(low=0.41, high=.58, size=(25,1))
            state.initialOpinions[50:75] = np.random.uniform(low=0.74, high= 1, size=(25,1))
   
            state.couplingWeights = og_coupling.weights_no_coupling(config.popSize, config.ntopics)
            all_experiments_history = {}
            print "(uniqForce, upperBound)  = ({}, {})".format(uniqForce, upperBound)
            print "countInitials = {}".format(countInitials + 1)
            
            for gameOrders in noGames:
                #cProfile.run('og_core.run_until_convergence(config, state, ufuncs)')
                state = og_core.run_until_convergence(config, state, ufuncs)
                print("One Experiment Done" , "gameOrders = " , gameOrders+1)
                all_experiments_history[ 'experiment' + str(gameOrders+1)] = state.history[0:state.nextHistoryIndex,:,:]
            og_io.saveMatrix('uB' + str(upperBound) + '*uS' + str(config.uniqstrength) + 
                             '*initCount' + str(countInitials+21) + '.mat', all_experiments_history)

print all_experiments_history.keys()
print all_experiments_history['experiment1'].shape

"""
Explanation: User Defined States and parameters Can go in the following cell:
End of explanation
"""


time, population_size, no_of_topics = evolution = all_experiments_history['experiment1'].shape
evolution = all_experiments_history['experiment1'].reshape(time, population_size)

fig = plt.figure()
plt.plot(evolution)
plt.xlabel('Time')
plt.ylabel('Opinionds')
plt.title('Evolution of Opinions')
fig.set_size_inches(10,5)
plt.show()

"""
Explanation: Plot the experiment done above:
End of explanation
"""


state = og_state.WorldState(adj=default_adj, 
                            couplingWeights=default_weights, 
                            initialOpinions=default_initialOpinions, 
                            initialHistorySize=100, 
                            historyGrowthScale=2)
state.validate()

#
# load configuration
#
config = og_cfg.staticParameters()
config.readFromFile('staticParameters.cfg')
config.threshold = 0.01
config.printOut()

#
# seed PRNG: must do this before any random numbers are
# ever sampled during default generation
#
print(("SEEDING PRNG: "+str(config.startingseed)))
np.random.seed(config.startingseed)

"""
Explanation: Skew Uniqueness Tendency Driver:
I observed when having tendency for uniqueness is drawn from normal distribution, we do not get an interesting result. For example, initial intuition was that uniqueness for tendency would delay stabilization of the network, however, it did not. So, here we draw uniqueness tendencies from skew normal distribution.
When most neighbors tend to go in one directions, then probability of individuals to go to the opposite direction would be more than the niose in the same direction:
End of explanation
"""


# These are the default matrices for the state of the system:
# If you want to change them, you can generate a new one in the following cell
default_weights = og_coupling.weights_no_coupling(config.popSize, config.ntopics)
default_initialOpinions = og_opinions.initialize_opinions(config.popSize, config.ntopics)
default_adj = og_adj.make_adj(config.popSize, 'full')

state = og_state.WorldState(adj=default_adj, 
                            couplingWeights=default_weights, 
                            initialOpinions=default_initialOpinions, 
                            initialHistorySize=100, 
                            historyGrowthScale=2)
state.validate()

#
# run
#
numberOfCommunities = 3
communityPopSize    = 25
config.popSize = numberOfCommunities * communityPopSize

# List of upper bound probability of interaction between communities
uppBound_list = np.array([.001, 0.004, 0.007, 0.01, 0.013, 0.016, 0.019])
#
# List of uniqueness Strength parameter
#
individStrength = np.arange(0.00001, 0.000251, 0.00006)
individStrength = np.append(0, individStrength)
individStrength = np.array([0.0])
skewstrength = 2.0

tau = 0.62
config.iterationMax = 30000
config.printOut()

#
# functions for use by the simulation engine
#
ufuncs = og_cfg.UserFunctions(og_select.PickTwoWeighted,
                              og_stop.iterationStop,
                              og_pot.createTent(tau))


                              
noInitials = np.arange(1) # Number of different initial opinions.
noGames = np.arange(1)    # Number of different game orders.
# Run experiments with different adjacencies, different initials, and different order of games.
for uniqForce in individStrength:
    config.uniqstrength = uniqForce
    for upperBound in uppBound_list:
        """
        Generate different adjacency matrix with different prob. of interaction
        between different communities
        """
        state.adj = og_adj.CommunitiesMatrix(communityPopSize, numberOfCommunities, upperBound)
        print"(upperBound, uniqForce) = (", upperBound, "," , uniqForce , ")"            
        for countInitials in noInitials:
         
            # Pick three communities with similar opinions (stable state) to begin with!
            state.initialOpinions = np.zeros((config.popSize, 1))
            state.initialOpinions[0:25]  = np.random.uniform(low=0.08, high=.1, size=(25,1))
            state.initialOpinions[25:50] = np.random.uniform(low=0.49, high=.51, size=(25,1))
            state.initialOpinions[50:75] = np.random.uniform(low=0.9, high= .92, size=(25,1))
   
            state.couplingWeights = og_coupling.weights_no_coupling(config.popSize, config.ntopics)
            all_experiments_history = {}

            print "countInitials=", countInitials + 1
            
            for gameOrders in noGames:
                #cProfile.run('og_core.run_until_convergence(config, state, ufuncs)')
                state = og_core.run_until_convergence(config, state, ufuncs)
                state.history = state.history[0:state.nextHistoryIndex,:,:]
                idx_IN_columns = [i for i in xrange(np.shape(state.history)[0]) if (i % (config.popSize)) == 0]
                state.history = state.history[idx_IN_columns,:,:]
                all_experiments_history[ 'experiment' + str(gameOrders+1)] = state.history
            og_io.saveMatrix('uB' + str(upperBound) + '*uS' + str(config.uniqstrength) + 
                             '*initCount' + str(countInitials+1) + '.mat', all_experiments_history)


all_experiments_history.keys()

time, population_size, no_of_topics = all_experiments_history['experiment1'].shape
evolution = all_experiments_history['experiment1'].reshape(time, population_size)

fig = plt.figure()
plt.plot(evolution)
plt.xlabel('Time')
plt.ylabel('Opinionds')
plt.title('Evolution of Opinions of 3 communities')
fig.set_size_inches(10, 5)
plt.show()

"""
Explanation: Initiate State
End of explanation
"""


