from django.test import TestCase, Client
from shortener_url.models import Url
from rest_framework import status
import json

class TestShortenerUrl(TestCase):

    def setUp(self):
        Url.objects.create(original_url="https://web.whatsapp.com/", custom_alias="whatsapp", shortened_url="http://shortener/u/whatsapp")

    def test_shortener_alias(self):
        data = {
            'url': 'http://www.google.com.br',
            'CUSTOM_ALIAS': 'google'
        }
        response = self.client.post('/create', json.dumps(data), content_type="application/json")
        self.assertEqual(status.HTTP_201_CREATED, response.status_code)

    def test_shortener_without_url(self):
        data = {
            'url': '',
            'CUSTOM_ALIAS': ''
        }
        response = self.client.post('/create', json.dumps(data), content_type="application/json")
        self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code)

    def test_shortener(self):
        data = {
            'url': 'https://www.youtube.com/',
            'CUSTOM_ALIAS': ''
        }
        response = self.client.post('/create', json.dumps(data), content_type="application/json")
        self.assertEqual(status.HTTP_201_CREATED, response.status_code)

    def test_shortener_existing_alias(self):
        data = {
            'url': 'https://web.whatsapp.com/',
            'CUSTOM_ALIAS': 'whatsapp'
        }
        response = self.client.post('/create', json.dumps(data), content_type="application/json").json()
        self.assertEqual('001', response['err_code'])

class TestRetrieveUrl(TestCase):

    def setUp(self):
        Url.objects.create(original_url="https://web.whatsapp.com/", custom_alias="whatsapp", shortened_url="http://shortener/u/whatsapp")

    def test_retrieve_alias(self):
        response = self.client.get('/retrieve/whatsapp')
        self.assertEqual(status.HTTP_302_FOUND, response.status_code)

    def test_retrieve_url_not_found(self):
        response = self.client.get('/retrieve/gmail').json()
        self.assertEqual('002', response['err_code'])

    def test_retrieve_without_alias(self):
        response = self.client.get('/retrieve/')
        self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code)

class TestTopVisitedUrl(TestCase):

    def test_top_visited_url(self):
        response = self.client.get('/top_visited')
        self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)


import requests
from bs4 import BeautifulSoup
def strip_string(string1):
    for i in string1:
        if i=='<':
            var1 = string1[string1.index(i):string1.index(">")+1]
            string1 = string1.replace(var1, "")
    return string1
def scrape_meaning(a):
    URL = f"https://www.dictionary.com/browse/{a}"
    r = requests.get(URL)
    soup = BeautifulSoup(r.content, 'html5lib')
    meaning = str(soup.find('span', attrs={'class': 'one-click-content css-nnyc96 e1q3nk1v1'}))
    if meaning.endswith(" ") or meaning.endswith(""):
        meaning = meaning[:-7]
    meaning1 = str(strip_string(meaning))
    if ":" in meaning1:
        meaning1 = meaning1[:meaning1.index(":")]

    if len(meaning1) > 90:
        meaning1 = meaning1[:90] + "\n" + "-" + meaning1[90:]

    return meaning1
def scrape_usage(a):
    URL = f"https://www.dictionary.com/browse/{a}"
    r = requests.get(URL)

    soup = BeautifulSoup(r.content, 'html5lib')
    usage = str(soup.find('span', attrs={'class': 'luna-example italic'}))
    usage1 = str(strip_string(usage))
    if len(usage1) > 90:
        usage1 = usage1[:90] + "\n" + "-" + usage1[90:]
    return usage1


from typing import List, Dict

from fastapi import WebSocket
from fastapi import HTTPException, status

from sqlalchemy import exc
from sqlalchemy.ext.asyncio import AsyncSession

from database.models.chat_models.chat_model import Chat
from database.models.chat_models.members_model import ChatMember
from database.models.chat_models.messages_model import Message
from src.schemas.chat_schema import CreateUserRoom, SaveMessage, MessageOut, ChatMemberOut

class ChatCrud:
    def __init__(self, db_session: AsyncSession) -> None:
        self._session: AsyncSession = db_session

    async def _close_session(self):
        if self._session.is_active:
            await self._session.close()

    async def _get_chat_by_id(self, chat_id) -> Chat | None:
        return await self._session.get(Chat, chat_id)

    async def get_chat_history(self, chat_id: int) -> List[MessageOut]:
        chat: Chat = await self._session.get(Chat, chat_id)
        if chat is None:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Chat not found")
        messages = list()
        for message in chat.messages:
            messages.append(MessageOut(**message.__dict__))

        await self._close_session()
        return messages

    async def create_chat(self):
        chat = Chat()
        self._session.add(chat)
        try:
            await self._session.commit()
        except exc.IntegrityError:
            await self._session.rollback()

        return chat.id

    async def create_user_room(self, create_data: CreateUserRoom):
        chat_id = await self.create_chat()
        added_userd = await self.add_user_to_chat([create_data.owner_id], chat_id)

        await self._close_session()
        return added_userd

    async def add_user_to_chat(self, user_id_list: List[str], chat_id: int) -> List[ChatMemberOut]:
        added_users: List[ChatMemberOut] = list()

        if await self._get_chat_by_id(chat_id) is None:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Chat not found")

        for user_id in user_id_list:
            member = ChatMember(chat_id=chat_id, user_id=user_id)
            self._session.add(member)
            added_users.append(ChatMemberOut(chat_id=chat_id, user_id=user_id))

        try:
            await self._session.commit()
        except exc.IntegrityError:
            await self._session.rollback()
        return added_users

    async def user_in_chat(self, chat_id, user_id):
        chat: Chat = await self._session.get(Chat, chat_id)
        if chat is None:
            return False

        members = [user_id for members in chat.members if user_id == members.user_id]
        if user_id in members:
            return True

    async def save_message(self, message_data: SaveMessage):
        if not await self.user_in_chat(message_data.chat_id, message_data.user_id):
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)

        chat: Chat = await self._session.get(Chat, message_data.chat_id)
        if chat is None:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)

        message = Message(**message_data.dict())
        chat.messages.append(message)

        try:
            self._session.add(chat)
            await self._session.commit()
        except exc.IntegrityError:
            await self._session.rollback()

        await self._close_session()

class Notifier:
    def __init__(self):
        self.connections: Dict[int, Dict] = dict()

    async def send_in_group(self, chat_id, user_id, message):
        chat_users = self.connections[chat_id]
        for user in chat_users:
            if user != user_id:
                await chat_users[user].send_text(message)

    async def connect(self, chat_id: int, user_id: str, websocket: WebSocket):
        await websocket.accept()

        if self.connections == {} or len(self.connections) == 0:
            self.connections[chat_id] = {}

        if self.connections.get(chat_id, None) is None:
            self.connections[chat_id] = {}

        self.connections[chat_id].update({user_id: websocket})

    def remove(self, chat_id: int, user_id: str):
        try:
            self.connections[chat_id].pop(user_id)

            # delete chat_room
            if len(self.connections[chat_id]) == 0:
                self.connections.pop(chat_id)
        except KeyError:
            pass


import jc
from connectors.core.connector import get_logger, ConnectorError
from .constants import LOGGER_NAME

logger = get_logger(LOGGER_NAME)

def convert(config, params):
    parser = params.get('parser')
    cmd_output = params.get('command_output')
    raw = params.get('raw', False)

    if not parser or not cmd_output:
        logger.exception('Missing required input')
        raise ConnectorError('Missing required input')

    parser = parser.replace('-', '_')

    if parser in jc.streaming_parser_mod_list():
        logger.exception(f"'{parser}' parser not valid. Streaming parsers are not supported.")
        raise ConnectorError(f"'{parser}' parser not valid. Streaming parsers are not supported.")

    if parser not in jc.standard_parser_mod_list():
        logger.exception(f"'{parser}' parser not valid.")
        raise ConnectorError(f"'{parser}' parser not valid.")

    parser_info = jc.parser_info(parser)
    logger.info(f"Selected parser: {parser_info}")

    return jc.parse(parser, cmd_output, raw=raw, quiet=True)


from __future__ import annotations

from typing import TypedDict

from movielog.reviews import serializer
from movielog.utils import export_tools, list_tools
from movielog.utils.logging import logger

StatGroup = TypedDict("StatGroup", {"reviewYear": str, "reviewsCreated": int})

def export() -> None:  # noqa: WPS210
    logger.log("==== Begin exporting {}...", "review stats")

    all_reviews = serializer.deserialize_all()

    stat_groups = [
        StatGroup(
            reviewYear="all",
            reviewsCreated=len(all_reviews),
        )
    ]

    reviews_by_year = list_tools.group_list_by_key(
        all_reviews, lambda review: str(review.date.year)
    )
    for year, reviews_for_year in reviews_by_year.items():
        stat_groups.append(
            StatGroup(
                reviewYear=str(year),
                reviewsCreated=len(reviews_for_year),
            )
        )

    export_tools.serialize_dicts_to_folder(
        dicts=stat_groups,
        folder_name="review_stats",
        filename_key=lambda stat_file: stat_file["reviewYear"],
    )


from __future__ import print_function, division  # requires Python >= 2.6

# numpy and scipy imports
import numpy as np
import math
from scipy.sparse import kron, identity
from scipy.sparse.linalg import eigsh  # Lanczos routine from ARPACK

from collections import namedtuple

####Initial parameter
#physical parameter
J = 1
Jz = 1

#number of states kept
Nstate = 10
#number of iterations
Nlength = 100

#exact solution
ExactEnergy = -math.log(2) + 0.25

Block = namedtuple("Block", ["length", "basis_size", "operator_dict"])
EnlargedBlock = namedtuple("EnlargedBlock", ["length", "basis_size", "operator_dict"])

def is_valid_block(block):
    for op in block.operator_dict.values():
        if op.shape[0] != block.basis_size or op.shape[1] != block.basis_size:
            return False
    return True

is_valid_enlarged_block = is_valid_block

# Model-specific code for the Heisenberg XXZ chain
model_d = 2  # single-site basis size

Sz1 = np.array([[0.5, 0], [0, -0.5]], dtype='d')  # single-site S^z
Sp1 = np.array([[0, 1], [0, 0]], dtype='d')  # single-site S^+

H1 = np.array([[0, 0], [0, 0]], dtype='d')  # single-site portion of H is zero

def H2(Sz1, Sp1, Sz2, Sp2):  # two-site part of H
    return (
        (J / 2) * (kron(Sp1, Sp2.conjugate().transpose()) + kron(Sp1.conjugate().transpose(), Sp2)) +
        Jz * kron(Sz1, Sz2)
    )

