text:
"""
pip install pypokedex

TODO:
    - [ ] Implement ranges for unknown properties
"""
import json
import ubelt as ub
import re

# azumarill,BUBBLE,ICE_BEAM,PLAY_ROUGH,38,12,15,13

# https://pvpoke.com/custom-rankings/
great_meta = """
abomasnow,POWDER_SNOW,WEATHER_BALL_ICE,ENERGY_BALL
altaria,DRAGON_BREATH,SKY_ATTACK,DRAGON_PULSE
azumarill,BUBBLE,ICE_BEAM,HYDRO_PUMP
bastiodon,SMACK_DOWN,STONE_EDGE,FLAMETHROWER
clefable,CHARM,METEOR_MASH,PSYCHIC
cresselia,PSYCHO_CUT,GRASS_KNOT,MOONBLAST
deoxys_defense,COUNTER,THUNDERBOLT,ROCK_SLIDE
drifblim,HEX,ICY_WIND,SHADOW_BALL
ferrothorn,BULLET_SEED,POWER_WHIP,THUNDER
galvantula,VOLT_SWITCH,DISCHARGE,LUNGE
haunter,SHADOW_CLAW,SHADOW_PUNCH,SHADOW_BALL
hypno,CONFUSION,THUNDER_PUNCH,SHADOW_BALL
hypno_shadow-shadow,CONFUSION,ICE_PUNCH,THUNDER_PUNCH
lapras,ICE_SHARD,SURF,SKULL_BASH
machamp_shadow-shadow,COUNTER,CROSS_CHOP,ROCK_SLIDE
marowak_alolan,FIRE_SPIN,SHADOW_BALL,BONE_CLUB
melmetal,THUNDER_SHOCK,ROCK_SLIDE,SUPER_POWER
munchlax,LICK,BODY_SLAM,BULLDOZE
raichu_alolan,VOLT_SWITCH,THUNDER_PUNCH,WILD_CHARGE
registeel,LOCK_ON,FLASH_CANNON,FOCUS_BLAST
sableye,SHADOW_CLAW,FOUL_PLAY,POWER_GEM
scrafty,COUNTER,POWER_UP_PUNCH,FOUL_PLAY
shiftry,SNARL,LEAF_BLADE,FOUL_PLAY
skarmory,AIR_SLASH,SKY_ATTACK,BRAVE_BIRD
stunfisk,THUNDER_SHOCK,MUD_BOMB,DISCHARGE
stunfisk_galarian,MUD_SHOT,ROCK_SLIDE,EARTHQUAKE
swampert,MUD_SHOT,HYDRO_CANNON,SLUDGE_WAVE
swampert,MUD_SHOT,HYDRO_CANNON,EARTHQUAKE
toxicroak,COUNTER,MUD_BOMB,SLUDGE_BOMB
umbreon,SNARL,FOUL_PLAY,LAST_RESORT
venusaur,VINE_WHIP,FRENZY_PLANT,SLUDGE_BOMB
victreebel_shadow-shadow,RAZOR_LEAF,LEAF_BLADE,SLUDGE_BOMB
vigoroth,COUNTER,BODY_SLAM,BULLDOZE
whiscash,MUD_SHOT,MUD_BOMB,BLIZZARD
wigglytuff,CHARM,ICE_BEAM,PLAY_ROUGH
"""

less_encountered = """
dewgong,ICE_SHARD,ICY_WIND,WATER_PULSE
froslass,POWDER_SNOW,AVALANCHE,SHADOW_BALL
mantine,BUBBLE,BUBBLE_BEAM,ICE_BEAM
medicham,COUNTER,DYNAMIC_PUNCH,ICE_PUNCH
meganium,VINE_WHIP,FRENZY_PLANT,EARTHQUAKE
pelipper,WING_ATTACK,WEATHER_BALL_WATER,HURRICANE
tropius,AIR_SLASH,LEAF_BLADE,AERIAL_ACE
tropius,RAZOR_LEAF,LEAF_BLADE,AERIAL_ACE
zweilous,DRAGON_BREATH,BODY_SLAM,DARK_PULSE
"""

def normalize(n):
    return n.upper().replace(' ', '_')

class PogoAPI(ub.NiceRepr):
    """
    Object to help with access to data from pogoapi.net
    """
    def __init__(api):
        api.base = 'https://pogoapi.net/api/v1/'
        api.routes = {
            'pokemon_stats': api.base + 'pokemon_stats.json',
            'current_pokemon_moves': api.base + 'current_pokemon_moves.json',
            'pokemon_evolutions': api.base + 'pokemon_evolutions.json',
            'cp_multiplier': api.base + 'cp_multiplier.json',
        }
        api.data = {}
        for key, url in api.routes.items():
            data_fpath = ub.grabdata(url, verbose=1)
            with open(data_fpath, 'r') as file:
                data = json.load(file)
            api.data[key] = data

        # Make the API global for now
        pokemon_stats = api.data['pokemon_stats']
        _name_to_stats = ub.group_items(pokemon_stats, lambda item: item['pokemon_name'].lower())
        _name_to_stats = dict(_name_to_stats)
        api.name_to_stats = _name_to_stats

        _name_to_items = ub.group_items(
            api.data['current_pokemon_moves'],
            lambda item: item['pokemon_name'].lower())
        _name_to_items.default_factory = None
        _name_to_items = dict(_name_to_items)

        # base = 'http://pokeapi.co/api/v2/pokemon/'
        api.name_to_moves = _name_to_items

        evolutions = api.data['pokemon_evolutions']
        _name_to_evolutions = ub.group_items(evolutions, lambda item: item['pokemon_name'].lower())
        _name_to_evolutions = dict(_name_to_evolutions)

        for key, form_stats in api.name_to_stats.items():
            if key not in _name_to_evolutions:
                noevos = []
                for s in form_stats:
                    empty = ub.dict_isect(s, {'form', 'pokemon_name', 'pokemon_id'})
                    empty['evolutions'] = []
                    noevos.append(empty)
                _name_to_evolutions[key] = noevos

        import networkx as nx
        evo_graph = nx.DiGraph()
        for name, form_evo_list in _name_to_evolutions.items():
            for form_evo in form_evo_list:
                u = form_evo['pokemon_name'].lower()
                evo_graph.add_node(u)
                for evo in form_evo['evolutions']:
                    v = evo['pokemon_name'].lower()
                    evo_graph.add_edge(u, v)

        # if 0:
        #     print(forest_str(evo_graph))

        api.name_to_family = {}
        api.name_to_base = {}
        evo_graph.remove_edges_from(nx.selfloop_edges(evo_graph))
        api.evo_graph = evo_graph
        for cc in list(nx.connected_components(api.evo_graph.to_undirected())):
            bases = [n for n in cc if len(evo_graph.pred[n]) == 0]
            base = bases[0]
            for n in cc:
                api.name_to_family[n] = cc
                api.name_to_base[n] = base

        # base_pokmeon = [n for n in evo_graph.nodes if len(evo_graph.pred[n]) == 0]
        api.name_to_evolutions = _name_to_evolutions

        # api.name_to_family = {}
        # for base in base_pokmeon:
        #     family = list(nx.dfs_postorder_nodes(evo_graph, base))
        #     for name in family:
        #         api.name_to_family[name] = family
        #         evos = api.name_to_evolutions[name]
        #         for evo in evos:
        #             evo['base'] = base

        #     for evo in evos['evolutions']:
        #         evo['base']

        api.learnable = {
            'stunfisk_galarian': {
                'fast': [
                    'MUD_SHOT',
                    'METAL_CLAW',
                ],
                'charge': [
                    'EARTHQUAKE',
                    'FLASH_CANNON',
                    'MUDDY_WATER',
                    'ROCK_SLIDE',
                ]
            }
        }

    def __nice__(self):
        return str(list(api.routes.keys()))

    def normalize_name_and_form(api, name, form=None):
        if name.endswith('-shadow'):
            if form is None:
                form = 'Shadow'
            else:
                assert form == 'Shadow', f'{api}, {name}'
            name = name.split('-shadow')[0]

        if name.endswith('galarian'):
            if form is None:
                form = 'Galarian'
            if form == 'Normal':
                form = 'Galarian'  # hack
            # else:
            #     assert form == 'Galarian', f'{api}, {name}'
            name = name.split('_galarian')[0]

        if name == 'farfetchd':
            name = "farfetch\u2019d"

        if form is None:
            form = 'Normal'

        return name, form

    def get_info(api, name, form=None):
        """
        api = PogoAPI()
        name = 'machamp-shadow'
        print(ub.repr2(api.get_info(name)))
        form = None
        name = 'beedrill'
        print(ub.repr2(api.get_info(name)))
        name = 'farfetchd_galarian'
        print(ub.repr2(api.get_info(name)))
        name = 'stunfisk_galarian'
        print(ub.repr2(api.get_info(name)))
        """
        try:
            name_, form_ = api.normalize_name_and_form(name, form)
        except Exception:
            raise Exception(f'name={name}, form={form}')

        try:
            infos = [
                api.name_to_stats[name_],
                api.name_to_moves[name_],
                api.name_to_evolutions[name_],
            ]
        except Exception:
            raise Exception(f'name={name}, form={form}, name_={name_}, form_={form_}')

        info = {}
        for all_infos in infos:
            part = None
            for _info in all_infos:
                if _info['form'] == form_:
                    part = _info
            if part is None:
                raise KeyError
            info.update(part)

        if 1:
            fast_moves = set()
            charge_moves = set()

            for move in info['fast_moves']:
                fast_moves.add(normalize(move))
            for move in info['elite_fast_moves']:
                fast_moves.add(normalize(move))
            for move in info['charged_moves']:
                charge_moves.add(normalize(move))
            for move in info['elite_charged_moves']:
                charge_moves.add(normalize(move))

            if form_ == 'Normal':
                if info['form'] == 'Shadow':
                    charge_moves.add('FRUSTRATION')
                    charge_moves.add('RETURN')

            if name_ not in api.learnable:
                api.learnable[name_] = {}
            api.learnable[name_]['fast'] = sorted(fast_moves)
            api.learnable[name_]['charge'] = sorted(charge_moves)
        return info

api = PogoAPI()

