예제 #1
0
def test_class_docstring():
    @add_metaclass(DocInheritMeta(style="numpy"))
    class Parent(object):
        """
        Parent class.

        Returns
        -------
        foo
        """

    class Mixin(object):
        """
        This is mixin which does something.

        """

    class Child(Mixin, Parent):
        """
        Attributes
        ----------
        bar
        """

    assert getdoc(Child) == \
           'This is mixin which does something.\n\nAttributes\n----------\nbar\n\nReturns\n-------\nfoo'
예제 #2
0
파일: batch.py 프로젝트: clintval/pendant
class JobDefinition(metaclass=DocInheritMeta(
        style="google", abstract_base_class=True)  # type: ignore
                    ):
    """A Batch job definition."""
    def __new__(cls, *args: str, **kwargs: str) -> 'JobDefinition':
        """Create a new Batch job definition."""
        this: JobDefinition = super().__new__(cls)
        this._revision = '0'
        return this

    @property
    @abstractmethod
    def name(self) -> str:
        """Return the name of the job definition."""

    @property
    def parameters(self) -> Tuple[str]:
        """Return the parameters of the job definition."""
        return tuple(inspect.signature(
            self.__init__).parameters.keys())  # type: ignore

    @property
    def revision(self) -> str:
        """Return the revision of the job definition."""
        return self._revision

    @abstractmethod
    def validate(self) -> None:
        """Validate this job definition after initialization."""

    def at_revision(self, revision: str) -> 'JobDefinition':
        """Set this job definition to a specific revision."""
        self._revision = revision
        return self

    def make_job_name(self, moment: Optional[datetime] = None) -> str:
        """Format a Batch job name from this definition."""
        moment = datetime.now() if moment is None else moment
        return format_ISO8601(moment) + '_' + self.name

    def to_dict(self) -> Dict[str, str]:
        """Return a dictionary of all parameters and their values as strings."""
        mapping: Dict[str, str] = {
            key: str(getattr(self, key))
            for key in self.parameters
        }
        return mapping

    def __str__(self) -> str:
        return f'{self.name}:{self.revision}'

    def __repr__(self) -> str:
        parts = [
            f'{key}={repr(getattr(self, key))}' for key in self.parameters
        ]
        signature = ', '.join(parts)
        return f'{self.__class__.__qualname__}({signature})'
예제 #3
0
class Schedule(metaclass=DocInheritMeta(style="numpy")):
    """Base class for value schedule, used for decaying values in exploration methods.

    Parameters
    ----------
    start_value : float
        initial value at first timestep.
    interval : int
        update interval in number of timesteps (default is -1, which means every episode).

    Attributes
    ----------
    t : int
        current timestep index.
    value : float
        current value.
    interval : int
        update interval in number of timesteps.
    """
    def __init__(self, start_value, interval=-1):
        self.timestep = 0
        self.value = start_value
        self.interval = interval

    def _step(self):
        """Implement the logic of the schedule for one iteration of the value."""
        pass

    def step(self, new_episode):
        """Wrap the schedule logic to determine based on interval when to iterate value.

        Parameters
        ----------
        new_episode : bool
            flag to indicate whether this step is one that resets the environment.
        """
        if self.interval > 0:
            if self.timestep % self.interval == 0:
                self._step()
        else:
            if new_episode:
                self._step()
        self.timestep += 1

    def get(self):
        """Get value, bounded in [0, 1].

        Returns
        -------
        float
            current value given by schedule.
        """
        return max([0., min([1., self.value])])
예제 #4
0
class Market(metaclass=DocInheritMeta(style="numpy",
                                      abstract_base_class=True)):
    """
    An abstract market class where an implementation needs to implement send_messages. A market always has an
    order book which is set in :py:meth:`__init__`.

    Attributes
    ----------
    ob: OrderBook
        The markets order book.
    tick_size: float
        The minimum multiple of price difference in a market.
    tick_dec: int
        Number of decimals in tick_size
    multipler: int
        10**tick_dec. Used to multiply price to int

    """
    def __init__(self,
                 tick_size=0.01,
                 ob_type='py',
                 price_level_type='ordered_dict',
                 price_levels_type='sorted_dict'):
        """
        Parameters
        ----------
        tick_size: float
            The minimum multiple of price difference in a market.
        ob_type: str
            Type of order book.
        price_levels_type: str
            Type or price levels for the order book.
        price_level_type: str
            Type of order level for the price levels
        """
        self.ob = get_ob(ob_type, price_level_type, price_levels_type)
        self.tick_size = tick_size
        self.tick_dec = int(np.log10(1 / tick_size))
        self.multiplier = 10**self.tick_dec

    @abc.abstractmethod
    def send_message(self, message: dict) -> (list, tuple):
        """ The market receives a message and returns a possible trade or an order in the book.
예제 #5
0
from itertools import islice, repeat
from copy import deepcopy
from custom_inherit import DocInheritMeta

DOC_INHERIT = DocInheritMeta(style='numpy', abstract_base_class=False)

DOC_INHERIT_ABSTRACT = DocInheritMeta(style='numpy', abstract_base_class=True)


class SaveLoad(metaclass=DOC_INHERIT_ABSTRACT):
    """Base class for converting to/from JSON.

    Attributes
    ----------
    classes : `dict`
        Class group.

    Examples
    --------
    >>> class SaveLoadGroup(SaveLoad):
    ...     classes = {}
    ...
    >>> class SaveLoadObject(SaveLoadGroup):
    ...     def __init__(self, attr=None):
    ...         self.attr = attr
    ...     def save(self):
    ...         data = super().save()
    ...         data['attr'] = self.attr
    ...         return data
    ...
    >>> SaveLoadGroup.add_class(SaveLoadObject)
예제 #6
0
class _Node(metaclass=DocInheritMeta(style="numpy_with_merge",
                                     include_special_methods=True)):
    """
    Base class of all nodes in a wonterfact tree,  i.e. the graphical
    representation of a tensor factorization model.
    """
    def __init__(self, name=None, **kwargs):
        """
        Parameters
        ----------
        name: str or None, optional, default None
            A name for the node. Very useful for debugging but not necessary.
            Each node's name in a tree should be unique. If None, a unique name
            is generated.
        """
        self.name = name
        if not self.name:
            base62.sign = "_"
            self.name = "n_" + base62.encode(id(self))

    def census(self,
               nodebook=None):  # Needs to be overridden in ChildNode class.
        """
        Returns a single element set with itself in it.

        Returns
        -------
        set
        """
        if nodebook is None:
            nodebook = set()
        nodebook.add(self)
        return nodebook

    # Needs to be overridden in DynNodeData
    def should_update(self, iteration_number=None):
        """
        Always returns True

        Returns
        -------
        bool
        """
        return True

    def _set_inference_mode(self, mode="EM"):
        if mode not in ("EM", "VBEM"):
            raise ValueError(
                "Wrong inference mode value: must be 'EM of 'VBEM' ")
        self._inference_mode = mode

    def _check_filiation_ok(self, child=None, parent=None, **kwargs):
        """
        Raise an error if the node cannot accept to bound with either a new
        parent or a new child, given self class, input node's class and input
        kwargs.
        """
        pass

    def _check_model_validity(self):
        """
        Raise an error if one suspects that the model (i.e. the tree structure)
        is locally wrong
        """
        pass

    def __repr__(self):
        str_out = "{}(name='{}')".format(type(self).__name__, self.name)
        return str_out

    def clear_cache(self):
        list_of_names = [
            name for name, value in inspect.getmembers(self.__class__)
            if isinstance(value, cached_property)
        ]
        for name in list_of_names:
            try:
                self.__delattr__(name)
            except AttributeError:
                pass
        list_of_names = [
            name for name, value in inspect.getmembers(self.__class__)
            if isinstance(value, _LruCacheWire)
        ]
        for name in list_of_names:
            getattr(self, name).cache_clear()