initial_block = Block(length=1, basis_size=model_d, operator_dict={
    "H": H1,
    "conn_Sz": Sz1,
    "conn_Sp": Sp1,
})

def enlarge_block(block):
    mblock = block.basis_size
    o = block.operator_dict

    enlarged_operator_dict = {
        "H": kron(o["H"], identity(model_d)) + kron(identity(mblock), H1) + H2(o["conn_Sz"], o["conn_Sp"], Sz1, Sp1),
        "conn_Sz": kron(identity(mblock), Sz1),
        "conn_Sp": kron(identity(mblock), Sp1),
    }

    return EnlargedBlock(length=(block.length + 1),
                         basis_size=(block.basis_size * model_d),
                         operator_dict=enlarged_operator_dict)

def rotate_and_truncate(operator, transformation_matrix):
    return transformation_matrix.conjugate().transpose().dot(operator.dot(transformation_matrix))

def single_dmrg_step(sys, env, m):
    assert is_valid_block(sys)
    assert is_valid_block(env)

    # Enlarge each block by a single site.
    sys_enl = enlarge_block(sys)

    if sys is env:  # no need to recalculate a second time
        env_enl = sys_enl
    else:
        env_enl = enlarge_block(env)

    assert is_valid_enlarged_block(sys_enl)
    assert is_valid_enlarged_block(env_enl)

    # Construct the full superblock Hamiltonian.
    m_sys_enl = sys_enl.basis_size
    m_env_enl = env_enl.basis_size
    sys_enl_op = sys_enl.operator_dict
    env_enl_op = env_enl.operator_dict
    superblock_hamiltonian = kron(sys_enl_op["H"], identity(m_env_enl)) + kron(identity(m_sys_enl), env_enl_op["H"]) + \
                             H2(sys_enl_op["conn_Sz"], sys_enl_op["conn_Sp"], env_enl_op["conn_Sz"], env_enl_op["conn_Sp"])

    energy, psi0 = eigsh(superblock_hamiltonian, k=1, which="SA")

    psi0 = psi0.reshape([sys_enl.basis_size, -1], order="C")
    rho = np.dot(psi0, psi0.conjugate().transpose())

    evals, evecs = np.linalg.eigh(rho)
    possible_eigenstates = []
    for eval, evec in zip(evals, evecs.transpose()):
        possible_eigenstates.append((eval, evec))
    possible_eigenstates.sort(reverse=True, key=lambda x: x[0])  # largest eigenvalue first

    my_m = min(len(possible_eigenstates), m)
    transformation_matrix = np.zeros((sys_enl.basis_size, my_m), dtype='d', order='F')
    for i, (eval, evec) in enumerate(possible_eigenstates[:my_m]):
        transformation_matrix[:, i] = evec

    truncation_error = 1 - sum([x[0] for x in possible_eigenstates[:my_m]])
    print("truncation error:", truncation_error)

    new_operator_dict = {}
    for name, op in sys_enl.operator_dict.items():
        new_operator_dict[name] = rotate_and_truncate(op, transformation_matrix)

    newblock = Block(length=sys_enl.length,
                     basis_size=my_m,
                     operator_dict=new_operator_dict)

    return newblock, energy

def graphic(sys, env):
    graphic = ("=" * sys.length) + "**" + ("-" * env.length)
    return graphic

def infinite_system_algorithm(L, m):
    # initial
    block = initial_block
    # Repeatedly enlarge the system by performing a single DMRG step, using a
    # reflection of the current block as the environment.
    while 2 * block.length < L:
        print("L =", block.length * 2 + 2)
        print(graphic(block, block))
        block, energy = single_dmrg_step(block, block, m=m)
        print("E/L =", energy / (block.length * 2))
        print("error=", ExactEnergy - energy / (block.length * 2))

if __name__ == "__main__":
    np.set_printoptions(precision=10, suppress=True, threshold=10000, linewidth=300)

    infinite_system_algorithm(L=Nlength, m=Nstate)


#!/usr/bin/python3
from brownie import web3, Attack
from scripts.deploy import deploy
from scripts.helpful_scripts import get_account
from colorama import Fore
from time import time

# * colours
green = Fore.GREEN
red = Fore.RED
blue = Fore.BLUE
magenta = Fore.MAGENTA
reset = Fore.RESET

def print_colour(solved):
    if solved:
        print(f"{blue}Is complete: {green}{True}{reset}")
    else:
        print(f"{blue}Is complete: {red}{solved}{reset}")

def hack(contract_address=None, attacker=None):
    if not contract_address:
        target, _ = deploy()
        _, attacker = get_account()
    else:
        print(f"{red}Something is wrong{reset}")
        exit(-1)

    print_colour(target.isComplete())

    # # Get the value of ans
    # block_number = web3.eth.blockNumber
    # block_hash = web3.eth.getBlock(block_number - 1)["hash"].hex()
    # timestamp = web3.eth.getBlock("latest")["timestamp"]

    # # print(block_hash + timestamp)

    # # Compute the answer using the same algorithm as the contract
    # ans_hash = web3.solidityKeccak(["bytes32", "uint32"], [block_hash, timestamp]).hex()
    # print(ans_hash)

    # answer = int("0x" + ans_hash[-2:], 0)
    # print(answer)
    # print(target.answer())

    attcking_contract = Attack.deploy(target.address, {"from": attacker})

    attcking_contract.hack({"from": attacker, "value": "1 ether"})

    # target.guess(answer, {"from": attacker, "value": "1 ether"}).wait(1)

    print_colour(target.isComplete())

    assert target.isComplete() == True

def main(contract_address=None):
    if contract_address:
        hack(contract_address, get_account())
    else:
        hack()

if __name__ == "__main__":
    main()


import art
import subprocess
clear = lambda: subprocess.call('cls||clear', shell=True)

print(art.logo)
list = []
def add_list(name,bid):

  new_dic = {
    "name": name,
    "bid": bid
  }
  list.append(new_dic)

def declare_winner(list):
  max_bid = 0
  for dic in list:
    if max_bid < int(dic["bid"]):
      max_bid = int(dic["bid"])
      winner = dic["name"]
  print(f"Winner of the auction is (The one who pays the most:D) {winner} with the price ${max_bid}")

while True:
  name = input("What is your name?: ")
  bid = input("What is your bid?: $")
  add_list(name, bid)
  while True:
    flag = input("Is there any one who want to bid too?(yes or no)").lower()
    if flag != "yes" and flag != "no":
      print("Invalid input! Let's try again")
      continue
    else:
      break
  if flag == "yes":
    clear()
    continue
  else:
    clear()
    break

declare_winner(list)


import streamlit as st
import altair as alt
import inspect
from vega_datasets import data

@st.experimental_memo
def get_chart_99637(use_container_width: bool):
    import altair as alt
    from vega_datasets import data

    airports = data.airports()
    states = alt.topo_feature(data.us_10m.url, feature='states')

    # US states background
    background = alt.Chart(states).mark_geoshape(
        fill='lightgray',
        stroke='white'
    ).properties(
        width=500,
        height=300
    ).project('albersUsa')

    # airport positions on background
    points = alt.Chart(airports).mark_circle(
        size=10,
        color='steelblue'
    ).encode(
        longitude='longitude:Q',
        latitude='latitude:Q',
        tooltip=['name', 'city', 'state']
    )

    chart = background + points

    tab1, tab2 = st.tabs(["Streamlit theme (default)", "Altair native theme"])

    with tab1:
        st.altair_chart(chart, theme="streamlit", use_container_width=True)
    with tab2:
        st.altair_chart(chart, theme=None, use_container_width=True)

try:
    st.expander("See code").code(inspect.getsource(get_chart_99637))
    get_chart_99637(use_container_width=True)
except Exception as e:
    st.exception(e)


from __future__ import annotations

from typing import Literal, Optional

from cognite.client import data_modeling as dm
from pydantic import Field

from ._core import DomainModel, DomainModelApply, TypeList, TypeApplyList

__all__ = ["CogPool", "CogPoolApply", "CogPoolList", "CogPoolApplyList", "CogPoolFields", "CogPoolTextFields"]

CogPoolTextFields = Literal["name", "time_unit", "timezone"]
CogPoolFields = Literal["max_price", "min_price", "name", "time_unit", "timezone"]

_COGPOOL_PROPERTIES_BY_FIELD = {
    "max_price": "maxPrice",
    "min_price": "minPrice",
    "name": "name",
    "time_unit": "timeUnit",
    "timezone": "timezone",
}

class CogPool(DomainModel):
    space: str = "market"
    max_price: Optional[float] = Field(None, alias="maxPrice")
    min_price: Optional[float] = Field(None, alias="minPrice")
    name: Optional[str] = None
    time_unit: Optional[str] = Field(None, alias="timeUnit")
    timezone: Optional[str] = None

    def as_apply(self) -> CogPoolApply:
        return CogPoolApply(
            external_id=self.external_id,
            max_price=self.max_price,
            min_price=self.min_price,
            name=self.name,
            time_unit=self.time_unit,
            timezone=self.timezone,
        )

class CogPoolApply(DomainModelApply):
    space: str = "market"
    max_price: Optional[float] = None
    min_price: Optional[float] = None
    name: Optional[str] = None
    time_unit: Optional[str] = None
    timezone: Optional[str] = None

    def _to_instances_apply(self, cache: set[str]) -> dm.InstancesApply:
        if self.external_id in cache:
            return dm.InstancesApply(dm.NodeApplyList([]), dm.EdgeApplyList([]))

        sources = []
        properties = {}
        if self.max_price is not None:
            properties["maxPrice"] = self.max_price
        if self.min_price is not None:
            properties["minPrice"] = self.min_price
        if self.time_unit is not None:
            properties["timeUnit"] = self.time_unit
        if properties:
            source = dm.NodeOrEdgeData(
                source=dm.ContainerId("market", "CogPool"),
                properties=properties,
            )
            sources.append(source)
        properties = {}
        if self.name is not None:
            properties["name"] = self.name
        if self.timezone is not None:
            properties["timezone"] = self.timezone
        if properties:
            source = dm.NodeOrEdgeData(
                source=dm.ContainerId("market", "Market"),
                properties=properties,
            )
            sources.append(source)
        if sources:
            this_node = dm.NodeApply(
                space=self.space,
                external_id=self.external_id,
                existing_version=self.existing_version,
                sources=sources,
            )
            nodes = [this_node]
        else:
            nodes = []

        edges = []
        cache.add(self.external_id)

        return dm.InstancesApply(dm.NodeApplyList(nodes), dm.EdgeApplyList(edges))