def calc_cp(attack, defense, stamina, level):
    """
    References:
        https://www.dragonflycave.com/pokemon-go/stats
    """
    # cpm_step_lut = {
    #     0: 0.009426125469,
    #     10: 0.008919025675,
    #     20: 0.008924905903,
    #     30: 0.00445946079,
    # }
    cmp_lut = {
        1: 0.09399999678134918, 1.5: 0.1351374313235283, 2.0: 0.16639786958694458,
        2.5: 0.1926509141921997, 3.0: 0.21573247015476227, 3.5: 0.23657265305519104,
        4.0: 0.2557200491428375, 4.5: 0.27353037893772125, 5.0: 0.29024988412857056,
        5.5: 0.3060573786497116, 6.0: 0.3210875988006592, 6.5: 0.33544503152370453,
        7.0: 0.3492126762866974, 7.5: 0.362457737326622, 8.0: 0.37523558735847473,
        8.5: 0.38759241108516856, 9.0: 0.39956727623939514, 9.5: 0.4111935495172506,
        10.0: 0.4225000143051148, 10.5: 0.4329264134104144, 11.0: 0.443107545375824,
        11.5: 0.4530599538719858, 12.0: 0.46279838681221, 12.5: 0.4723360780626535,
        13.0: 0.4816849529743195, 13.5: 0.4908558102324605, 14.0: 0.4998584389686584,
        14.5: 0.5087017565965652, 15.0: 0.517393946647644, 15.5: 0.5259425118565559,
        16.0: 0.5343543291091919, 16.5: 0.5426357612013817, 17.0: 0.5507926940917969,
        17.5: 0.5588305993005633, 18.0: 0.5667545199394226, 18.5: 0.574569147080183,
        19.0: 0.5822789072990417, 19.5: 0.5898879119195044, 20.0: 0.5974000096321106,
        20.5: 0.6048236563801765, 21.0: 0.6121572852134705, 21.5: 0.6194041110575199,
        22.0: 0.6265671253204346, 22.5: 0.633649181574583, 23.0: 0.6406529545783997,
        23.5: 0.6475809663534164, 24.0: 0.654435634613037, 24.5: 0.6612192690372467,
        25.0: 0.667934000492096, 25.5: 0.6745819002389908, 26.0: 0.6811649203300476,
        26.5: 0.6876849085092545, 27.0: 0.6941436529159546, 27.5: 0.7005428969860077,
        28.0: 0.7068842053413391, 28.5: 0.7131690979003906, 29.0: 0.719399094581604,
        29.5: 0.7255756109952927, 30.0: 0.7317000031471252, 30.5: 0.7347410172224045,
        31.0: 0.7377694845199585, 31.5: 0.740785576403141, 32.0: 0.7437894344329834,
        32.5: 0.7467812150716782, 33.0: 0.7497610449790955, 33.5: 0.7527291029691696,
        34.0: 0.7556855082511902, 34.5: 0.7586303651332855, 35.0: 0.7615638375282288,
        35.5: 0.7644860669970512, 36.0: 0.7673971652984619, 36.5: 0.7702972739934921,
        37.0: 0.7731865048408508, 37.5: 0.7760649472475052, 38.0: 0.7789327502250671,
        38.5: 0.78179006, 39.0: 0.78463697, 39.5: 0.78747358,
        40.0: 0.79030001, 40.5: 0.79280001, 41.0: 0.79530001,
        41.5: 0.79780001, 42.0: 0.8003, 42.5: 0.8028,
        43.0: 0.8053, 43.5: 0.8078, 44.0: 0.81029999,
        44.5: 0.81279999, 45.0: 0.81529999,
    }
    # cpm_step = cpm_step_lut[(level // 10) * 10]
    # cpm_step
    cp_multiplier = cmp_lut[level]

    # https://gamepress.gg/pokemongo/cp-multiplier
    # https://gamepress.gg/pokemongo/pokemon-stats-advanced#:~:text=Calculating%20CP,*%20CP_Multiplier%5E2)%20%2F%2010
    a, d, s = attack, defense, stamina

    adjusted = {
        'attack': a * cp_multiplier,
        'defense': d * cp_multiplier,
        'stamina': int(s * cp_multiplier),
    }
    cp = int(a * (d ** 0.5) * (s ** 0.5) * (cp_multiplier ** 2) / 10)
    return cp, adjusted

# class Moves():
#     def __init__(moves):
#         pass
#     pass