예제 #7
0
class MoodleBasicHelper(metaclass=DocInheritMeta(style='google_with_merge',
                                                 include_special_methods=True)
                        ):
    @classmethod
    def format_string(cls, string: str) -> str:
        '''
        Replace all invalid characters with underscore.

        Args:
            string (str): input string

        Returns:
            str: valid string in lowercase.

        '''

        if not string:
            raise ValueError('string is empty')

        string = re.sub(r'[^\w-]+', '_', string)

        return string.lstrip('_.-').lower()[:50]

    @classmethod
    def email_to_username(cls, email: str) -> str:
        '''
        Normalizes an email to get a username. This function
        calculates the username by getting the string before the
        @ symbol, removing special characters, removing comments,
        converting string to lowercase, and adds 1 if the username
        has an integer value already in the string.

        Args:
            email: A valid email address

        Returns:
            str: A username string

        Raises:
          ValueError: if email is empty
        '''

        if not email:
            raise ValueError('email is missing')

        username = email.split('@')[0]

        username = username.split('+')[0]

        username = re.sub(r'\([^)]*\)', '', username)

        username = re.sub(r'[^\w-]+', '', username)

        username = username.lower()

        logger.debug(f'Normalized email {email!r} to {username!r}')

        return username

    @classmethod
    def get_user_group(cls, user: User) -> str:
        '''Map LMS user's role to one of student, instructor or a grader.

        Args:
            user (User): User dict with 'role' key

        Returns:
            str: Jupyterhub role

        Raises:
            KeyError: No appropriate role was found.

        '''

        if user['role'] == 'student':
            group = 'students'
        elif user['role'] in ('editingteacher', 'manager', 'coursecreator',
                              'instructional_support'):
            group = 'instructors'
        elif user['role'] in ('teaching_assistant', 'teacher'):
            group = 'graders'
        else:
            raise KeyError(user['role'])

        return group

    @classmethod
    def format_course(cls, course: JsonType) -> Course:
        '''Format raw json response to convinient dictionary.

        Args:
            course (JsonType): Raw Json from LMS containing course data.

        Returns:
            Course: JsonDict with course information

        '''

        return JsonDict({
            'id':
            course['id'],
            'course_id':
            cls.format_string(course['shortname']),
            'title':
            course['displayname'],
            'category':
            course['categoryid'],
            'need_nbgrader':
            course['categoryid'] == int(
                os.environ['MOODLE_NBGRADER_CATEGORY_ID']),
            'instructors': [],
            'students': [],
            'graders': [],
            'lms_lineitems_endpoint':
            f'{os.environ["MOODLE_BASE_URL"]}/mod/lti/services.php/{course["id"]}/lineitems'
        })

    @classmethod
    def format_user(cls, user: JsonType) -> User:
        '''Format raw json response to convinient dictionary.

        Args:
            user (JsonType): Raw Json from LMS containing user data.

        Returns:
            User: JsonDict with user information

        '''

        return JsonDict({
            'id': user['id'],
            'first_name': user['firstname'],
            'last_name': user['lastname'],
            'username': cls.format_string(user['username']),
            'email': user['email'],
            'roles': [role['shortname'] for role in user['roles']],
        })

    @classmethod
    def skip_course(
        cls,
        course: Course,
        filters: t.Dict[str, t.Union[t.Sequence[t.AnyStr], t.AnyStr]],
    ) -> bool:
        '''Determines should the course be skipped according to provided filters

        Args:
            course (Course): Course created by calling format_course method.
            filters (t.Dict[str, t.Union[t.Sequence[t.AnyStr], t.AnyStr]]):
                key-value pairs where value can be both single value or list
                of valid items.

        Returns:
            bool: True if the course failed filters check. False otherwise.

        '''

        for field, value in filters.items():

            # if provided filter is a sequence
            # check that course's value is in that sequence
            if not isinstance(value, str) and hasattr(type(value), '__iter__'):

                if not value:
                    raise ValueError(f'Empty sequence found: {field}')

                if course[field] not in value:
                    return True

            else:

                if course[field] != value:
                    return True

        return False
예제 #8
0
class MarketEnv(metaclass=DocInheritMeta(style="numpy",
                                         abstract_base_class=True)):
    """ A market environment extending the `OpenAI gym class <https://github.com/openai/gym/blob/master/gym/core.py/>`_ .

    The main functions are the step and reset functions. In the step function the agent sends orders and receives a reward
    based on the orders/trades.

    A basic rendering function using `Dash <https://plot.ly/products/dash/>`_ is implemented. This opens a Flask server
    in another thread that renders different information about the market.

    Attributes
    ----------
    capital : float
        The capital of the agent
    funds : float
        The cash/funds of the agent
    possession : float
        The size of the asset the agent possesses
    price_n : int
        Number of price points to render back in time.
    trades_list : list
        All trades


    """
    # Set this in SOME subclasses
    metadata = {'render.modes': []}
    reward_range = (-float('inf'), float('inf'))
    spec = None

    def __init__(self,
                 market_type='cyext',
                 tick_size=0.01,
                 ob_type='cy',
                 price_level_type='cydeque',
                 price_levels_type='cylist',
                 initial_funds=10000,
                 T_ID=1,
                 **kwargs):
        """

        Parameters
        ----------
        market_type : str
            The type of market to be used
        market_setup : dict
            Parameters for the market
        initial_funds : float
            The initial cash of the agent
        T_ID : int
            The agent's trader id
        """

        # Market
        self._market_type = market_type
        self._market_setup = dict(tick_size=tick_size,
                                  ob_type=ob_type,
                                  price_level_type=price_level_type,
                                  price_levels_type=price_levels_type)

        # Init Funds
        self.capital = initial_funds
        self.funds = initial_funds
        self.initial_funds = initial_funds

        # Rendering
        self.render_app = None
        self.open_tab = False
        self.first_render = True
        self.price_n = 10000

        self.trades_list = []
        self.T_ID = T_ID

        self.obs_shape_n = 4  # Quotes

    def step(self, action):
        """ Sends orders based on the agents action and receives a reward based on trades that have occurred.

        The action is first converted to messages to the market. Then the messages are sent and trades are received.
        The reward is then calculated based on the trades.

        Parameters
        ----------
        action : numpy.array

        Returns
        -------
        obs : list[tuple, tuple]
            The current quotes of the market and private information about the agents portfolio
        reward : float
        done : bool
            If the episode has finished
        info : dict
            Additional information
        """
        messages = self.get_messages(action)
        trades, done, info = self.send_messages(messages)
        reward = self.get_reward(trades, done)
        obs = self.get_obs()
        return obs, reward, done, info

    @abc.abstractmethod
    def get_messages(self, action: np.array) -> tuple:
        """ Returns messages based on the action received.

        Parameters
        ----------
        action : numpy.array

        Returns
        -------
        messages: tuple

        """

    @abc.abstractmethod
    def get_reward(self, trades: list) -> tuple:
        """ Returns a reward based on trades

        Parameters
        ----------
        trades : list

        Returns
        -------
        reward : float

        """

    @abc.abstractmethod
    def send_messages(self, messages: tuple) -> (list, bool, dict):
        """ Sends all the messages to the market

        Parameters
        ----------
        messages : tuple

        Returns
        -------
        trades : list
        done : bool
            If the episode has finished or not.
        info : dict
            Extra information about the environment
        """

    @abc.abstractmethod
    def get_private_variables(self) -> tuple:
        """ Returns private variables of for the agent to observe. For example the possession of the agent.
        Returns
        -------
        private_variables : tuple

        """

    def get_obs(self) -> tuple:
        """ Returns an observation of the market. Currently the quotes.
        Returns
        -------
        observation : tuple

        """
        if hasattr(self, 'quotes'):
            obs = self.quotes
        else:
            obs = self.market.ob.price_levels.get_quotes()
            self.quotes = obs

        obs = (obs, self.get_private_variables())
        return obs

    def reset(self, market=None):
        """ Resets the environment with a market or creates a new one. Also resets the render app if needed.
        Parameters
        ----------
        market : Market
            If to use a specific market instead of a newly created one.

        Returns
        -------
        observation : tuple

        """
        if market is not None:
            self.market = market
        else:
            self.market = get_market(self._market_type, **self._market_setup)

        obs = self.get_obs()
        self.capital = self.initial_funds
        self.possession = 0
        self.funds = self.initial_funds

        if self.render_app:
            self.render_app.use_reloader = False
            self.render_app.__setattr__('ask', deque(maxlen=self.price_n))
            self.render_app.__setattr__('bid', deque(maxlen=self.price_n))
            self.render_app.__setattr__('time', deque(maxlen=self.price_n))
            self.render_app.__setattr__('sellbook', {})
            self.render_app.__setattr__('buybook', {})
            self.render_app.__setattr__('trades_', [])

        return obs, self.get_private_variables()

    def render(self, mode=None):
        """ Renders the environment in a dash app.

        """
        if self.first_render:
            self.render_app.use_reloader = False
            self.render_app.__setattr__('ask', deque(maxlen=self.price_n))
            self.render_app.__setattr__('bid', deque(maxlen=self.price_n))
            self.render_app.__setattr__('time', deque(maxlen=self.price_n))
            self.render_app.__setattr__('sellbook', {})
            self.render_app.__setattr__('buybook', {})
            self.render_app.__setattr__('trades_', [])

            self.app_thread = threading.Thread(
                target=self.render_app.run_server)
            self.app_thread.start()
            if not self.open_tab:
                self.open_tab = webbrowser.open_new('http://127.0.0.1:8050')

            self.first_render = False

        time_ = pd.to_datetime(self.market.time)
        ask, ask_vol, bid, bid_vol = self.quotes
        self.render_app.ask.append(ask)
        self.render_app.bid.append(bid)
        self.render_app.time.append(time_)

        self.render_app.trades_ = self.trades_list[-40:]
        ask, ask_vol, bid, bid_vol = self.quotes
        n = 50
        buy_book = {}
        for i in range(n):
            p = bid - i
            try:
                size = self.market.ob.price_levels.get_level(BUY, p).size
            except (KeyError, IndexError) as e:
                pass

            buy_book[p] = size
        n = 50
        sell_book = {}
        for i in range(n):
            p = ask + i
            try:
                size = self.market.ob.price_levels.get_level(SELL, p).size
            except (KeyError, IndexError) as e:
                pass
            sell_book[p] = size

        self.render_app.buybook = buy_book
        self.render_app.sellbook = sell_book

    def close(self):
        """ Shuts down the render app if needed.

        """
        if self.render_app:
            requests.post('http://127.0.0.1:8050/shutdown')
        return

    @abc.abstractmethod
    def seed(self, seed=None):
        """Sets the seed for this env's random number generator(s).
        """

        return

    # Set these in ALL subclasses
    @property
    @abc.abstractmethod
    def action_space(self):
        """gym.spaces : The action space of the environment."""

    @property
    @abc.abstractmethod
    def observation_space(self):
        """gym.spaces : The obervation space of the environment."""

    @property
    def unwrapped(self):
        """Completely unwrap this env.
        Returns:
            gym.Env: The base non-wrapped gym.MarketEnv instance
        """
        return self

    def __str__(self):
        if self.spec is None:
            return '<{} instance>'.format(type(self).__name__)
        else:
            return '<{}<{}>>'.format(type(self).__name__, self.spec.id)