class CogPoolList(TypeList[CogPool]):
    _NODE = CogPool

    def as_apply(self) -> CogPoolApplyList:
        return CogPoolApplyList([node.as_apply() for node in self.data])

class CogPoolApplyList(TypeApplyList[CogPoolApply]):
    _NODE = CogPoolApply


import math
from enum import Enum
from numbers import Number
from typing import Tuple

import torch
import torch.nn.functional as F
import torch.distributed as dist
from torch.optim.lr_scheduler import CosineAnnealingLR, LinearLR
from accelerate import init_empty_weights

from transformers import (
    AutoModelForCausalLM,
    AutoConfig,
    mpu,
    ParallelOPTForCausalLM,
    ParallelGPTJForCausalLM,
    ParallelGPT2LMHeadModel,
    ParallelLlamaForCausalLM)

parallel_model_map = {
    "opt": ParallelOPTForCausalLM,
    "gpt2": ParallelGPT2LMHeadModel,
    "gptj": ParallelGPTJForCausalLM,
    "llama": ParallelLlamaForCausalLM
}

def get_entropy(gen_logits, inf_mask, mask, model_parallel=False):
    inf_mask = torch.isinf(gen_logits) | inf_mask
    if model_parallel:
        full_probs = mpu.parallel_softmax(gen_logits.float(), dim=-1)
        full_logprobs = mpu.parallel_log_softmax(gen_logits.float(), dim=-1)
        full_logprobs = full_logprobs.masked_fill(inf_mask, 0)
        ent = -mpu.parallel_sum(full_probs * full_logprobs, dim=-1)
    else:
        full_probs = F.softmax(gen_logits, dim=-1, dtype=torch.float32)
        full_logprobs = F.log_softmax(gen_logits, dim=-1, dtype=torch.float32)
        full_logprobs = full_logprobs.masked_fill(inf_mask, 0)
        ent = -torch.sum(full_probs * full_logprobs, dim=-1)
    ent = ent * mask
    return ent

def get_log_probs(logits, ids, mask, inf_mask=None, model_parallel=False):
    if model_parallel:
        logprobs = -mpu.parallel_logprobs(logits, ids)
        if inf_mask is not None:
            gathered_inf_mask = mpu.parallel_gather(inf_mask, -1, ids.unsqueeze(-1)).squeeze(-1)
            logprobs = logprobs.masked_fill(gathered_inf_mask, -float("inf"))
        # logprobs = mpu.parallel_log_softmax(logits.float(), dim=-1)
        # if inf_mask is not None:
        #     logprobs = logprobs.masked_fill(inf_mask, -float("inf"))
        # logprobs = mpu.parallel_gather(logprobs, -1, ids.unsqueeze(-1)).squeeze(-1)
    else:
        logprobs = F.log_softmax(logits, dim=-1)
        if inf_mask is not None:
            logprobs = logprobs.masked_fill(inf_mask, -float("inf"))
        logprobs = torch.gather(logprobs, dim=-1, index=ids.unsqueeze(-1)).squeeze(-1)
    logprobs = logprobs.masked_fill(~(mask.bool()), 0)

    # we ensure that the selected logprobs are not inf or nan
    assert all((~torch.isinf(logprobs.view(-1))) & (~torch.isnan(logprobs.view(-1))))

    return logprobs

def get_x_entropy(logits_1, logits_2, inf_mask, mask, model_parallel=False):
    inf_mask = torch.isinf(logits_1) | torch.isinf(logits_2) | inf_mask
    if model_parallel:
        full_probs = mpu.parallel_softmax(logits_1.float(), dim=-1)
        full_logprobs = mpu.parallel_log_softmax(logits_2.float(), dim=-1)
        full_logprobs = full_logprobs.masked_fill(inf_mask, 0)
        xent = -mpu.parallel_sum(full_probs * full_logprobs, dim=-1)
    else:
        full_probs = F.softmax(logits_1, dim=-1, dtype=torch.float32)
        full_logprobs = F.log_softmax(logits_2, dim=-1, dtype=torch.float32)
        full_logprobs = full_logprobs.masked_fill(inf_mask, 0)
        xent = -torch.sum(full_probs * full_logprobs, dim=-1)
    xent = xent * mask
    return xent

def get_rev_kl(log_p, log_q, mask):
    log_ratio = (log_p - log_q) * mask
    kl = log_ratio.float().exp() - 1 - log_ratio
    return kl

def get_global_statistics(xs: torch.Tensor) -> Tuple[float, float, int]:
    """
    Computes element-wise mean and variance of the tensor across processes
    """
    sum_and_count = torch.tensor([xs.sum(), xs.numel()], device=xs.device)
    dist.all_reduce(sum_and_count, dist.ReduceOp.SUM)
    global_sum, count = sum_and_count
    global_mean = global_sum / count

    sum_var = torch.sum((xs - global_mean) ** 2)
    dist.all_reduce(sum_var, dist.ReduceOp.SUM)
    global_var = sum_var / count
    return global_mean, global_var, count

def whiten(xs: torch.Tensor, shift_mean=True, distributed=True) -> torch.Tensor:
    """Whitens values"""
    if distributed and dist.is_initialized():
        mean, var, _ = get_global_statistics(xs)
    else:
        var, mean = torch.var_mean(xs)

    whitened = (xs - mean) * torch.rsqrt(var + 1e-8)
    if not shift_mean:
        whitened += mean
    return whitened

def significant(x: Number, ndigits=2) -> Number:
    """
    Cut the number up to its `ndigits` after the most significant
    """
    if isinstance(x, torch.Tensor):
        x = x.item()

    if not isinstance(x, Number) or x == 0:
        return x

    return round(x, ndigits - int(math.floor(math.log10(abs(x)))))

class OptimizerName(str, Enum):
    """Supported optimizer names"""

    ADAM: str = "adam"
    ADAMW: str = "adamw"
    ADAM_8BIT_BNB: str = "adam_8bit_bnb"
    ADAMW_8BIT_BNB: str = "adamw_8bit_bnb"
    SGD: str = "sgd"

def get_optimizer_class(name: OptimizerName):
    """
    Returns the optimizer class with the given name

    Args:
        name (str): Name of the optimizer as found in `OptimizerNames`
    """
    if name == OptimizerName.ADAM:
        return torch.optim.Adam
    if name == OptimizerName.ADAMW:
        return torch.optim.AdamW
    if name == OptimizerName.SGD.value:
        return torch.optim.SGD
    supported_optimizers = [o.value for o in OptimizerName]
    raise ValueError(
        f"`{name}` is not a supported optimizer. "
        f"Supported optimizers are: {supported_optimizers}"
    )

class SchedulerName(str, Enum):
    """Supported scheduler names"""

    COSINE_ANNEALING = "cosine_annealing"
    LINEAR = "linear"

def get_scheduler_class(name: SchedulerName):
    """
    Returns the scheduler class with the given name
    """
    if name == SchedulerName.COSINE_ANNEALING:
        return CosineAnnealingLR
    if name == SchedulerName.LINEAR:
        return LinearLR
    supported_schedulers = [s.value for s in SchedulerName]
    raise ValueError(
        f"`{name}` is not a supported scheduler. "
        f"Supported schedulers are: {supported_schedulers}"
    )


import json
import boto3
from datetime import datetime

# get ec2 metrics
def get_metric_ec2(namespace, metricname, client):
    response = client.get_metric_statistics(
            Namespace=namespace,
            MetricName=metricname,
            Dimensions=[
                {
                    'Name': 'InstanceId',
                    'Value': 'i-092988b42c6dea77e'
                }
            ],
            StartTime=datetime(2022, 5, 21),
            EndTime=datetime.now(),
            Period=3600,
            Statistics=[
                'SampleCount', 'Average', 'Sum', 'Minimum', 'Maximum'
            ]
        )
    return response

# get s3  metrics
def get_metric_s3(namespace, metricname, storageType, client):
    response = client.get_metric_statistics(
            Namespace=namespace,
            MetricName=metricname,
            Dimensions=[
                {
                    'Name': 'StorageType',
                    'Value': storageType
                },
                {
                    'Name': 'BucketName',
                    'Value': 'tdg-s3-bucket'
                }
            ],
            StartTime=datetime(2022, 5, 21),
            EndTime=datetime.now(),
            Period=3600,
            Statistics=[
                'SampleCount', 'Average', 'Sum', 'Minimum', 'Maximum'
            ]
        )
    return response

def lambda_handler(event, context):

    headers = {
        'Access-Control-Allow-Origin': '*'
    }

    if "headers" in event and "referer" in event["headers"] and "18.215.185.124" in event["headers"]["referer"]:

        path = event["rawPath"].split("/")
        namespace = "AWS/" + path[1]
        metricname = path[2]

        if namespace == "AWS/EC2":
            client = boto3.client('cloudwatch', region_name='us-east-1')
            return {
                'statusCode': 200,
                'headers': headers,
                'body': json.dumps(get_metric_ec2(namespace, metricname, client), default=str)
            }
        else:
            client = boto3.client('cloudwatch', region_name='eu-west-3')
            storageType = path[3]
            return {
                'statusCode': 200,
                'headers': headers,
                'body': json.dumps(get_metric_s3(namespace, metricname, storageType, client), default=str)
            }

    else:
        return {
            'statusCode': 403,
            'headers': headers,
            'body': json.dumps("Access denied")
        }


import time
from datetime import datetime
from models.cancel_queue import CancelQueue
from models.last_updated_tracker import LastUpdatedTracker
from sqlalchemy import insert, update, select
from .get_orders import get_orders
from .get_orders_items import get_orders_items
from ... import my_logger, SP_EXCEPTIONS, pacific_zone
from ...helpers.db_config import db_conn

conn = db_conn()

"""
Hitting Sp Api Orders & Orders Item Api to fetch user's orders information
And Updating the Amazon Orders Cancel Queue Based on order's is_buyer_requested_cancellation status
"""