class Pokemon(ub.NiceRepr):
    """

    Example:
        import sys, ubelt
        sys.path.append(ubelt.expandpath('~/misc/pkmn'))
        from query_team_builder import *  # NOQA
        self = Pokemon('weedle')

        list(self.family())

    """
    def __init__(self, name, ivs=None, level=None, moves=None, shadow=False,
                 form='Normal', cp=None, autobuild=True, shiny=False,
                 adjusted=None):
        self.name = name.lower()
        self.level = level
        self.ivs = ivs
        self.moves = moves
        self.shadow = shadow
        self.shiny = shiny
        if shadow:
            form = 'Shadow'
        self.form = form
        self.api = api
        self.cp = cp
        self.adjusted = adjusted

        self.populate_stats()

        if cp is None:
            if level is not None and self.ivs is not None:
                if autobuild:
                    self.populate_cp()
        else:
            # If CP is specified, get the one that is closest
            # TODO: handle unknown ivs
            if level is None and self.ivs is not None:
                self.populate_level()

    def __json__(self):
        return {
            'name': self.name,
            'ivs': self.ivs,
            'level': self.level,
            'form': self.form,
            'shadow': self.shadow,
            'shiny': self.shiny,
            'moves': self.moves,
            # Depends on other properties
            'cp': self.cp,
            'adjusted': self.adjusted,
        }

    def copy(self, **overwrite):
        """
        Create a copy of this pokemon with possibly different attributes
        """
        kw = self.__json__()
        # Invalidate depenencies
        if 'ivs' in overwrite:
            if overwrite['ivs'] != kw['ivs']:
                kw.pop('cp', None)
                kw.pop('adjusted', None)

        if 'level' in overwrite:
            if overwrite['level'] != kw['level']:
                kw.pop('cp', None)
                kw.pop('adjusted', None)

        if 'cp' in overwrite:
            if overwrite['cp'] != kw['cp']:
                kw.pop('level', None)

        kw.update(overwrite)
        new = Pokemon(**kw)
        return new

    def display_name(self):
        parts = [self.name]
        if self.shadow:
            shadow_glyph = '😈'
            parts.append(shadow_glyph)

        elif self.form not in {'Normal', 'Shadow', 'Purified'}:
            parts.append('({})'.format(self.form))

        if self.form == 'Purified':
            purified_glyph = '👼'
            parts.append(purified_glyph)

        if self.shiny:
            shiny_glpyh = '✨'
            parts.append(shiny_glpyh)
        disp_name = ''.join(parts)
        return disp_name

    def __nice__(self):
        disp_name = self.display_name()
        info = '{}, {}, {}, {}, {}'.format(disp_name, self.cp, self.level, self.ivs, self.moves)
        return info
        # return str([self.name] + self.moves + [self.level] + self.ivs)

    def lookup_moves(self):
        possible_moves = api.name_to_moves[self.name]
        return possible_moves

    def populate_level(self, max_level=45):
        """ Try and find the level given the info """
        import numpy as np
        # hacky, could be more elegant
        target_cp = self.cp
        iva, ivd, ivs = self.ivs
        attack = self.info['base_attack'] + iva
        defense = self.info['base_defense'] + ivd
        stamina = self.info['base_stamina'] + ivs

        found_level = None

        for cand_level in np.arange(1, max_level + 0.5, 0.5):
            # TODO: could binary search
            cp, adjusted = calc_cp(attack, defense, stamina, cand_level)
            if cp == target_cp:
                found_level = cand_level
            elif cp > target_cp:
                break

        if found_level is None:
            raise Exception('cp does not match ivs')
        else:
            self.level = found_level

    def populate_stats(self):
        info = api.get_info(name=self.name, form=self.form)
        self.learnable = api.learnable[self.name]
        self.info = info
        # self.items = items

    def evolved(self):
        """
        Ignore:
            self = Pokemon('gastly', ivs=[6, 13, 15], cp=400)
            self.evolved()

            self = Pokemon('eevee', ivs=[6, 13, 15], cp=400)
            self.evolved()

            self = Pokemon('mew', ivs=[6, 13, 15], cp=400)
            self.evolved()
        """
        possibilities = []
        for other in self.family(onlyadj=True):
            # other.populate_cp()
            possibilities.append(other)
        return possibilities

    def purify(self):
        """
        Example:
            >>> self = Pokemon('ralts', ivs=[6, 13, 15], level=20,
            >>>                 shadow=True, shiny=True)
            >>> new = self.purify()
            >>> print('self = {!r}'.format(self))
            >>> print('new  = {!r}'.format(new))
        """
        if not self.shadow:
            raise Exception('Only can purify shadow pokemon')

        overwrite = {}
        if self.ivs is not None:
            new_ivs = tuple([min(15, s + 2) for s in self.ivs])
            overwrite['ivs'] = new_ivs
        overwrite['form'] = 'Purified'
        overwrite['shadow'] = False
        # TODO: replace frustration with return
        new = self.copy(**overwrite)
        return new

    def family(self, ancestors=True, node=False, onlyadj=False):
        """
        Get other members of this pokemon family

        Yields:
            Pokemon: other members of this family

        Ignore:
            self = Pokemon('gastly', ivs=[6, 13, 15])
            self = Pokemon('haunter', ivs=[6, 13, 15])
            self = Pokemon('gengar', ivs=[6, 13, 15])
            list(self.family())

            self = Pokemon('magikarp', ivs=[6, 13, 15])
            list(self.family())

            self = Pokemon('eevee', ivs=[6, 13, 15])
            list(self.family(onlyadj=True))

            self = Pokemon('ralts', ivs=[6, 13, 15], shadow=True)
            list(self.family(onlyadj=True))
            list(self.family())
        """
        import networkx as nx
        blocklist = set()
        if not node:
            blocklist.add(self.name)

        if not ancestors:
            toadd = set(nx.ancestors(api.evo_graph, self.name))
            blocklist.update(toadd)

        cc = api.name_to_family[self.name]
        if onlyadj:
            keeplist = set(api.evo_graph.adj[self.name])
            blocklist = set(cc) - keeplist

        kw = {
            'level': self.level,
            'form': self.form,
            'ivs': self.ivs,
            'shadow': self.shadow,
            'shiny': self.shiny,
        }
        for name in cc:
            if name not in blocklist:
                if name == self.name:
                    other = Pokemon(name, cp=self.cp, **kw)
                else:
                    other = Pokemon(name, **kw)
                yield other

                if other.shadow:
                    yield other.purify()

    def populate_cp(self):
        level = self.level
        iva, ivd, ivs = self.ivs
        attack = self.info['base_attack'] + iva
        defense = self.info['base_defense'] + ivd
        stamina = self.info['base_stamina'] + ivs
        cp, adjusted = calc_cp(attack, defense, stamina, level)
        self.cp = cp
        self.adjusted = adjusted
        return cp, adjusted

    def check_evolution_cps(self, max_cp=1500, max_level=45):
        """
        self = Pokemon('gastly', ivs=[6, 13, 15])
        self.check_evolution_cps()

        self = Pokemon('gyarados', ivs=[6, 13, 15])
        self.check_evolution_cps()

        self = Pokemon('magikarp', ivs=[6, 13, 15])
        self.check_evolution_cps()
        """
        import numpy as np
        evos = list(self.family(ancestors=False))

        if len(evos) == 0:
            print('no evolutions available')

        for evo in evos:
            other = evo

            best_level = None
            for level in list(np.arange(1, max_level + 0.5, 0.5)):
                # TODO: could binary search
                other.level = level
                other.populate_cp()
                if other.cp <= max_cp:
                    best_level = level
                else:
                    break
            other.level = best_level
            other.populate_cp()

            print('To achieve other = {!r}'.format(other))
            self.level = best_level
            cp, adjusted = self.populate_cp()
            print('self = {!r}'.format(self))
            print('Pokemon CP must be less than this to be used in league')
            print('cp = {!r}'.format(cp))

    def leage_rankings_for(self, have_ivs, max_cp=1500, max_level=45):
        """
        Given a set of IVs for this pokemon compute the leage rankings
        """
        leage_df = self.find_leage_rankings(max_cp=max_cp, max_level=max_level)
        leage_df = leage_df.set_index(['iva', 'ivd', 'ivs'])

        if abs(min(leage_df['cp'].max() - min(3000, max_cp), 0)) > 200:
            print('Out of this leage {}'.format(max_cp))
        else:
            rows = []
            for haves in have_ivs:
                # ultra_row = ultra_df.loc[haves]
                leage_row = leage_df.loc[haves]
                rows.append({
                    'iva': haves[0],
                    'ivd': haves[1],
                    'ivs': haves[2],
                    'rank': leage_row['rank'],
                    'level': leage_row['level'],
                    'cp': leage_row['cp'],
                    'stat_product': leage_row['stat_product'],
                    'attack': leage_row['attack'],
                    'defense': leage_row['defense'],
                    'stamina': leage_row['stamina'],
                    'percent': leage_row['percent'],
                })
            import pandas as pd
            rankings = pd.DataFrame.from_dict(rows)
            #
            print('')
            print('Leage {} Rankings'.format(max_cp))
            print('self = {!r}'.format(self))
            print(rankings.sort_values('rank'))

    def find_leage_rankings(self, max_cp=1500, max_level=45):
        """
        Calculate the leage rankings for this pokemon's IVs, based on the
        adjusted stat product heuristic.

        Ignore:
            >>> import sys, ubelt
            >>> sys.path.append(ubelt.expandpath('~/misc/pkmn'))
            >>> from query_team_builder import *  # NOQA
            >>> self = Pokemon('beedrill')
            >>> beedrill_df = self.find_leage_rankings(max_cp=1500)

            >>> # Find the best IVs that we have for PVP
            >>> self = Pokemon('empoleon')
            >>> have_ivs = [
            >>>     (0, 10, 14),
            >>>     (1, 11, 5),
            >>>     (1, 5, 7),
            >>>     (1, 9, 13),
            >>>     (2, 15, 13),
            >>>     (2, 2, 10),
            >>>     (2, 6, 9),
            >>>     (3, 13, 11),
            >>>     (3, 3, 2),
            >>>     (4, 13, 13),
            >>>     (5, 13, 14),
            >>>     (4, 14, 14),
            >>>     (7, 13, 3),
            >>>     (13, 14, 14),
            >>>     (15, 14, 14),
            >>> ]

            >>> self = Pokemon('beedrill')
            >>> have_ivs = [
            >>>     (0, 8, 14),
            >>>     (0, 12, 14),
            >>>     (1,  3, 10),
            >>>     (1, 13, 6),
            >>>     (4, 11, 13),
            >>>     (4, 14, 13),
            >>>     (1, 13, 7),
            >>>     (1, 10, 8),
            >>>     (4, 13, 13),
            >>>     (4, 14, 14),
            >>>     (4, 15, 12),
            >>>     (5, 14, 11),
            >>>     (11, 15, 14),
            >>>     (15, 15, 15),
            >>>     (12, 15, 15),
            >>> ]
            >>> self.leage_rankings_for(have_ivs)

            >>> have_ivs = [
            >>>     (4, 13, 10),
            >>>     (5, 11, 14),
            >>>     (4, 13, 11),
            >>>     (6, 13, 15),
            >>>     (7, 12, 13),
            >>>     (7, 14, 14),
            >>>     (7, 15, 15),
            >>>     (7, 2, 9),
            >>>     (10, 15, 11),
            >>>     (15, 15, 15),
            >>>     (7, 15, 15),
            >>> ]
            >>> self = Pokemon('gengar')
            >>> print('self.info = {}'.format(ub.repr2(self.info, nl=2)))
            >>> self.leage_rankings_for(have_ivs)

            >>> self = Pokemon('haunter')
            >>> print('self.info = {}'.format(ub.repr2(self.info, nl=2)))
            >>> self.leage_rankings_for(have_ivs)

            >>> have_ivs = [
            >>>     (12, 11, 14),
            >>>     (12, 15, 15),
            >>>     (15, 15, 15),
            >>> ]
            >>> Pokemon('blaziken').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('blaziken').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('blaziken').leage_rankings_for(have_ivs, max_cp=np.inf)

            >>> have_ivs = [
            >>>     (0, 2, 14),
            >>>     (4, 2, 13),
            >>>     (11, 13, 12),
            >>>     (4, 13, 9),
            >>>     (15, 12, 13),
            >>>     (13, 14, 13),
            >>>     (13, 14, 13),
            >>>     (14, 14, 10),
            >>>     (6, 15, 11),  # purified
            >>>     (13, 15, 14),  # purified
            >>> ]
            >>> Pokemon('swampert').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('swampert').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('swampert').leage_rankings_for(have_ivs, max_cp=np.inf)

            >>> have_ivs = [
            >>>     (1, 2, 15),
            >>>     (12, 15, 14),
            >>>     (14, 15, 14),
            >>>     (14, 14, 14),
            >>>     (14, 13, 15),
            >>>     (15, 15, 10),
            >>> ]
            >>> Pokemon('sceptile').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('sceptile').leage_rankings_for(have_ivs, max_cp=2500)

            >>> have_ivs = [
            >>>     (0, 10, 15),
            >>>     (1, 14, 11),
            >>>     (11, 14, 13),
            >>>     (12, 12, 13),
            >>>     (14, 13, 13),
            >>>     (2, 13, 12),
            >>>     (2, 13, 15),
            >>>     (2, 14, 14),
            >>>     (2, 15, 14),
            >>>     (3, 12, 11),
            >>>     (3, 4, 15),
            >>>     (3, 13, 14),
            >>>     (3, 5, 2),
            >>>     (4, 10, 13),
            >>>     (4, 12, 15), # shadow
            >>>     (5, 15, 12),
            >>>     (7, 13, 15),
            >>>     (7, 15, 8),
            >>>     (15, 13, 15),
            >>> ]
            >>> Pokemon('gyarados').leage_rankings_for(have_ivs, max_cp=np.inf)
            >>> Pokemon('gyarados').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('gyarados').leage_rankings_for(have_ivs, max_cp=1500)

            >>> have_ivs = [
            >>>     (14, 14, 15),
            >>>     (10, 14, 15),
            >>>     (15, 15, 15),
            >>>     (15, 15, 15),
            >>> ]
            >>> Pokemon('rhyperior').leage_rankings_for(have_ivs, max_cp=np.inf)

            >>> have_ivs = [
            >>>     (14, 14, 14),
            >>>     (12, 13, 14),
            >>>     (13, 14, 14),
            >>>     (15, 13, 14),
            >>>     (8, 6, 8),
            >>> ]
            >>> Pokemon('vigoroth').leage_rankings_for(have_ivs, max_cp=1500)

            >>> have_ivs = [
            >>>     (6, 15, 13),
            >>>     (3, 4, 14),
            >>>     (2, 9, 15),
            >>>     (6, 14, 15),
            >>>     (7, 15, 15),
            >>>     (10, 15, 15),
            >>> ]
            >>> Pokemon('shiftry').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('shiftry').leage_rankings_for(have_ivs, max_cp=2500)

            >>> have_ivs = [
            >>>     (15, 15, 14),
            >>>     (0, 7, 8),
            >>>     (3, 12, 14),
            >>>     (5, 5, 15),
            >>>     (4, 7, 12),
            >>>     (15, 14, 14),
            >>>     (10, 14, 15),
            >>> ]
            >>> Pokemon('alakazam').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('alakazam').leage_rankings_for(have_ivs, max_cp=2500)

            >>> have_ivs = [
            >>>     (0, 15, 6),
            >>>     (11, 10, 10),
            >>>     (12, 12, 11),
            >>>     (15, 10, 12),
            >>> ]
            >>> Pokemon('salamence').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('salamence').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('salamence').leage_rankings_for(have_ivs, max_cp=np.inf)

            >>> have_ivs = [
            >>>     (6, 10, 10),
            >>>     (11, 9, 14),
            >>>     (13, 12, 14),
            >>>     (15, 15, 15),
            >>>     (15, 15, 5),
            >>> ]
            >>> Pokemon('flygon').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('flygon').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('flygon').leage_rankings_for(have_ivs, max_cp=np.inf)

            >>> have_ivs = [
            >>>     (6, 11, 11),
            >>>     (10, 11, 10),
            >>>     (10, 11, 12),
            >>>     (6, 14, 4),
            >>>     (15, 12, 15),
            >>>     (15, 7, 15),
            >>> ]
            >>> Pokemon('mamoswine').leage_rankings_for(have_ivs, max_cp=1500)
            >>> Pokemon('mamoswine').leage_rankings_for(have_ivs, max_cp=2500)
            >>> Pokemon('mamoswine').leage_rankings_for(have_ivs, max_cp=np.inf)

        """
        rows = []
        import itertools as it
        import numpy as np

        for iva, ivd, ivs in it.product(range(16), range(16), range(16)):
            attack = self.info['base_attack'] + iva
            defense = self.info['base_defense'] + ivd
            stamina = self.info['base_stamina'] + ivs

            best_level = None
            best_cp = None
            best_adjusted = None
            for level in list(np.arange(1, max_level + 0.5, 0.5)):
                cand_cp, adjusted = calc_cp(attack, defense, stamina, level)
                if cand_cp <= max_cp:
                    best_cp = cand_cp
                    best_level = level
                    best_adjusted = adjusted
                else:
                    break

            row = {
                'iva': iva,
                'ivd': ivd,
                'ivs': ivs,
                'cp': best_cp,
                'level': best_level,
                'attack': best_adjusted['attack'],
                'defense': best_adjusted['defense'],
                'stamina': best_adjusted['stamina'],
            }
            rows.append(row)

        import kwarray
        df = kwarray.DataFrameArray.from_dict(rows)
        df = df.pandas()
        df['stat_product'] = (df['attack'] * df['defense'] * df['stamina']) / 1000
        df = df.sort_values('stat_product', ascending=False)
        df['rank'] = np.arange(1, len(df) + 1)
        df = df.set_index('rank', drop=False)
        min_ = df['stat_product'].min()
        max_ = df['stat_product'].max()
        df['percent'] = ((df['stat_product'] - min_) / (max_ - min_)) * 100
        return df

    def calc_cp(self):
        if self.level is None:
            best_cp = 0
            best_level = 0
            # levels = [y + x for x in range(1, 45) for y in [0, 0.5]]
            for level in range(1, 45):
                iva, ivd, ivs = self.ivs
                if iva is None:
                    iva = 10
                if ivd is None:
                    ivd = 10
                if ivs is None:
                    ivs = 10
                attack = self.info['base_attack'] + iva
                defense = self.info['base_defense'] + ivd
                stamina = self.info['base_stamina'] + ivs
                cand_cp = calc_cp(attack, defense, stamina, level)
                if cand_cp > 1500:
                    break
                best_cp = cand_cp
                best_level = level
                print('best_cp = {!r}'.format(best_cp))
                print('best_level = {!r}'.format(best_level))

        level = self.level
        iva, ivd, ivs = self.ivs
        attack = self.info['base_attack'] + iva
        defense = self.info['base_defense'] + ivd
        stamina = self.info['base_stamina'] + ivs
        cp, adjusted = calc_cp(attack, defense, stamina, level)
        return cp

    @classmethod
    def from_pvpoke_row(cls, row):
        """
        Example:
            from query_team_builder import *  # NOQA
            row = 'victreebel_shadow-shadow,RAZOR_LEAF,LEAF_BLADE,FRUSTRATION,22.5,4,14,14'
            self = Pokemon.from_pvpoke_row(row)
        """
        name = row[0]
        shadow = False
        if name.endswith('-shadow'):
            name = name.split('-shadow')[0]
            # weird case for victreebel
            if name.endswith('_shadow'):
                name = name.split('_shadow')[0]
            shadow = True
        level = None
        ivs = [None, None, None]

        moves = []
        idx = 0
        for idx in range(1, 4):
            if idx >= len(row):
                break
            if not re.match('[a-z]+', row[idx].lower()):
                break
            moves.append(row[idx])
        idx += 1
        if idx < len(row):
            level = float(row[idx])
        idx += 1
        if idx < len(row):
            ivs = list(map(int, row[idx:]))
        self = cls(name, level, ivs, moves, shadow=shadow)
        return self

    def to_pvpoke_url(self):
        parts = []
        parts.append(self.name)
        if self.level is not None:
            parts.append(str(self.level))

        needs_441 = False

        if self.ivs and all(v is not None for v in self.ivs):
            parts.extend(list(map(str, self.ivs)))
            needs_441 = True
        if self.shadow:
            parts.append('shadow')
            needs_441 = True

        if needs_441:
            parts.append('4-4-1')  # no idea what this is

        if self.moves:
            parts.append('m')
            moves = self.moves + ([None] * max(0, 3 - len(self.moves)))
            fm, cm1, cm2 = moves

            fixup = {
                'FUTURE_SIGHT': 'FUTURESIGHT',
            }
            cm1 = fixup.get(cm1, cm1)
            cm2 = fixup.get(cm2, cm2)

            fm_idx = api.learnable[self.name]['fast'].index(fm)
            cm1_idx = api.learnable[self.name]['charge'].index(cm1) + 1
            parts.append(str(fm_idx))
            parts.append(str(cm1_idx))
            if cm2 is not None:
                if cm2.lower() == 'frustration':
                    # hack for frustration
                    cm2_idx = 0
                else:
                    cm2_idx = api.learnable[self.name]['charge'].index(cm2) + 1
                parts.append(str(cm2_idx))
        else:
            parts.append('m-1-1-2')

        # parts.append(self.moves[0])
        # parts.append(self.moves[1])
        # parts.append('0')
        # parts.append(self.moves[2])
        # parts.append('1')
        # need a lut
        # for move in self.moves:
        #     pass
        code = '-'.join(parts)
        return code