예제 #9
0
class AbstractParams(metaclass=DocInheritMeta(style="numpy")):
    """
    An abstract class to hold the parameters of a QAOA run and compute the
    angles from them.

    Parameters
    ----------
    hyperparameters:
        The hyperparameters containing the hamiltonian, the number of steps
        and possibly more (e.g. the total annealing time).
        ``hyperparametesr = (hamiltonian, n_steps, ...)``
    parameters: Tuple
        The QAOA parameters, that can be optimized. E.g. the gammas and betas
        or the annealing timesteps. AbstractParams doesn't implement
        this, but all child classes do.

    Attributes
    ----------
    pair_qubit_coeffs : np.array
        Coefficients of the pair terms in the hamiltonian
    qubits_pairs : List
        List of the qubit pairs in the hamiltonian. Same ordering as
        `pair_qubit_coeffs`
    single_qubit_coeffs : np.array
        Coefficients of the bias terms
    qubits_singles : List
        List of the bias qubits in the hamiltonian. Same ordering as
        `single_qubit_coeffs`
    reg : List
        List of all qubits for the X-rotations
    """

    # pylint: disable=too-many-instance-attributes

    def __init__(self, hyperparameters: Tuple):
        # Note
        # ----
        # ``AbstractParams.__init__`` doesn't do anything with the
        # argument ``parameters``. In the child classes we have to super from
        # them and handle them correctly. Additionally we might need to handle
        # extra hyperparameters.

        hamiltonian, self.n_steps = hyperparameters[:2]

        # extract the qubit lists from the hamiltonian
        self.reg = hamiltonian.get_qubits()
        self.qubits_pairs = []
        self.qubits_singles = []

        # and fill qubits_singles and qubits_pairs according to the terms in the hamiltonian
        for term in hamiltonian:
            if len(term) == 1:
                self.qubits_singles.append(term.get_qubits()[0])
            elif len(term) == 2:
                self.qubits_pairs.append(term.get_qubits())
            elif len(term) == 0:
                pass  # could give a notice, that multiples of the identity are
                # ignored, since unphysical
            else:
                raise NotImplementedError(
                    "As of now we can only handle hamiltonians with at most two-qubit terms"
                )

        # extract the cofficients of the terms from the hamiltonian
        # if creating `ExtendedParams` form this, we can delete this attributes
        # again. Check if there are complex coefficients and issue a warning.
        self.single_qubit_coeffs = np.array(
            [term.coefficient for term in hamiltonian if len(term) == 1])
        if np.any(np.iscomplex(self.single_qubit_coeffs)):
            warnings.warn(
                "hamiltonian contained complex coefficients. Ignoring imaginary parts"
            )
        self.single_qubit_coeffs = self.single_qubit_coeffs.real
        # and the same for the pair qubit coefficients
        self.pair_qubit_coeffs = np.array(
            [term.coefficient for term in hamiltonian if len(term) == 2])
        if np.any(np.iscomplex(self.pair_qubit_coeffs)):
            warnings.warn(
                "hamiltonian contained complex coefficients. Ignoring imaginary parts"
            )
        self.pair_qubit_coeffs = self.pair_qubit_coeffs.real

    def __repr__(self):
        """Return an overview over the parameters and hyperparameters

        Todo
        ----
        Split this into ``__repr__`` and ``__str__`` with a more verbose
        output in ``__repr__``.
        """
        string = "Hyperparameters:\n"
        string += "\tregister: " + str(self.reg) + "\n"
        string += "\tqubits_singles: " + str(self.qubits_singles) + "\n"
        string += "\tsingle_qubit_coeffs: " + str(
            self.single_qubit_coeffs) + "\n"
        string += "\tqubits_pairs: " + str(self.qubits_pairs) + "\n"
        string += "\tpair_qubit_coeffs: " + str(self.pair_qubit_coeffs) + "\n"
        string += "\tn_steps: " + str(self.n_steps) + "\n"
        return string

    def __len__(self):
        """
        Returns
        -------
        int:
            the length of the data produced by self.raw() and accepted by
            self.update_from_raw()
        """
        raise NotImplementedError()

    @property
    def x_rotation_angles(self):
        """2D array with the X-rotation angles.

        1st index goes over n_steps and the 2nd index over the qubits to
        apply X-rotations on. These are needed by ``qaoa.cost_function.make_qaoa_memory_map``
        """
        raise NotImplementedError

    @property
    def z_rotation_angles(self):
        """2D array with the ZZ-rotation angles.

        1st index goes over the n_steps and the 2nd index over the qubit
        pairs, to apply ZZ-rotations on. These are needed by ``qaoa.cost_function.make_qaoa_memory_map``
        """
        raise NotImplementedError

    @property
    def zz_rotation_angles(self):
        """2D array with Z-rotation angles.

        1st index goes over the n_steps and the 2nd index over the qubit
        pairs, to apply Z-rotations on. These are needed by ``qaoa.cost_function.make_qaoa_memory_map``
        """
        raise NotImplementedError

    def update_from_raw(self, new_values: Union[list, np.array]):
        """
        Update all the parameters from a 1D array.

        The input has the same format as the output of ``self.raw()``.
        This is useful for ``scipy.optimize.minimize`` which expects
        the parameters that need to be optimized to be a 1D array.

        Parameters
        ----------
        new_values:
            A 1D array with the new parameters. Must have length  ``len(self)``
            and the ordering of the flattend ``parameters`` in ``__init__()``.

        """
        raise NotImplementedError()

    def raw(self):
        """
        Return the parameters in a 1D array.

        This 1D array is needed by ``scipy.optimize.minimize`` which expects
        the parameters that need to be optimized to be a 1D array.

        Returns
        -------
        np.array:
            The parameters in a 1D array. Has the same output
            format as the expected input of ``self.update_from_raw``. Hence
            corresponds to the flattened `parameters` in `__init__()`

        """
        raise NotImplementedError()

    def raw_rotation_angles(self):
        """
        Flat array of the rotation angles for the memory map for the
        parametric circuit.

        Returns
        -------
        np.array:
            Returns all single rotation angles in the ordering
            ``(x_rotation_angles, gamma_singles, zz_rotation_angles)`` where
            ``x_rotation_angles = (beta_q0_t0, beta_q1_t0, ... , beta_qn_tp)``
            and the same for ``z_rotation_angles`` and ``zz_rotation_angles``

        """
        raw_data = np.concatenate((self.x_rotation_angles.flatten(),
                                   self.z_rotation_angles.flatten(),
                                   self.zz_rotation_angles.flatten()))
        return raw_data

    @classmethod
    def linear_ramp_from_hamiltonian(cls,
                                     hamiltonian: PauliSum,
                                     n_steps: int,
                                     time: float = None):
        """Alternative to ``__init__`` that already fills ``parameters``.

        Calculate initial parameters from a hamiltonian corresponding to a
        linear ramp annealing schedule and return a ``QAOAParams`` object.

        Parameters
        ----------
        hamiltonian:
            ``hamiltonian`` for which to calculate the initial QAOA parameters.
        n_steps:
            Number of timesteps.
        time:
            Total annealing time. Defaults to ``0.7*n_steps``.

        Returns
        -------
        Type[AbstractParams]
            The initial parameters for a linear ramp for ``hamiltonian``.

        """
        raise NotImplementedError()

    @classmethod
    def from_AbstractParameters(cls, abstract_params, parameter: Tuple = None):
        """Alternative to ``__init__`` that takes ``hyperparameters`` from an
        existing ``AbstractQAOAParameter`` object.

        Create a ConcreteQAOAParameters instance from an
        AbstractQAOAParameters instance with hyperparameters from
        ``abstract_params`` and normal parameters from ``parameters``


        Parameters
        ----------
        abstract_params: AbstractParams
            An AbstractQAOAParameters instance to which to add the parameters
        parameter:
            A tuple containing the parameters. Must be the same as in
            ``__init__``

        Returns
        -------
        AbstractParams
            A copy of itself. This function makes only sense for the child
            classes of ``AbstractQAOAParameters``
        """
        params = copy.deepcopy(abstract_params)
        params.__class__ = cls

        return params

    def plot(self, ax=None, **kwargs):
        """
        Plots ``self`` in a sensible way to the canvas ``ax``, if provided.

        Parameters
        ----------
        ax: matplotlib.axes._subplots.AxesSubplot
            The canvas to plot itself on
        kwargs:
            All remaining keyword arguments are passed forward to the plot
            function

        """
        raise NotImplementedError()