def process_cancel_orders(posted_after, posted_before):
    try:
        my_logger.info(
            f"posted_after {posted_after} | posted_before {posted_before} | Datetime Now in PacificTime {(datetime.now(pacific_zone)).isoformat()}"
        )
        time.sleep(3)
        orders = get_orders(
            posted_after,
            posted_before,
            marketplace_ids=["ATVPDKIKX0DER"],
            order_statuses=["PartiallyShipped", "Unshipped"],
            fulfillment_channels=["MFN"]
        )
        if len(orders) > 0:
            my_logger.info(f"orders length {len(orders)}")
            last_purchase_date = posted_before
            for order in orders:
                time.sleep(3)
                order_items = get_orders_items(
                    order_id=order.get("AmazonOrderId"))
                if order_items:
                    my_logger.info(f"order_id {order.get('AmazonOrderId')} \n order_items {len(order_items)}")
                    for order_item in order_items:
                        is_buyer_requested_cancellation = order_item.get("BuyerRequestedCancel").get(
                            "IsBuyerRequestedCancel"
                        ) if order_item.get("BuyerRequestedCancel") else "false"
                        buyer_cancellation_reason = order_item.get("BuyerRequestedCancel").get(
                            "BuyerCancelReason"
                        ) if order_item.get("BuyerRequestedCancel") else None
                        # Updating the Cancel Queue
                        if is_buyer_requested_cancellation != "false":
                            cqs = conn.query(CancelQueue).where(
                                CancelQueue.order_number == order.get('AmazonOrderId'),
                                CancelQueue.order_item_id == order_item.get('OrderItemId'),
                                CancelQueue.sku == order_item.get('SellerSKU')
                            ).first()
                            if not cqs:
                                my_logger.info(f"Sku {order_item.get('SellerSKU','')} is cancelled and saving in db.")
                                cq = CancelQueue(order.get('AmazonOrderId',''))
                                cq.order_item_id = order_item.get('OrderItemId','')
                                cq.sku = order_item.get('SellerSKU','')
                                cq.buyer_cancel_date = datetime.now(pacific_zone)
                                cq.purchase_date = datetime.strptime(order.get('PurchaseDate'), "%Y-%m-%dT%H:%M:%SZ")
                                cq.last_processed_at = datetime.now(pacific_zone)
                                cq.desktopshipper_cancel = 0
                                cq.skubana_cancel = 0
                                cq.amazon_cancel = 0
                                cq.buyer_cancellation_reason = buyer_cancellation_reason
                                cq.processing_status = 'PENDING'
                                cq.created_at = datetime.now(pacific_zone)
                                cq.updated_at = datetime.now(pacific_zone)
                                conn.merge(cq)
                                conn.commit()
                            else:
                                my_logger.debug(
                                    f"Sku {order_item.get('SellerSKU','')} is already in the queue, skipping."
                                )
                last_purchase_date = order.get('PurchaseDate')

            # Updating Order Tracker
            stmt = (
                select(LastUpdatedTracker)
                .where(LastUpdatedTracker.tracker_type == 'orders')
            )
            last_tracker = conn.execute(stmt).first()
            if not last_tracker:
                stmt = (
                    insert(LastUpdatedTracker)
                    .values(
                        tracker_type="orders",
                        last_updated_at=datetime.strptime(last_purchase_date, "%Y-%m-%dT%H:%M:%SZ"),
                        created_at=datetime.now(pacific_zone),
                        updated_at=datetime.now(pacific_zone)
                    )
                )
                conn.execute(stmt)
                conn.commit()
            else:
                stmt = (
                    update(LastUpdatedTracker)
                    .where(LastUpdatedTracker.tracker_type == "orders")
                    .values(
                        last_updated_at=datetime.strptime(last_purchase_date, "%Y-%m-%dT%H:%M:%SZ"),
                        updated_at=datetime.now(pacific_zone)
                    )
                )
                conn.execute(stmt)
                conn.commit()
            return True
        else:
            my_logger.error(f"No Orders")
            return False
    except SP_EXCEPTIONS as e:
        my_logger.error(str(e))
        orders = None
        raise e


import cv2

filepath = "vtest.avi"
# cap = cv2.VideoCapture(filepath)
# Webカメラを使うときはこちら

class Capture:
    """
    Captureクラスは動画の動体検出をサポートするメソッドを提供します。
    """
    def __init__(self,movie_path:str|None=None,device:int=0) -> None:
        """initialize capture mode. and configs."""

        if not movie_path:
            self.cap = cv2.VideoCapture(device)
        else:
            try:
                if movie_path[-3:] != 'mp4':
                    raise "Non-mp4 files are not supported."
                self.cap = cv2.VideoCapture(movie_path)
            except:
                raise """cannot capture video. not found video.
                        arrow to capture 'mp4'.
                        """

        self.avg = None

    def motion_detection(self,reseption:float=0.8):
        while True:
            # 1フレームずつ取得する。
            ret, frame = self.cap.read()
            if not ret:
                break

            # グレースケールに変換
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # 比較用のフレームを取得する
            if self.avg is None:
                self.avg = gray.copy().astype("float")
                continue

            # 現在のフレームと移動平均との差を計算
            cv2.accumulateWeighted(gray, self.avg, reseption)
            frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(self.avg))

            # デルタ画像を閾値処理を行う
            thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
            # 画像の閾値に輪郭線を入れる
            contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            frame = cv2.drawContours(frame, contours, -1, (0, 255, 0), 3)

            # 結果を出力
            cv2.imshow("Frame", frame)
            key = cv2.waitKey(30)
            if key == 27:
                break

        self.cap.release()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    cc = Capture().motion_detection(reseption=0.5)


import sys
import rdkit
from argparse import ArgumentParser
from rdkit import Chem, DataStructs
from rdkit.Chem import AllChem

import pandas as pd

pred_mols = pd.read_csv('',
                        header=None).values.reshape(-1)

ref_path = 'actives.txt'

with open(ref_path) as f:
    next(f)
    true_mols = [line.split(',')[0] for line in f]
print('number of active reference', len(true_mols))

true_mols = [Chem.MolFromSmiles(s) for s in true_mols]
true_mols = [x for x in true_mols if x is not None]
true_fps = [AllChem.GetMorganFingerprintAsBitVect(x, 3, 2048) for x in true_mols]

pred_mols = [Chem.MolFromSmiles(s) for s in pred_mols]
pred_mols = [x for x in pred_mols if x is not None]
pred_fps = [AllChem.GetMorganFingerprintAsBitVect(x, 3, 2048) for x in pred_mols]

fraction_similar = 0

sim_distribution = []
for i in range(len(pred_fps)):
    sims = DataStructs.BulkTanimotoSimilarity(pred_fps[i], true_fps)

    if max(sims) >= 0.4:
        fraction_similar += 1
    sim_distribution.append(max(sims))

print('novelty:', 1 - fraction_similar / len(pred_mols))

similarity = 0
for i in range(len(pred_fps)):
    sims = DataStructs.BulkTanimotoSimilarity(pred_fps[i], pred_fps[:i])
    similarity += sum(sims)

n = len(pred_fps)
n_pairs = n * (n - 1) / 2
diversity = 1 - similarity / n_pairs
print('diversity:', diversity)


from django.shortcuts import render
from django.http import HttpResponse
from django.template import Template,Context
# Create your views here.

def suma(request,num1,num2):
	res = num1+num2
	contenido = """

El resultado es: %s

	 """ %res
	return HttpResponse(contenido)

def prueba1(request):
	externo = open("C:/Users/erick/OneDrive/Escritorio/Trabajos Django/ESYG/templates/prueba.html")
	plantilla = Template(externo.read())
	externo.close()
	ctx = Context()
	contenido = plantilla.render(ctx)
	return HttpResponse(contenido)

def prueba2(request):
	return render(request,"prueba.html")


import pandas as pd
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
from numpy.random import rand
from random import sample, shuffle
plt.close()
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
from itertools import permutations
import matplotlib.pyplot as plt

# The training data is two points x^{n-1}, x^n, separated by dt.
# We will integrate the equations with dt, and choose Nt random points
# per solution, and choose three points (the third point is the label for
# the Euler algorithm.

# solution to the differential equation
t1 = 0.
t2 = 2.*np.arccos(-1)

# Equivalent to
def circle (t, s):
    x = -s[1]
    y =  s[0]
    return x, y

# Equivalent to def circle above
#circle = lambda t, s: np.array([-s[1], s[0]])

F = circle

# Time interval
t0, tmax = 0, 6.28

# Generate 20 initial condition curves: x0, y0 where x0 \in [0, 5], y0 \in [0, 5]
# This will generate 20 circles with different radii
# Also generate 20 array t_eval, with sorted random numbers between t0 and tmax.
# Each list will contain Nt values.

Nruns = 150   # number of runs
x0max = 1
y0max = 1
dt = .02

# Alternative approach:
# Generate a collection of points (x[n-1], x[n], x[n+1] (for initial conditions)
x0 = x0max * rand(Nruns)
y0 = y0max * rand(Nruns)

# Only work with a single run and validate on the same run (easier problem)
#x0 = x0max * np.ones([Nruns])
#y0 = y0max * np.ones([Nruns])
t0 = t1 + (t2-t1) * rand(Nruns)  # starting times, random between [0, 6.28]
#x0 = np.asarray([x0max])

sols = {}
y_list = []
for i in range(Nruns):
    # solution always evaluated at the same points
    t_eval = np.linspace(t0[i], t0[i]+2*dt, 3)
    # x[0][i] and y0[i] are always 1
    # What is the second argument of solve_ivp?  Min/max t for simulation
    # t_eval are three time points: t0[i], t0[i]+dt, t0[i]+2*dt
    # Initial Conditions (I.C.): x0[i], y[0][i] for run i
    sol = sols[i] = solve_ivp(F, [t0[i], t0[i]+2*dt], [x0[i], y0[i]], t_eval=t_eval)
    print(f"({i}) t_eval: ", t_eval)
    print(f"({i}) t[i]: ", t0[i])
    print(f"({i}) x0[i]: ", x0[i])
    print(f"({i}) y0[i]: ", y0[i])
    print()
    #print(sol.t)
    plt.plot(sol.t, sol.y[0], color='r', lw=1)
    plt.plot(sol.t, sol.y[1], color='b', lw=1)
    #plt.scatter(sol.t, sol.y[0], color='r')
    #plt.scatter(sol.t, sol.y[1], color='b')
    plt.xlabel('t')
    #plt.ylabel('sample points')
plt.show()
plt.close()
#quit()

# select 100 triplets (t[2*i], x[2*i], x[2*i+1]) where x[2i] is the solution at t^n
# and x[2i+1] is the solution at t^{n+1}

# Even numbers (the odd numbers are 'dt' away )
even = list(range(0, len(t_eval), 2))
Nsamples_per_run = 10
x0_seq3 = []
x1_seq3 = []
t_seq3 = []
for i in range(Nruns):
    sol = sols[i]
    t_seq3.append( list(sol.t) )
    x0_seq3.append( list(sol.y[0]) )
    x1_seq3.append( list(sol.y[1]) )