def pvp_inventory():
    """
    The idea is you put info about your candidates here and we find good mons
    to power up.
    """
    inventory = [
        Pokemon('Magnezone', (14, 14, 14), cp=1815, form='Normal'),
        Pokemon('Magnemite', (7, 14, 9), cp=792),
        Pokemon('Magnemite', (10, 14, 13), cp=747),
        Pokemon('Magnemite', (13, 9, 15), cp=602),
        Pokemon('Magneton', (13, 14, 13), cp=550, form='Shadow'),
        Pokemon('Magnemite', (15, 13, 7), cp=293, form='Shadow'),
        Pokemon('Magnemite', (2, 14, 15), cp=283, form='Shadow'),
    ]

    inventory = [
        Pokemon('sirfetch’d', (4, 11, 12), cp=1924, form='Galarian'),
        Pokemon('farfetch’d', (12, 15, 15), cp=1495, form='Galarian'),
        Pokemon('farfetch’d', (14, 14, 15), cp=948, form='Galarian'),
    ]

    inventory = [
        Pokemon('bulbasaur', (7, 13, 12), cp=382, form='Shadow'),
        Pokemon('bulbasaur', (4, 8, 13), cp=366, form='Shadow'),
        Pokemon('bulbasaur', (7, 12, 8), cp=227, form='Shadow'),
    ]

    inventory = [
        Pokemon('Clefable', (12, 13, 12), cp=1828),
        Pokemon('Clefairy', (4, 2, 7), cp=389),
    ]

    inventory = [
        Pokemon('Jigglypuff', (10, 14, 15), cp=631),
        Pokemon('Jigglypuff', (10, 12, 15), cp=286),
    ]

    inventory = [
        Pokemon('poliwag', (10, 13, 14), cp=335),
        Pokemon('poliwag', (10, 14, 13), cp=335),
    ]

    inventory = [
        Pokemon('drifloon', (15, 15, 1), cp=695),
        Pokemon('drifloon', (0, 9, 14), cp=527),
        Pokemon('drifloon', (15, 15, 12), cp=509),
        Pokemon('drifloon', (14, 15, 14), cp=508),
        Pokemon('drifloon', (14, 11, 14), cp=497),
        Pokemon('drifloon', (11, 13, 12), cp=489, shiny=True),
        Pokemon('drifloon', (0, 4, 8), cp=336),
        Pokemon('drifloon', (12, 10, 12), cp=118),
    ]

    inventory = [
        Pokemon('shelmet', (10, 15, 8), cp=351),
        Pokemon('shelmet', (0, 13, 0), cp=166),
        Pokemon('shelmet', (15, 10, 12), cp=158),
    ]

    inventory = [
        Pokemon('Karrablast', (10, 4, 12), cp=824),
        Pokemon('Karrablast', (13, 13, 13), cp=655),
        Pokemon('Karrablast', (13, 14, 15), cp=16),
    ]

    inventory = [
        Pokemon('Ralts', (14, 14, 13)),
        Pokemon('Ralts', (14, 11, 12)),
        Pokemon('Ralts', (0, 11, 0), shadow=True),
        Pokemon('Ralts', (1, 14, 2), shadow=True),
        Pokemon('Ralts', (12, 12, 6), shadow=True),
        Pokemon('Ralts', (5, 14, 14)),
        Pokemon('Ralts', (7, 11, 11)),
    ]

    inventory = [
        Pokemon('Toxicroak', (11, 13, 14)),
        Pokemon('Croagunk', (9, 11, 13), cp=794),
        Pokemon('Croagunk', (8, 6, 8), cp=429),
    ]

    inventory = [
        Pokemon('Snorlax', (7, 6, 13), shadow=True),
        Pokemon('Snorlax', (0, 0, 13), shadow=0),
        Pokemon('Snorlax', (8, 15, 14), shadow=0, cp=1155),
        Pokemon('Snorlax', (8, 12, 11), shadow=0, cp=2106),
        Pokemon('Snorlax', (9, 15, 10), shadow=0, cp=2487),
        Pokemon('Snorlax', (1, 15, 14), shadow=0, cp=1372),
        Pokemon('Snorlax', (7, 11, 15), shadow=0, cp=3044),
        Pokemon('Snorlax', (2, 15, 1), shadow=1),
        Pokemon('Munchlax', (14, 11, 14), shadow=0, cp=1056),
    ]

    inventory = [
        Pokemon('Obstagoon', (11, 15, 13), cp=1478, form='Galarian'),
        Pokemon('zigzagoon', (10, 14, 14), cp=268, form='Galarian'),
        Pokemon('zigzagoon', (11, 12, 13), cp=268, form='Galarian'),
        Pokemon('zigzagoon', (11, 12, 15), cp=270, form='Galarian'),
        Pokemon('zigzagoon', (12, 11, 15), cp=272, form='Galarian'),
    ]

    inventory = [
        Pokemon('Meditite', (5, 12, 4), cp=25),
        Pokemon('Medicham', (14, 12, 12), cp=1116),
        Pokemon('Medicham', (15, 15, 10), cp=966),
    ]

    for self in inventory:
        list(self.family())

    candidates = list(ub.flatten(list(pkmn.family(ancestors=False)) for pkmn in inventory)) + inventory

    groups = ub.group_items(candidates, key=lambda p: p.name)

    leages = {
        'master': {'max_cp': float('inf')},
        'ultra': {'max_cp': 2500},
        'great': {'max_cp': 1500},
        'little': {'max_cp': 500},
    }

    max_level = 45  # for XL candy
    max_level = 40  # normal

    for name, group in groups.items():
        print('\n\n------------\n\n')
        print('name = {!r}'.format(name))
        for leage_name, leage_filters in leages.items():
            max_cp = leage_filters['max_cp']
            print('')
            print(' ========== ')
            print(' --- {} in {} --- '.format(name, leage_name))
            not_eligible = [p for p in group if p.cp is not None and p.cp > max_cp]
            print('not_eligible = {!r}'.format(not_eligible))
            have_ivs = [p.ivs for p in group if p.cp is None or p.cp <= max_cp]
            if len(have_ivs) > 0:
                first = ub.peek(group)
                first.leage_rankings_for(have_ivs, max_cp=max_cp,
                                         max_level=max_level)
            else:
                print('none eligable')

def main():
    mode = 'great'
    if mode == 'great':
        candidate_csv_text = ub.codeblock(
            '''
            registeel,LOCK_ON,FLASH_CANNON,FOCUS_BLAST,22,10,14,15
            stunfisk_galarian,MUD_SHOT,ROCK_SLIDE,EARTHQUAKE,25,11,14,14
            # altaria,DRAGON_BREATH,SKY_ATTACK,DRAGON_PULSE,26.5,14,12,13

            skarmory,AIR_SLASH,SKY_ATTACK,FLASH_CANNON,26,11,13,10

            azumarill,BUBBLE,ICE_BEAM,HYDRO_PUMP,38,12,15,13
            dewgong,ICE_SHARD,ICY_WIND,WATER_PULSE,26.5,15,08,15

            # umbreon,SNARL,FOUL_PLAY,LAST_RESORT,24.5,15,10,15
            # farfetchd_galarian,FURY_CUTTER,LEAF_BLADE,BRAVE_BIRD,33.5,12,15,15

            hypno,CONFUSION,SHADOW_BALL,THUNDER_PUNCH,25.5,13,15,14
            # hypno,CONFUSION,SHADOW_BALL,FOCUS_BLAST,25.5,13,15,14

            # machamp-shadow,COUNTER,ROCK_SLIDE,CROSS_CHOP,18,5,11,10
            victreebel_shadow-shadow,RAZOR_LEAF,LEAF_BLADE,FRUSTRATION,22.5,4,14,14
            ''')
    elif mode == 'ultra':
        candidate_csv_text = ub.codeblock(
            '''
            cresselia,PSYCHO_CUT,MOONBLAST,FUTURE_SIGHT
            togekiss,CHARM,FLAMETHROWER,ANCIENT_POWER
            articuno,ICE_SHARD,ICY_WIND,HURRICANE
            swampert,MUD_SHOT,MUDDY_WATER,EARTHQUAKE
            venusaur,VINE_WHIP,FRENZY_PLANT,SLUDGE_BOMB
            ''')
    else:
        raise KeyError(mode)

    candidates = []
    for line in candidate_csv_text.split('\n'):
        line = line.strip()
        if line.startswith('#'):
            continue
        if line:
            row = line.split(',')
            cand = Pokemon.from_pvpoke_row(row)
            candidates.append(cand)

    # for self in candidates:
    #     self.populate_stats()

    # for self in candidates:
    #     print('self = {!r}'.format(self))
    #     print(self.calc_cp())

    print(ub.repr2(api.learnable))

    if mode == 'ultra':
        base = 'https://pvpoke.com/team-builder/all/2500'
    elif mode == 'great':
        base = 'https://pvpoke.com/team-builder/all/1500'
    sep = '%2C'
    import itertools as it
    print('candidates = {!r}'.format(candidates))
    for team in it.combinations(candidates, 3):
        # if not any('registeel' in p.name for p in team):
        #     continue
        if not any('victree' in p.name for p in team):
            continue
        if len(set(p.name for p in team)) != 3:
            continue
        suffix = sep.join([p.to_pvpoke_url() for p in team])
        url = base + '/' + suffix
        print(url)

    # altaria-26.5-14-12-13-4-4-1-m-0-3-2%2Cskarmory-26-11-13-10-4-4-1-m-0-3-2%2Cazumarill-38-12-15-13-4-4-1-m-0-2-1

    # !pip install beautifulsoup4

    # from bs4 import BeautifulSoup
    # import requests
    # got = requests.request('get', url)
    # print(got.text)
    # soup = BeautifulSoup(got.text, 'html.parser')
    # for div in soup.find_all(name='div'):
    #     if 'grade' in div.attrs:
    #         print('div = {!r}'.format(div))

if __name__ == '__main__':
    """
    CommandLine:
        python ~/misc/pkmn/query_team_builder.py
    """
    main()

max_stars_repo_name: Erotemic/misc

max_issues_repo_name: Erotemic/misc

max_forks_repo_name: Erotemic/misc

reason: import numpy,import networkx


text:
# Copyright (c) 2018-2020, NVIDIA CORPORATION.
# 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.

# from time import process_time_ns   # only in 3.7!
from time import clock_gettime, CLOCK_MONOTONIC_RAW

import numpy as np

from gpu_metric_poller import startGpuMetricPolling, stopGpuMetricPolling

