text:
"""The :mod:`~control.optimal` module provides support for optimization-based
controllers for nonlinear systems with state and input constraints.

"""

import numpy as np
import scipy as sp
import scipy.optimize as opt
import control as ct
import warnings
import logging
import time

from . import config
from .exception import ControlNotImplemented
from .timeresp import TimeResponseData

__all__ = ['find_optimal_input']

# Define module default parameter values
_optimal_defaults = {
    'optimal.minimize_method': None,
    'optimal.minimize_options': {},
    'optimal.minimize_kwargs': {},
    'optimal.solve_ivp_method': None,
    'optimal.solve_ivp_options': {},
}

class OptimalControlProblem():
    """Description of a finite horizon, optimal control problem.

    The `OptimalControlProblem` class holds all of the information required to
    specify an optimal control problem: the system dynamics, cost function,
    and constraints.  As much as possible, the information used to specify an
    optimal control problem matches the notation and terminology of the SciPy
    `optimize.minimize` module, with the hope that this makes it easier to
    remember how to describe a problem.

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the optimal input will be computed.
    timepts : 1D array_like
        List of times at which the optimal input should be computed.
    integral_cost : callable
        Function that returns the integral cost given the current state
        and input.  Called as integral_cost(x, u).
    trajectory_constraints : list of tuples, optional
       List of constraints that should hold at each point in the time
       vector.  Each element of the list should consist of a tuple with
       first element given by :meth:`~scipy.optimize.LinearConstraint` or
       :meth:`~scipy.optimize.NonlinearConstraint` and the remaining
       elements of the tuple are the arguments that would be passed to
       those functions.  The constraints will be applied at each time
       point along the trajectory.
    terminal_cost : callable, optional
        Function that returns the terminal cost given the current state
        and input.  Called as terminal_cost(x, u).
    initial_guess : 1D or 2D array_like
        Initial inputs to use as a guess for the optimal input.  The
        inputs should either be a 2D vector of shape (ninputs, horizon)
        or a 1D input of shape (ninputs,) that will be broadcast by
        extension of the time axis.
    log : bool, optional
        If `True`, turn on logging messages (using Python logging module).
        Use ``logging.basicConfig`` to enable logging output (e.g., to a file).
    kwargs : dict, optional
        Additional parameters (passed to :func:`scipy.optimal.minimize`).

    Returns
    -------
    ocp : OptimalControlProblem
        Optimal control problem object, to be used in computing optimal
        controllers.

    Additional parameters
    ---------------------
    solve_ivp_method : str, optional
        Set the method used by :func:`scipy.integrate.solve_ivp`.
    solve_ivp_kwargs : str, optional
        Pass additional keywords to :func:`scipy.integrate.solve_ivp`.
    minimize_method : str, optional
        Set the method used by :func:`scipy.optimize.minimize`.
    minimize_options : str, optional
        Set the options keyword used by :func:`scipy.optimize.minimize`.
    minimize_kwargs : str, optional
        Pass additional keywords to :func:`scipy.optimize.minimize`.

    Notes
    -----
    To describe an optimal control problem we need an input/output system, a
    time horizon, a cost function, and (optionally) a set of constraints on
    the state and/or input, either along the trajectory and at the terminal
    time.  This class sets up an optimization over the inputs at each point in
    time, using the integral and terminal costs as well as the trajectory and
    terminal constraints.  The `compute_trajectory` method sets up an
    optimization problem that can be solved using
    :func:`scipy.optimize.minimize`.

    The `_cost_function` method takes the information computes the cost of the
    trajectory generated by the proposed input.  It does this by calling a
    user-defined function for the integral_cost given the current states and
    inputs at each point along the trajectory and then adding the value of a
    user-defined terminal cost at the final point in the trajectory.

    The `_constraint_function` method evaluates the constraint functions along
    the trajectory generated by the proposed input.  As in the case of the
    cost function, the constraints are evaluated at the state and input along
    each point on the trajectory.  This information is compared against the
    constraint upper and lower bounds.  The constraint function is processed
    in the class initializer, so that it only needs to be computed once.

    If `basis` is specified, then the optimization is done over coefficients
    of the basis elements.  Otherwise, the optimization is performed over the
    values of the input at the specified times (using linear interpolation for
    continuous systems).

    The default values for ``minimize_method``, ``minimize_options``,
    ``minimize_kwargs``, ``solve_ivp_method``, and ``solve_ivp_options`` can
    be set using config.defaults['optimal.<keyword>'].

    """
    def __init__(
            self, sys, timepts, integral_cost, trajectory_constraints=[],
            terminal_cost=None, terminal_constraints=[], initial_guess=None,
            basis=None, log=False, **kwargs):
        """Set up an optimal control problem."""
        # Save the basic information for use later
        self.system = sys
        self.timepts = timepts
        self.integral_cost = integral_cost
        self.terminal_cost = terminal_cost
        self.terminal_constraints = terminal_constraints
        self.basis = basis

        # Process keyword arguments
        self.solve_ivp_kwargs = {}
        self.solve_ivp_kwargs['method'] = kwargs.pop(
            'solve_ivp_method', config.defaults['optimal.solve_ivp_method'])
        self.solve_ivp_kwargs.update(kwargs.pop(
            'solve_ivp_kwargs', config.defaults['optimal.solve_ivp_options']))

        self.minimize_kwargs = {}
        self.minimize_kwargs['method'] = kwargs.pop(
            'minimize_method', config.defaults['optimal.minimize_method'])
        self.minimize_kwargs['options'] = kwargs.pop(
            'minimize_options', config.defaults['optimal.minimize_options'])
        self.minimize_kwargs.update(kwargs.pop(
            'minimize_kwargs', config.defaults['optimal.minimize_kwargs']))

        # Make sure there were no extraneous keywords
        if kwargs:
            raise TypeError("unrecognized keyword(s): ", str(kwargs))

        # Process trajectory constraints
        if isinstance(trajectory_constraints, tuple):
            self.trajectory_constraints = [trajectory_constraints]
        elif not isinstance(trajectory_constraints, list):
            raise TypeError("trajectory constraints must be a list")
        else:
            self.trajectory_constraints = trajectory_constraints

        # Make sure that we recognize all of the constraint types
        for ctype, fun, lb, ub in self.trajectory_constraints:
            if not ctype in [opt.LinearConstraint, opt.NonlinearConstraint]:
                raise TypeError(f"unknown constraint type {ctype}")

        # Process terminal constraints
        if isinstance(terminal_constraints, tuple):
            self.terminal_constraints = [terminal_constraints]
        elif not isinstance(terminal_constraints, list):
            raise TypeError("terminal constraints must be a list")
        else:
            self.terminal_constraints = terminal_constraints

        # Make sure that we recognize all of the constraint types
        for ctype, fun, lb, ub in self.terminal_constraints:
            if not ctype in [opt.LinearConstraint, opt.NonlinearConstraint]:
                raise TypeError(f"unknown constraint type {ctype}")

        #
        # Compute and store constraints
        #
        # While the constraints are evaluated during the execution of the
        # SciPy optimization method itself, we go ahead and pre-compute the
        # `scipy.optimize.NonlinearConstraint` function that will be passed to
        # the optimizer on initialization, since it doesn't change.  This is
        # mainly a matter of computing the lower and upper bound vectors,
        # which we need to "stack" to account for the evaluation at each
        # trajectory time point plus any terminal constraints (in a way that
        # is consistent with the `_constraint_function` that is used at
        # evaluation time.
        #
        constraint_lb, constraint_ub, eqconst_value = [], [], []

        # Go through each time point and stack the bounds
        for t in self.timepts:
            for type, fun, lb, ub in self.trajectory_constraints:
                if np.all(lb == ub):
                    # Equality constraint
                    eqconst_value.append(lb)
                else:
                    # Inequality constraint
                    constraint_lb.append(lb)
                    constraint_ub.append(ub)

        # Add on the terminal constraints
        for type, fun, lb, ub in self.terminal_constraints:
            if np.all(lb == ub):
                # Equality constraint
                eqconst_value.append(lb)
            else:
                # Inequality constraint
                constraint_lb.append(lb)
                constraint_ub.append(ub)

        # Turn constraint vectors into 1D arrays
        self.constraint_lb = np.hstack(constraint_lb) if constraint_lb else []
        self.constraint_ub = np.hstack(constraint_ub) if constraint_ub else []
        self.eqconst_value = np.hstack(eqconst_value) if eqconst_value else []

        # Create the constraints (inequality and equality)
        self.constraints = []
        if len(self.constraint_lb) != 0:
            self.constraints.append(sp.optimize.NonlinearConstraint(
                self._constraint_function, self.constraint_lb,
                self.constraint_ub))
        if len(self.eqconst_value) != 0:
            self.constraints.append(sp.optimize.NonlinearConstraint(
                self._eqconst_function, self.eqconst_value,
                self.eqconst_value))

        # Process the initial guess
        self.initial_guess = self._process_initial_guess(initial_guess)

        # Store states, input, used later to minimize re-computation
        self.last_x = np.full(self.system.nstates, np.nan)
        self.last_coeffs = np.full(self.initial_guess.shape, np.nan)

        # Reset run-time statistics
        self._reset_statistics(log)

        # Log information
        if log:
            logging.info("New optimal control problem initailized")

    #
    # Cost function
    #
    # Given the input U = [u[0], ... u[N]], we need to compute the cost of
    # the trajectory generated by that input.  This means we have to
    # simulate the system to get the state trajectory X = [x[0], ...,
    # x[N]] and then compute the cost at each point:
    #
    #   cost = sum_k integral_cost(x[k], u[k]) + terminal_cost(x[N], u[N])
    #
    # The initial state used for generating the simulation is stored in the
    # class parameter `x` prior to calling the optimization algorithm.
    #
    def _cost_function(self, coeffs):
        if self.log:
            start_time = time.process_time()
            logging.info("_cost_function called at: %g", start_time)

        # Retrieve the initial state and reshape the input vector
        x = self.x
        coeffs = coeffs.reshape((self.system.ninputs, -1))

        # Compute time points (if basis present)
        if self.basis:
            if self.log:
                logging.debug("coefficients = " + str(coeffs))
            inputs = self._coeffs_to_inputs(coeffs)
        else:
            inputs = coeffs

        # See if we already have a simulation for this condition
        if np.array_equal(coeffs, self.last_coeffs) and \
           np.array_equal(x, self.last_x):
            states = self.last_states
        else:
            if self.log:
                logging.debug("calling input_output_response from state\n"
                              + str(x))
                logging.debug("initial input[0:3] =\n" + str(inputs[:, 0:3]))

            # Simulate the system to get the state
            # TODO: try calling solve_ivp directly for better speed?
            _, _, states = ct.input_output_response(
                self.system, self.timepts, inputs, x, return_x=True,
                solve_ivp_kwargs=self.solve_ivp_kwargs, t_eval=self.timepts)
            self.system_simulations += 1
            self.last_x = x
            self.last_coeffs = coeffs
            self.last_states = states

            if self.log:
                logging.debug("input_output_response returned states\n"
                              + str(states))

        # Trajectory cost
        if ct.isctime(self.system):
            # Evaluate the costs
            costs = [self.integral_cost(states[:, i], inputs[:, i]) for
                     i in range(self.timepts.size)]

            # Compute the time intervals
            dt = np.diff(self.timepts)

            # Integrate the cost
            # TODO: vectorize
            cost = 0
            for i in range(self.timepts.size-1):
                # Approximate the integral using trapezoidal rule
                cost += 0.5 * (costs[i] + costs[i+1]) * dt[i]

        else:
            # Sum the integral cost over the time (second) indices
            # cost += self.integral_cost(states[:,i], inputs[:,i])
            cost = sum(map(
                self.integral_cost, np.transpose(states[:, :-1]),
                np.transpose(inputs[:, :-1])))

        # Terminal cost
        if self.terminal_cost is not None:
            cost += self.terminal_cost(states[:, -1], inputs[:, -1])

        # Update statistics
        self.cost_evaluations += 1
        if self.log:
            stop_time = time.process_time()
            self.cost_process_time += stop_time - start_time
            logging.info(
                "_cost_function returning %g; elapsed time: %g",
                cost, stop_time - start_time)

        # Return the total cost for this input sequence
        return cost

    #
    # Constraints
    #
    # We are given the constraints along the trajectory and the terminal
    # constraints, which each take inputs [x, u] and evaluate the
    # constraint.  How we handle these depends on the type of constraint:
    #
    # * For linear constraints (LinearConstraint), a combined (hstack'd)
    #   vector of the state and input is multiplied by the polytope A matrix
    #   for comparison against the upper and lower bounds.
    #
    # * For nonlinear constraints (NonlinearConstraint), a user-specific
    #   constraint function having the form
    #
    #      constraint_fun(x, u)
    #
    #   is called at each point along the trajectory and compared against the
    #   upper and lower bounds.
    #
    # * If the upper and lower bound for the constraint are identical, then we
    #   separate out the evaluation into two different constraints, which
    #   allows the SciPy optimizers to be more efficient (and stops them from
    #   generating a warning about mixed constraints).  This is handled
    #   through the use of the `_eqconst_function` and `eqconst_value` members.
    #
    # In both cases, the constraint is specified at a single point, but we
    # extend this to apply to each point in the trajectory.  This means
    # that for N time points with m trajectory constraints and p terminal
    # constraints we need to compute N*m + p constraints, each of which
    # holds at a specific point in time, and implements the original
    # constraint.
    #
    # To do this, we basically create a function that simulates the system
    # dynamics and returns a vector of values corresponding to the value of
    # the function at each time.  The class initialization methods takes
    # care of replicating the upper and lower bounds for each point in time
    # so that the SciPy optimization algorithm can do the proper
    # evaluation.
    #
    # In addition, since SciPy's optimization function does not allow us to
    # pass arguments to the constraint function, we have to store the initial
    # state prior to optimization and retrieve it here.
    #
    def _constraint_function(self, coeffs):
        if self.log:
            start_time = time.process_time()
            logging.info("_constraint_function called at: %g", start_time)

        # Retrieve the initial state and reshape the input vector
        x = self.x
        coeffs = coeffs.reshape((self.system.ninputs, -1))

        # Compute time points (if basis present)
        if self.basis:
            inputs = self._coeffs_to_inputs(coeffs)
        else:
            inputs = coeffs

        # See if we already have a simulation for this condition
        if np.array_equal(coeffs, self.last_coeffs) \
           and np.array_equal(x, self.last_x):
            states = self.last_states
        else:
            if self.log:
                logging.debug("calling input_output_response from state\n"
                              + str(x))
                logging.debug("initial input[0:3] =\n" + str(inputs[:, 0:3]))

            # Simulate the system to get the state
            _, _, states = ct.input_output_response(
                self.system, self.timepts, inputs, x, return_x=True,
                solve_ivp_kwargs=self.solve_ivp_kwargs, t_eval=self.timepts)
            self.system_simulations += 1
            self.last_x = x
            self.last_coeffs = coeffs
            self.last_states = states

        # Evaluate the constraint function along the trajectory
        value = []
        for i, t in enumerate(self.timepts):
            for ctype, fun, lb, ub in self.trajectory_constraints:
                if np.all(lb == ub):
                    # Skip equality constraints
                    continue
                elif ctype == opt.LinearConstraint:
                    # `fun` is the A matrix associated with the polytope...
                    value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
                elif ctype == opt.NonlinearConstraint:
                    value.append(fun(states[:, i], inputs[:, i]))
                else:      # pragma: no cover
                    # Checked above => we should never get here
                    raise TypeError(f"unknown constraint type {ctype}")

        # Evaluate the terminal constraint functions
        for ctype, fun, lb, ub in self.terminal_constraints:
            if np.all(lb == ub):
                # Skip equality constraints
                continue
            elif ctype == opt.LinearConstraint:
                value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
            elif ctype == opt.NonlinearConstraint:
                value.append(fun(states[:, i], inputs[:, i]))
            else:      # pragma: no cover
                # Checked above => we should never get here
                raise TypeError(f"unknown constraint type {ctype}")

        # Update statistics
        self.constraint_evaluations += 1
        if self.log:
            stop_time = time.process_time()
            self.constraint_process_time += stop_time - start_time
            logging.info(
                "_constraint_function elapsed time: %g",
                stop_time - start_time)

        # Debugging information
        if self.log:
            logging.debug(
                "constraint values\n" + str(value) + "\n" +
                "lb, ub =\n" + str(self.constraint_lb) + "\n" +
                str(self.constraint_ub))

        # Return the value of the constraint function
        return np.hstack(value)

    def _eqconst_function(self, coeffs):
        if self.log:
            start_time = time.process_time()
            logging.info("_eqconst_function called at: %g", start_time)

        # Retrieve the initial state and reshape the input vector
        x = self.x
        coeffs = coeffs.reshape((self.system.ninputs, -1))

        # Compute time points (if basis present)
        if self.basis:
            inputs = self._coeffs_to_inputs(coeffs)
        else:
            inputs = coeffs

        # See if we already have a simulation for this condition
        if np.array_equal(coeffs, self.last_coeffs) and \
           np.array_equal(x, self.last_x):
            states = self.last_states
        else:
            if self.log:
                logging.debug("calling input_output_response from state\n"
                              + str(x))
                logging.debug("initial input[0:3] =\n" + str(inputs[:, 0:3]))

            # Simulate the system to get the state
            _, _, states = ct.input_output_response(
                self.system, self.timepts, inputs, x, return_x=True,
                solve_ivp_kwargs=self.solve_ivp_kwargs, t_eval=self.timepts)
            self.system_simulations += 1
            self.last_x = x
            self.last_coeffs = coeffs
            self.last_states = states

            if self.log:
                logging.debug("input_output_response returned states\n"
                              + str(states))

        # Evaluate the constraint function along the trajectory
        value = []
        for i, t in enumerate(self.timepts):
            for ctype, fun, lb, ub in self.trajectory_constraints:
                if np.any(lb != ub):
                    # Skip inequality constraints
                    continue
                elif ctype == opt.LinearConstraint:
                    # `fun` is the A matrix associated with the polytope...
                    value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
                elif ctype == opt.NonlinearConstraint:
                    value.append(fun(states[:, i], inputs[:, i]))
                else:      # pragma: no cover
                    # Checked above => we should never get here
                    raise TypeError(f"unknown constraint type {ctype}")

        # Evaluate the terminal constraint functions
        for ctype, fun, lb, ub in self.terminal_constraints:
            if np.any(lb != ub):
                # Skip inequality constraints
                continue
            elif ctype == opt.LinearConstraint:
                value.append(fun @ np.hstack([states[:, i], inputs[:, i]]))
            elif ctype == opt.NonlinearConstraint:
                value.append(fun(states[:, i], inputs[:, i]))
            else:      # pragma: no cover
                # Checked above => we should never get here
                raise TypeError("unknown constraint type {ctype}")

        # Update statistics
        self.eqconst_evaluations += 1
        if self.log:
            stop_time = time.process_time()
            self.eqconst_process_time += stop_time - start_time
            logging.info(
                "_eqconst_function elapsed time: %g", stop_time - start_time)

        # Debugging information
        if self.log:
            logging.debug(
                "eqconst values\n" + str(value) + "\n" +
                "desired =\n" + str(self.eqconst_value))

        # Return the value of the constraint function
        return np.hstack(value)

    #
    # Initial guess
    #
    # We store an initial guess in case it is not specified later.  Note
    # that create_mpc_iosystem() will reset the initial guess based on
    # the current state of the MPC controller.
    #
    # Note: the initial guess is passed as the inputs at the given time
    # vector.  If a basis is specified, this is converted to coefficient
    # values (which are generally of smaller dimension).
    #
    def _process_initial_guess(self, initial_guess):
        if initial_guess is not None:
            # Convert to a 1D array (or higher)
            initial_guess = np.atleast_1d(initial_guess)

            # See whether we got entire guess or just first time point
            if initial_guess.ndim == 1:
                # Broadcast inputs to entire time vector
                try:
                    initial_guess = np.broadcast_to(
                        initial_guess.reshape(-1, 1),
                        (self.system.ninputs, self.timepts.size))
                except ValueError:
                    raise ValueError("initial guess is the wrong shape")

            elif initial_guess.shape != \
                 (self.system.ninputs, self.timepts.size):
                raise ValueError("initial guess is the wrong shape")

            # If we were given a basis, project onto the basis elements
            if self.basis is not None:
                initial_guess = self._inputs_to_coeffs(initial_guess)

            # Reshape for use by scipy.optimize.minimize()
            return initial_guess.reshape(-1)

        # Default is zero
        return np.zeros(
            self.system.ninputs *
            (self.timepts.size if self.basis is None else self.basis.N))

    #
    # Utility function to convert input vector to coefficient vector
    #
    # Initially guesses from the user are passed as input vectors as a
    # function of time, but internally we store the guess in terms of the
    # basis coefficients.  We do this by solving a least squares problem to
    # find coefficients that match the input functions at the time points (as
    # much as possible, if the problem is under-determined).
    #
    def _inputs_to_coeffs(self, inputs):
        # If there is no basis function, just return inputs as coeffs
        if self.basis is None:
            return inputs

        # Solve least squares problems (M x = b) for coeffs on each input
        coeffs = np.zeros((self.system.ninputs, self.basis.N))
        for i in range(self.system.ninputs):
            # Set up the matrices to get inputs
            M = np.zeros((self.timepts.size, self.basis.N))
            b = np.zeros(self.timepts.size)

            # Evaluate at each time point and for each basis function
            # TODO: vectorize
            for j, t in enumerate(self.timepts):
                for k in range(self.basis.N):
                    M[j, k] = self.basis(k, t)
                    b[j] = inputs[i, j]

            # Solve a least squares problem for the coefficients
            alpha, residuals, rank, s = np.linalg.lstsq(M, b, rcond=None)
            coeffs[i, :] = alpha

        return coeffs

    # Utility function to convert coefficient vector to input vector
    def _coeffs_to_inputs(self, coeffs):
        # TODO: vectorize
        inputs = np.zeros((self.system.ninputs, self.timepts.size))
        for i, t in enumerate(self.timepts):
            for k in range(self.basis.N):
                phi_k = self.basis(k, t)
                for inp in range(self.system.ninputs):
                    inputs[inp, i] += coeffs[inp, k] * phi_k
        return inputs

    #
    # Log and statistics
    #
    # To allow some insight into where time is being spent, we keep track of
    # the number of times that various functions are called and (optionally)
    # how long we spent inside each function.
    #
    def _reset_statistics(self, log=False):
        """Reset counters for keeping track of statistics"""
        self.log = log
        self.cost_evaluations, self.cost_process_time = 0, 0
        self.constraint_evaluations, self.constraint_process_time = 0, 0
        self.eqconst_evaluations, self.eqconst_process_time = 0, 0
        self.system_simulations = 0

    def _print_statistics(self, reset=True):
        """Print out summary statistics from last run"""
        print("Summary statistics:")
        print("* Cost function calls:", self.cost_evaluations)
        if self.log:
            print("* Cost function process time:", self.cost_process_time)
        if self.constraint_evaluations:
            print("* Constraint calls:", self.constraint_evaluations)
            if self.log:
                print(
                    "* Constraint process time:", self.constraint_process_time)
        if self.eqconst_evaluations:
            print("* Eqconst calls:", self.eqconst_evaluations)
            if self.log:
                print(
                    "* Eqconst process time:", self.eqconst_process_time)
        print("* System simulations:", self.system_simulations)
        if reset:
            self._reset_statistics(self.log)

    #
    # Optimal control computations
    #

    # Compute the optimal trajectory from the current state
    def compute_trajectory(
            self, x, squeeze=None, transpose=None, return_states=None,
            initial_guess=None, print_summary=True, **kwargs):
        """Compute the optimal input at state x

        Parameters
        ----------
        x : array-like or number, optional
            Initial state for the system.
        return_states : bool, optional
            If True, return the values of the state at each time (default =
            False).
        squeeze : bool, optional
            If True and if the system has a single output, return the system
            output as a 1D array rather than a 2D array.  If False, return the
            system output as a 2D array even if the system is SISO.  Default
            value set by config.defaults['control.squeeze_time_response'].
        transpose : bool, optional
            If True, assume that 2D input arrays are transposed from the
            standard format.  Used to convert MATLAB-style inputs to our
            format.

        Returns
        -------
        res : OptimalControlResult
            Bundle object with the results of the optimal control problem.
        res.success: bool
            Boolean flag indicating whether the optimization was successful.
        res.time : array
            Time values of the input.
        res.inputs : array
            Optimal inputs for the system.  If the system is SISO and squeeze
            is not True, the array is 1D (indexed by time).  If the system is
            not SISO or squeeze is False, the array is 2D (indexed by the
            output number and time).
        res.states : array
            Time evolution of the state vector (if return_states=True).

        """
        # Allow 'return_x` as a synonym for 'return_states'
        return_states = ct.config._get_param(
            'optimal', 'return_x', kwargs, return_states, pop=True, last=True)

        # Store the initial state (for use in _constraint_function)
        self.x = x

        # Allow the initial guess to be overriden
        if initial_guess is None:
            initial_guess = self.initial_guess
        else:
            initial_guess = self._process_initial_guess(initial_guess)

        # Call ScipPy optimizer
        res = sp.optimize.minimize(
            self._cost_function, initial_guess,
            constraints=self.constraints, **self.minimize_kwargs)

        # Process and return the results
        return OptimalControlResult(
            self, res, transpose=transpose, return_states=return_states,
            squeeze=squeeze, print_summary=print_summary)

    # Compute the current input to apply from the current state (MPC style)
    def compute_mpc(self, x, squeeze=None):
        """Compute the optimal input at state x

        This function calls the :meth:`compute_trajectory` method and returns
        the input at the first time point.

        Parameters
        ----------
        x: array-like or number, optional
            Initial state for the system.
        squeeze : bool, optional
            If True and if the system has a single output, return the system
            output as a 1D array rather than a 2D array.  If False, return the
            system output as a 2D array even if the system is SISO.  Default
            value set by config.defaults['control.squeeze_time_response'].

        Returns
        -------
        input : array
            Optimal input for the system at the current time.  If the system
            is SISO and squeeze is not True, the array is 1D (indexed by
            time).  If the system is not SISO or squeeze is False, the array
            is 2D (indexed by the output number and time).  Set to `None`
            if the optimization failed.

        """
        res = self.compute_trajectory(x, squeeze=squeeze)
        return res.inputs[:, 0]

    # Create an input/output system implementing an MPC controller
    def create_mpc_iosystem(self):
        """Create an I/O system implementing an MPC controller"""
        # Check to make sure we are in discrete time
        if self.system.dt == 0:
            raise ct.ControlNotImplemented(
                "MPC for continuous time systems not implemented")

        def _update(t, x, u, params={}):
            coeffs = x.reshape((self.system.ninputs, -1))
            if self.basis:
                # Keep the coeffecients unchanged
                # TODO: could compute input vector, shift, and re-project (?)
                self.initial_guess = coeffs
            else:
                # Shift the basis elements by one time step
                self.initial_guess = np.hstack(
                    [coeffs[:, 1:], coeffs[:, -1:]]).reshape(-1)
            res = self.compute_trajectory(u, print_summary=False)

            # New state is the new input vector
            return res.inputs.reshape(-1)

        def _output(t, x, u, params={}):
            # Start with initial guess and recompute based on input state (u)
            self.initial_guess = x
            res = self.compute_trajectory(u, print_summary=False)
            return res.inputs[:, 0]

        return ct.NonlinearIOSystem(
            _update, _output, dt=self.system.dt,
            inputs=self.system.nstates, outputs=self.system.ninputs,
            states=self.system.ninputs * \
                (self.timepts.size if self.basis is None else self.basis.N))