#-----------------------------------------------------------------------------------
# I now have the training data on which to base the neural network:
#   times:  (t_i^n, t_i^{n+1}), i=0, 1, Nruns*Nsamples_per_run
#   x0_pairs: (x[0]_i^n, x[0]_i^{n+1}), i=0, 1, Nruns*Nsamples_per_run
#   x1_pairs: (x[1]_i^n, x[1]_i^{n+1}), i=0, 1, Nruns*Nsamples_per_run

class NN(nn.Module):
    """Simple neural network accepting two features as input and returning a single output

    In the context of PINNs, the neural network is used as universal function approximator
    to approximate the solution of the differential equation
    """
    def __init__(self, num_hidden: int, dim_hidden: int, act=nn.Tanh()):
        super(NN, self).__init__()

        # input t into the network
        # There are two inputs and two outputs
        # The inputs are x^{n-1}, x^{n} where x \in R^2
        self.layer_in = nn.Linear(4, dim_hidden)
        self.layer_out = nn.Linear(dim_hidden, 2)

        num_middle = num_hidden
        self.middle_layers = nn.ModuleList(
            [nn.Linear(dim_hidden, dim_hidden) for _ in range(num_middle)]
        )
        self.act = act

    def forward(self, t):
        out = self.act(self.layer_in(t))
        for layer in self.middle_layers:
            out = self.act(layer(out))
        out = self.layer_out(out)
        return out

# Create DataSet and DataLoader
# https://pytorch.org/tutorials/beginner/data_loading_tutorial.html

class myDataset(Dataset):
    def __init__(self, t_seq, x0_seq, x1_seq):
        super(myDataset, self).__init__()
        self.t_seq = torch.tensor(t_seq, dtype=torch.float32)
        self.x0_seq = torch.tensor(x0_seq, dtype=torch.float32)
        self.x1_seq = torch.tensor(x1_seq, dtype=torch.float32)

    def __len__(self):
        return len(self.t_seq // 3)

    def __getitem__(self, i):
        # return 3 sequences of size 3
        return torch.tensor([*self.t_seq[i], *self.x0_seq[i], *self.x1_seq[i]])

dataset = myDataset(t_seq3, x0_seq3, x1_seq3)
dataloader = DataLoader(dataset, batch_size=4, shuffle=True, drop_last=True)

# Armed with the dataset and dataloader, I can implement the neural network
# For each batch, pass it through the NN, and output an approximation of d((x,y))/dt
# Apply Euler to obtain the solution at the next time step. Define MSE loss, and do back propagation
# The samples need not be in temporal order

learning_rate = 1.e-2
loss_fn = torch.nn.MSELoss(reduction='mean')
model = NN(num_hidden=2, dim_hidden=30, act=nn.ReLU())
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)

# Loss function is hardly decreasing. WHY?
nb_epochs = 400
losses = []
epoch_losses = []
for epoch in range(nb_epochs):
    # shuffle each epoch?
    epoch_loss = 0
    count = 0
    for sample in dataloader:
        optimizer.zero_grad()  # correct place?
        # Will the order be important? No if fully connected network
        s = sample[:, [3,4,6,7]]  # x[0]^n, x[1]^n
        dsdt_approx = model(s)
        try:
            snew = s[:,[1,3]] + dt * dsdt_approx  # One step Euler
        except:
            #print("break: size s: ", s.shape)
            # incorrect batch size (not sure actually)
            quit()
            break
        s_exact = sample[:, [5,8]]
        s_approx = snew
        loss = loss_fn(snew, s_exact)
        epoch_loss += loss.item()
        loss.backward()
        losses.append(loss)
        optimizer.step()
        count += 1
    epoch_losses.append(epoch_loss / count)
print("epoch_losses: ", len(epoch_losses), epoch_losses)

# With a trained NN, choose some initial condition and draw a trajectory (x, y)

# The network is not training when I input sequences into the NN!

dt1 = dt #* 0.5

sv0 = torch.tensor([1., 0.])  # at t = 0
# The exact solution is x = cos(t), y = sin(t)
sv1 = torch.tensor([np.cos(dt), np.sin(dt1)])
# sol.y[0] is y[0], sol.y[1] is y[1]
# sv: first the point at t^n, followed by the point at t^{n+1}
sv = torch.tensor([*sv0, *sv1], dtype=torch.float32)   # y[0] and y[1] are interleaved (y[0][0], y[1][0], y[0][1], y[1][1])
x_lst = []
y_lst = []
t_lst = []

print("sv: ", sv)
# The model requires three points. This means I must find a way to compute the first two points. I will consider the exact solution.
# In reality, I could apply two steps of a Runga-Kutta algorithms, or several steps of Euler with smaller time step.

# Save initial condition (x,y,t) (same as y[0], y[1], t)
x_lst.append(sv0[0].item())
y_lst.append(sv0[1].item())
t_lst.append(0)
print("x_lst= ", x_lst)

sv_nm1 = sv0.clone()
sv_n   = sv1.clone()
sv = torch.tensor([*sv_nm1, *sv_n], dtype=torch.float32)   # y[0] and y[1] are interleaved (y[0][0], y[1][0], y[0][1], y[1][1])

# The method is unstable
for i in range(600):
    # My sv update is incorrect
    dydt = model(sv)  # model takes in first two points. How is that done?
    #sv_new = sv[2:] + dt1 * dydt
    sv_np1 = sv_n + dt1 * dydt
    sv_nm1 = sv_n.clone()  # Inefficient, but clear, implementation
    sv_n   = sv_np1.clone()
    sv = torch.tensor([*sv_nm1, *sv_n], dtype=torch.float32)
    x_lst.append(sv_np1[0].item())
    y_lst.append(sv_np1[1].item())
    t_lst.append((i+1)*dt1)

x_lst = np.asarray(x_lst)
y_lst = np.asarray(y_lst)
t_lst = np.asarray(t_lst)

plt.figure(figsize=(10, 6))
plt.plot(t_lst, x_lst, label='$x_0$')
plt.plot(t_lst, y_lst, label='$x_1$')
plt.xlabel('t')
plt.ylabel('$x_0$, $x_1$')
radius = np.sqrt(x_lst**2 + y_lst**2)
plt.plot(t_lst, radius, label='radius')
plt.legend()
plt.grid()
plt.show()
plt.close()


"""
This module contains the implementation of the Classes: BaseEnvironment, BaseWrapper, BaseObservationWrapper, BaseActionWrapper,
BaseRewardWrapper, BaseGridWorld, BaseCarOnHill, BaseCartPole, BaseInvertedPendulum and LQG.

Then there are the Mujoco Environments Wrappers: BaseMujoco, BaseHalfCheetah, BaseAnt, BaseHopper, BaseHumanoid, BaseSwimmer,
BaseWalker2d

The Class BaseEnvironment inherits from the Class AbstractUnit and from ABC.

The Class BaseEnvironment is an abstract Class used as base class for all types of environments.

The Class BaseWrapper is used as generic wrapper Class. The Classes BaseObservationWrapper, BaseActionWrapper and
BaseRewardWrapper are abstract Classes, that when sub-classed can be used to wrap something specific of an environment.

The Classes BaseGridWorld, BaseCarOnHill, BaseCartPole, BaseInvertedPendulum simply wrap the corresponding Classes of MushroomRL
but they are changed so to inherit from the Class BaseEnvironment so that they can be used in this library.

The Class LQG is a re-adaptation of code copied from: https://github.com/T3p/potion/blob/master/potion/envs/lq.py

The Classes BaseHalfCheetah, BaseAnt, BaseHopper, BaseHumanoid, BaseSwimmer and BaseWalker2d inherit from the Class BaseMujoco
and are simply wrappers of the corresponding OpenAI gym Classes.
"""

from abc import ABC, abstractmethod
import numpy as np
import scipy
import math

from gym.envs.mujoco.half_cheetah_v3 import HalfCheetahEnv
from gym.envs.mujoco.ant_v3 import AntEnv
from gym.envs.mujoco.hopper_v3 import HopperEnv
from gym.envs.mujoco.humanoid_v3 import HumanoidEnv
from gym.envs.mujoco.swimmer_v3 import SwimmerEnv
from gym.envs.mujoco.walker2d_v3 import Walker2dEnv

from mushroom_rl.utils.spaces import Box
from mushroom_rl.core.environment import MDPInfo
from mushroom_rl.environments import GridWorld, CarOnHill, CartPole, InvertedPendulum

from ARLO.abstract_unit.abstract_unit import AbstractUnit