예제 #10
0
파일: common.py 프로젝트: ntaylorwss/pavlov
class BaseModel(metaclass=DocInheritMeta(style="numpy")):
    """Base class for all kinds of reinforcement learning models.

    All models should take in some form of feature-extracting topology,
    which is how the user defines the architecture of the model.
    From there this model should configure the input and output according
    to the other components it's connected to via the Agent.
    The public interface should also be common among models, so that is
    defined here as well.

    Parameters
    ----------
    topology : pavlov.Topology
        feature-extracting Keras model graph object defining the body of the model.

    Attributes
    ----------
    topology : pavlov.Topology
        feature-extracting Keras model graph object defining the body of the model.
    """
    def __init__(self, topology):
        self.topology = topology

    def has_nan(self):
        """Throw an informative error if any model weights have gone to nan."""
        pass

    def _configure_model(self, session):
        """Generate necessary models, probably finishing off `self.topology` to do so."""
        pass

    def configure(self, agent, session):
        """Associate model with action space from environment; convert topology to model."""
        self.batch_size = agent.batch_size
        self.action_space = agent.env.action_space
        self.action_type = self.action_space.__class__.__name__.lower()
        self.topology.configure(agent)
        self._configure_model(session)

    def fit(self, states, actions, rewards, next_states, dones):
        """Fit model to batch from experience. May or may not use all inputs.

        Parameters
        ----------
        states : np.ndarray
            states to fit to.
        actions : np.ndarray
            actions to fit to.
        rewards : np.ndarray
            rewards to fit to.
        next_states : np.ndarray
            next states to fit to.
        dones : np.ndarray
            done flags to fit to.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def predict(self, state):
        """Return model output for given state input. May be policy or value.

        Parameters
        ----------
        state : np.ndarray
            state input to produce output for.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError
예제 #11
0
class AbstractClient(metaclass=DocInheritMeta(style="numpy")):
    """Abstract base class for all clients.

    Parameters
    ----------
    session : ``pycf3.Session`` (default: ``None``)
        The session to use to send the requests. By default a
        ``pyc3.RetrySession`` with 3 retry is created. More info:
        https://2.python-requests.org,
        https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html.
    cache : ``diskcache.Cache``, ``diskcache.Fanout``,
            ``pycf3.NoCache`` or ``None`` (default: ``None``)
        Any instance of ``diskcache.Cache``, ``diskcache.Fanout`` or
        ``None`` (Default). If it's ``None`` a ``diskcache.Cache`` istance
        is created with the parameter ``directory = pycf3.DEFAULT_CACHE_DIR``.
        More information: http://www.grantjenks.com/docs/diskcache
    cache_expire : ``float`` or None (default=``None``)
        Seconds until item expires (default ``None``, no expiry)
        More information: http://www.grantjenks.com/docs/diskcache

    """

    session: requests.Session = attr.ib(factory=RetrySession, repr=False)
    cache: t.Union[dcache.Cache, dcache.FanoutCache] = attr.ib()
    cache_expire: float = attr.ib(default=None, repr=False)

    @cache.default
    def _cache_default(self):
        return dcache.Cache(directory=DEFAULT_CACHE_DIR)

    def _determine_coordinate_system(self, ra, dec, glon, glat, sgl, sgb):

        # first we put all the parameters in a single dictionary
        params = {
            "ra": ra,
            "dec": dec,
            "glon": glon,
            "glat": glat,
            "sgl": sgl,
            "sgb": sgb,
        }

        # next we remove all keys with the default value `None`
        params = {k: v for k, v in params.items() if v is not None}

        # if we have 0 values no coordinate was given
        if len(params) == 0:
            raise ValueError(
                "No coordinate was provided. "
                "Please provide (ra, dec)', '(glon, glat)' or '(sgl, sgb)'.")

        # if we only have one we need to check wich one and inform about
        # the mising companinon
        if len(params) == 1:
            pname = list(params.keys())[0]
            coordinate_system, companion_dict = ((ALPHA_TO_COORDINATE[pname],
                                                  DELTA) if pname
                                                 in ALPHA_TO_COORDINATE else
                                                 (DELTA_TO_COORDINATE[pname],
                                                  ALPHA))
            companion = companion_dict[coordinate_system]
            raise ValueError(f"No {companion} provided")

        # if we have more than two parameter we have mixed coordinate system
        if len(params) > 2:
            raise MixedCoordinateSystemError(", ".join(params))

        # now we need to detemine the coordinate system
        coordinate_system_candidates = {
            ALPHA_DELTA_TO_COORDINATE[p]
            for p in params.keys()
        }

        # if we have more than 1 candidate we mix coordinates again
        if len(coordinate_system_candidates) > 1:
            raise MixedCoordinateSystemError(", ".join(params))

        # we have one coordinate system and we need to dermermine which
        # is alpha and wich is delta
        coordinate_system = coordinate_system_candidates.pop()
        alpha_coordinate_name = ALPHA[coordinate_system]
        delta_coordinate_name = DELTA[coordinate_system]

        alpha = params[alpha_coordinate_name]
        delta = params[delta_coordinate_name]

        return coordinate_system, alpha, delta

    def _search(
        self,
        coordinate_system,
        alpha,
        delta,
        distance,
        velocity,
        **get_kwargs,
    ):

        # The validations
        if coordinate_system not in CoordinateSystem:
            raise TypeError("coordinate_system must be a member of "
                            "pycf3.core.CoordinateSystem enum")

        if not isinstance(alpha, (int, float)):
            raise TypeError(f"{ALPHA[coordinate_system]} must be int or float")

        if not isinstance(delta, (int, float)):
            raise TypeError(f"{DELTA[coordinate_system]} must be int or float")
        elif not (-90 <= delta <= 90):
            raise ValueError(
                f"{DELTA[coordinate_system]} must be >= -90 and <= 90")

        if (distance, velocity) == (None, None):
            raise ValueError(
                "You must provide the distance or the velocity value")
        elif distance is not None and velocity is not None:
            raise ValueError(
                "You cant provide velocity and distance at the same time")

        if distance is not None:
            if not isinstance(distance, (int, float)):
                raise TypeError("'distance' must be int or float")
            elif not (0 < distance <= self.MAX_DISTANCE):
                raise ValueError(
                    f"'distance' must be > 0 and <= {self.MAX_DISTANCE}")
            parameter, value = Parameter.distance, distance

        elif velocity is not None:
            if not isinstance(velocity, (int, float)):
                raise TypeError("'velocity' must be int or float")
            elif not (0 < velocity <= self.MAX_VELOCITY):
                raise ValueError(
                    f"'velocity' must be > 0 and <= {self.MAX_VELOCITY}")

            parameter, value = Parameter.velocity, velocity

        payload = {
            "coordinate": [float(alpha), float(delta)],
            "system": coordinate_system.value,
            "parameter": parameter.value,
            "value": float(value),
        }

        # start the cache orchestration
        base = (
            self.CALCULATOR,
            coordinate_system.value,
        )
        key = dcache.core.args_to_key(base=base,
                                      args=(self.URL, ),
                                      kwargs=payload,
                                      typed=False)

        with self.cache as cache:
            cache.expire()
            response = cache.get(key, default=dcache.core.ENOVAL, retry=True)
            if response == dcache.core.ENOVAL:
                response = self.session.get(self.URL,
                                            json=payload,
                                            **get_kwargs)
                response.raise_for_status()
                cache.set(
                    key,
                    response,
                    expire=self.cache_expire,
                    tag="@".join(key[:2]),
                    retry=True,
                )

        result = Result(
            calculator=self.CALCULATOR,
            url=self.URL,
            coordinate=coordinate_system,
            calculated_by=parameter,
            alpha=alpha,
            delta=delta,
            distance=distance,
            velocity=velocity,
            response_=response,
        )

        return result

    # =========================================================================
    # INTERNALS
    # =========================================================================

    def __repr__(self):
        """x.__repr__() <==> repr(x)."""
        cls = type(self).__name__
        calculator = f"calculator='{self.CALCULATOR}'"
        cachedir = f"cache_dir='{getattr(self.cache, 'directory', '')}'"
        expire = f"cache_expire={self.cache_expire}"
        return f"{cls}({calculator}, {cachedir}, {expire})"

    # =========================================================================
    # API
    # =========================================================================

    def calculate_distance(
        self,
        velocity,
        *,
        ra=None,
        dec=None,
        glon=None,
        glat=None,
        sgl=None,
        sgb=None,
        **get_kwargs,
    ):
        """Calculate a distance based on the given velocity and location.

        The mandatory parameters are ``velocity`` and a position
        expressed in two components depending on the chosen coordinate system:

         - ``ra`` and ``dec`` for an equatorial system.
         - ``glon`` and ``glat`` for a galactic system.
         - ``sgl`` and ``sgb`` for a supergalactic system.

        Coordinates cannot be mixed between systems, and must be expressed in
        J2000 as 360° decimal.

        The returned distance(s) are expressed in Mpc, and potentially can
        be more than one value.

        Parameters
        ----------
        velocity : ``int`` or ``float``
            Model velocity in km/s.
        ra : ``int`` or ``float`` (optional)
            Right ascension. If you provide ``ra`` you need to provide also
            ``dec``.
        dec : ``int`` or ``float`` (optional)
            Declination. ``dec`` must be >= -90 and <= 90. If you provide
            ``dec`` you need to provide also ``ra``.
        glon : ``int`` or ``float`` (optional)
            Galactic longitude. If you provide ``glon`` you need to provide
            also ``glat``.
        glat: ``int`` or ``float`` (optional)
            Galactic latitude. ``glat`` must be >= -90 and <= 90. If you
            provide ``glat`` you need to provide also ``glon``.
        sgl : ``int`` or ``float`` (optional)
            Super-galactic longitude. If you provide ``sgl`` you need to
            provide also ``sgb``.
        sgb: ``int`` or ``float`` (optional)
            Super-galactic latitude. ``sgb`` must be >= -90 and <= 90.
            If you  provide ``sgb`` you need to provide also ``sgl``.
        get_kwargs:
            Optional arguments that ``request.get`` takes.

        Returns
        -------
        pycf3.Result :
            Result object that automatically parses the entire model
            returned by the remote calculator.

        """
        coordinate_system, alpha, delta = self._determine_coordinate_system(
            ra=ra, dec=dec, glon=glon, glat=glat, sgl=sgl, sgb=sgb)
        response = self._search(
            coordinate_system=coordinate_system,
            alpha=alpha,
            delta=delta,
            distance=None,
            velocity=velocity,
            **get_kwargs,
        )
        return response

    def calculate_velocity(
        self,
        distance,
        *,
        ra=None,
        dec=None,
        glon=None,
        glat=None,
        sgl=None,
        sgb=None,
        **get_kwargs,
    ):
        """Calculate a velocity based on the given distance and location.

        The mandatory parameters are ``distance`` and a position
        expressed in two components depending on the chosen coordinate system:

         - ``ra`` and ``dec`` for an equatorial system.
         - ``glon`` and ``glat`` for a galactic system.
         - ``sgl`` and ``sgb`` for a supergalactic system.

        Coordinates cannot be mixed between systems, and must be expressed in
        J2000 as 360° decimal.

        The returned velocity are expressed in Km/s.

        Parameters
        ----------
        distance : ``int`` or ``float``
            Distance(s) in Mpc.
        ra : ``int`` or ``float`` (optional)
            Right ascension. If you provide ``ra`` you need to provide also
            ``dec``.
        dec : ``int`` or ``float`` (optional)
            Declination. ``dec`` must be >= -90 and <= 90. If you provide
            ``dec`` you need to provide also ``ra``.
        glon : ``int`` or ``float`` (optional)
            Galactic longitude. If you provide ``glon`` you need to provide
            also ``glat``.
        glat: ``int`` or ``float`` (optional)
            Galactic latitude. ``glat`` must be >= -90 and <= 90. If you
            provide ``glat`` you need to provide also ``glon``.
        sgl : ``int`` or ``float`` (optional)
            Super-galactic longitude. If you provide ``sgl`` you need to
            provide also ``sgb``.
        sgb: ``int`` or ``float`` (optional)
            Super-galactic latitude. ``sgb`` must be >= -90 and <= 90.
            If you  provide ``sgb`` you need to provide also ``sgl``.
        get_kwargs:
            Optional arguments that ``request.get`` takes.

        Returns
        -------
        pycf3.Result :
            Result object that automatically parses the entire model
            returned by the remote calculator.

        """
        coordinate_system, alpha, delta = self._determine_coordinate_system(
            ra=ra, dec=dec, glon=glon, glat=glat, sgl=sgl, sgb=sgb)
        response = self._search(
            coordinate_system=coordinate_system,
            alpha=alpha,
            delta=delta,
            distance=distance,
            velocity=None,
            **get_kwargs,
        )
        return response

    # =========================================================================
    # OLD API
    # =========================================================================

    @deprecated(
        category=CFDeprecationWarning,
        action="once",
        reason="Use `calculate_velocity` or `calculate_distance` instead",
        version="2020.12",
    )
    def equatorial_search(
        self,
        ra=187.78917,
        dec=13.33386,
        distance=None,
        velocity=None,
        **get_kwargs,
    ):
        """Search by equatorial coordinates.

        The coordinates are expressed in J2000 as 360° decimal.

        .. deprecated:: 2020.12
            "Use `calculate_velocity` or `calculate_distance` instead"

        Parameters
        ----------
        ra : ``int`` or ``float`` (default: ``187.78917``)
            Right ascension.
        dec : ``int`` or ``float`` (default: ``13.33386``)
            Declination. dec must be >= -90 and <= 90
        distance : ``int``, ``float`` or ``None`` (default: ``None``)
            Distance(s) in Mpc.
        velocity : ``int``, ``float`` or ``None`` (default: ``None``)
            Velocity in km/s. The returned distance potentially can be more
            than ine value.
        get_kwargs:
            Optional arguments that ``request.get`` takes.

        Returns
        -------
        pycf3.Result :
            Result object that automatically parses the entire model
            returned by the remote calculator.

        """
        response = self._search(
            CoordinateSystem.equatorial,
            alpha=ra,
            delta=dec,
            distance=distance,
            velocity=velocity,
            **get_kwargs,
        )
        return response

    @deprecated(
        category=CFDeprecationWarning,
        action="once",
        reason="Use `calculate_velocity` or `calculate_distance` instead",
        version="2020.12",
    )
    def galactic_search(
        self,
        glon=282.96547,
        glat=75.41360,
        distance=None,
        velocity=None,
        **get_kwargs,
    ):
        """Search by galactic coordinates.

        The coordinates are expressed in J2000 as 360° decimal.

        .. deprecated:: 2020.12
            "Use `calculate_velocity` or `calculate_distance` instead"

        Parameters
        ----------
        glon : ``int`` or ``float`` (default: ``282.96547``)
            Galactic longitude.
        glat: ``int`` or ``float`` (default: ``75.41360``)
            Galactic latitude. dec must be >= -90 and <= 90
        distance : ``int``, ``float`` or ``None`` (default: ``None``)
            Distance(s) in Mpc.
        velocity : ``int``, ``float`` or ``None`` (default: ``None``)
            Velocity in km/s. The returned distance potentially can be more
            than ine value.
        get_kwargs:
            Optional arguments that ``request.get`` takes.

        Returns
        -------
        pycf3.Result :
            Result object that automatically parses the entire model
            returned by the remote calculator.

        """
        response = self._search(
            CoordinateSystem.galactic,
            alpha=glon,
            delta=glat,
            distance=distance,
            velocity=velocity,
            **get_kwargs,
        )
        return response

    @deprecated(
        category=CFDeprecationWarning,
        action="once",
        reason="Use `calculate_velocity` or `calculate_distance` instead",
        version="2020.12",
    )
    def supergalactic_search(
        self,
        sgl=102.0,
        sgb=-2.0,
        distance=None,
        velocity=None,
        **get_kwargs,
    ):
        """Search super-galactic coordinates.

        The coordinates are expressed in J2000 as 360° decimal.

        .. deprecated:: 2020.12
            "Use `calculate_velocity` or `calculate_distance` instead"

        Parameters
        ----------
        sgl : ``int`` or ``float`` (default: ``102``)
            Super-galactic longitude.
        sgb: ``int`` or ``float`` (default: ``-2``)
            Super-galactic latitude. dec must be >= -90 and <= 90
        distance : ``int``, ``float`` or ``None`` (default: ``None``)
            Distance(s) in Mpc.
        velocity : ``int``, ``float`` or ``None`` (default: ``None``)
            Velocity in km/s. The returned distance potentially can be more
            than ine value.
        get_kwargs:
            Optional arguments that ``request.get`` takes.

        Returns
        -------
        pycf3.Result :
            Result object that automatically parses the entire model
            returned by the remote calculator.

        """
        response = self._search(
            CoordinateSystem.supergalactic,
            alpha=sgl,
            delta=sgb,
            distance=distance,
            velocity=velocity,
            **get_kwargs,
        )
        return response