# Optimal control result
class OptimalControlResult(sp.optimize.OptimizeResult):
    """Result from solving an optimal control problem.

    This class is a subclass of :class:`scipy.optimize.OptimizeResult` with
    additional attributes associated with solving optimal control problems.

    Attributes
    ----------
    inputs : ndarray
        The optimal inputs associated with the optimal control problem.
    states : ndarray
        If `return_states` was set to true, stores the state trajectory
        associated with the optimal input.
    success : bool
        Whether or not the optimizer exited successful.
    problem : OptimalControlProblem
        Optimal control problem that generated this solution.
    cost : float
        Final cost of the return solution.
    system_simulations, {cost, constraint, eqconst}_evaluations : int
        Number of system simulations and evaluations of the cost function,
        (inequality) constraint function, and equality constraint function
        performed during the optimzation.
    {cost, constraint, eqconst}_process_time : float
        If logging was enabled, the amount of time spent evaluating the cost
        and constraint functions.

    """
    def __init__(
            self, ocp, res, return_states=False, print_summary=False,
            transpose=None, squeeze=None):
        """Create a OptimalControlResult object"""

        # Copy all of the fields we were sent by sp.optimize.minimize()
        for key, val in res.items():
            setattr(self, key, val)

        # Remember the optimal control problem that we solved
        self.problem = ocp

        # Reshape and process the input vector
        coeffs = res.x.reshape((ocp.system.ninputs, -1))

        # Compute time points (if basis present)
        if ocp.basis:
            inputs = ocp._coeffs_to_inputs(coeffs)
        else:
            inputs = coeffs

        # See if we got an answer
        if not res.success:
            warnings.warn(
                "unable to solve optimal control problem\n"
                "scipy.optimize.minimize returned " + res.message, UserWarning)

        # Save the final cost
        self.cost = res.fun

        # Optionally print summary information
        if print_summary:
            ocp._print_statistics()
            print("* Final cost:", self.cost)

        if return_states and inputs.shape[1] == ocp.timepts.shape[0]:
            # Simulate the system if we need the state back
            _, _, states = ct.input_output_response(
                ocp.system, ocp.timepts, inputs, ocp.x, return_x=True,
                solve_ivp_kwargs=ocp.solve_ivp_kwargs, t_eval=ocp.timepts)
            ocp.system_simulations += 1
        else:
            states = None

        # Process data as a time response (with "outputs" = inputs)
        response = TimeResponseData(
            ocp.timepts, inputs, states, issiso=ocp.system.issiso(),
            transpose=transpose, return_x=return_states, squeeze=squeeze)

        self.time = response.time
        self.inputs = response.outputs
        self.states = response.states