class BaseEnvironment(AbstractUnit, ABC):
    """
    This is the base environment Class based on the OpenAI Gym class. Part of this class is a re-adaptation of code copied from:
    -OpenAI gym:
    cf. https://github.com/openai/gym/blob/master/gym/core.py

    -MushroomRL:
    cf. https://github.com/MushroomRL/mushroom-rl/blob/dev/mushroom_rl/core/environment.py

    This can be sub-classed by the user to create their own specific environment.

    You must use environment spaces from  MushroomRL (e.g: Box, Discrete).

    This Class is an abstract Class and it inherits from the Class AbstractUnit.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process'):
        """
        Non-Parameters Members
        ----------------------
        observation_space: This must be a space from MushroomRL like Box, Discrete.

        action_space: This must be a space from MushroomRL like Box, Discrete.

        gamma: This is the value of the gamma of the MDP, that is in this Class.

        horizon: This is the horizon of the MDP, that is in this Class.

        The other parameters and non-parameters members are described in the Class AbstractUnit.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity, n_jobs=n_jobs, job_type=job_type)

        self.action_space = None
        self.observation_space = None
        self.gamma = None
        self.horizon = None

    def __repr__(self):
         return 'BaseEnvironment('+'observation_space='+str(self.observation_space)+', action_space='+str(self.action_space)\
                +', gamma='+str(self.gamma)+', horizon='+str(self.horizon)+', obj_name='+str(self.obj_name)\
                +', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)+', log_mode='+str(self.log_mode)\
                +', checkpoint_log_path='+str(self.checkpoint_log_path)+', verbosity='+str(self.verbosity)\
                +', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)+', logger='+str(self.logger)+')'

    @abstractmethod
    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        raise NotImplementedError

    @abstractmethod
    def reset(self, state=None):
        """
        Method used to reset the environment.
        """

        raise NotImplementedError

    @abstractmethod
    def render(self, mode='human'):
        """
        Method used to render an environment.
        """

        raise NotImplementedError

    def close(self):
        """
        Method used to perform necessary cleanup.
        """

        pass

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return

    def stop(self):
        """
        Method used to stop an MDP. This is needed for backward compatibility with MushroomRL.
        """

        pass

    def unwrapped(self):
        """
        This method completely unwraps the env contained in the class.

        Returns:
        ----------
        self: The base non-wrapped env instance
        """

        return self

    @property
    def info(self):
        """
        Property method that constructs an object of Class mushroom_rl.environment.MDPInfo
        """

        #each block must modify the observation_space and action_space according to the transformation they did
        return MDPInfo(observation_space=self.observation_space, action_space=self.action_space, gamma=self.gamma,
                       horizon=self.horizon)

    def _sample_from_box(self, space):
        """
        Parameters
        ----------
        space: The space to which to sample from. It must be an object of Class Box.

        This method was copied from OpenAI gym: cf. https://github.com/openai/gym/blob/master/gym/spaces/box.py

        Generates a single random sample inside of the Box. In creating a sample of the box, each coordinate is sampled according
        to the form of the interval:
        * [a, b] : uniform distribution
        * [a, inf) : shifted exponential distribution
        * (-inf, b] : shifted negative exponential distribution
        * (-inf, inf) : normal distribution
        """

        if(not isinstance(space, Box)):
            exc_msg = 'The method \'_sample_from_box\' can only be applied on \'Box\' spaces!'
            self.logger.exception(msg=exc_msg)
            raise TypeError(exc_msg)
        else:
            bounded_below = -np.inf < space.low
            bounded_above = np.inf > space.high

            if(space.high.dtype.kind == 'f'):
                high = space.high
            else:
                high = space.high.astype('int64') + 1

            sample = np.empty(space.shape)

            #Masking arrays which classify the coordinates according to interval type
            unbounded = ~bounded_below & ~bounded_above
            upp_bounded = ~bounded_below & bounded_above
            low_bounded = bounded_below & ~bounded_above
            bounded = bounded_below & bounded_above

            #Vectorized sampling by interval type
            sample[unbounded] = self.local_prng.normal(size=unbounded[unbounded].shape)

            sample[low_bounded] = self.local_prng.exponential(size=low_bounded[low_bounded].shape) + space.low[low_bounded]

            sample[upp_bounded] = -self.local_prng.exponential(size=upp_bounded[upp_bounded].shape) + space.high[upp_bounded]

            sample[bounded] = self.local_prng.uniform(low=space.low[bounded], high=high[bounded], size=bounded[bounded].shape)

            if space.high.dtype.kind == 'i':
                sample = np.floor(sample)

            sample = sample.astype(space.high.dtype)

            return sample

    def sample_from_box_action_space(self):
        """
        This method samples from a Box Action Space.
        """

        sample = self._sample_from_box(space=self.action_space)

        return sample

    def sample_from_box_observation_space(self):
        """
        This method samples from a Box Observation Space.
        """

        sample = self._sample_from_box(space=self.observation_space)

        return sample

    def set_params(self, params_dict):
        """
        Parameters
        ----------
        params_dict: This is a dictionary containing the parameters of the environment and their new value.
        """

        if(isinstance(params_dict, dict)):
            for tmp_key in list(params_dict.keys()):
                if(hasattr(self, tmp_key)):
                    setattr(self, tmp_key, params_dict[tmp_key])
                else:
                    exc_msg = 'The environment does not have the member \''+str(tmp_key)+'\'!'
                    self.logger.exception(msg=exc_msg)
                    raise AttributeError(exc_msg)
        else:
            exc_msg = '\'params_dict\' must be a dictionary!'
            self.logger.exception(msg=exc_msg)
            raise TypeError(exc_msg)

    def get_params(self, params_names):
        """
        Parameters
        ----------
        params_names: This is a list of strings and they represent the names of the parameters of which we want to get the value.

        Returns
        -------
        params_dict: This is a dictionary with as keys the strings in the list params_names and as values the current value of
                     such parameter.
        """

        params_dict = {}

        for tmp_key in params_names:
            if(hasattr(self, tmp_key)):
                params_dict.update({tmp_key: getattr(self, tmp_key)})
            else:
                exc_msg = 'The environment does not have the member \''+str(tmp_key)+'\'!'
                self.logger.exception(msg=exc_msg)
                raise AttributeError(exc_msg)

        return params_dict

class BaseWrapper(BaseEnvironment):
    """
    This is the base wrapper Class based on the OpenAI Wrapper Class. Part of this class is a re-adaptation of code copied from
    OpenAI gym: https://github.com/openai/gym/blob/master/gym/core.py

    This is used as base Class for observation wrappers, action wrappers and reward wrappers.

    Other kind of wrappers can be created inheriting from this Class.
    """

    def __init__(self, env, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process'):
        """
        Parameters
        ----------
        env: This is the environment that needs to be wrapped. It must be an object of a Class inheriting from the Class
             BaseEnvironment.

        The other parameters and non-parameters members are described in the Class BaseEnvironment.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity, n_jobs=n_jobs, job_type=job_type)

        self.env = env

        #The env must be an object of a Class inheriting from the Class BaseEnvironment
        if(not isinstance(self.env, BaseEnvironment)):
            exc_msg = 'The \'env\' must be an object of a Class inheriting from the Class BaseEnvironment!'
            self.logger.exception(msg=exc_msg)
            raise TypeError(exc_msg)

        self.action_space = self.env.action_space
        self.observation_space = self.env.observation_space
        self.gamma = self.env.gamma
        self.horizon = self.env.horizon

    def __repr__(self):
         return str(self.__class__.__name__)+'('+'env='+str(self.env)+', observation_space='+str(self.observation_space)\
                +', action_space='+str(self.action_space)+', gamma='+str(self.gamma)+', horizon='+str(self.horizon)\
                +', obj_name='+str(self.obj_name)+', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)\
                +', log_mode='+str(self.log_mode)+', checkpoint_log_path='+str(self.checkpoint_log_path)\
                +', verbosity='+str(self.verbosity)+', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)\
                +', logger='+str(self.logger)+')'

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        return self.env.step(action=action)

    def reset(self, state=None):
        """
        Method used to reset the environment.
        """

        return self.env.reset(state=state)

    def render(self, mode='human'):
        """
        Method used to render an environment.
        """

        return self.env.render(mode=mode)

    def close(self):
        """
        Method used to perform necessary cleanup.
        """

        return self.env.close()

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return self.env.seed(seed=seed)

    def stop(self):
        return self.env.stop()

    def unwrapped(self):
        """
        Method to unwrap the environment.
        """

        return self.env.unwrapped()

class BaseObservationWrapper(BaseWrapper):
    """
    Part of this Class is a readaptation of code copied from OpenAI gym: https://github.com/openai/gym/blob/master/gym/core.py

    To create an observation wrapper you must create a new Class inheriting from this Class. You must override the observation
    method, and in the __init__ you must:
        -Call the __init__ of BaseWrapper via: super().__init__(env)
        -Properly modify the observation space.

    This is an abstract Class: it must be sub-classed.
    """

    def reset(self, state=None):
        """
        Method used to reset the environment.
        """

        observation = self.env.reset(state=state)
        return self.observation(observation=observation)

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        observation, reward, absorbing, info = self.env.step(action=action)
        return self.observation(observation=observation), reward, absorbing, {}

    @abstractmethod
    def observation(self, observation):
        """
        Method used to transform the observations.
        """

        raise NotImplementedError

class BaseActionWrapper(BaseWrapper):
    """
    Part of this Class is a readaptation of code copied from OpenAI gym: https://github.com/openai/gym/blob/master/gym/core.py

    To create an action wrapper you must create a new Class inheriting from this Class. You must override the action method and
    in the __init__ you must:
        -Call the __init__ of BaseWrapper via: super().__init__(env)
        -Properly modify the action space.

    This is an abstract Class: it must be sub-classed.
    """

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        return self.env.step(self.action(action=action))

    @abstractmethod
    def action(self, action):
        """
        Method used to transform the actions.
        """

        raise NotImplementedError

class BaseRewardWrapper(BaseWrapper):
    """
    Part of this Class is a readaptation of code copied from OpenAI gym: https://github.com/openai/gym/blob/master/gym/core.py

    To create a reward wrapper you must create a new Class inheriting from this Class. You must override the reward method, and
    in the __init__ you must:
        -Call the __init__ of BaseWrapper via: super().__init__(env)

    This is an abstract Class: it must be sub-classed.
    """

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        observation, reward, absorbing, info = self.env.step(action=action)
        return observation, self.reward(reward=reward), absorbing, {}

    @abstractmethod
    def reward(self, reward):
        """
        Method used to transform the rewards.
        """

        raise NotImplementedError

class BaseGridWorld(GridWorld, BaseEnvironment):
    """
    This Class wraps the GridWorld Class from MushroomRL: this is needed for the correct working of this library.
    """

    def __init__(self, height, width, goal, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3,
                 n_jobs=1, job_type='process', start=(0,0)):
        super().__init__(height=height, width=width, goal=goal, start=start)
        super(BaseEnvironment, self).__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode,
                                              checkpoint_log_path=checkpoint_log_path, verbosity=verbosity, n_jobs=n_jobs,
                                              job_type=job_type)
        self.horizon = super().info.horizon
        self.gamma = super().info.gamma

        self.observation_space   = super().info.observation_space
        self.action_space =  super().info.action_space

    def __repr__(self):
        return 'BaseGridWorld('+'observation_space='+str(self.observation_space)+', action_space='+str(self.action_space)\
                +', gamma='+str(self.gamma)+', horizon='+str(self.horizon)+', obj_name='+str(self.obj_name)\
                +', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)+', log_mode='+str(self.log_mode)\
                +', checkpoint_log_path='+str(self.checkpoint_log_path)+', verbosity='+str(self.verbosity)\
                +', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)+', logger='+str(self.logger)+')'

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return

class BaseCarOnHill(CarOnHill, BaseEnvironment):
    """
    This Class wraps the CarOnHill Class from MushroomRL: this is needed for the correct working of this library.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', horizon=100, gamma=.95):
        super().__init__(horizon=horizon, gamma=gamma)
        super(BaseEnvironment, self).__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode,
                                              checkpoint_log_path=checkpoint_log_path, verbosity=verbosity, n_jobs=n_jobs,
                                              job_type=job_type)

        self.horizon = super().info.horizon
        self.gamma = super().info.gamma

        self.observation_space = super().info.observation_space
        self.action_space =  super().info.action_space

    def __repr__(self):
        return 'BaseCarOnHill('+'observation_space='+str(self.observation_space)+', action_space='+str(self.action_space)\
                +', gamma='+str(self.gamma)+', horizon='+str(self.horizon)+', obj_name='+str(self.obj_name)\
                +', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)+', log_mode='+str(self.log_mode)\
                +', checkpoint_log_path='+str(self.checkpoint_log_path)+', verbosity='+str(self.verbosity)\
                +', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)+', logger='+str(self.logger)+')'

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return