class Benchmark:

    resultsDict = {}
    metricNameCellWidth = 20
    valueCellWidth = 40

    def __init__(self, func, name="", args=None):
        """
        func = the callable to wrap
        name = name of callable, needed mostly for bookkeeping
        args = args to pass the callable (default is no args)
        """
        self.func = func
        self.name = name or func.__name__
        self.args = args or ()

    def run(self, n=1):
        """
        Run self.func() n times and compute the average of all runs for all
        metrics after discarding the min and max values for each.
        """
        retVal = None
        # Return or create the results dict unique to the function name
        funcResultsDict = self.resultsDict.setdefault(self.name, {})

        # FIXME: use a proper logger
        print("Running %s" % self.name, end="", flush=True)

        try:
            exeTimes = []
            gpuMems = []
            gpuUtils = []

            if n > 1:
                print("  - iteration ", end="", flush=True)

            for i in range(n):
                if n > 1:
                    print(i+1, end="...", flush=True)
                gpuPollObj = startGpuMetricPolling()
                # st = process_time_ns()
                st = clock_gettime(CLOCK_MONOTONIC_RAW)
                retVal = self.func(*self.args)
                stopGpuMetricPolling(gpuPollObj)

                # exeTime = (process_time_ns() - st) / 1e9
                exeTime = clock_gettime(CLOCK_MONOTONIC_RAW) - st
                exeTimes.append(exeTime)
                gpuMems.append(gpuPollObj.maxGpuUtil)
                gpuUtils.append(gpuPollObj.maxGpuMemUsed)

            print("  - done running %s." % self.name, flush=True)

        except Exception as e:
            funcResultsDict["ERROR"] = str(e)
            print("   %s | %s" % ("ERROR".ljust(self.metricNameCellWidth),
                                  str(e).ljust(self.valueCellWidth)))
            stopGpuMetricPolling(gpuPollObj)
            return

        funcResultsDict["exeTime"] = self.__computeValue(exeTimes)
        funcResultsDict["maxGpuUtil"] = self.__computeValue(gpuMems)
        funcResultsDict["maxGpuMemUsed"] = self.__computeValue(gpuUtils)

        for metricName in ["exeTime", "maxGpuUtil", "maxGpuMemUsed"]:
            val = funcResultsDict[metricName]
            print("   %s | %s" % (metricName.ljust(self.metricNameCellWidth),
                                  str(val).ljust(self.valueCellWidth)),
                  flush=True)

        return retVal

    def __computeValue(self, vals):
        """
        Return the avergage val from the list of vals filtered to remove 2
        std-deviations from the original average.
        """
        avg = np.mean(vals)
        std = np.std(vals)
        filtered = [x for x in vals if
                    ((avg - (2*std)) <= x <= (avg + (2*std)))]
        if(len(filtered) != len(vals)):
            print("filtered outliers: %s" % (set(vals) - set(filtered)))

        return np.average(filtered)

ext: py

max_stars_repo_name: BradReesWork/cugraph

max_issues_repo_name: BradReesWork/cugraph

max_forks_repo_name: BradReesWork/cugraph

reason: import numpy


text:
import gym
import tensorflow as tf
from tensorflow import keras
import os
from general import export_plot
from network_utils import build_mlp
import numpy as np
import tensorflow_probability as tfp
from baseline_network import BaselineNetwork
import sys

class Normal_action_sample(keras.layers.Layer):
    def __init__(self,) :
        super().__init__()

    def build(self, action_dim):
        self.action_dim = action_dim[-1]
        self.log_std = self.add_weight(shape = [self.action_dim],
                                       initializer = 'random_uniform',
                                       trainable = True)

    def call(self, inputs) :
        r = keras.backend.random_normal([self.action_dim])
        x = tf.math.multiply(r, tf.math.exp(self.log_std))
        x = tf.math.add(x, inputs)
        return x

class PG() :
    def __init__(self,env : gym.Env, config, r_seed) :
        if not os.path.exists(config.output_path) :
            os.makedirs(config.output_path)
        self.config = config
        self.r_seed = r_seed
        self.env = env
        self.env.seed(self.r_seed)

        self.discrete = isinstance(env.action_space, gym.spaces.Discrete)
        self.observation_dim = self.env.observation_space.shape[0]
        self.action_dim = self.env.action_space.n if self.discrete else self.env.action_space.shape[0]
        self.lr = self.config.learning_rate
        self.build()
        self.set_summary()
        self.set_optimizer()

    def build(self) :
        self.observation = keras.Input(
            dtype = tf.float32,
            shape = (self.observation_dim,),
        )
        self.action = build_mlp(self.observation, self.action_dim, self.config.n_layers,
                        self.config.layer_size, self.config.activation)
        self.action_logit = keras.Model(inputs = self.observation, outputs = self.action)

        if self.discrete :
            sampled_action = tf.squeeze(tf.random.categorical(self.action,1))
        else :
            self.normal_layer = Normal_action_sample()
            sampled_action = self.normal_layer(self.action)

        self.sample_action = keras.Model(inputs = self.observation, outputs = sampled_action, name='sample_action')
        self.sample_action.summary()
        if self.config.use_baseline :
            self.baseline_network = BaselineNetwork(self.config, self.observation)
            self.baseline_network.build()

    def set_optimizer(self) :
        self.optimizer = keras.optimizers.Adam()

    def loss_func(self, observations, actions, advantages) :
        if self.discrete :
            self.logprob = -tf.nn.sparse_softmax_cross_entropy_with_logits(actions, self.action_logit(observations))
        else :
            self.logprob = tfp.distributions.MultivariateNormalDiag(
                loc = self.action_logit(observations),
                scale_diag= tf.math.exp(self.normal_layer.log_std),
            ).log_prob(actions)
        advantages = tf.cast(advantages, tf.float32)
        return -tf.math.multiply(self.logprob, advantages)

    def train_step(self, observations, actions, advantages) :
        with tf.GradientTape() as tape :
            self.loss = self.loss_func(observations, actions, advantages)
        gradients = tape.gradient(self.loss, self.sample_action.trainable_variables)
        self.optimizer.learning_rate = self.lr
        self.optimizer.apply_gradients(zip(gradients, self.sample_action.trainable_variables))

    def set_summary(self) :
        self.file_writer = tf.summary.create_file_writer(self.config.output_path)
        self.file_writer.set_as_default()

    def get_returns(self, paths) :
        all_returns = []
        for path in paths :
            rewards = path["reward"]
            gammas = self.config.gamma**np.arange(len(rewards))
            returns = np.flip(np.cumsum(np.flip(rewards * gammas)))
            rev_gammas = self.config.gamma**(-np.arange(len(rewards)))
            returns = rev_gammas * returns
            all_returns.append(returns)
        returns = np.concatenate(all_returns)

        return returns

    def normalize_advantage(self, advantages) :
        adv = (advantages - tf.math.reduce_mean(advantages))/tfp.stats.stddev(advantages)
        return adv

    def calculate_advantage(self, returns, observations) :
        if self.config.use_baseline :
            advantages = self.baseline_network.calculate_advantage(returns, observations)
        else :
            advantages = returns

        if self.config.normalize_advantage :
            advantages = self.normalize_advantage(advantages)

        return advantages

    def add_sumary(self, t) :
        tf.summary.scalar('Avg Reward', self.avg_reward, step= t)
        tf.summary.scalar('Max Reward', self.max_reward, step= t)
        tf.summary.scalar('Std Reward', self.std_reward, step= t)
        tf.summary.scalar('Eval Reward', self.eval_reward, step= t)

    def init_averages(self) :
        self.avg_reward = 0
        self.max_reward = 0
        self.std_reward = 0
        self.eval_reward = 0

    def update_averages(self, rewards, scores_eval) :
        self.avg_reward = np.mean(rewards)
        self.max_reward = np.max(rewards)
        self.std_reward = np.sqrt(np.var(rewards)/ len(rewards))

        if len(scores_eval) > 0 :
            self.eval_reward = scores_eval[-1]

    def sample_path(self, env, num_episodes = None) :
        episode = 0
        episode_rewards = []
        paths = []
        t = 0
        while (num_episodes or t < self.config.batch_size) :
            state = env.reset()
            states, actions, rewards = [], [], []
            episode_reward = 0

            for step in range(self.config.max_ep_len) :
                states.append(state)
                action = self.sample_action(states[-1][None])
                if self.discrete :
                    action = int(action)
                else :
                    action = action[0]
                state, reward, done, info = env.step(action)
                # env.render()
                actions.append(action)
                rewards.append(reward)
                episode_reward += reward
                t += 1
                if (done or step == self.config.max_ep_len-1) :
                    episode_rewards.append(episode_reward)
                    break
                if (not num_episodes) and t == self.config.batch_size :
                    break

            path = {
                'observation' : np.array(states),
                'reward' : np.array(rewards),
                'action' : np.array(actions)
            }
            paths.append(path)
            episode += 1
            if num_episodes and episode >= num_episodes :
                break

        return paths, episode_rewards

    def train(self) :
        last_eval = 0
        last_record = 0
        scores_eval = []

        self.init_averages()

        for t in range(self.config.num_batches) :
            paths, total_rewards = self.sample_path(self.env)
            scores_eval = scores_eval + total_rewards
            observations = np.concatenate([path['observation'] for path in paths])
            actions = np.concatenate([path["action"] for path in paths])
            rewards = np.concatenate([path["reward"] for path in paths])
            returns = self.get_returns(paths)

            advantages = self.calculate_advantage(returns, observations)

            if self.config.use_baseline :
                self.baseline_network.update_baseline(returns, observations)

            self.train_step(observations, actions, advantages)

            if (t % self.config.summary_freq == 0) :
                self.update_averages(total_rewards, scores_eval)
                self.add_sumary(t)

            avg_reward = np.mean(total_rewards)
            sigma_reward = np.sqrt(np.var(total_rewards)/len(total_rewards))
            sys.stdout.write('\r')
            sys.stdout.flush()
            msg = "Average reward: {0:04.2f} +/- {1:04.2f} step:{2}/{3}         ".format(avg_reward, sigma_reward,
                  t, self.config.num_batches)
            print(msg, end='')

            if  self.config.record and not ((t+1)% self.config.record_freq):
                sys.stdout.write('\n')
                sys.stdout.flush()
                print('Recording')
                self.record()

        sys.stdout.write('\n')
        sys.stdout.flush()
        print('Training done.')
        print(self.normal_layer.log_std.numpy())
        export_plot(scores_eval, 'Score', self.config.env_name, self.config.plot_output)

    def evaluate(self, env= None, num_episodes=1):
        if env==None : env = self.env
        self.sample_path(env, num_episodes)

    def record(self) :
        env = gym.make(self.config.env_name)
        env.seed(self.r_seed)
        env = gym.wrappers.Monitor(env, self.config.record_path, video_callable= lambda x: True, resume=True)
        self.evaluate(env, 1)

    def run(self) :
        if self.config.record :
            self.record()
        self.train()
        if self.config.record :
            self.record()


text:
import pandas as pd
import numpy as np

def compute_distance(samples, centers):

    """
    This computes the euclidean distance of each sample from all the
    cluster centers

    Parameters
    ----------

    samples : numpy.ndarray
        - all the data points in the sample

    centers : numpy.ndarray
        - the centroids of the clusters already selected

    Returns
    ---------

    numpy.ndarray
        an array with all the samples and their distances from each of the
        cluster centers
    """

    k, num_features = centers.shape
    n_samples = samples.shape[0]

    distance = np.empty((n_samples, k))

    for i in range(k):

        # taking the euclidean distance
        distance[:, i] = np.linalg.norm(samples - centers[i, :], axis=1)

    return distance