# Compute the input for a nonlinear, (constrained) optimal control problem
def solve_ocp(
        sys, horizon, X0, cost, trajectory_constraints=None, terminal_cost=None,
        terminal_constraints=[], initial_guess=None, basis=None, squeeze=None,
        transpose=None, return_states=False, log=False, **kwargs):

    """Compute the solution to an optimal control problem

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the optimal input will be computed.

    horizon : 1D array_like
        List of times at which the optimal input should be computed.

    X0: array-like or number, optional
        Initial condition (default = 0).

    cost : callable
        Function that returns the integral cost given the current state
        and input.  Called as `cost(x, u)`.

    trajectory_constraints : list of tuples, optional
        List of constraints that should hold at each point in the time vector.
        Each element of the list should consist of a tuple with first element
        given by :meth:`scipy.optimize.LinearConstraint` or
        :meth:`scipy.optimize.NonlinearConstraint` and the remaining
        elements of the tuple are the arguments that would be passed to those
        functions.  The following tuples are supported:

        * (LinearConstraint, A, lb, ub): The matrix A is multiplied by stacked
          vector of the state and input at each point on the trajectory for
          comparison against the upper and lower bounds.

        * (NonlinearConstraint, fun, lb, ub): a user-specific constraint
          function `fun(x, u)` is called at each point along the trajectory
          and compared against the upper and lower bounds.

        The constraints are applied at each time point along the trajectory.

    terminal_cost : callable, optional
        Function that returns the terminal cost given the current state
        and input.  Called as terminal_cost(x, u).

    terminal_constraints : list of tuples, optional
        List of constraints that should hold at the end of the trajectory.
        Same format as `constraints`.

    initial_guess : 1D or 2D array_like
        Initial inputs to use as a guess for the optimal input.  The inputs
        should either be a 2D vector of shape (ninputs, horizon) or a 1D
        input of shape (ninputs,) that will be broadcast by extension of the
        time axis.

    log : bool, optional
        If `True`, turn on logging messages (using Python logging module).

    return_states : bool, optional
        If True, return the values of the state at each time (default = False).

    squeeze : bool, optional
        If True and if the system has a single output, return the system
        output as a 1D array rather than a 2D array.  If False, return the
        system output as a 2D array even if the system is SISO.  Default value
        set by config.defaults['control.squeeze_time_response'].

    transpose : bool, optional
        If True, assume that 2D input arrays are transposed from the standard
        format.  Used to convert MATLAB-style inputs to our format.

    kwargs : dict, optional
        Additional parameters (passed to :func:`scipy.optimal.minimize`).

    Returns
    -------
    res : OptimalControlResult
        Bundle object with the results of the optimal control problem.

    res.success : bool
        Boolean flag indicating whether the optimization was successful.

    res.time : array
        Time values of the input.

    res.inputs : array
        Optimal inputs for the system.  If the system is SISO and squeeze is
        not True, the array is 1D (indexed by time).  If the system is not
        SISO or squeeze is False, the array is 2D (indexed by the output
        number and time).

    res.states : array
        Time evolution of the state vector (if return_states=True).

    Notes
    -----
    Additional keyword parameters can be used to fine tune the behavior of
    the underlying optimization and integrations functions.  See
    :func:`OptimalControlProblem` for more information.

    """
    # Process keyword arguments
    if trajectory_constraints is None:
        # Backwards compatibility
        trajectory_constraints = kwargs.pop('constraints', [])

    # Allow 'return_x` as a synonym for 'return_states'
    return_states = ct.config._get_param(
        'optimal', 'return_x', kwargs, return_states, pop=True)

    # Process (legacy) method keyword
    if kwargs.get('method'):
        if kwargs.get('minimize_method'):
            raise ValueError("'minimize_method' specified more than once")
        kwargs['minimize_method'] = kwargs.pop('method')

    # Set up the optimal control problem
    ocp = OptimalControlProblem(
        sys, horizon, cost, trajectory_constraints=trajectory_constraints,
        terminal_cost=terminal_cost, terminal_constraints=terminal_constraints,
        initial_guess=initial_guess, basis=basis, log=log, **kwargs)

    # Solve for the optimal input from the current state
    return ocp.compute_trajectory(
        X0, squeeze=squeeze, transpose=transpose, return_states=return_states)

