Skip to content

Commit

Permalink
Merge pull request #8 from danyoungday/ci
Browse files Browse the repository at this point in the history
Fixing Github actions to perform CI
  • Loading branch information
danyoungday authored Sep 4, 2024
2 parents 0a381fb + b981ec1 commit 4df4dde
Show file tree
Hide file tree
Showing 34 changed files with 174 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
max-line-length = 120
max-line-length = 120
exclude = inputSpecs.py
4 changes: 2 additions & 2 deletions .github/workflows/enroads.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ jobs:
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Download En-ROADS sdk
env:
ENROADS_URL: ${{ secrets.ENROADS_URL }}
ENROADS_ID: ${{ secrets.ENROADS_ID }}
ENROADS_PASSWORD: ${{ secrets.ENROADS_PASSWORD }}
ENROADS_URL: ${{ secrets.ENROADS_URL }}
run: python -m enroadspy.download_sdk
- name: Lint with PyLint
run: pylint .
run: pylint ./*
- name: Lint with Flake8
run: flake8
- name: Run unit tests
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ suggestion-mode=yes

good-names=X,F,X0

fail-under=9.8
fail-under=9.6
10 changes: 4 additions & 6 deletions app/components/outcome.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""
OutcomeComponent class for the outcome section of the app.
"""
import sys

from dash import Input, Output, html, dcc
import dash_bootstrap_components as dbc
import pandas as pd
Expand All @@ -26,7 +24,7 @@ def __init__(self, evolution_handler: EvolutionHandler, all_cand_idxs: list[str]
"_global_population_in_2100"]
self.plot_outcomes = ["Temperature change from 1850",
"Adjusted cost of energy per GJ",
"Government net revenue from adjustments",
"Government net revenue from adjustments",
"Total Primary Energy Demand"]

def plot_outcome_over_time(self, outcome: str, outcomes_jsonl: list[list[dict[str, float]]], cand_idxs: list[int]):
Expand All @@ -36,7 +34,7 @@ def plot_outcome_over_time(self, outcome: str, outcomes_jsonl: list[list[dict[st
TODO: Fix colors to match parcoords
"""
outcomes_dfs = [pd.DataFrame(outcomes_json) for outcomes_json in outcomes_jsonl]
color_map = [c for c in px.colors.qualitative.Plotly]
color_map = px.colors.qualitative.Plotly

fig = go.Figure()
showlegend = True
Expand Down Expand Up @@ -87,7 +85,7 @@ def plot_outcome_over_time(self, outcome: str, outcomes_jsonl: list[list[dict[st
"xanchor": "center"},
)
return fig

def create_outcomes_div(self):
"""
Note: We have nested loads here. The outer load is for both graphs and triggers when the outcomes store
Expand Down Expand Up @@ -173,7 +171,7 @@ def update_outcomes_store(*context_values):
outcomes_jsonl = [outcomes_df[self.plot_outcomes].to_dict("records") for outcomes_df in outcomes_dfs]

# Parse energy demand policy
colors = ["brown", "red", "blue", "green", "pink", "lightblue", "orange"]
# colors = ["brown", "red", "blue", "green", "pink", "lightblue", "orange"]
energies = ["coal", "oil", "gas", "renew and hydro", "bio", "nuclear", "new tech"]
demands = [f"Primary energy demand of {energy}" for energy in energies]
selected_dfs = [outcomes_dfs[i] for i in self.all_cand_idxs[:-2]]
Expand Down
2 changes: 1 addition & 1 deletion enroadspy/enroads_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def run_enroads(self, input_str=None):
En-ROADS main function requires a file as input.
Possible variable values are stored in inputSpecs.jsonl.
From the documentation:
The input string is a space-delimited list of index-value pairs, where a colon separates the
The input string is a space-delimited list of index-value pairs, where a colon separates the
index number from the value number with no spaces. Index numbers are zero-based.
NOTE: The indices are the line numbers in inputSpecs.jsonl starting from 0, NOT the id column.
"""
Expand Down
1 change: 0 additions & 1 deletion evolution/candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from pathlib import Path

import numpy as np
import pandas as pd
import torch

from enroadspy import load_input_specs
Expand Down
10 changes: 7 additions & 3 deletions evolution/crossover/crossover.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
"""
Abstract crossover class to be inherited.
"""
from abc import ABC, abstractmethod

from evolution.candidate import Candidate
from evolution.mutation.mutation import Mutation


class Crossover(ABC):
"""
Abstract class for crossover operations.
"""
def __init__(self, mutator: Mutation=None):
def __init__(self, mutator: Mutation = None):
self.mutator = mutator

@abstractmethod
def crossover(self, cand_id: str, parent1: Candidate, parent2: Candidate) -> list[Candidate]:
"""
Crosses over 2 parents to create offspring. Returns a list so we can return multiple if needed.
"""
raise NotImplementedError
raise NotImplementedError
12 changes: 10 additions & 2 deletions evolution/crossover/uniform_crossover.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
"""
Standard uniform crossover
"""
import torch

from evolution.candidate import Candidate
from evolution.crossover.crossover import Crossover
from evolution.mutation.mutation import Mutation


class UniformCrossover(Crossover):
"""
Crosses over 2 parents.
We do not keep track of what's in the models and assume they are loaded correct with the parents.
"""
def __init__(self, mutator: Mutation=None):
def __init__(self, mutator: Mutation = None):
super().__init__(mutator)
self.type = "uniform"

def crossover(self, cand_id: str, parent1: Candidate, parent2: Candidate) -> list[Candidate]:
child = Candidate(cand_id, [parent1.cand_id, parent2.cand_id], parent1.model_params, parent1.actions, parent1.outcomes)
child = Candidate(cand_id,
[parent1.cand_id, parent2.cand_id],
parent1.model_params,
parent1.actions,
parent1.outcomes)
with torch.no_grad():
child.model.load_state_dict(parent1.model.state_dict())
for param, param2 in zip(child.model.parameters(), parent2.model.parameters()):
Expand Down
8 changes: 4 additions & 4 deletions evolution/evaluation/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def generate_new_zero_carbon_df(self) -> pd.DataFrame:
"_new_tech_breakthrough_setting": [0, 1, 2]
}
return pd.DataFrame(data)

def generate_renewable_df(self, context: list[str], n=10, seed=42) -> pd.DataFrame:
"""
Generates our renewables breakthrough context.
Expand All @@ -47,7 +47,7 @@ def generate_renewable_df(self, context: list[str], n=10, seed=42) -> pd.DataFra
min_val = input_specs[input_specs["varId"] == col]["minValue"].iloc[0]
max_val = input_specs[input_specs["varId"] == col]["maxValue"].iloc[0]
data[col] = [default_val] + rng.uniform(min_val, max_val, n-1).tolist()

data_df = pd.DataFrame(data)
return data_df

Expand All @@ -62,7 +62,7 @@ def __init__(self, context: list[str]):
if context == ["_new_tech_breakthrough_setting"]:
df = self.generate_new_zero_carbon_df()
self.context_vals = df[context].values

elif context == breakthrough_cols:
df = self.generate_renewable_df(context)
self.context_vals = df[context].values
Expand All @@ -79,7 +79,7 @@ def __init__(self, context: list[str]):
else:
df[col] = scaler.fit_transform(df[col].values.reshape(-1, 1))
self.tensor_context = torch.tensor(df.values, dtype=torch.float32)

def __getitem__(self, idx):
return self.tensor_context[idx], self.context_vals[idx]

Expand Down
3 changes: 1 addition & 2 deletions evolution/evaluation/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class Evaluator:
Evaluates candidates by generating the actions and running the enroads model on them.
Generates and stores context data based on config using ContextDataset.
"""
def __init__(self, context: list[str], actions: list[str], outcomes: dict[str, bool]):

def __init__(self, context: list[str], actions: list[str], outcomes: dict[str, bool]):
self.actions = actions
self.outcomes = outcomes
self.outcome_manager = OutcomeManager(outcomes)
Expand Down
6 changes: 4 additions & 2 deletions evolution/mutation/mutation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""
Abstract mutation class to implement.
"""
from abc import ABC, abstractmethod

import torch


class Mutation(ABC):
"""
Abstract class handling mutation.
Expand Down Expand Up @@ -31,5 +35,3 @@ def mutate_(self, candidate):
Mutate model in-place
"""
raise NotImplementedError


8 changes: 6 additions & 2 deletions evolution/mutation/uniform_mutation.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"""
Standard uniform mutation implementation.
"""
from evolution.candidate import Candidate
from evolution.mutation.mutation import Mutation


class UniformMutation(Mutation):
"""
Uniformly mutates
Uniformly mutates
"""
def __init__(self, mutation_factor, mutation_rate):
super().__init__(mutation_factor, mutation_rate)
Expand All @@ -14,4 +18,4 @@ def mutate_(self, candidate: Candidate):
Mutate model with gaussian percentage in-place
"""
for param in candidate.model.parameters():
self.gaussian_pct_(param.data)
self.gaussian_pct_(param.data)
8 changes: 7 additions & 1 deletion evolution/outcomes/average_cost.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
"""
Implementation of the AverageCostOutcome class.
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class AverageCostOutcome(Outcome):
"""
Average cost after decision point year.
"""
def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
"""
Returns the average cost from 2025 onwards.
"""
cost_col = outcomes_df["Adjusted cost of energy per GJ"]
cost = cost_col.iloc[2025-1990:].mean()
return cost
return cost
8 changes: 7 additions & 1 deletion evolution/outcomes/cost_change_year.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
"""
Outcome that checks the first year the cost of energy changes by more than a given threshold.
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class CostChangeYearOutcome(Outcome):
"""
Cost change year outcome implementation
"""
def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
"""
Returns the first year the cost of energy changes by more than a given threshold (2 default)
Expand All @@ -18,4 +24,4 @@ def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
year = 2101
else:
year = greater_years.index[0] + 1990
return year
return year
6 changes: 6 additions & 0 deletions evolution/outcomes/energy_change.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
"""
Energy change outcome implementation
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class EnergyChangeOutcome(Outcome):
"""
Checks the percent the distribution changed in a given year averaged over the whole outcomes_df.
"""
def __init__(self):
# We don't want fossil fuels because it double counts
energies = ["bio", "coal", "gas", "oil", "renew and hydro", "new tech", "nuclear"]
Expand Down
9 changes: 8 additions & 1 deletion evolution/outcomes/enroads.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
"""
Basic enroads outcome
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class EnroadsOutcome(Outcome):
"""
Just checks the final year of an enroads outcome column.
"""
def __init__(self, outcome: str):
self.outcome = outcome

def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
"""
Simple case where we return the specified outcome in 2100.
"""
return outcomes_df[self.outcome].iloc[-1]
return outcomes_df[self.outcome].iloc[-1]
6 changes: 5 additions & 1 deletion evolution/outcomes/gdp_mse.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""
This outcome is not used in our evolution but rather is how we get the correct slider values to match the ar6 ssps.
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class GDPOutcome(Outcome):
"""
Outcome used in finding the context values to match the scenarios in the AR6 database for the 5 scenarios.
Expand All @@ -26,4 +30,4 @@ def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
gdp_col = outcomes_df["Global GDP"]
sim_gdp = (gdp_col.iloc[self.year_idxs] * 1000).values
mse = ((sim_gdp - self.label_gdp) ** 2).mean()
return mse
return mse
9 changes: 8 additions & 1 deletion evolution/outcomes/max_cost.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
"""
Max cost outcome implementation.
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class MaxCostOutcome(Outcome):
"""
Gets the max cost over the entire range of years.
"""
def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
"""
Returns the max cost of energy.
"""
cost_col = outcomes_df["Adjusted cost of energy per GJ"]
cost = cost_col.max()
return cost
return cost
9 changes: 8 additions & 1 deletion evolution/outcomes/near_cost.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
"""
Near cost outcome implementation.
"""
import pandas as pd

from evolution.outcomes.outcome import Outcome


class NearCostOutcome(Outcome):
"""
Gets the average cost over the next 10 years.
"""
def process_outcomes(self, _, outcomes_df: pd.DataFrame) -> float:
"""
Returns the average cost of energy over the next 10 years.
"""
cost_col = outcomes_df["Adjusted cost of energy per GJ"]
cost = cost_col.iloc[2025-1990:2035-1990].mean()
return cost
return cost
3 changes: 3 additions & 0 deletions evolution/outcomes/outcome.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""
Outcome interface.
"""
from abc import ABC, abstractmethod

import pandas as pd
Expand Down
4 changes: 4 additions & 0 deletions evolution/outcomes/outcome_manager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Converts the title of an outcome from the config to the corresponding outcome object that processes the outcomes_df and
actions_dict.
"""
import pandas as pd

from evolution.outcomes.actions import ActionsOutcome
Expand Down
Loading

0 comments on commit 4df4dde

Please sign in to comment.