예제 #12
0
class GradesBaseSender(metaclass=DocInheritMeta(style='google_with_merge', include_special_methods=True)):
    '''
    This class helps to send student grades from nbgrader database.
    Classes that inherit from this class must implement
    the send_grades() method.

    Args:
        course_id (str): Course id or name used in nbgrader
        assignment_name (str): Assignment name that needs to be processed
            and from which the grades are retrieved

    Attributes:
        helper (NBGraderHelper): .
        course (Course): .
        all_lineitems (list): .
        headers (dict): .
        course_id (str): .
        assignment_name (str): .

    '''

    def __init__(self, course_id: str, assignment_name: str):
        '''
        Check if required enviroments is set.

        Args:
            course_id (str): .
            assignment_name (str): .

        Raises:
            EnvironmentError: If one of environment variable is not set.

        '''

        envs = ('LTI13_PRIVATE_KEY', 'LTI13_TOKEN_URL', 'LTI13_CLIENT_ID')

        if any(os.environ.get(env) is None for env in envs):
            raise EnvironmentError(', '.join(envs) + ' should be set.')

        self.course_id = course_id
        self.assignment_name = assignment_name

        self.helper = NBGraderHelper()

        self.course = self.helper.get_course(self.course_id)

        self.all_lineitems = []
        self.headers = {}

    async def send_grades(self):
        raise NotImplementedError

    @property
    def grader_name(self) -> str:
        return f'grader-{self.course_id}'

    @property
    def gradebook_dir(self) -> str:
        return f'/home/{self.grader_name}/{self.course_id}'

    def _retrieve_grades_from_db(self) -> t.Tuple[int, t.List[dict]]:
        '''Gets grades from the database'''

        out: t.List[dict] = []

        max_score = 0

        # Create the connection to the gradebook database
        with self.helper.get_db(self.course_id) as gb:

            try:

                # retrieve the assignment record
                assignment_row = gb.find_assignment(self.assignment_name)

                max_score = assignment_row.max_score

                submissions = gb.assignment_submissions(self.assignment_name)

                logger.info(
                    f'Found {len(submissions)} submissions for assignment: {self.assignment_name}'
                )

            except MissingEntry as exc:
                logger.error(f'Assignment not found in database: {exc}')
                raise GradesSenderMissingInfoError(self.course_id) from exc

            for submission in submissions:

                # retrieve the student to use the lms id
                student = gb.find_student(submission.student_id)

                out.append({
                    'score': submission.score,
                    'lms_user_id': student.lms_user_id
                })

        logger.info(f'Grades found: {out}')
        logger.info(f'Maximum score for this assignment {max_score}')

        return max_score, out
예제 #13
0
class BaseAPIClient(metaclass=DocInheritMeta(style='google_with_merge',
                                             include_special_methods=True)):
    '''Moodle webservice interface.
    Based on gist of https://gist.github.com/kaqfa

    Initialize Moodle API Client.

    In order to make REST API requests a client need to obtain
    both Moodle URL and Moodle API token.

    Read more about Moodle REST API in docs/Setup Moodle.md

    You can pass url or token directly as a keyword-only
    string values or pass url_env_name or key_env_name keyword-only
    arguments to obtain url or token from environment if they
    differ from defaults.

    You can also provide client with a custom endpoint if it's
    neccesary in your case. With classic setup it defaults to
    /webservice/rest/server.php

    Args:
        url (:obj:`str`, optional): Moodle domain name. Defaults to None.
        key (:obj:`str`, optional): Moodle web service token. Defaults to None.
        endpoint (:obj:`str`, optional): Custom endpoint. Defaults to None.
        url_env_name (:obj:`str`, optional): Custom environment variable name. Defaults to None.
        key_env_name (:obj:`str`, optional): Custom environment variable name. Defaults to None.

    '''

    DEFAULT_ENDPOINT: str = '/webservice/rest/server.php'
    URL_ENV_NAME: str = 'MOODLE_BASE_URL'
    KEY_ENV_NAME: str = 'MOODLE_API_TOKEN'

    key: str
    url: str
    endpoint: str

    def __init__(
        self,
        url: t.Optional[str] = None,
        key: t.Optional[str] = None,
        endpoint: t.Optional[str] = None,
        url_env_name: t.Optional[str] = None,
        key_env_name: t.Optional[str] = None,
    ):

        # If url or key is not provided,
        # Check that appropriate enviroment variable is set
        # if env_name is provided prefer it over default one.
        # raise EnvironmentError if env is not set.
        for attr, env_name in (
            (url, 'url_env_name'),
            (key, 'key_env_name'),
        ):

            if attr is None:

                attr_name = locals()[env_name] or getattr(
                    self, env_name.upper())

                attr = os.getenv(attr_name)

                if not attr:

                    raise EnvironmentError(f'{attr_name} must be set.')

            setattr(self, env_name[:3], attr)

        self.endpoint: str = endpoint or self.DEFAULT_ENDPOINT

    def rest_api_parameters(
        self,
        in_args: t.Union[t.Sequence, t.Mapping],
        prefix: str = '',
        out_dict: t.Optional[dict] = None,
    ) -> JsonType:
        '''
        Transform dictionary/array structure to a flat dictionary, with key names
        defining the structure.

        Examples:

            Convert parameters::

                rest_api_parameters({'courses':[{'id': 1,'name': 'course1'}]})
                >>> {'courses[0][id]': 1, 'courses[0][name]':'course1'}
        '''

        if out_dict is None:
            out_dict = {}

        if not isinstance(in_args, (list, dict)):
            out_dict[prefix] = in_args
            return out_dict

        prefix += '{0}' if not prefix else '[{0}]'

        sequence = enumerate(in_args) if isinstance(in_args,
                                                    list) else in_args.items()

        for idx, item in sequence:
            self.rest_api_parameters(item, prefix.format(idx), out_dict)

        return out_dict

    def call(self, api_func: str, **kwargs: t.Any) -> FluidResponse:
        '''
        Calls Moodle API <api_func> with keyword arguments.

        Args:
            api_func (:obj:`str`): name of function in Moodle web client.
            **kwargs: any parameters to send with the request.

        Examples:

            Calling *core_course_update_courses* function::

                call_mdl_function(
                    'core_course_update_courses',
                    courses = [{'id': 1, 'fullname': 'My favorite course'}]
                )

        Returns:
            FluidResponse: Convinient wrapper for received data.


        Raises:
            requests.HTTPError: Network or server error
            MoodleHTTPException: non successful HTTP status.
            MoodleAPIException: Syntax error in API call.
        '''

        parameters = self.rest_api_parameters(kwargs)

        logger.debug(f'''
        Calling {api_func!r}

        {dump_json(parameters)[1:-1] or 'Without parameters'}
        ''')

        parameters.update({
            'wstoken': self.key,
            'moodlewsrestformat': 'json',
            'wsfunction': api_func,
        })

        resp: Response = requests.post(self.url + self.endpoint, parameters)

        resp.raise_for_status()

        # can raise MoodleHTTPException and MoodleAPIException
        return FluidResponse(resp, api_func)
예제 #14
0
from custom_inherit import DocInheritMeta

try:
    from inspect import signature
except ImportError:
    from inspect import getargspec as signature


def style(x, y):
    return "valid"


""" With ABC"""


@add_metaclass(DocInheritMeta(style=style, abstract_base_class=True))
class Parent(object):
    def method(self, x, y=None):
        """"""
        pass

    @classmethod
    def clsmthd(cls):
        """"""
        pass

    @staticmethod
    def static():
        """"""
        pass