def fit(X_train, k, n_init=10, max_iter=200):

    """
    This function classifies the non-labeled data into a given number of
    clusters k using simple KMeans algorithm.
    It returns labels for each data point according to the cluster
    it belongs and also
    cluster centers. This is a type of unsupervised learning
    method to classify data.

    Parameters
    ----------
    X_train : numpy.ndarray or a pandas.DataFrame,
    shape=(n_samples, n_features)
        - Input data that is to be clustered with features in the columns
          and samples in rows

    k : an integer(int)
        - The number of clusters we need.

    Returns
    -------
    list
        A list of the centers of each cluster.

    list
        A list of labels for cluster assignment for all samples in the data

    Examples
    --------
    >>> from Kmeans_python.fit import fit
    >>> import numpy as np
    >>> import pandas as pd
    >>> X = np.array([[1, 2], [1, 4], [1, 0],
    ...               [10, 2], [10, 4], [10, 0]])
    >>> centers, labels = fit(X, 2)
    """

    type_df = isinstance(X_train, pd.DataFrame)

    if not (type_df | (isinstance(X_train, np.ndarray))):
        raise ValueError("Invalid input type for samples. \
            X_train must be pandas dataframe or a numpy array.")

    if (n_init <= 0) | (not (isinstance(n_init, int))):
        raise ValueError(f"Invalid number of initializations \
            : {n_init} must be bigger than zero.")

    if (max_iter <= 0) | (not (isinstance(max_iter, int))):
        raise ValueError("Number of interations should be a positive integer."
                         " max_iter=%d must be bigger than zero." % max_iter)

    if (k < 1) | (not (isinstance(k, int))):
        raise ValueError("Invalid number of clusters."
                         " k=%d must be an integer >= 1." % k)

    if isinstance(X_train, pd.DataFrame):

        X = X_train.to_numpy()
    else:
        X = X_train

    for i in range(X.shape[1]):
        if np.any([isinstance(val, str) for val in X[:, i]]):
            raise ValueError("Input samples got string values. \
                All X_train values must be numeric.")

    n_samples, n_features = X.shape

    labels = np.empty((n_samples, 1))

    inertia = np.inf

    i = 0

    while i <= n_init:

        # randomly initializing centers
        centers = X[np.random.choice(range(n_samples), k, replace=False), :]

        for j in range(max_iter):

            # calculating euclidean distance of each sample from the centers
            centroid_distances = compute_distance(X, centers)
            labels = np.argmin(centroid_distances, axis=1)

            # updating the centers with mean
            for m in range(k):

                centers[m, :] = np.mean(X[np.where(labels == m)[0], :], axis=0)

        sq_min = np.square(np.min(centroid_distances, axis=1))
        updated_inertia = np.sum(sq_min, axis=0)

        # keeping the best possible values of clusters
        if updated_inertia < inertia:

            inertia = updated_inertia
            centers_final = centers
            labels_final = labels

            if inertia == 0:
                break

        i = i + 1

    return centers_final, labels_final


text:
# -*- coding: utf-8 -*-
#
# regression.py
#
# This module is part of dstools.
#

"""
Various scikit-learn compatible regression algorithms.
"""

__author__ = 'Severin E. R. Langberg'
__email__ = 'Langberg91@gmail.no'

import torch
import inspect
import datetime

import numpy as np
import pandas as pd
import torch.nn.init as init
import torch.utils.data as data_utils

from tempfile import mkdtemp
from dateutil.parser import parse
from torch.autograd import Variable
from sklearn.externals.joblib import Memory
from sklearn.metrics import mean_absolute_error
from sklearn.base import BaseEstimator, RegressorMixin
from sklearn.utils.validation import check_X_y, check_array, check_is_fitted

# WIP:
class ROSA(BaseEstimator):
    """The Response Optimal Sequential Alternation (ROSA) algorithm as
    proposed by Liland, K. H., Næs, T., and Indahl, U. G. (2016),
    ROSA—a fast extension of par- tial least squares regression for
    multiblock data analy- sis, J. Chemometrics, doi: 10.1002/cem.2824.

    """

    def __init__(self, n_components=None):

        self.n_components = n_components

        # NOTE: Variables set during instance.
        self._org_X = None
        self._org_y = None

        self.coef_ = None

    def fit(self, X, y):
        """

        Parameters
        ----------
        X : array-like or sparse matrix of shape = [n_samples, n_features]
            The training input samples.
        y : array-like, shape = [n_samples] or [n_samples, n_outputs]
            The target values (class labels in classification, real numbers in
            regression).

        Returns
        -------
        self : object
            Returns self.

        """

        A = self.n_components

        for Xi in X:
            _, _ = check_X_y(Xi, y)

        self._org_X, self._org_y = X, y

        nb = len(X)
        n, _ = np.shape(X[0])

        pk = [np.shape(X[num])[1] for num in range(nb)]

        count = np.zeros(nb)
        order = np.zeros(A)
        T = np.zeros((n, A))
        q = np.zeros(A)

        Pb = [0] * nb

        Wb = [np.zeros((x, n)) for x in pk]
        W = np.zeros((sum(pk), A))

        X_cent, y_cent = X, y#self.centering(X, y)

        inds = [np.arange(var) for var in pk]

        for i in range(1, nb):
            inds[i] += int(np.sum(pk[:i]))

        v = [0] * nb
        t = np.zeros((n, nb))
        r = np.zeros((n, nb))

        for a in range(A):

            for k in range(nb):
                v[k] = np.dot(X_cent[k].T, y_cent)
                t[:, k] = np.dot(X_cent[k], v[k])

            if a > 0:
                t = t - T[:, :a].dot(np.dot(T[:, :a].T, t))

            for k in range(nb):
                t[:, k] = t[:, k] / np.linalg.norm(t[:, k])

                offset = np.dot(np.dot(t[:, k], t[:, k].T), y_cent)
                r[:, k] = y_cent - offset

            i = np.argmin(np.sum(r ** 2))
            count[i] += 1
            order[a] = i

            T[:, a] = t[:, i]
            q[a] = np.dot(np.transpose(y_cent), T[:, a])

            y_cent = r[:, i]

            weight = Wb[i][:, :int(count[i])]

            v[i] -= weight.dot(np.dot(weight.T, v[i]))
            normalized = v[i] / np.linalg.norm(v[i])
            Wb[i][:, int(count[i])] = normalized
            W[inds[i], a] = Wb[i][:, int(count[i])]

        ## Postprocessing
        for k in range(nb):
            Pb[k] = np.dot(X_cent[k].T, T)

        PtW = np.triu(np.dot((np.concatenate(Pb, axis=0)).T, W))

        # PtW_ext = self._broadcast(PtW, W)

        Beta = np.cumsum(np.divide(W, PtW) * q, axis=1)

        #Beta = np.cumsum(W / PtW * q, axis=1)

    @staticmethod
    def centering(X, y):

        y_cent = y - np.mean(y, axis=0)
        X_cent = [Xi - np.mean(Xi, axis=0) for Xi in X]
        return X_cent, y_cent[:, np.newaxis]

    @staticmethod
    def _broadcast(array1, array2):

        if np.size(array2) > np.size(array1):
            largest = array2
        else:
            largest = array1

        print(largest)
        print(array1)

    def predict(self, X):
        """

        Parameters
        ----------
        X : array-like of shape = [n_samples, n_features]
            The input samples.

        Returns
        -------
        y : array of shape = [n_samples]
            Returns :math:`x^2` where :math:`x` is the first column of `X`.
        """
        X = check_array(X)

# WIP:
class TorchRegressor(BaseEstimator, RegressorMixin):
    """A scikit-learn compatible pytorch regressor.

    From Wiki: CUDA is a parallel computing platform and application
        programming interface (API) model created by Nvidia.

    Args:
        input_dim (int): The number of original features.
        output_dim (int): The number of features ...

        num_epochs (int): The number of iterations
        batch_size (int):
        shuffle (bool, {True, False}): Determines whether to
        learning_rate (float):

    """

    # QUESTION: Can replace input_dim with X.shape[1]???
    def __init__(self, output_dim=1, input_dim=100, hidden_layer_dims=[100, 100],
                 num_epochs=1, learning_rate=0.01, batch_size=128, shuffle=False,
                 callbacks=[], use_gpu=True, verbose=1):

        self.output_dim = output_dim
        self.input_dim = input_dim
        self.hidden_layer_dims = hidden_layer_dims
        self.num_epochs = num_epochs
        self.learning_rate = learning_rate
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.callbacks = callbacks
        self.use_gpu = use_gpu
        self.verbose = verbose

        # NOTE: Variables set during instance.
        self._gpu = None
        self._model = None
        self._history = None
        self._shape_layers = None

    def _gpu_dependency(self):
        # Determine if CUDA is available and should use GPU.

        if torch.cuda.is_available():
            self._gpu = use_gpu
        else:
            self._gpu = False

        if self._gpu:
            self._model = self._model.cuda()

        return self

    def _construct_model(self):
        # Build the torch NN model (???).

        self._layer_dims = [self.input_dim] + self.hidden_layer_dims + [self.output_dim]

        # A container of modules in each model layer.
        self._model = torch.nn.Sequential()

        # Loop through the layer dimensions and add to the sequential container
        # (1) an input layer (2) a hidden layer with relu activation.
        for idx, dim in enumerate(self._layer_dims):

            if (idx < len(self._layer_dims) - 1):

                module = torch.nn.Linear(dim, self._layer_dims[idx + 1])
                init.xavier_uniform_(module.weight)

                self._model.add_module(
                    ('').join(('linear', str(idx))), module
                )
            if (idx < len(self._layer_dims) - 2):
                self._model.add_module(
                    ('').join(('relu', str(idx))), torch.nn.ReLU()
                )

        # Setup following the use of GPU.
        self._gpu_dependency()

        return self

    def transform_X_y(self, X, y):
        # Prepare data for BGD optimizer.

        # Convert numpy arrays to torch tensor object.
        torch_X = torch.from_numpy(X).float()
        torch_y = torch.from_numpy(y).float()

        # Create CUDA device objects.
        if self._gpu:
            torch_X, torch_y = torch_X.cuda(), torch_y.cuda()

        # Create torch.utils.data.dataset.TensorDataset objects of X and y
        # tensors.
        train = data_utils.TensorDataset(torch_X, torch_y)

        # Combines a dataset and a sampler, and provides single- or
        # multi-process iterators over the dataset.
        train_loader = data_utils.DataLoader(
            train, batch_size=self.batch_size, shuffle=self.shuffle
        )

        return train_loader

    def optimizer(self, method='adam'):

        # Selects Adam Optimization Algorithm which is an extension to SGD.
        if method == 'adam':
            algorithm = torch.optim.Adam(
                self._model.parameters(), lr=self.learning_rate
            )
        else:
            raise NotImplementedError('')

        return algorithm

    def loss_function(self, method='mse'):

        if method == 'mse':
            function = torch.nn.MSELoss(size_average=False)
        else:
            raise NotImplementedError('')

        return function

    def _eval_loss(self, y_train, y_pred, function):

        _loss = function(
            y_pred,
            Variable(y_train.cuda().float() if self._gpu else y_train.float())
        )

        return _loss

    def _train_model(self, X, y, optimizer='adam', loss='mse'):

        train_loader = self.transform_X_y(X, y)
        optimizer = self.optimizer(method=optimizer)
        loss_function = self.loss_function(method=loss)

        self._history = {'loss': [], 'val_loss': [], 'mse_loss': []}

        finished = False
        while not finished:

            for epoch in range(self.num_epochs):

                loss = None
                for num, (minibatch, y_train) in enumerate(train_loader):
                    y_train, y_pred, loss = self._forward(
                        minibatch, y_train, loss_function, optimizer
                    )

                error = mean_absolute_error(y_train, y_pred)

                self._history['mse_loss'].append(loss.data[0])
                self._history['loss'].append(error)

                if self.verbose > 0:
                    self._status_report(epoch, error, loss.data[0])

            finished = self._eval_callbacks()

    def _forward(self, minibatch, y_train, loss_function, optimizer):

        y_pred = np.squeeze(self._model(Variable(minibatch)))

        loss = self._eval_loss(y_train, y_pred, loss_function)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if self._gpu:
            y_train, y_pred = y_train.cpu().numpy(), y_pred.cpu().data.numpy()
        else:
            y_train, y_pred = y_train.numpy(), y_pred.data.numpy()

        return y_train, y_pred, loss

    def _status_report(self, epoch, error, mse_loss):

        print('Results for epoch {}, loss {}, mse_loss {}'
              ''.format(epoch + 1, error, mse_loss))

    def _eval_callbacks(self):

        for callback in self.callbacks:
            callback.call(self._model, self._history)

            if callback.finish:
                return True
            else:
                return False

    def fit(self, X, y):
        """
        Trains the pytorch regressor.
        """

        # NOTE: Replace with @property.
        assert (type(self.input_dim) == int), 'input_dim parameter must be defined'
        assert (type(self.output_dim) == int), 'output_dim must be defined'

        self._construct_model()
        self._train_model(X, y)

        return self

    def predict(self, X, y=None):
        """
        Makes a prediction using the trained pytorch model
        """

        if self._history == None:
            raise RuntimeError('Regressor has not been fit')

        results = []
        split_size = np.ceil(len(X) / self.batch_size)

        # In case the requested size of prediction is too large for memory (especially gpu)
        # split into batchs, roughly similar to the original training batch size. Not
        # particularly scientific but should always be small enough.
        for batch in np.array_split(X, split_size):
            x_pred = Variable(torch.from_numpy(batch).float())
            y_pred = self._model(x_pred.cuda() if self._gpu else x_pred)
            y_pred_formatted = y_pred.cpu().data.numpy() if self._gpu else y_pred.data.numpy()
            results = np.append(results, y_pred_formatted)

        return results

    def score(self, X, y, sample_weight=None):
        """Scores the data using the trained pytorch model. Returns negative
        Mean Squared Error.
        """

        y_pred = self.predict(X, y)

        return mean_absolute_error(y, y_pred) * (-1)