class BaseCartPole(CartPole, BaseEnvironment):
    """
    This Class wraps the CartPole Class from MushroomRL: this is needed for the correct working of this library.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', m=2., M=8., l=.5, g=9.8, mu=1e-2, max_u=50., noise_u=10., horizon=3000, gamma=.95):
        super().__init__(m=m, M=M, l=l, g=g, mu=mu, max_u=max_u, noise_u=noise_u, horizon=horizon, gamma=gamma)
        super(BaseEnvironment, self).__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode,
                                              checkpoint_log_path=checkpoint_log_path, verbosity=verbosity, n_jobs=n_jobs,
                                              job_type=job_type)

        self.horizon = super().info.horizon
        self.gamma = super().info.gamma

        self.observation_space  = super().info.observation_space
        self.action_space  =  super().info.action_space

    def __repr__(self):
        return 'BaseCartPole('+'observation_space='+str(self.observation_space)+', action_space='+str(self.action_space)\
                +', gamma='+str(self.gamma)+', horizon='+str(self.horizon)+', obj_name='+str(self.obj_name)\
                +', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)+', log_mode='+str(self.log_mode)\
                +', checkpoint_log_path='+str(self.checkpoint_log_path)+', verbosity='+str(self.verbosity)\
                +', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)+', logger='+str(self.logger)+')'

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return

class BaseInvertedPendulum(InvertedPendulum, BaseEnvironment):
    """
    This Class wraps the InvertedPendulum Class from MushroomRL: this is needed for the correct working of this library.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', random_start=False, m=1., l=1., g=9.8, mu=1e-2, max_u=5., horizon=5000, gamma=.99):
        super().__init__(random_start=random_start, m=m, l=l, g=g, mu=mu, max_u=max_u, horizon=horizon, gamma=gamma)
        super(BaseEnvironment, self).__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode,
                                              checkpoint_log_path=checkpoint_log_path, verbosity=verbosity, n_jobs=n_jobs,
                                              job_type=job_type)

        self.horizon = super().info.horizon
        self.gamma = super().info.gamma

        self.observation_space = super().info.observation_space
        self.action_space =  super().info.action_space

    def __repr__(self):
        return 'BaseInvertedPendulum('+'observation_space='+str(self.observation_space)\
                +', action_space='+str(self.action_space)+', gamma='+str(self.gamma)+', horizon='+str(self.horizon)\
                +', obj_name='+str(self.obj_name)+', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)\
                +', log_mode='+str(self.log_mode)+', checkpoint_log_path='+str(self.checkpoint_log_path)\
                +', verbosity='+str(self.verbosity)+', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)\
                +', logger='+str(self.logger)+')'

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return

class LQG(BaseEnvironment):
    """
    Environment implementing a Linear-Quadratic Gaussian control (LQG) problem: s_{t+1} = A s_t + B a_t + env_noise

    The reward function is given by: r_{t+1} = - s_t^T Q s_t - a_t^T R a_t

    Note that there is also a noise on the controller: if you pick action a_t then you will be able to execute action:
    a_t + controller_noise. This only plays a role when rolling out the policy: we consider a gaussian policy with mean a_t and
    covariante matrix given by controller_noise.

    This is a re-adaptation of code copied from: https://github.com/T3p/potion/blob/master/potion/envs/lq.py
    """

    def __init__(self, obj_name, A=np.eye(1), B=np.eye(1), Q=np.eye(1), R=np.eye(1), max_pos=1.0, max_action=1.0,
                 env_noise=np.eye(1), controller_noise=np.eye(1), horizon=10, gamma=0.9, seeder=2, log_mode='console',
                 checkpoint_log_path=None, verbosity=3, n_jobs=1, job_type='process'):
        """
        Parameters
        ----------
        A: This is the state dynamics matrix.

           The default is np.eye(1).

        B: This is the action dynamics matrix.

           The default is np.eye(1).

        Q: This is the cost weight matrix for the state. It must be a positive-definite matrix (to always have a negative reward).

           The default is np.eye(1).

        R: This is the cost weight matrix for the action. It must be a positive-definite matrix (to always have a negative
           reward).

           The default is np.eye(1).

        max_pos: This is the maximum value that the state can reach.

                 The default is 1.0.

        max_action: This is the maximum value that the action can reach.

                    The default is 1.0.

        env_noise: This is the covariance matrix representing the environment noise.

                   The default is np.eye(1).

        controller_noise: This is the covariance matrix representing the controller noise.

                          The default is np.eye(1).

        horizon: This is the horizon of the MDP.

                 The default is 10.

        gamma: This is the discount factor of the MDP.

               The default is 0.9.

        Non-Parameters Members
        ----------------------
        is_eval_phase: This is True if the environment is used for evaluating a policy: what happens is that the controller_noise
                       is added to the action selected by the policy, and then fed to the simulator.

                       Otherwise it is False.

                       This is used to represent the fact that even if we have learnt a theoretically optimal policy, in practice
                       to execute it there is going to be some noise and so the resulting action taken in the real world will be
                       different from the one selected by the policy.

                       This parameter can be set automatically by the evaluation metric.

        The other parameters and non-parameters members are described in the Class BaseEnvironment.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity, n_jobs=n_jobs, job_type=job_type)

        self.A = A
        self.B = B
        self.Q = Q
        self.R = R

        #state dimension
        self.ds = self.A.shape[1]

        #action dimension
        self.da = self.B.shape[1]

        if(self.da == 1):
            if(self.R < 0):
                exc_msg = 'The matrix \'R\' must be positive-definite so that the reward is always negative!'
                self.logger.exception(msg=exc_msg)
                raise ValueError(exc_msg)
        else:
            if(not np.all(np.linalg.eigvals(self.R) > 0)):
                exc_msg = 'The matrix \'R\' must be positive-definite so that the reward is always negative!'
                self.logger.exception(msg=exc_msg)
                raise ValueError(exc_msg)

        if(self.ds == 1):
            if(self.Q < 0):
                exc_msg = 'The matrix \'Q\' must be positive-definite so that the reward is always negative!'
                self.logger.exception(msg=exc_msg)
                raise ValueError(exc_msg)
        else:
            if(not np.all(np.linalg.eigvals(self.Q) > 0)):
                exc_msg = 'The matrix \'Q\' must be positive-definite so that the reward is always negative!'
                self.logger.exception(msg=exc_msg)
                raise ValueError(exc_msg)

        #task horizon
        self.horizon = horizon

        #discount factor
        self.gamma = gamma

        #max state for clipping
        self.max_pos = max_pos*np.ones(self.ds)

        #max action for clipping
        self.max_action = max_action*np.ones(self.da)

        #environment noise
        self.env_noise = env_noise
        #check that the env_noise has the right shape:
        if(self.env_noise.shape[1] != self.ds):
            exc_msg = 'The \'env_noise\' co-variance matrix is not of the right shape!'
            self.logger.exception(msg=exc_msg)
            raise ValueError(exc_msg)

        self.viewer = None

        self.action_space = Box(low=-self.max_action, high=self.max_action)

        self.observation_space = Box(low=-self.max_pos, high=self.max_pos)

        self.controller_noise = controller_noise
        #check that the controller_noise has the right shape:
        if(self.controller_noise.shape[1] != self.da):
            exc_msg = 'The \'controller_noise\' co-variance matrix is not of the right shape!'
            self.logger.exception(msg=exc_msg)
            raise ValueError(exc_msg)

        #when this is true the controller noise is added to the action that the policy originally picked.
        self.is_eval_phase = False

    def __repr__(self):
        return 'LQG('+'observation_space='+str(self.observation_space)+', action_space='+str(self.action_space)\
                +', gamma='+str(self.gamma)+', horizon='+str(self.horizon)+', A='+str(self.A)+', B='+str(self.B)\
                +', Q='+str(self.Q)+', R='+str(self.R)+', env_noise='+str(self.env_noise)\
                +', controller_noise='+str(self.controller_noise)+', is_eval_phase='+str(self.is_eval_phase)\
                +', obj_name='+str(self.obj_name)+', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)\
                +', log_mode='+str(self.log_mode)+', checkpoint_log_path='+str(self.checkpoint_log_path)\
                +', verbosity='+str(self.verbosity)+', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)\
                +', logger='+str(self.logger)+')'

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        if(self.is_eval_phase):
            action = action + self.local_prng.multivariate_normal(mean=np.zeros(self.da), cov=self.controller_noise)

        u = np.clip(np.ravel(np.atleast_1d(action)), -self.max_action, self.max_action)

        #makes the noise different at each step:
        env_noise = np.dot(self.env_noise, self.local_prng.standard_normal(self.ds))

        xn = np.clip(np.dot(self.A, self.state.T) + np.dot(self.B, u) + env_noise, -self.max_pos, self.max_pos)

        cost = np.dot(self.state, np.dot(self.Q, self.state)) + np.dot(u, np.dot(self.R, u))

        self.state = xn.ravel()

        self.timestep += 1

        return np.array(self.state), -np.asscalar(cost), self.timestep >= self.horizon, {'danger':0}

    def reset(self, state=None):
        """
        By default, random uniform initialization.
        """

        self.timestep = 0
        if state is None:
            self.state = np.array(self.local_prng.uniform(low=-self.max_pos, high=self.max_pos, size=self.ds))
        else:
            self.state = np.array(state)

        return np.array(self.state)

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        if(seed is None):
            self.set_local_prng(new_seeder=seed)

    def render(self, mode='human', close=False):
        """
        Method used to render the environment.
        """

        #this is here since it would otherwise open a plot window
        from gym.envs.classic_control import rendering

        if self.ds not in [1, 2]:
            return
        if close:
            if self.viewer is not None:
                self.viewer.close()
                self.viewer = None
            return

        screen_width = 600
        world_width = math.ceil((self.max_pos[0] * 2) * 1.5)
        xscale = screen_width / world_width
        ballradius = 3

        if self.ds == 1:
            screen_height = 400
        else:
            world_height = math.ceil((self.max_pos[1] * 2) * 1.5)
            screen_height = math.ceil(xscale * world_height)
            yscale = screen_height / world_height

        if self.viewer is None:
            clearance = 0  # y-offset
            self.viewer = rendering.Viewer(screen_width, screen_height)
            mass = rendering.make_circle(ballradius * 2)
            mass.set_color(.8, .3, .3)
            mass.add_attr(rendering.Transform(translation=(0, clearance)))
            self.masstrans = rendering.Transform()
            mass.add_attr(self.masstrans)
            self.viewer.add_geom(mass)
            if self.ds == 1:
                self.track = rendering.Line((0, 100), (screen_width, 100))
            else:
                self.track = rendering.Line((0, screen_height / 2), (screen_width, screen_height / 2))
            self.track.set_color(0.5, 0.5, 0.5)
            self.viewer.add_geom(self.track)
            zero_line = rendering.Line((screen_width / 2, 0), (screen_width / 2, screen_height))
            zero_line.set_color(0.5, 0.5, 0.5)
            self.viewer.add_geom(zero_line)

        x = self.state[0]
        ballx = x * xscale + screen_width / 2.0
        if self.ds == 1:
            bally = 100
        else:
            y = self.state[1]
            bally = y * yscale + screen_height / 2.0
        self.masstrans.set_translation(ballx, bally)

        return self.viewer.render(return_rgb_array=mode == 'rgb_array')

    def get_optimal_K(self):
        """
        Returns
        -------
        Computes the optimal parameters K and returns the optimal policy given by: -K*s where s is the state.

        Note that the addition of the controller noise is taken care of in the evaluation phase.
        """

        X = np.matrix(scipy.linalg.solve_discrete_are(self.A, self.B, self.Q, self.R))

        K = np.matrix(scipy.linalg.inv(self.B.T*X*self.B+self.R)*(self.B.T*X*self.A))

        return -K

class BaseMujoco(BaseEnvironment):
    """
    This Class wraps the Mujoco environments. Every Mujoco environment inherits from this Class.

    This is an abstract Class.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process'):
        """
        Non-Parameters Members
        ----------------------
        mujoco_env: This is a Mujoco environment instance: an object of a Class inheriting from OpenAI gym Class:
                    gym.envs.mujoco.mujoco_env.MujocoEnv.

        n_steps: This is a counter for the horizon of the environment. It is the number of total steps that have happened so far.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        #this is set in the sub-Class:
        self.mujoco_env = None

        self.n_steps = 0

    def __repr__(self):
        return str(self.__class__.__name__)+'('+'observation_space='+str(self.observation_space)\
               +', action_space='+str(self.action_space)+', gamma='+str(self.gamma)+', horizon='+str(self.horizon)\
               +', obj_name='+str(self.obj_name)+', seeder='+str(self.seeder)+', local_prng='+str(self.local_prng)\
               +', log_mode='+str(self.log_mode)+', checkpoint_log_path='+str(self.checkpoint_log_path)\
               +', verbosity='+str(self.verbosity)+', n_jobs='+str(self.n_jobs)+', job_type='+str(self.job_type)\
               +', logger='+str(self.logger)+')'

    def _update_counter(self, out_step):
        """
        Parameters
        ----------
        out_step: The output from the mujoco_env object step() method.

        Returns
        -------
        tuple(out_step): The modified environment step() method output.

        Updates the n_steps and if the horizon is reached the output of the method step() from the environment is modified: done
        is set to True.
        """

        if(hasattr(self, 'n_steps')):
            out_step = list(out_step)

            self.n_steps += 1

            if(self.n_steps >= self.horizon):
                out_step[2] = True
                self.n_steps = 0

        return tuple(out_step)

    def step(self, action):
        """
        Method used to run one step of the environment dynamics.
        """

        out = self.mujoco_env.step(action=action)

        out = self._update_counter(out_step=out)

        return out

    def reset(self, state=None):
        """
        Method used to reset the environment.
        """

        obs = self.mujoco_env.reset()

        self.n_steps = 0

        return obs

    def seed(self, seed=None):
        """
        Method used to seed the environment.
        """

        return self.mujoco_env.seed(seed=seed)

    def render(self, mode='human'):
        """
        Method used to render the environment.
        """

        return self.mujoco_env.render(mode=mode)

    def set_local_prng(self, new_seeder):
        """
        Method used to adjust to the fact that OpenAI does not use my system of using local prng but have their own way of doing
        it. Without this method by calling the original method set_local_prng() implemented in the Class AbstractUnit there would
        be no effect on the environment.

        The method set_local_prng() is called in the Metric Classes and in some of the DataGeneration Classes so this method
        needs to work properly.
        """

        self.seed(seed=new_seeder)

    def _initialise_mdp_properties(self, gamma, horizon):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

        horizon: This is the MDP horizon. It must be an integer.
        """

        #no gamma, no horizon in Mujoco: i select them
        self.gamma = gamma
        self.horizon = horizon

        #I need to use the environment spaces from MushroomRL:
        self.action_space = Box(self.mujoco_env.action_space.low, self.mujoco_env.action_space.high)

        self.observation_space = Box(self.mujoco_env.observation_space.low, self.mujoco_env.observation_space.high)