# Create a model predictive controller for an optimal control problem
def create_mpc_iosystem(
        sys, horizon, cost, constraints=[], terminal_cost=None,
        terminal_constraints=[], log=False, **kwargs):
    """Create a model predictive I/O control system

    This function creates an input/output system that implements a model
    predictive control for a system given the time horizon, cost function and
    constraints that define the finite-horizon optimization that should be
    carried out at each state.

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the optimal input will be computed.

    horizon : 1D array_like
        List of times at which the optimal input should be computed.

    cost : callable
        Function that returns the integral cost given the current state
        and input.  Called as cost(x, u).

    constraints : list of tuples, optional
        List of constraints that should hold at each point in the time vector.
        See :func:`~control.optimal.solve_ocp` for more details.

    terminal_cost : callable, optional
        Function that returns the terminal cost given the current state
        and input.  Called as terminal_cost(x, u).

    terminal_constraints : list of tuples, optional
        List of constraints that should hold at the end of the trajectory.
        Same format as `constraints`.

    kwargs : dict, optional
        Additional parameters (passed to :func:`scipy.optimal.minimize`).

    Returns
    -------
    ctrl : InputOutputSystem
        An I/O system taking the current state of the model system and
        returning the current input to be applied that minimizes the cost
        function while satisfying the constraints.

    Notes
    -----
    Additional keyword parameters can be used to fine tune the behavior of
    the underlying optimization and integrations functions.  See
    :func:`OptimalControlProblem` for more information.

    """
    # Set up the optimal control problem
    ocp = OptimalControlProblem(
        sys, horizon, cost, trajectory_constraints=constraints,
        terminal_cost=terminal_cost, terminal_constraints=terminal_constraints,
        log=log, **kwargs)

    # Return an I/O system implementing the model predictive controller
    return ocp.create_mpc_iosystem()

#
# Functions to create cost functions (quadratic cost function)
#
# Since a quadratic function is common as a cost function, we provide a
# function that will take a Q and R matrix and return a callable that
# evaluates to associted quadratic cost.  This is compatible with the way that
# the `_cost_function` evaluates the cost at each point in the trajectory.
#
def quadratic_cost(sys, Q, R, x0=0, u0=0):
    """Create quadratic cost function

    Returns a quadratic cost function that can be used for an optimal control
    problem.  The cost function is of the form

      cost = (x - x0)^T Q (x - x0) + (u - u0)^T R (u - u0)

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the cost function is being defined.
    Q : 2D array_like
        Weighting matrix for state cost.  Dimensions must match system state.
    R : 2D array_like
        Weighting matrix for input cost.  Dimensions must match system input.
    x0 : 1D array
        Nominal value of the system state (for which cost should be zero).
    u0 : 1D array
        Nominal value of the system input (for which cost should be zero).

    Returns
    -------
    cost_fun : callable
        Function that can be used to evaluate the cost at a given state and
        input.  The call signature of the function is cost_fun(x, u).

    """
    # Process the input arguments
    if Q is not None:
        Q = np.atleast_2d(Q)
        if Q.size == 1:         # allow scalar weights
            Q = np.eye(sys.nstates) * Q.item()
        elif Q.shape != (sys.nstates, sys.nstates):
            raise ValueError("Q matrix is the wrong shape")

    if R is not None:
        R = np.atleast_2d(R)
        if R.size == 1:         # allow scalar weights
            R = np.eye(sys.ninputs) * R.item()
        elif R.shape != (sys.ninputs, sys.ninputs):
            raise ValueError("R matrix is the wrong shape")

    if Q is None:
        return lambda x, u: ((u-u0) @ R @ (u-u0)).item()

    if R is None:
        return lambda x, u: ((x-x0) @ Q @ (x-x0)).item()

    # Received both Q and R matrices
    return lambda x, u: ((x-x0) @ Q @ (x-x0) + (u-u0) @ R @ (u-u0)).item()