class TikhonovCV(BaseEstimator):
    """Tikhonov regularized least-squares regression by QR-decomposition with
    leave-one-out cross-validation of prediction estimates.

    Args:
        reg_mat (array like): Regularization matrix. Assigns identity matrix by
                              default.
        alpha (int, float): Regularization parameter. Assigns 1.0 by default.

    Kwargs:
        fit_intercept (bool): Includes an intercept coefficient to the
                              regression model parameters.
        normalize (bool): Normalizes the data matrix.

    """

    def __init__(self, reg_mat=None, alpha=None, fit_intercept=True,
                 normalize=False):

        self.reg_mat = reg_mat
        self.alpha = alpha
        self.fit_intercept = fit_intercept
        self.normalize = normalize

        # NOTE: Variable set with instance.
        self.coef_ = None
        self._msecv = None
        self._rescv_ = None
        self._orthog = None
        self._upper_tri = None

    @property
    def mescv(self):
        """Returns cross-validated Mean Square Error"""

        return self._mescv

    @property
    def rescv(self):
        """Returns cross-validated model residuals."""

        return self._rescv

    def _check_setup(self, X, y):
        # Checks feature matrix X and target vector y array.
        # Constructs regularization matrix.
        # Converts y to (n x 1) array.

        X, y = check_X_y(X, y)

        if not self.alpha:
            self.alpha = 1.0

        if self.reg_mat is None:
            _tikh_mat = np.eye(X.shape[1]) * np.sqrt(self.alpha)
        else:
            _tikh_mat = self.reg_mat * np.sqrt(self.alpha)

        if not _tikh_mat.shape[1] == X.shape[1]:
            self.tikh_mat = _tikh_mat[:, :X.shape[1]]
        else:
            self.tikh_mat = _tikh_mat

        if len(np.shape(y)) < 2:
            return X, y[:, np.newaxis]
        else:
            return X, y

    @staticmethod
    def _normalize(X, y):
        # Divides feature matrix and target vector by column sums.

        X_col_sums = np.sum(X, axis=0)
        X_norm = np.divide(X, X_col_sums)

        y_col_sum = np.sum(y, axis=0)
        y_norm = y / y_col_sum

        return X_norm, y_norm

    def _stacked_matrices(self, X, y):
        # Stacks regularization and feature matrix, and expands target data.

        num_data_samples, _ = np.shape(X)
        num_reg_samples, _ = np.shape(self.tikh_mat)

        reg_dummies = np.zeros((num_reg_samples, 1))

        if self.fit_intercept:
            intercept = np.ones((num_data_samples, 1))
            variables = np.hstack((intercept, X))
            regularization = np.hstack((reg_dummies, self.tikh_mat))

            stacked_data = np.vstack((variables, regularization))

        else:
            stacked_data = np.vstack((X, self.tikh_mat))

        stacked_target = np.vstack((y, reg_dummies))

        return stacked_data, stacked_target

    def _decompose(self, X):
        # Performs QR decomposition of feature matrix.

        return np.linalg.qr(X)

    def fit(self, X, y):
        """Estimate least-squares regression coefficients.

        Args:
            X (array like): Feature matrix as (samples x features).
            y (array like): Target vector.

        """

        _X, _y = self._check_setup(X, y)

        if self.normalize:
            _data_stack, _target_stack = self._stacked_matrices(_X, _y)

            data_stack, target_stack = self._normalize(
                _data_stack, _target_stack
            )
        else:
            data_stack, target_stack = self._stacked_matrices(_X, _y)

        self._orthog, self._upper_tri = self._decompose(data_stack)

        proj = np.dot(np.transpose(self._orthog), target_stack)
        self.coef_ = np.dot(np.linalg.inv(self._upper_tri), proj)

        return self

    def predict(self, X):
        """Predict observations for each input data point.

        Args:
            X (array like): Feature matrix as (samples x features).

        """

        _X = check_array(X)

        if self.fit_intercept:
            return np.dot(_X, self.coef_[1:]) + self.coef_[0]

        else:
            return np.dot(_X, self.coef_)

    def cross_val_predict(self, X, y):
        """Perform cross-validated prediction for each input data point.

        Args:
            X (array like): Feature matrix as (samples x features).
            y (array like): Target vector.

        """

        self.fit(X, y)

        pred = self.predict(X)
        res = y - pred[:, 0]

        diag = np.sum(np.power(self._orthog[:X.shape[0], :], 2), axis=1)
        self._rescv = res / (1 - diag)
        self._mescv = np.dot(self._rescv, self._rescv) / self._rescv.size

        return pred[:, 0] - self._rescv


text:
# coding=utf-8
# Copyleft 2019 project LXRT.

import ast
import argparse
import random

import numpy as np
import torch

def get_optimizer(optim):
    # Bind the optimizer
    if optim == 'rms':
        print("Optimizer: Using RMSProp")
        optimizer = torch.optim.RMSprop
    elif optim == 'adam':
        print("Optimizer: Using Adam")
        optimizer = torch.optim.Adam
    elif optim == 'adamax':
        print("Optimizer: Using Adamax")
        optimizer = torch.optim.Adamax
    elif optim == 'sgd':
        print("Optimizer: sgd")
        optimizer = torch.optim.SGD
    elif 'bert' in optim:
        optimizer = 'bert'      # The bert optimizer will be bind later.
    else:
        assert False, "Please add your optimizer %s in the list." % optim

    return optimizer

def parse_args():
    parser = argparse.ArgumentParser()

    # Data Splits
    parser.add_argument("--train", default='train')
    parser.add_argument("--valid", default='valid')
    parser.add_argument("--test", default=None)

    # Training Hyper-parameters
    parser.add_argument('--batchSize', dest='batch_size', type=int, default=256)
    parser.add_argument('--optim', default='bert')
    parser.add_argument('--lr', type=float, default=3e-4)   # 1e-4
    parser.add_argument('--epochs', type=int, default=10)
    parser.add_argument('--dropout', type=float, default=0.1)
    # changed from 9595 seed to None so we always have to set it to value from 0 to 100
    parser.add_argument('--seed', type=int, default=None, help='random seed')

    # Debugging
    parser.add_argument('--output', type=str, default='snap/test')
    parser.add_argument("--fast", action='store_const', default=False, const=True)
    parser.add_argument("--tiny", action='store_const', default=False, const=True)
    parser.add_argument("--tqdm", action='store_const', default=False, const=True)

    # Model Loading
    parser.add_argument('--load', type=str, default=None,
                        help='Load the model (usually the fine-tuned model).')
    parser.add_argument('--loadLXMERT', dest='load_lxmert', type=str, default=None,
                        help='Load the pre-trained LXMERT model.')
    parser.add_argument('--loadLXMERTQA', dest='load_lxmert_qa', type=str, default=None,
                        help='Load the pre-trained LXMERT model with QA answer head.')
    parser.add_argument("--fromScratch", dest='from_scratch', action='store_const', default=False, const=True,
                        help='If none of the --load, --loadLXMERT, --loadLXMERTQA is set, '
                             'the model would be trained from scratch. If --fromScratch is'
                             ' not specified, the model would load BERT-pre-trained weights by'
                             ' default. ')

    # Optimization
    parser.add_argument("--mceLoss", dest='mce_loss', action='store_const', default=False, const=True)

    # LXRT Model Config
    # Note: LXRT = L, X, R (three encoders), Transformer
    parser.add_argument("--llayers", default=9, type=int, help='Number of Language layers')
    parser.add_argument("--xlayers", default=5, type=int, help='Number of CROSS-modality layers.')
    parser.add_argument("--rlayers", default=5, type=int, help='Number of object Relationship layers.')

    # LXMERT Pre-training Config
    parser.add_argument("--taskMatched", dest='task_matched', action='store_const', default=False, const=True)
    parser.add_argument("--taskMaskLM", dest='task_mask_lm', action='store_const', default=False, const=True)
    parser.add_argument("--taskObjPredict", dest='task_obj_predict', action='store_const', default=False, const=True)
    parser.add_argument("--taskQA", dest='task_qa', action='store_const', default=False, const=True)
    parser.add_argument("--visualLosses", dest='visual_losses', default='obj,attr,feat', type=str)
    parser.add_argument("--qaSets", dest='qa_sets', default=None, type=str)
    parser.add_argument("--wordMaskRate", dest='word_mask_rate', default=0.15, type=float)
    parser.add_argument("--objMaskRate", dest='obj_mask_rate', default=0.15, type=float)

    # Training configuration
    parser.add_argument("--multiGPU", action='store_const', default=False, const=True)
    parser.add_argument("--numWorkers", dest='num_workers', default=0, type=int)

    # JChiyah for multiple runs and averages
    parser.add_argument("--num_runs", default=1, type=int)
    parser.add_argument('--mode', required=True, choices=['train', 'test', 'predict'])
    parser.add_argument(
        '--simmc2_input_features',
        help='The input features to use in inference, default=["user_utterance", "image"]',
        default='')
    parser.add_argument(
        '--image_feature_file', type=str, required=True,
        help='Suffix of the feature .tsv files to use',
        default='all_gt_boxes')
    parser.add_argument(
        '--image_categories', type=str,
        help='Suffix of the category feature .tsv files to use',
        default='colour_types_compound')
    parser.add_argument(
        '--visualise', action='store_true',
        help='Visualise some examples, load must be true',
        default=False)
    parser.add_argument(
        '--simmc2_sample', type=int,
        help='Load only a sample of the datasets of this size',
        default=5)
    parser.add_argument(
        '--wandb_id', type=str,
        help='id to use for wandb if loading an older model',
        default=None)
    parser.add_argument(
        '--final_layer', type=str,
        help='final layer of the model, linear usually works best but sequential was the submitted',
        default='linear')
    parser.add_argument(
        '--ablation', type=str,
        help='what to ignore from the model architecture during more in-depth ablations',
        choices=['visual_encoder', 'lxrt_encoder', 'multiple_similar_objects', None], default=None)

    # Parse the arguments.
    args = parser.parse_args()

    # Bind optimizer class.
    args.optimizer = get_optimizer(args.optim)

    default_input_features = {
        'user_utterance': True,
        'img_features': True,
        'img_bounding_boxes': GOLD_DATA,
        'dialogue_history_turns': 1,
        'previous_object_turns': 1,
        'descriptions': True,
        'object_indexes_in_descriptions': 'number',
        'object_counts': True,
    }
    input_feature_choices = {
        'user_utterance': [True, False],
        'img_features': [True, False],
        'img_bounding_boxes': [False, GOLD_DATA],
        'dialogue_history_turns': [False, 'all'] + list(range(0, 10)),
        'previous_object_turns': [False, 'all'] + list(range(0, 10)),
        'descriptions': [True, False, GOLD_DATA, 'colours_only', 'asset_only'],
        'object_indexes_in_descriptions': [False, 'number', 'token_local', 'token_global'],
        'object_counts': [True, False],
    }

    tmp_input_feats = args.simmc2_input_features.split(',')
    args.simmc2_input_features = default_input_features.copy()
    # fill args.simmc2_input_features with each feature value
    for feat_str in tmp_input_feats:
        if feat_str == '':
            continue

        if '=' not in feat_str:
            raise ValueError(
                f"Features should have the format: feat_name=feat_value, ... but found {tmp_input_feats}")
        key, value = feat_str.strip().split('=')

        if key not in args.simmc2_input_features.keys():
            raise ValueError(
                f"Key {key} not recognised in possible inputs: {args.simmc2_input_features.keys()}")
        value = eval(value)
        if value not in input_feature_choices[key]:
            raise ValueError(
                f"Feature {key} cannot have value {value}. Choices: {input_feature_choices[key]}")

        if 'turns' in key and not value:
            value = 0                       # set minimum value to be 0, not False or None

        args.simmc2_input_features[key] = value

    print(args.simmc2_input_features)

    if args.visualise and not args.load:
        raise ValueError(f"You need to give the --load argument for visualise!")

    # # Set seeds
    # torch.manual_seed(args.seed)
    # random.seed(args.seed)
    # np.random.seed(args.seed)

    return args