class BaseHalfCheetah(BaseMujoco):
    """
    This Class wraps the Mujoco environment: HalfCheetahEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file='half_cheetah.xml', forward_reward_weight=1.0,
                 ctrl_cost_weight=0.1, reset_noise_scale=0.1, exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = HalfCheetahEnv(xml_file=xml_file, forward_reward_weight=forward_reward_weight,
                                         ctrl_cost_weight=ctrl_cost_weight, reset_noise_scale=reset_noise_scale,
                                         exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)

class BaseAnt(BaseMujoco):
    """
    This Class wraps the Mujoco environment: AntEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file='ant.xml', ctrl_cost_weight=0.5, contact_cost_weight=5e-4,
                 healthy_reward=1.0, terminate_when_unhealthy=True, healthy_z_range=(0.2, 1.0), contact_force_range=(-1.0, 1.0),
                 reset_noise_scale=0.1, exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = AntEnv(xml_file=xml_file, ctrl_cost_weight=ctrl_cost_weight, contact_cost_weight=contact_cost_weight,
                                 healthy_reward=healthy_reward, terminate_when_unhealthy=terminate_when_unhealthy,
                                 healthy_z_range=healthy_z_range, contact_force_range=contact_force_range,
                                 reset_noise_scale=reset_noise_scale,
                                 exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)

class BaseHopper(BaseMujoco):
    """
    This Class wraps the Mujoco environment: HopperEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file='hopper.xml', forward_reward_weight=1.0,
                 ctrl_cost_weight=1e-3, healthy_reward=1.0, terminate_when_unhealthy=True, healthy_state_range=(-100.0, 100.0),
                 healthy_z_range=(0.7, float('inf')), healthy_angle_range=(-0.2, 0.2), reset_noise_scale=5e-3,
                 exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = HopperEnv(xml_file=xml_file, forward_reward_weight=forward_reward_weight,
                                    ctrl_cost_weight=ctrl_cost_weight, healthy_reward=healthy_reward,
                                    terminate_when_unhealthy=terminate_when_unhealthy, healthy_state_range=healthy_state_range,
                                    healthy_z_range=healthy_z_range, healthy_angle_range=healthy_angle_range,
                                    reset_noise_scale=reset_noise_scale,
                                    exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)

class BaseHumanoid(BaseMujoco):
    """
    This Class wraps the Mujoco environment: HumanoidEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file='humanoid.xml', forward_reward_weight=1.25,
                 ctrl_cost_weight=0.1, contact_cost_weight=5e-7, contact_cost_range=(-np.inf, 10.0), healthy_reward=5.0,
                 terminate_when_unhealthy=True, healthy_z_range=(1.0, 2.0), reset_noise_scale=1e-2,
                 exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = HumanoidEnv(xml_file=xml_file, forward_reward_weight=forward_reward_weight,
                                      ctrl_cost_weight=ctrl_cost_weight, contact_cost_weight=contact_cost_weight,
                                      contact_cost_range=contact_cost_range, healthy_reward=healthy_reward,
                                      terminate_when_unhealthy=terminate_when_unhealthy, healthy_z_range=healthy_z_range,
                                      reset_noise_scale=reset_noise_scale,
                                      exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)

class BaseSwimmer(BaseMujoco):
    """
    This Class wraps the Mujoco environment: SwimmerEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file="swimmer.xml", forward_reward_weight=1.0,
                 ctrl_cost_weight=1e-4, reset_noise_scale=0.1, exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = SwimmerEnv(xml_file=xml_file, forward_reward_weight=forward_reward_weight,
                                     ctrl_cost_weight=ctrl_cost_weight, reset_noise_scale=reset_noise_scale,
                                     exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)

class BaseWalker2d(BaseMujoco):
    """
    This Class wraps the Mujoco environment: Walker2dEnv.
    """

    def __init__(self, obj_name, seeder=2, log_mode='console', checkpoint_log_path=None, verbosity=3, n_jobs=1,
                 job_type='process', gamma=0.99, horizon=1000, xml_file="walker2d.xml", forward_reward_weight=1.0,
                 ctrl_cost_weight=1e-3, healthy_reward=1.0, terminate_when_unhealthy=True, healthy_z_range=(0.8, 2.0),
                 healthy_angle_range=(-1.0, 1.0), reset_noise_scale=5e-3, exclude_current_positions_from_observation=True):
        """
        Parameters
        ----------
        gamma: This is the MDP discount factor. It must be a float.

               The default is 0.99.

        horizon: This is the MDP horizon. It must be an integer.

                 The default is 1000.
        """

        super().__init__(obj_name=obj_name, seeder=seeder, log_mode=log_mode, checkpoint_log_path=checkpoint_log_path,
                         verbosity=verbosity)

        self.mujoco_env = Walker2dEnv(xml_file=xml_file, forward_reward_weight=forward_reward_weight,
                                      ctrl_cost_weight=ctrl_cost_weight, healthy_reward=healthy_reward,
                                      terminate_when_unhealthy=terminate_when_unhealthy, healthy_z_range=healthy_z_range,
                                      healthy_angle_range=healthy_angle_range, reset_noise_scale=reset_noise_scale,
                                      exclude_current_positions_from_observation=exclude_current_positions_from_observation)

        self._initialise_mdp_properties(gamma=gamma, horizon=horizon)