#
# Functions to create constraints: either polytopes (A x <= b) or ranges
# (lb # <= x <= ub).
#
# As in the cost function evaluation, the main "trick" in creating a constrain
# on the state or input is to properly evaluate the constraint on the stacked
# state and input vector at the current time point.  The constraint itself
# will be called at each point along the trajectory (or the endpoint) via the
# constrain_function() method.
#
# Note that these functions to not actually evaluate the constraint, they
# simply return the information required to do so.  We use the SciPy
# optimization methods LinearConstraint and NonlinearConstraint as "types" to
# keep things consistent with the terminology in scipy.optimize.
#
def state_poly_constraint(sys, A, b):
    """Create state constraint from polytope

    Creates a linear constraint on the system state of the form A x <= b that
    can be used as an optimal control constraint (trajectory or terminal).

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    A : 2D array
        Constraint matrix
    b : 1D array
        Upper bound for the constraint

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert arguments to arrays and make sure dimensions are right
    A = np.atleast_2d(A)
    b = np.atleast_1d(b)
    if len(A.shape) != 2 or A.shape[1] != sys.nstates:
        raise ValueError("polytope matrix must match number of states")
    elif len(b.shape) != 1 or A.shape[0] != b.shape[0]:
        raise ValueError("number of bounds must match number of constraints")

    # Return a linear constraint object based on the polynomial
    return (opt.LinearConstraint,
            np.hstack([A, np.zeros((A.shape[0], sys.ninputs))]),
            np.full(A.shape[0], -np.inf), b)

def state_range_constraint(sys, lb, ub):
    """Create state constraint from polytope

    Creates a linear constraint on the system state that bounds the range of
    the individual states to be between `lb` and `ub`.  The upper and lower
    bounds can be set of `inf` and `-inf` to indicate there is no constraint
    or to the same value to describe an equality constraint.

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    lb : 1D array
        Lower bound for each of the states.
    ub : 1D array
        Upper bound for each of the states.

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert bounds to lists and make sure they are the right dimension
    lb = np.atleast_1d(lb)
    ub = np.atleast_1d(ub)
    if lb.shape != (sys.nstates,) or ub.shape != (sys.nstates,):
        raise ValueError("state bounds must match number of states")

    # Return a linear constraint object based on the polynomial
    return (opt.LinearConstraint,
            np.hstack(
                [np.eye(sys.nstates), np.zeros((sys.nstates, sys.ninputs))]),
            np.array(lb), np.array(ub))

# Create a constraint polytope on the system input
def input_poly_constraint(sys, A, b):
    """Create input constraint from polytope

    Creates a linear constraint on the system input of the form A u <= b that
    can be used as an optimal control constraint (trajectory or terminal).

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    A : 2D array
        Constraint matrix
    b : 1D array
        Upper bound for the constraint

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert arguments to arrays and make sure dimensions are right
    A = np.atleast_2d(A)
    b = np.atleast_1d(b)
    if len(A.shape) != 2 or A.shape[1] != sys.ninputs:
        raise ValueError("polytope matrix must match number of inputs")
    elif len(b.shape) != 1 or A.shape[0] != b.shape[0]:
        raise ValueError("number of bounds must match number of constraints")

    # Return a linear constraint object based on the polynomial
    return (opt.LinearConstraint,
            np.hstack(
                [np.zeros((A.shape[0], sys.nstates)), A]),
            np.full(A.shape[0], -np.inf), b)

def input_range_constraint(sys, lb, ub):
    """Create input constraint from polytope

    Creates a linear constraint on the system input that bounds the range of
    the individual states to be between `lb` and `ub`.  The upper and lower
    bounds can be set of `inf` and `-inf` to indicate there is no constraint
    or to the same value to describe an equality constraint.

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    lb : 1D array
        Lower bound for each of the inputs.
    ub : 1D array
        Upper bound for each of the inputs.

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert bounds to lists and make sure they are the right dimension
    lb = np.atleast_1d(lb)
    ub = np.atleast_1d(ub)
    if lb.shape != (sys.ninputs,) or ub.shape != (sys.ninputs,):
        raise ValueError("input bounds must match number of inputs")

    # Return a linear constraint object based on the polynomial
    return (opt.LinearConstraint,
            np.hstack(
                [np.zeros((sys.ninputs, sys.nstates)), np.eye(sys.ninputs)]),
            lb, ub)