GOLD_DATA = 'gold'
args = parse_args()


text:
import random
from typing import List, Optional

import numpy as np

from block import Block

class Division:
    data = None

    def __init__(self, min_block_size: int):
        self.height = np.size(Division.data, 0)
        self.width = np.size(Division.data, 1)
        self.min_block_size = min_block_size
        self.amount_of_blocks_in_width = self.width // min_block_size
        self.amount_of_blocks_in_height = self.height // min_block_size
        self.additional_width = self.width - min_block_size * self.amount_of_blocks_in_width
        self.additional_height = self.height - min_block_size * self.amount_of_blocks_in_height
        self.blocks = [[Block(w, h, min_block_size, min_block_size)
                        for w in range(self.amount_of_blocks_in_width)]
                       for h in range(self.amount_of_blocks_in_height)]
        if self.additional_width:
            for i in range(self.amount_of_blocks_in_height):
                block = self.blocks[i][-1]
                block.width = min_block_size + self.additional_width
                block.widen = True
        if self.additional_height:
            for block in self.blocks[-1]:
                block.height = min_block_size + self.additional_height
                block.higher = True

    def count_distance(self) -> float:
        distance = 0
        for row in self.blocks:
            for block in row:
                distance += block.count_distance(self.data)
        return distance / (self.height * self.width)

    def get_random_block(self) -> Block:
        return random.choice(random.choice(self.blocks))

    def get_random_atypical_size_block(self) -> Optional[Block]:
        atypical_size_blocks = [block for row in self.blocks for block in row if block.widen or block.higher]
        return random.choice(atypical_size_blocks) if atypical_size_blocks else None

    def get_neighbour_blocks(self, block: Block, left_right: bool = True, up_down: bool = True) -> List[Block]:
        neighbourhood = []
        x = block.array_width_position
        y = block.array_height_position
        if 0 <= x - 1 and left_right:
            neighbour = self.blocks[y][x - 1]
            if block.height == neighbour.height and block.height_position == neighbour.height_position:
                neighbourhood.append(neighbour)
        if x + 1 < self.amount_of_blocks_in_width and left_right:
            neighbour = self.blocks[y][x + 1]
            if block.height == neighbour.height and block.height_position == neighbour.height_position:
                neighbourhood.append(neighbour)
        if 0 <= y - 1 and up_down:
            neighbour = self.blocks[y - 1][x]
            if block.width == neighbour.width and block.width_position == neighbour.width_position:
                neighbourhood.append(neighbour)
        if y + 1 < self.amount_of_blocks_in_height and up_down:
            neighbour = self.blocks[y + 1][x]
            if block.width == neighbour.width and block.width_position == neighbour.width_position:
                neighbourhood.append(neighbour)
        return neighbourhood

    def change_size(self, lesser: Block, greater: Block) -> None:
        if lesser.width_position == greater.width_position and lesser.width == greater.width:
            lesser.height += self.additional_height
            greater.height -= self.additional_height
            lesser.higher = True
            greater.higher = False
            if lesser.height_position < greater.height_position:
                greater.height_position += self.additional_height
            else:
                lesser.height_position -= self.additional_height
        elif lesser.height_position == greater.height_position and lesser.height == greater.height:
            lesser.width += self.additional_width
            greater.width -= self.additional_width
            lesser.widen = True
            greater.widen = False
            if lesser.width_position < greater.width_position:
                greater.width_position += self.additional_width
            else:
                lesser.width_position -= self.additional_width
        else:
            raise Exception("Impossible to change size.")
        lesser.clear_distance()
        greater.clear_distance()

    def __str__(self) -> str:
        array_representation = np.empty((self.height, self.width), np.uint8(1))
        for row in self.blocks:
            for block in row:
                for i in range(block.height_position, block.height_position + block.height):
                    for j in range(block.width_position, block.width_position + block.width):
                        array_representation[i][j] = block.value
        str_representation = ""
        for i, row in enumerate(array_representation):
            for j, value in enumerate(row):
                str_representation += f'{value}'
                if j + 1 != len(row):
                    str_representation += ' '
            if i + 1 != len(array_representation):
                str_representation += "\n"
        return str(str_representation)

max_stars_repo_name: ErykKrupa/metaheuristic-algorithms

max_issues_repo_name: ErykKrupa/metaheuristic-algorithms

max_forks_repo_name: ErykKrupa/metaheuristic-algorithms


text:
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
standard_library.install_aliases()
import unittest

import chainer
from chainer import testing
from chainer.testing import condition
import numpy as np

import chainerrl

@testing.parameterize(
    *testing.product({
        'n': [1, 5],
        'dtype': [np.float64, np.float32],
    })
)
class TestConjugateGradient(unittest.TestCase):

    def _test(self, xp):
        # A must be symmetric and positive-definite
        random_mat = xp.random.normal(size=(self.n, self.n)).astype(self.dtype)
        A = random_mat.dot(random_mat.T)
        x_ans = xp.random.normal(size=self.n).astype(self.dtype)
        b = A.dot(x_ans)

        def A_product_func(vec):
            self.assertEqual(xp, chainer.cuda.get_array_module(vec))
            self.assertEqual(vec.shape, b.shape)
            return A.dot(vec)

        x = chainerrl.misc.conjugate_gradient(A_product_func, b)
        self.assertEqual(x.dtype, self.dtype)
        self.assertTrue(chainer.cuda.get_array_module(x), xp)
        xp.testing.assert_allclose(x, x_ans, rtol=1e-3)

    @condition.retry(3)
    def test_cpu(self):
        self._test(np)

    @testing.attr.gpu
    @condition.retry(3)
    def test_gpu(self):
        self._test(chainer.cuda.cupy)

max_stars_repo_name: atiroms/chainerrl

max_issues_repo_name: atiroms/chainerrl

max_forks_repo_name: atiroms/chainerrl


text:
import numpy as np
from pych.extern import Chapel

@Chapel()
def ex_numpy(a=np.ndarray):
    """
    use Time;
    var b: [a.domain] real;
    b = 2.0;
    for i in a.domain {
        a[i] = getCurrentTime() / 60;
        writeln(a[i]);
    }
    writeln(a);
    writeln("Done.");
    """
    return None

def test_chapel_slicing():
    a = np.ones((10), dtype=np.float)
    b = a[::2]
    assert all(a == [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ])
    # this assert ensures array originally started as filled with ones
    ex_numpy(b)
    assert all(a[::2] == b)
    # this assert ensures a was updated when b was, since slices of numpy arrays
    # share memory with the original array
    # b should contain references to the current time, but since that will vary
    # from run to run, this is a more robust comparison

    c = np.ones((10), dtype=np.float)
    # c starts out the same as a
    d = c[::2]
    for i, e in enumerate(d):
        d[i] = i
    # d should contain 0.0 through 4.0, updating alternating indexes in c
    # starting with the first index to contain the same contents.  The
    # assert in the following line reflects this knowledge.

    assert all(c == [ 0.0,  1.0,  1.0,  1.0,  2.0,  1.0,  3.0,  1.0,  4.0,  1.0]) and all(d == [ 0.0,  1.0,  2.0,  3.0,  4.0])

max_stars_repo_name: lydia-duncan/pychapel

max_issues_repo_name: lydia-duncan/pychapel

max_forks_repo_name: lydia-duncan/pychapel


text:
import pandas as pd
import matplotlib.pyplot as plt
import requests
import math
from termcolor import colored as cl
import numpy as np

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20, 10)

# EXTRACTING STOCK DATA

def get_historical_data(symbol, start_date):
    api_key = 'YOUR API KEY'
    api_url = f'https://api.twelvedata.com/time_series?symbol={symbol}&interval=1day&outputsize=5000&apikey={api_key}'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
    df = df[df.index >= start_date]
    df.index = pd.to_datetime(df.index)
    return df

aapl = get_historical_data('AAPL', '2020-01-01')
aapl

def sma(data, n):
    sma = data.rolling(window = n).mean()
    return pd.DataFrame(sma)

n = [20, 50]
for i in n:
    aapl[f'sma_{i}'] = sma(aapl['close'], i)

aapl.tail()

plt.plot(aapl['close'], label = 'aapl', linewidth = 5, alpha = 0.3)
plt.plot(aapl['sma_20'], label = 'SMA 20')
plt.plot(aapl['sma_50'], label = 'SMA 50')
plt.title('aapl Simple Moving Averages (20, 50)')
plt.legend(loc = 'upper left')
plt.show()

def implement_sma_strategy(data, short_window, long_window):
    sma1 = short_window
    sma2 = long_window
    buy_price = []
    sell_price = []
    sma_signal = []
    signal = 0

    for i in range(len(data)):
        if sma1[i] > sma2[i]:
            if signal != 1:
                buy_price.append(data[i])
                sell_price.append(np.nan)
                signal = 1
                sma_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        elif sma2[i] > sma1[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(data[i])
                signal = -1
                sma_signal.append(-1)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            sma_signal.append(0)

    return buy_price, sell_price, sma_signal

sma_20 = aapl['sma_20']
sma_50 = aapl['sma_50']

buy_price, sell_price, signal = implement_sma_strategy(aapl['close'], sma_20, sma_50)

plt.plot(aapl['close'], alpha = 0.3, label = 'aapl')
plt.plot(sma_20, alpha = 0.6, label = 'SMA 20')
plt.plot(sma_50, alpha = 0.6, label = 'SMA 50')
plt.scatter(aapl.index, buy_price, marker = '^', s = 200, color = 'darkblue', label = 'BUY SIGNAL')
plt.scatter(aapl.index, sell_price, marker = 'v', s = 200, color = 'crimson', label = 'SELL SIGNAL')
plt.legend(loc = 'upper left')
plt.title('aapl SMA CROSSOVER TRADING SIGNALS')
plt.show()

position = []
for i in range(len(signal)):
    if signal[i] > 1:
        position.append(0)
    else:
        position.append(1)

for i in range(len(aapl['close'])):
    if signal[i] == 1:
        position[i] = 1
    elif signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]

sma_20 = pd.DataFrame(sma_20).rename(columns = {0:'sma_20'})
sma_50 = pd.DataFrame(sma_50).rename(columns = {0:'sma_50'})
signal = pd.DataFrame(signal).rename(columns = {0:'sma_signal'}).set_index(aapl.index)
position = pd.DataFrame(position).rename(columns = {0:'sma_position'}).set_index(aapl.index)

frames = [sma_20, sma_50, signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy

rets = aapl.close.pct_change().dropna()
strat_rets = strategy.sma_position[1:]*rets

plt.title('Daily Returns')
rets.plot(color = 'blue', alpha = 0.3, linewidth = 7)
strat_rets.plot(color = 'r', linewidth = 1)
plt.show()

rets_cum = (1 + rets).cumprod() - 1
strat_cum = (1 + strat_rets).cumprod() - 1

plt.title('Cumulative Returns')
rets_cum.plot(color = 'blue', alpha = 0.3, linewidth = 7)
strat_cum.plot(color = 'r', linewidth = 2)
plt.show()