예제 #15
0
class OrderBook(metaclass=DocInheritMeta(style="numpy",
                                         abstract_base_class=True)):
    """ A abstract class defining a order book interface.

    The main functions are the functions for the messages. Sending a limit order, market order etc.
    The order book handles the matching and storing of limit orders.

    Important notes is the speed of adding a limit order, cancelling a limit order or updating a limit order.

    Attributes
    ----------
    orders : dict
        All current orders in the order book, key is the order id
    order_id : int
        The internal order id set by the order book. Is incremented for each order sent to the order book.

    """
    def __init__(self, price_level_type='cydeque', price_levels_type='cylist'):
        self.price_levels = get_price_levels(price_levels_type,
                                             price_level_type)
        self.orders = {}
        self.order_id = 0

    @abc.abstractmethod
    def limit(self, price: int, side: int, size: float, trader_id: int,
              time: str) -> (list, tuple):
        """
        Handles a limit order sent to the order book. Matches the limit order if possible,
        otherwise puts it in the order book.

        Parameters
        ----------
        price: float
            Price of the order.
        side: int
            BUY or SELL, see :py:mod:´OrderBookRL.order_book.constants´
        size: float
            Size of the order.
        trader_id: int
            Id of the trader sending the order
        time: str

        Returns
        -------
            trades, order_in_book
                trades: list
                    If trades have occurred due the the message, all the trades are returned. Otherwise empty list
                order_in_book: tuple
                    If a limit order has been placed which size has not fully been matched, the remaining order in book
                    is returned. Otherwise -1.

        """

    @abc.abstractmethod
    def market_order(self, size: float, side: int, trader_id: int,
                     time: str) -> list:
        """
        Handles a market order sent to the order book. Matches the market order if possible.

        Parameters
        ----------
        side: int
            BUY or SELL, see :py:mod:´OrderBookRL.order_book.constants´
        size: float
            Size of the order.
        trader_id: int
            Id of the trader sending the order
        time: str

        Returns
        -------
            trades: list
                If trades have occurred due the the message, all the trades are returned. Otherwise empty list

        """

    @abc.abstractmethod
    def market_order_funds(self, funds: float, side: int, trader_id: int,
                           time: str) -> list:
        """
        Handles a market order sent to the order book. Matches the market order if possible.

        Parameters
        ----------
        funds: float
            Size of the order.
        side: int
            BUY or SELL, see :py:mod:´OrderBookRL.order_book.constants´
        trader_id: int
            Id of the trader sending the order
        time: str

        Returns
        -------
            trades: list
                If trades have occurred due the the message, all the trades are returned. Otherwise empty list

        """

    @abc.abstractmethod
    def cancel(self, order_id: int):
        """
        Attempts to cancel a order by its order id

        Parameters
        ----------
        order_id: int
            The order id of the order to be cancelled

        """

    @abc.abstractmethod
    def update(self, order_id: int, size: float):
        """
예제 #16
0
파일: actors.py 프로젝트: ntaylorwss/pavlov
class Actor(metaclass=DocInheritMeta(style="numpy")):
    """Component responsible both for exploration and converting model predictions to actions.

    Returns 2 actions at a time: the first to be consumed by the model,
                                 the second to be consumed by the environment.

    Attributes
    -------
    explore_and_convert_fns : dict of {str : dict of {str : function}}
        Functions corresponding to every combination of action space and model type.
        Action space is the first key, model type is the second key.
        The functions add exploration and convert the chosen action to the correct format.
    action_space : gym.Space
        The action space object of the associated environment.
    action_type : str
        The class name of the action space as a lowercased string.
    prediction_type : {'value', 'policy'}
        Indicates whether the model outputs action-values or a policy vector for the state.
    """
    def __init__(self):
        # a dictionary of functions makes choosing the method of conversion easier
        self.explore_and_convert_fns = {
            'discrete': {
                'policy': self._discrete_policy,
                'value': self._discrete_value
            },
            'multidiscrete': {
                'policy': self._multidiscrete_policy,
                'value': self._multidiscrete_value
            },
            'box': {
                'policy': self._box_policy,
                'value': self._box_value
            },
            'multibinary': {
                'policy': self._multibinary_policy,
                'value': self._multibinary_value
            }
        }
        self.action_space = None
        self.action_type = None
        self.prediction_type = None

    def configure(self, agent):
        """Associate actor with agent, picking up information about its action space and model."""
        self.action_space = agent.env.action_space
        self.action_type = self.action_space.__class__.__name__.lower()
        self.prediction_type = agent.model.prediction_type

    def convert_pred(self, pred):
        """Look up explore_and_convert function and apply it to model prediction."""
        return self.explore_and_convert_fns[self.action_type][
            self.prediction_type](pred)

    def warming_action(self):
        """Choose a random action without involving the model.

        Returns
        -------
        action_for_model : numpy.ndarray
            The action that will be consumed by the model for learning.
        action_for_env : numpy.ndarray or int or float
            The action that will be consumed by the environment to step.
        """
        action_for_env = self.action_space.sample()
        if self.action_type == 'discrete':
            action_for_model = np.eye(self.action_space.n)[action_for_env]
        elif self.action_type == 'multidiscrete':
            action_for_model = [
                np.eye(n)[action_for_env[i]]
                for i, n in enumerate(self.action_space.nvec)
            ]
        elif self.action_type == 'box':
            action_for_model = action_for_env
        elif self.action_type == 'multibinary':
            action_for_model = [np.eye(2)[env_a] for env_a in action_for_env]
        return action_for_model, action_for_env

    def step(self, new_episode):
        """Move one timestep ahead. Main purpose is to advance value schedules.

        Parameters
        ----------
        new_episode : bool
            A flag indicating whether this step is one that resets the environment.
        """
        pass

    def _discrete_policy(self, pred):
        """Exploration and conversion function for a Discrete action space + Policy model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _discrete_value(self, pred):
        """Exploration and conversion function for a Discrete action space + Value model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _multidiscrete_policy(self, pred):
        """Exploration and conversion function for a Multi-Discrete action space + Policy model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _multidiscrete_value(self, pred):
        """Exploration and conversion function for a Multi-Discrete action space + Value model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _box_policy(self, pred):
        """Exploration and conversion function for a Box action space + Policy model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _box_value(self, pred):
        """Exploration and conversion function for a Box action space + Value model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _multibinary_policy(self, pred):
        """Exploration and conversion function for a Multi-Binary action space + Policy model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        raise NotImplementedError

    def _multibinary_value(self, pred):
        """Exploration and conversion function for a Multi-Binary action space + Value model.

        Parameters
        ----------
        pred : numpy.ndarray
            The prediction output by the model, to be converted.

        Raises
        ------
        NotImplementedError.
        """
        pass
예제 #17
0
    """
    Python 2 and python 3 differ in decorator for abstract property.
    in python 3 (gt 3.3) it is:
        @property
        @abstractproperty
    in python 2
        @abstractproperty
    """

    if PY2:  # pylint: disable=no-else-return
        return abstractproperty(func)
    else:
        return property(abstractmethod(func))


@add_metaclass(DocInheritMeta(style="google", abstract_base_class=True))
class BaseObjectStore(object):
    """
    An abstract class that defines the APIs for the methods of an object store
    for CIM objects including CIMClass, CIMInstance, and CIMQualifierDeclaration
    objects that constitute a WBEM server repository.  This
    class provides the abstract methods for creating, accessing, and deleting,
    CIM objects of a single CIM object type in the repository.

    CIM objects in the object store are identified by a name which is part of
    the methods that access the CIM objects and must be unique within a single
    object store.

    Each object store conatins only a single CIM object type.
    """
    @abstractmethod