#
# Create a constraint polytope/range constraint on the system output
#
# Unlike the state and input constraints, for the output constraint we need to
# do a function evaluation before applying the constraints.
#
# TODO: for the special case of an LTI system, we can avoid the extra function
# call by multiplying the state by the C matrix for the system and then
# imposing a linear constraint:
#
#     np.hstack(
#         [A @ sys.C, np.zeros((A.shape[0], sys.ninputs))])
#
def output_poly_constraint(sys, A, b):
    """Create output constraint from polytope

    Creates a linear constraint on the system output of the form A y <= b that
    can be used as an optimal control constraint (trajectory or terminal).

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    A : 2D array
        Constraint matrix
    b : 1D array
        Upper bound for the constraint

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert arguments to arrays and make sure dimensions are right
    A = np.atleast_2d(A)
    b = np.atleast_1d(b)
    if len(A.shape) != 2 or A.shape[1] != sys.noutputs:
        raise ValueError("polytope matrix must match number of outputs")
    elif len(b.shape) != 1 or A.shape[0] != b.shape[0]:
        raise ValueError("number of bounds must match number of constraints")

    # Function to create the output
    def _evaluate_output_poly_constraint(x, u):
        return A @ sys._out(0, x, u)

    # Return a nonlinear constraint object based on the polynomial
    return (opt.NonlinearConstraint,
            _evaluate_output_poly_constraint,
            np.full(A.shape[0], -np.inf), b)

def output_range_constraint(sys, lb, ub):
    """Create output constraint from range

    Creates a linear constraint on the system output that bounds the range of
    the individual states to be between `lb` and `ub`.  The upper and lower
    bounds can be set of `inf` and `-inf` to indicate there is no constraint
    or to the same value to describe an equality constraint.

    Parameters
    ----------
    sys : InputOutputSystem
        I/O system for which the constraint is being defined.
    lb : 1D array
        Lower bound for each of the outputs.
    ub : 1D array
        Upper bound for each of the outputs.

    Returns
    -------
    constraint : tuple
        A tuple consisting of the constraint type and parameter values.

    """
    # Convert bounds to lists and make sure they are the right dimension
    lb = np.atleast_1d(lb)
    ub = np.atleast_1d(ub)
    if lb.shape != (sys.noutputs,) or ub.shape != (sys.noutputs,):
        raise ValueError("output bounds must match number of outputs")

    # Function to create the output
    def _evaluate_output_range_constraint(x, u):
        # Separate the constraint into states and inputs
        return sys._out(0, x, u)

    # Return a nonlinear constraint object based on the polynomial
    return (opt.NonlinearConstraint, _evaluate_output_range_constraint, lb, ub)

redpajama_set_name: RedPajamaGithub


Whether you're relocating or just moving around the corner, we are passionate about helping you to find your perfect home. We believe that when you're renting a property, there is no reason to accept anything less than the perfect place.
We know you want more than just a great property and with Fit Property you'll enjoy a totally unique and bespoke service. All our Property Managers are friendly, fair and understand your home is your life.
At Fit Property you'll find more than just a great property, you'll enjoy a personal experience. From secure off street parking to being close to your favourite coffee bar. No matter how specific your request, tell us, and we'll do our best to find it.
I used Fit Property when I was a Student and still use them today as a young professional. This is due to the friendly and hassle free service I have received every time.
Getting the right place with Fit Property is easy. Tell us what's important to you – location, price or quality and we'll find you the perfect fit.
Amazing 30th Floor One Bedroom Apartment.
Representing best-in-class property across Sheffield. From outstanding studio flats in the city to family homes in the Peaks, only the best will do.
We understand that every situation is not the same. We offer a one-to-one service thats tailored to you. There are definitely no stereotypes.
Your time is your most valuable asset. Enjoy life without the daily problems of being a landlord. Our expertise and experience guarantees peace of mind.
Common questions and answers about our tenant property lettings and maintenance services in Sheffield.
1How do I book a viewing?
Call the office, 0114 212 5773 or simply click Arrange a Viewing on any of our properties.
1How do I reserve and sign for a property?
Once you've found a property you like and paid the application fee, the property will be taken off the market. The application will then be processed.
2What do I do if I'm not in Sheffield?
If you are not able to get into our office, all the paperwork can be completed by email and the tenancy agreement can be signed digitally via DocuSign. Application fees can be paid over the phone.
3What does the application fee cover?
The application fee covers our cost of processing your application, referencing, credit checks, preparing the tenancy and guarantor agreement and any administration related to the signing.
4Do I need a Credit Check?
Referencing for a property is outsourced to a company called Van Mildert. An affordability check will be done and if necessary an employment reference will be requested.
5Can I see a copy of the contract before signing?
6How do I book a viewing?
Call the office on 0114 212 5773 or simply click 'Arrange a Viewing' on any of our properties.
1How much deposit do I need to pay?
This varies depending on the property you are signing for, the deposit amount for each property is indicated on our website.
2What dates are the rent payments?
We collect rent on the 1st of every month.
3What if I am not able to pay?
Please call us 0114 272 5773, we'll be happy to discuss a manageable payment plan with you.
We collect rent by standing order. You simply need to complete a standing order mandate and hand it into your bank. They will then set up the payment instuction.
If you are moving into a managed property, your deposit will be protected with the Deposit Protection Service. Once you have paid your deposit, you will receive confirmation from the relevant Deposit Scheme by email within 28 days. You must keep this email as it contains important information that you'll need to get your deposit back. Alternatively, your landlord may protect your deposit if you are living in a property that Fit Property does not manage. Either way, we will advise you at the point of viewing the property.
1Do I have to move in on the day my tenancy starts?
No, you can move in on any day after the start date on your tenancy agreement. Subject to availability of move in appointments.
2What happens at the move in appointment?
There are a few documents we will need you to sign, including a full inventory for the property and you will be issued with the keys and a move in pack. If you have any questions about the property this would be an ideal opportunity.
3How do I get my keys?
Once the referencing process and all the paperwork has been successfully completed, one of the team will contact you to discuss the next steps. If you are moving into a property managed by Fit Property, we will book a move in appointment. The appointment will take place at the Fit Property office. If you are moving into a property that your landlord manages, their contact details will be provided for you to contact them direct and arrange key collection.
1How long will the repair take?
We aim to resolve all maintenance issues as soon as possible. If for any reason a maintenance issue takes longer to resolve, one of our team will update you and your housemates.
2How do I report maintenance?
We kindly ask that all maintenance issues are reported via our "Property app" this will be discussed at your move in appointment. Alternatively, we ask that you email your designated propety manager with the details of the repair. If your maintenance issue is an emergency please call the office on 0114 2725773. If the emergency occurs out of office hours please visit www.fitproperty.com/emergency for more information.
1How much is the application fee?
Personal service, like no other.
I lived in two great properties both managed by Fit Property. Any problems were always resolved quickly and moving in/out was hassle-free.
I recommend these guys to everyone!
I've rented with Fit Property for two years as a student. After I graduated I moved into one of their professional lets. Staff are prompt, helpful & friendly.


text:
YouTube Ads Cost: Formats, Rates, Pricing Models, ROI, and More
Elma Mrkonjić
When it comes to online advertising, YouTube is often an overlooked platform. However, with over two billion monthly active users and a growing number of people watching videos on the site daily, it's definitely worth considering for your marketing efforts.
This post will discuss the different types of ads, YouTube ads cost, and what factors influence pricing. We'll also look at the return on investment (ROI) for these ads and help you decide if paying for them is worth it.
Types of YouTube Ad Formats
YouTube Ads Cost
Different Pricing Models to Choose From
YouTube Ads ROI
How to Reduce Costs of YouTube Ads
A Quick Insight Into Setting Up a Campaign
Our Takeaway
YouTube Ads Formats
Before we get into YouTube ads prices, let's quickly get familiar with the main types of ads offered by the service.
There are six different types of YouTube ads: display, overlay, skippable, non-skippable, bumper, and sponsored cards.
Display ads are the most common type of YouTube promotion. They appear as a rectangular banner at the top or bottom of the video and can be either skippable or non-skippable.
Overlay Ads
You've probably seen these ads before. They appear over the video content itself and are semi-transparent. This is a popular advertising platform because it's very effective at capturing attention without being too distracting or annoying to viewers.
Skippable Ads
As the name suggests, skippable ads can be skipped after a certain amount of time. They usually appear before or during the video and must be watched for at least six seconds to count as an impression.
Non-Skippable Ads
These are the opposite of skippable ads. As their name suggests, they cannot be skipped and must be watched in their entirety before the video content plays. They also tend to be longer than skippable ads, typically lasting 15–30 seconds.
Bumper Ads
Bumper ads are short, non-skippable ads that last for only six seconds. They're perfect for when you want to get your message across quickly without taking up too much of the viewer's time.
Sponsored Cards
These are interactive cards that appear in the bottom right corner of videos and can be clicked on to learn more about the product or service being advertised.
Worth Knowing: YouTube Pre-Roll Ads
Pre-roll ads are the most common type of YouTube ad and appear before the video starts playing. They can be either skippable or non-skippable and typically last between 15-30 seconds.
How Much Do YouTube Ads Cost
The cost of YouTube ads varies depending on several factors, including the type of ad, the targeting options you choose, and whether you're using CPC or CPV bidding.
In general, YouTube ads cost between $0.01 and $0.03 per view, with the average CPM (cost per thousand impressions) coming in at around $3.53.
However, these prices can vary significantly depending on your target audience, the type of ad you're running, and other factors.
For example, if you're targeting a particular niche audience, you may see higher CPMs due to the smaller pool of potential viewers. Likewise, if you're running a skippable ad (which allows viewers to skip your ad after five seconds), you can expect to pay a lower CPM since not everyone will see the entire ad.
The bottom line is that there's no one-size-fits-all answer regarding YouTube prices. The best way to determine how much you'll need to spend is to test out different targeting options and bid types until you find what works best for your business.
What Determines YouTube Ads Cost
What determines how much YouTube advertising costs is influenced by a few different factors. The main one is your target audience: who you're trying to reach with your ad and where they are located. Pricing can also vary depending on the format of your ad, whether it's skippable or not, and the length of the video.
In general, here are some ballpark estimates for what you can expect to pay per view (CPV) for YouTube ads:
Display Ads: $0.05 – $0.30
Overlay Ads: $0.15 – $0.30
Skippable Ads: $0.20 – $0.35
Non-Skippable Ads: $0.30 – $0.45
Bumper Ads: $0.35 – $0.70
Sponsored Cards: $0.50 – $0.70
Of course, these are just estimates, and your actual costs may be higher or lower depending on your targeting and other factors.
YouTube Ads Pricing Models
There are two main pricing models for YouTube ads: cost per view (CPV) and cost per click (CPC). With CPV, you pay each time someone watches your ad for at least 30 seconds or until the end of the video, whichever comes first. CPC means that you only pay when someone clicks on your ad.
What Is YouTube's CPM for Ads?
In addition to CPV and CPC, you may also see pricing based on cost per thousand views (CPM). CPM means you pay for every thousand views of your ad regardless of whether anyone clicks on it.
YouTube's average CPM rates can vary depending on your video category, with categories costing more than others on average. For example, the average CPM for beauty videos is $11.20, while the average CPM for tech videos is only $0.80 (more on that below).
YouTube CPM Rates by Category
The average CPM rate for YouTube is around $11.20 for beauty videos and $0.80 for tech videos. However, these prices can vary significantly depending on your target audience and other factors.
Here are some average CPM rates by video category:
As you can see, some categories cost more to advertise in than others. This is because specific categories are more competitive than others, which drives up prices.
Which Pricing Model is Right for You?
The best way to determine which pricing model is suitable for you is to test out both options and see which one gives you the best results. CPC will be a more efficient use of their budget for most businesses, as viewers are more likely to click on an ad if they're interested in learning more about it.
However, there are some cases where CPV may be a better option. For example, if you're running a brand awareness campaign or targeting a very niche audience, CPV may be more effective since viewers are less likely to click on an ad they're not interested in.
YouTube Ads ROI: How to Measure Success
The best way to measure the success of your YouTube ad campaign is by looking at your return on investment (ROI). Simply divide your total cost by the number of conversions you generated to calculate ROI. For example, if you spent $500 on ads and generated 50 sales, your ROI would be $500/50, or $0.01 per view.
Create a tracking pixel and add it to your website to get started. This will allow you to track how many visitors come to your site from your ad and whether they make a purchase. You can also use Google Analytics to track conversions from YouTube ads.
Once you have this data, you can experiment with different targeting options and bid types to see which ones give you the best results.
YouTube Ads Retargeting
Another great way to increase the ROI of your YouTube ad campaign is by using retargeting. Retargeting allows you to show your ads to people who have already visited your website. This is a great way to reach your target audience and increase conversions.
There are several different types of retargeting available on YouTube, including:
Standard remarketing: Shows your ad to people who have visited your website or watched one of your videos
Dynamic remarketing: Shows tailored ads to people based on what they viewed on your website
Remarketing for mobile apps: Targets users who have installed one of your apps
Customer match: Targets customers who have already been uploaded to Google's customer list
To get started with retargeting, simply create a new campaign and select the Remarketing option. From there, you can choose which type of retargeting you want to use.
What Are Some Tips for Reducing the Cost of YouTube Ads?
Try using a longer video instead of a shorter one – this will help to lower your CPM rates
Make sure your videos are high quality and informative – viewers are more likely to click on an ad if they're interested in the content
Experiment with different targeting options and bid types to see which ones give you the best results
Use retargeting to reach more of your target audience
Monitor your return on investment (ROI) to gauge the success of your campaign
YouTube Ads Tutorial: How to Set Up a Campaign
Now that you know everything there is to know about YouTube advertisement rates, it's time to learn how to set up your campaign. Luckily, setting up a YouTube ad campaign is relatively simple and only takes a few minutes.
To get started, log in to your Google Ads account and click on the Campaigns tab. Then, click on the + button to create a new campaign. Next, select Video as your campaign type and choose your targeting options.
Once you've done that, you'll need to create your ad. To do this, click on the Ads & Extensions tab and select New Ad. From there, you will be able to choose from various ad formats, including in-stream and discovery ads.
After you've created your ad, it's time to start bidding. You can either bid manually or let Google automatically bid for you. If you're new to YouTube advertising, it's best to let Google automatically bid for your ad since they have a lot of experience with this type of thing.
And that's it! You're now ready to launch your first YouTube ad campaign. Just remember to keep an eye on your budget and ROI to ensure that you're getting the most out of your investment.
Is Paying for YouTube Ads Worth It: Our Takeaway
This is a difficult question to answer since it depends on several factors, including your budget, target audience, and goals. However, if you're looking to reach a large audience with your message and generate more leads or sales, then paying for YouTube ads may be worth it.
To make sure that you're getting the most out of your investment, be sure to track your ROI and monitor your budget closely. This will help you determine how worthwhile these campaigns are for your business.
What are some common mistakes advertisers make with YouTube ads?
Not tracking their return on investment (ROI)
Not monitoring their budget closely
Failing to experiment with different targeting options
Not using retargeting
What's the difference between in-stream and discovery ads?
In-stream ads are video ads that play before, during, or after other videos on YouTube. Discovery ads are video ads that appear on the YouTube homepage or search results.

redpajama_set_name: RedPajamaCommonCrawl


text:
An over-the-top Christmas rap video (created as a replacement to their annual newsletter) featuring Kim, Penn, Lola and Penn Charles Holderness singing in matching 'Xmas jammies' to their own version of Will Smith's Welcome to Miami fhas become an internet hit. Each member of the family gets their own rap section in the four-minute long video where they explain what they have achieved over the last 12 months. Dad Penn proudly boasts that daughter Lola can now count to 100 in Chinese and ran a triathlon, while four-year-old son Penn Charles is taking a hip-hop class. Mum Kim repeatedly informs viewers about her small bit-part in Iron Man 3 and athletic achievements. She then goes on to sing the praises of her husband by announcing he is quitting his job as a news anchor to start a video production company alongside her. The Holderness family then end the video with a shameless plug for aforementioned production company. metro
Magical piano plays all by itself
Woman surprises husband and new fiancee with gifts 2 years after her death


Samples are routinely collected from the treatment plants, service reservoirs and randomly selected customer properties.
Authorised Dublin City Council personnel working for the Central Laboratory who all carry identity cards collect these samples.
The safety of our customers is important to us and to ensure this, we have compiled some advice for determining a genuine Dublin City Council representative from a bogus caller.
Following collection, water samples are passed tothe Central Laboratory for analysis. The laboratory is independently assessed and accredited by the INAB for compliance with the ISO17025 standard.
It employs highly trained staff with extensive analytical skills in microbiology, organic and inorganic chemistry, metals analysis and materials testing.


text:
Телевизио́нные поля́ (полука́дры, ) — части целого кадра при передаче телевизионного изображения способом чересстрочной развёртки. Применяется в телевидении стандартной чёткости для повышения частоты мерцания экрана выше физиологического порога заметности при сохранении полосы пропускания канала передачи телевизионного сигнала.

Порогом заметности мерцаний считается частота в 48-50 Герц, поэтому при кадровой частоте в 25 кадров в секунду изображение заметно мерцает, а передача 50 полных кадров требует вдвое большей полосы пропускания при передаче. Поэтому при разработке телевизионных систем применена чересстрочная развёртка.
Каждый кадр изображения формируется за два последовательных прохода луча кинескопа — вначале рисуются нечётные строки (1, 3, 5 … 625), затем - чётные (2, 4, 6 … 624). Каждый такой проход занимает 1/50 секунды при европейском стандарте разложения 625/50 и 1/60 секунды для американского 525/60. Разная частота полей была принята из-за привязки к частоте переменного тока, составляющей 50 Гц в Европе и 60 Гц - в Америке. Синхронизация с частотой сети позволяла снизить заметность помех от переменного тока в условиях несовершенства техники. Такие помехи следуют с частотой, равной разности частоты сети и частоты полей.

При быстром движении объектов в кадре каждый следующий полукадр содержит информацию об изменении положения объекта за прошедшую 1/50 часть секунды. Отсюда возникает сдвиг изображения (так называемая гребёнка) при просмотре такого материала на экране компьютерного монитора — он работает в прогрессивной (построчной) развёртке.

Иногда такой материал именуется интерлейсным (i). Построчная развёртка именуется прогрессивной (p).
Отсюда формируется и обозначение 50i (50 полукадров), 25p (25 полных кадров — прогрессивная развёртка), 60i (60 полукадров) и т. д.

См. также
 Стандарт разложения (телевидение)
 Чересстрочная развёртка
 Видео
 Стандарты телевизионного вещания
 Прогрессивная развёртка
 Деинтерлейсинг

Литература

Ссылки
 Телевизионные поля в компьютерной графике — большой материал о множестве аспектов в работе с телевизионными полями
 Определение порядка телевизионных полей с помощью Adobe After Effects

Мониторы
Технологии телевидения

redpajama_set_name: RedPajamaWikipedia


Livestream, which was founded in 2007 as Mogulus and rebranded in May 2009, is a streaming platform that allows users to view and broadcast video content. The company offers a free ad-supported service and multi-tiered premium services.
In 2014, the company moved its headquarters from Manhattan to Brooklyn. We visited the offices in spring 2014.


Your best choice for fine and distictive hair products with a promise of superior quality and value.
Your choice for hair care products. Annie has products of distinction with a promise to our customers-quality of excellence in each and every product.


text: Country Music Duo Brooks & Dunn have sold over 30 million albums and amassed 20 chart-topping hits and over 80 industry awards making them the most successful duo of all time. Kix Brooks and Ronnie Dunn have been dedicated to helping others and in 2007 they received the ACM humanitarian award. For several years Ronnie's "Rock the Barn" event at his home near Nashville raised money for charities such as Gilda's club and the St. Thomas Breast Cancer center. In 2015 Kix received the inaugural CMA Foundation Humanitarian award as a reflection of his dedication to organizations such as Monroe Carell Jr. Children's Hospital at Vanderbilt, Monroe Harding's Children's home, the Country Music Association. Kix was pivotal in the creation of the keep the music playing campaign which is funded from proceeds from the CMA music fest. The campaign has provided more than $11 million into Tennesse schools for music education since 2006.


text:
Q: Adding a Pandas Dataframe-column to a new dataframe Using Pandas, I have some data that I want to add to my ``results'' dataframe. That is, I have
naics = someData
Which can look like this
   indnaics  ind1990
89    81393      873

however, it can have more than one row. I want to add these to my results dataframe, together with a variable called year. In case there is more than one row, it should be the same year value for all rows. This is what I am trying so far
for job in jobs:
    df2 =  iGetThisFromJob()
    years = df2.year.unique()
    naics = iGetThisFromJob()
    if len(naics) == 0:
        continue

    for year in years:
        wages = df2.incwage[df2.year == year]
    # Add all the data to results, this is how I try it
        rows = pd.DataFrame([dict(year=year, incwage=mean(wages), )])
    # I also want to add the column indnaics from my naics
        rows['naics'] = naics.indnaics
        results = results.append(rows, ignore_index=True)

However, despite naics.indnaics being full, I cannot add it this way to the rows object.

naics.indnaics

Out[1052]:
89    81393

rows['naics'] = naics.indnaics
      rows

Out[1051]:
        incwage  year naics
0  45853.061224  2002   NaN

If there is anything else that is not nice with my code, please tell. I'm only beginning to learn pandas.
Thanks!
/edit
Expected output:
        incwage  year   naics
0  45853.061224  2002   81393
0  45853.061224  2002   12312

/edit
Suggested solution:
index = arange(0, len(naics))
columns = ['year', 'incwage', 'naics']
rows = pd.DataFrame(index=index, columns=columns)
rows.year = year
rows.incwage = mean(wages)
rows.naics = naics.indnaics.values

A: The reason you get a NaN value, is because the index does not match (in rows['naics'] = naics.indnaics rows has index 0, while naics.indnaics has index 89), and assigning the value will try to align the indices.
You could for example solve that by taking only the value (by eg naics.indnaics.values). With a toy example:
In [30]: df = pd.DataFrame({'A':[0], 'B':[1]})
In [31]: df
Out[31]:
   A  B
0  0  1

In [32]: s = pd.Series([2], index=[83])
In [33]: s
Out[33]:
83    2
dtype: int64

In [35]: df['new_column'] = s
In [36]: df
Out[36]:
   A  B  new_column
0  0  1         NaN

In [37]: df['new_column'] = s.values
In [38]: df
Out[38]:
   A  B  new_column
0  0  1           2

If you want to add the series with possibly more values, there are a couple of options. I think of:
Eg reindexing the dataframe first to the length of the series:
In [75]: s
Out[75]:
83    2
84    4
dtype: int64

In [76]: df
Out[76]:
   A  B
0  0  1

In [77]: df = df.reindex(np.zeros(len(s)))
In [78]: df
Out[78]:
   A  B
0  0  1
0  0  1

In [79]: df['new_column'] = s.values

In [80]: df
Out[80]:
   A  B  new_column
0  0  1           2
0  0  1           4

or the other way around, add the dataframe to the series (that you first convert to a dataframe):
In [90]: ss = s.to_frame().set_index(np.array([0,0]))
In [91]: ss[df.columns] = df
In [92]: ss
Out[92]:
   0  A  B
0  2  0  1
0  4  0  1

[2 rows x 3 columns]

redpajama_set_name: RedPajamaStackExchange


Lighten up snack time with these healthy homemade potato chips. They're just as crispy as the kind from a bag but have less fat and less sodium. Plus it's easy and inexpensive to make!
You can make these with either russet potatoes or sweet potatoes (if you're feeling extra healthy!). Russet potatoes are high in vitamin B6, potassium, copper, and vitamin C. Potatoes are low in calories and high in fiber. The most nutrients are in the skin so be sure not to peel.
Sweet potatoes are high in vitamin A, vitamin C, manganese, copper, and pantothenic acid. They have a bit less carbohydrates than russet potatoes which make them a good choice for those who are carb conscious. Sweet potatoes are also an excellent source of beta-carotene which helps maintain healthy skin and eyes.
Make these party-ready by serving them alongside vegan ranch dip or goddess dip. The creamy dips will add even more flavor to this healthy snack and will remind guests of their favorite guilty pleasures, without the guilt!
Preheat the oven to 425 degrees Fahrenheit. Line a baking sheet with parchment paper or a baking mat.
Using a vegetable brush, rinse and scrub potatoes under running cool water. Remove any eyes. Pat dry using a paper towel or tea towel.
Using a mandoline, slice the potatoes into 1/8 inch rounds and spread evenly on a baking sheet.
Sprinkle with sea salt and bake for 12 minutes, or until golden and crispy. Remove from heat and let cool for 5 minutes.
Garnish with chives and any additional seasonings, if desired. Enjoy!


FA Coach Mentor Team Are Recruiting!
The Football Association is looking to appoint Coach Mentors to support local County Football Associations. FA Coach Mentors are recruited on a part-time basis to deliver an agreed sum of hours of mentoring support to grassroots coaches across a 12 month period. Mentors will provide quality targeted support that challenges coaches to excel in their work and enhance coaching standards nationally.
This particular role will be to support the SSE Wildcats Centres across West Riding. SSE Wildcats Centres aim to engage young girls aged 5-11 in fun football sessions.
If you are interested in applying for one of these roles, then please see the attached job advert, application form and role profile. The deadline for receipt of applications is 5pm on Wednesday 31st October 2018. Any applications received after this deadline will not be considered.


Consumer confidence has dropped sharply, primarily from what we see as temporary factors.
Economic fundamentals are still sound and could help stabilize sentiment.
We have yet to see the signs of excesses that have preceded past recessions.
Deteriorating confidence over the last several months has soured the economic landscape.
spending, and consequently, on output. Historical data show that the U.S. economy has entered a recession a average of 19 months after a peak in consumer confidence, and a drop in sentiment has been a warning sign for past economic cycles [Figure 1].
While confidence shifts can be meaningful obstacles to economic activity, we think the severity of the current decline is due, in part, to temporary factors such as the government shutdown, and the fundamentals are still in place for sentiment to stabilize.
Over the past several months, U.S. consumer sentiment has had to weather increasing trade risk, a 35-day government shutdown, unnerving headlines on geopolitical issues, a slowdown in global growth, and a near bear market in the S&P 500 Index. Many of these headwinds have understandably chilled confidence, and their collective impact can be daunting.
However, we think many of these headwinds will subside in the near term, if they haven't already. Trade tensions have had a widespread effect on corporate sentiment and economic activity, but we expect meaningful progress toward a U.S.-China trade resolution soon. Government shutdowns, especially when prolonged, have historically dampened consumer sentiment. For example, consumer confidence fell an average of 9.3 points in the final month of the 1995 and 2013 shutdowns, which were both at least 17 days. However, confidence (and economic activity) typically has rebounded after shutdowns, and we expect the same outcome here.
A downturn in major asset prices can also lead to consumers having a decreased sense of financial well-being, which can restrain spending and further reduce asset prices. The S&P 500 posted one of its worst slides of the current bull market in November and December 2018, but we believe stocks have bottomed, and may even make new highs in 2019. The S&P 500 has rebounded 14% since the late December lows, so we could see sentiment turn solely from stocks rallying.
We believe solid fundamentals are especially important during a mature business cycle, a point we've emphasized frequently in today's volatile environment. The bulk of U.S. economic data we've seen lately remains sound. The labor market continues to show strength. Manufacturing gauges rebounded last month. Inflation has largely normalized, and it remains manageable. Most important, we still see reasonable potential for a resurgence in capital investment, which could drive higher productivity and help extend the cycle.
Corporate earnings have also signaled the possibility of a longer runway for economic growth. Profit growth for S&P 500 companies recently peaked at 23% in the third quarter of 2018, the quarter before consumer confidence's latest high. Since 1980, the U.S. economy has entered a recession an average of 28 months after the last earnings peak in the economic cycle [Figure 2].
clarity on global economic conditions. At the same time, fiscal stimulus from the 2017 tax law, deregulation, and increased government spending remain in place, and it can take several years for the impact on economic output to fully play out, so we expect we'll continue to see benefits.
Fiscal stimulus this late in the cycle does have some potential to contribute to a build-up in economic excesses, but we don't see any alarming signs of late-cycle excesses or "red flags" in the economy. Growth in wages, which makes up about 70% of business costs, has been healthy but subdued enough to not significantly weigh on profit margins. Average hourly earnings for nonsupervisory workers has climbed as much as 3.5% year over year recently, but is still well below the 4% we've seen preceding recessions historically. Unit labor costs, or employers'compensation expenses per unit of output, grew 0.9% year over year through the third quarter of 2018, well below the 2% unit labor cost growth at historical consumer confidence peaks.
continue to exhibit stable growth for years, so the age of the expansion in itself isn't a red flag to us.
Global uncertainty is at its highest point in several years, but while the current environment is uncomfortable, we believe the steep drop in confidence has been exaggerated by some short-term factors, and we don't expect the recent decline in consumer confidence to have a meaningful impact on growth. Risks to economic fundamentals have increased, but the economy overall remains on solid footing, and, as discussed in our Outlook 2019, we expect GDP growth of 2.5–2.75% in 2019.
Any economic forecasts set forth in the presentation may not develop as predicted and there can be no guarantee that strategies promoted will be successful.


text:
BoDeans is an American rock band. The band has released 14 studio albums, four live albums, seven singles, and two compilation albums.

Albums

Studio albums

Compilations and live albums

Singles

Promotional singles

Videography
 Homebrewed: Live from the Pabst (2005, DVD)

Music videos
 "She's a Runaway" (1986)
 "Fadeaway" (1986)
 "Only Love" (1987)
 "Dreams" (1988)
 "Good Work" (1989)
 "You Don't Get Much" (1989)
 "Good Things" (1991)
 "Black White & Blood Red" (1991)
 "Closer to Free" (1996)
 "All the World" (2012)
 "American" (2012)

References

External links
BoDeans

Pop music group discographies


text:
Daniel Palazuelos, MD, MPH
Daniel Palazuelos, MD, MPH is a global health implementer-educator who holds positions at Harvard Medical School (HMS), the Brigham and Women's Hospital (BWH), and Partners In Health (PIH). He started his career in global health equity by living and working with community health workers (CHWs) in impoverished communities in Chiapas, Mexico, and these grassroots experiences have deeply influenced his approach to addressing the biggest challenges in global health. Over the last decade, he helped to launch Compañeros En Salud - México (PIH's program in Mexico), the Financing Alliance for Health (which helps governments design and fund ambitious, affordable, and at-scale community health programs), and the Community Health Impact Coalition (a five year quality initiative by some of the field's most innovative implementers to catalyze the adoption of high-impact community health systems design). During the COVID-19 pandemic, he served as the Technical Lead for the PIH-US Public Health Accompaniment Unit's work supporting migrant workers in Immokalee, Florida.
Now as a clinician-educator at BWH in Boston, the Director of Community Health Systems at PIH, a strategist for the PIH project in Mexico, a mentor and course director at HMS, and a residency assistant program director at BWH, his unified goal is to assure that our finest clinicians are trained to understand the community perspective, and our health care system strengthening efforts are aligned to the real needs of people living in the poorest and most difficult circumstances.


Lauren Chazal MBA has a strong command of the clinical trials industry from a site operations, business development and financial management perspective. She began her career in clinical trial operations with a privately-held multi-site clinical research organization. Since 2012, she has held a pivotal role in business development and relationship management for clinical research sites globally. Lauren has in-depth knowledge of the complex interactions between clinical research centers, CROs and sponsors combined with a keen understanding of the importance of access to high quality trial sites and patient recruitment solutions. Lauren graduated from Lehigh University in 2005 with a bachelor's degree in Finance and Marketing and in 2009 from University of Florida with a Master's in Business Administration.


At Goodrich & Associates, we have more than 65 years of cumulative experience helping clients plan for their financial future. Through the ebbs and flows of the market, we believe in two tools to help you chart your course. First, setting up and sticking to a Strategic Asset Allocation Model (SAAM) will help you stay diversified. Second, combining that with an Investment Policy Statement (IPS) will give you a holistic view of your financial assets. But we believe communication is the most important part of navigating your financial future. We're not just here to service your account. We serve as our clients' trusted advisors.


Concerning hotel and transportation for 2018 FHH subcommittee 2 meeting in USP, please visit http://www.usp.org/help/hotel-information.
These hotels offer promotion (USP corporate rate) for USP meeting participants, just click hotel name and book. Also free shuttle bus will be serviced between USP and Cambria Hotel & Suites Rockville only.


Copper-based fungicides like copper oxychloride, copper hydroxide, copper acetate etc are effective. You may combine them with mancozeb for better action.
Thanks. Pls which systemic fungicide can i use?
I appreciate your immense help so far.