예제 #18
0
class PriceLevel(metaclass=DocInheritMeta(style="numpy",
                                          abstract_base_class=True)):
    """ A price level containing orders.

    A price level contains orders with the same price. The total size of the level is the sum of all order sizes.

    Attributes
    ----------
    size : float
        The total size of all orders in the price level.
    orders
        All the orders in the price level.

    """
    def __init__(self):
        """ Initializes the size to 0.

        """
        self.size = 0

    def append(self, order: list):
        """ Adds an order to the end of the price level and adds the size.

        Parameters
        ----------
        order: list
            The order to be added.

        """
        self._add(order)
        self.size += order[O_SIZE]

    @abc.abstractmethod
    def _add(self, order: list):
        """ Adds the order to the end of the list

        Parameters
        ----------
        order: list
            The order to be added.

        """

    def delete(self, order: list):
        """ Deletes an order from the price level and removes the size.

        Parameters
        ----------
        order: list
            The order to be added.

        """
        self._remove(order)
        self.size -= order[O_SIZE]

    @abc.abstractmethod
    def _remove(self, order: list):
        """ Removes an order from the price level and removes the size.

        Parameters
        ----------
        order: list
            The order to be removed.

        """

    def update(self, order, diff: float):
        """ Updates an order from the price level with an added difference.

        Parameters
        ----------
        diff
        order: list
            The order to be added.

        """
        self.size += diff
        order[O_SIZE] += diff

    @abc.abstractmethod
    def get_first(self):
        """ Returns the first order of the price level

        Returns
        -------
        order
            The first order of the price level.

        """

    def delete_first(self, order: list):
        """ Deletes the first order from the price level and removes the size.

        Parameters
        ----------
        order: list
            The order to be removed.

        """
        self._remove_first()
        self.size -= order[O_SIZE]

    @abc.abstractmethod
    def _remove_first(self):
        """ Removes the first order from the price level.

        Parameters
        ----------
        order: list
            The order to be removed.

        """

    @abc.abstractmethod
    def is_not_empty(self) -> bool:
        """ Returns true if the price level is not empty

        Returns
        -------
        bool
            True if successful, False otherwise.

        """

    @abc.abstractmethod
    def get_last(self):
        """ Returns the last order of the price level

        Returns
        -------
        order
            The last order of the price level.

        """

    def delete_last(self, order: list):
        """ Deletes the last order from the price level and removes the size.

        Parameters
        ----------
        order: list
            The order to be removed.

        """
        self._remove_last()
        self.size -= order[O_SIZE]

    @abc.abstractmethod
    def _remove_last(self):
        """ Removes the last order from the price level.

        Parameters
        ----------
        order: list
            The order to be removed.

        """

    @abc.abstractmethod
    def is_empty(self):
        """ Returns true if the price level is empty
예제 #19
0
from types import MethodType, FunctionType

try:
    from inspect import signature
except ImportError:
    from inspect import getargspec as signature


def style(x, y):
    return "valid"


""" With ABC"""


@six.add_metaclass(DocInheritMeta(style=style, abstract_base_class=True))
class Parent(object):
    def method(self, x, y=None):
        """"""
        pass

    @classmethod
    def clsmthd(cls):
        """"""
        pass

    @staticmethod
    def static():
        """"""
        pass
예제 #20
0
class Agent(metaclass=DocInheritMeta(style="numpy")):
    """Composes an environment, data pipeline, model, and actor to perform reinforcement learning.

    The modular philosophy of the library means that this agent should be
    responsible for no more than calling its members to run timesteps,
    and to loop over timesteps to run episodes. The setup is:
        The environment provides a state,
        it's passed through the pipeline,
        the model makes a prediction for that state,
        the actor converts that prediction to an action,
        the environment consumes that action,
        the monitor keeps track of the important things,
        and sometimes the model learns from the replay buffer.

    Each of these components knows its Agent. This is how information is passed between modules.

    Parameters
    ----------
    env : gym.Env
        the environment the agent is acting in.
    state_pipeline : list of functions
        a list of functions that the state is passed through sequentially.
    model : pavlov.Model
        a reinforcement learning model that guides the agent.
    actor : pavlov.Actor
        responsible for converting model predictions to actions.
    buffer_size : int
        limit for how many observations to hold in replay buffer.
    batch_size : int
        number of observations to pull from replay_buffer at fit time.
    warmup_length : int
        number of random timesteps to execute before beginning
        to learn and apply the model. Replay buffer will be populated.
    repeated_actions : int
        number of env timesteps to repeat a chosen action for.
    report_frequency : int
        interval for printing to stdout, in number of episodes.
    state_dtype : type
        numpy datatype for states to be stored in in replay buffer.

    Attributes
    ----------
    env : gym.Env
        the environment the agent is acting in.
    env_state : np.array
        current state of environment.
    state_pipeline : list of functions
        a list of functions that the state is passed through sequentially.
    model : pavlov.Model
        a reinforcement learning model that guides the agent.
    actor : pavlov.Actor
        responsible for converting model predictions to actions.
    replay_buffer : pavlov.ReplayBuffer
        collection of historical observations.
    batch_size : int
        number of observations to pull from replay_buffer at fit time.
    warmup_length : int
        number of random timesteps to execute before beginning
        to learn and apply the model. Replay buffer will be populated.
    repeated_actions : int
        number of env timesteps to repeat a chosen action for.
    monitor : pavlov.Monitor
        keeps track of metrics and logs them.
    renders_by_episode : list of np.array
        environment timestep renderings by episode.
    """
    # incompatible pairs of action space type and model type
    incompatibles = [('box', 'dqnmodel'), ('discrete', 'ddpgmodel'),
                     ('multidiscrete', 'ddpgmodel'),
                     ('multibinary', 'ddpgmodel')]

    def __init__(self,
                 env,
                 state_pipeline,
                 model,
                 actor,
                 buffer_size,
                 batch_size,
                 warmup_length,
                 repeated_actions=1,
                 report_frequency=100,
                 state_dtype=np.float32):
        # check if model and action space are compatible
        for space_type, model_type in self.incompatibles:
            if (env.action_space.__class__.__name__.lower() == space_type
                    and model.__class__.__name__.lower() == model_type):
                raise util.exceptions.ActionModelMismatchError(
                    space_type, model_type)

        self.session = tf.Session()
        K.set_session(self.session)

        self.env = env
        self.env_state = self.env.reset()

        self.batch_size = batch_size
        self.warmup_length = warmup_length
        self.repeated_actions = repeated_actions
        self.renders_by_episode = [[]]
        self.start_timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%s')

        self.state_pipeline = state_pipeline
        self.replay_buffer = ReplayBuffer(buffer_size, state_dtype)
        self.model = model
        self.actor = actor
        self.monitor = Monitor(report_frequency, '/var/log')
        self.state_pipeline.configure(self)
        self.replay_buffer.configure(self)
        self.model.configure(self, self.session)
        self.actor.configure(self)
        self.monitor.configure(self, self.session)

        self._warmup_replay_buffer()

    @property
    def episode(self):
        """Gives the current episode number."""
        return len(self.renders_by_episode)

    def _warmup_replay_buffer(self):
        """Run replay buffer-populating timesteps before actually starting."""
        for i in range(self.warmup_length):
            done = self.run_timestep(warming=True)
            if done:
                self.reset()

    def episode_to_mp4(self, episode_num, out_dir):
        """Generates mp4 of agent's timesteps for given episode number.

        Only works with environments that render images at each timestep.

        Parameters
        ----------
        episode_num : int
            episode number that you want to take a video of (one-indexed).
        out_dir : str
            directory where you would like to place video file.
            filename is auto-generated.
        """
        frames = self.renders_by_episode[episode - 1]
        shape = frames[0].shape
        if not ((len(shape) == 2) or (len(shape) == 3 and shape[2] == 3)):
            raise TypeError("Environment renderings are not images")

        size = frames[0].shape[:-1]
        fps = 20
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        vid_out = cv2.VideoWriter()
        filename = '{}/{}-episode{}.mp4'.format(self.start_timestamp, out_dir,
                                                episode)
        vid_out.open(filename, fourcc, fps, size, True)
        for frame in frames:
            vid_out.write(frame)
        vid_out.release()

    def reset(self):
        """Resets environment to initial state for new episode."""
        self.env_state = self.env.reset()

    def run_timestep(self, warming=False, render=False):
        """Run one timestep in the environment.

        - Process current state with `state_pipeline`.
        - Generate prediction from `model`.
        - Convert prediction to action twice; one format for replay buffer, one for environment.
        - Apply action in environment, observe reward and next state.
        - Store experience in replay buffer.
        - Every `batch_size` timesteps, fit model to random batch from replay buffer.

        Parameters
        ----------
        warming : bool
            indicate whether this is a step meant to simply populate the replay buffer.
        render : bool
            indicate whether to log an image of the environment for video generation.
        """
        start_state = self.state_pipeline.transform(self.env_state)
        if warming:
            action_for_model, action_for_env = self.actor.warming_action()
        else:
            pred = self.model.predict(start_state)
            action_for_model, action_for_env = self.actor.convert_pred(
                pred)  # includes exploration

        timestep_reward = 0.
        for i in range(self.repeated_actions):
            if render:
                if not warming:
                    rendered_frame = self.env.render(mode='rgb_array')
                    self.renders_by_episode[-1].append(rendered_frame)
            else:
                self.renders_by_episode[-1].append([])
            self.env_state, frame_reward, done, info = self.env.step(
                action_for_env)
            timestep_reward += frame_reward
            if done:
                break

        if not warming:
            self.actor.step(done)
            self.monitor.step(timestep_reward)

        if done:
            next_state = np.zeros(self.replay_buffer['states'].shape[1:])
        else:
            next_state = self.state_pipeline.transform(self.env_state)

        self.replay_buffer.add(start_state, action_for_model, timestep_reward,
                               next_state, done)
        if not warming and self.replay_buffer.current_size >= self.batch_size:
            batch = self.replay_buffer.get_batch(self.batch_size)
            self.model.fit(**batch)

        return done

    def run_episode(self, render=False, do_logging=True):
        """Apply `run_timestep` until episode terminates.

        Parameters
        ----------
        render : bool
            indicate whether to log an image of the environment for video generation.
        do_logging : bool
            indicate whether to print out metrics for episode.
        """
        self.renders_by_episode.append([])
        while True:
            done = self.run_timestep(render=render)
            if done:
                self.monitor.new_episode(do_logging)
                self.reset()
                break

    def run_indefinitely(self, render=False, log=True):
        """Apply run_episode in an infinite loop; terminated by KeyboardInterrupt.

        The keyboard interrupt will be handled by first finishing the running episode,
        then terminating the loop and thus function.

        Parameters
        ----------
        render : bool
            indicate whether to log an image of the environment for video generation.
        do_logging : bool
            indicate whether to print out metrics for episode.
        """
        with util.interrupt.AtomicLoop() as loop:
            while loop.run:
                self.run_episode(render=render, do_logging=log)