Example #1
0
    def lqapprox(self, s0, x0):
        """
        Solves discrete time continuous state/action dynamic programming model using a linear quadratic approximation
        Args:
            s0: steady-state state
            x0:  steady-state action

        Returns:
            A LQmodel object
        """


        assert (self.dims.ni * self.dims.nj < 2), 'Linear-Quadratic not implemented for models with discrete state or choice'
        s0, x0 = np.atleast_1d(s0, x0)

        assert s0.size == self.dims.ds, 's0 must have %d values' % self.dims.ds
        assert x0.size == self.dims.dx, 'x0 must have %d values' % self.dims.dx

        s0, x0 = s0.astype(float), x0.astype(float)
        s0.shape = -1, 1
        x0.shape = -1, 1

        delta = self.time.discount

        # Fix shock at mean
        estar = self.random.w @ self.random.e.T
        estar.shape = -1, 1

        # Get derivatives
        f0, fx, fxx = self.reward(s0, x0, None, None, True)
        g0, gx, gxx = self.transition(s0, x0, None, None, None, estar, derivative=True)

        fs = jacobian(lambda y: self.reward(y.reshape((-1, 1)), x0, None, None), s0)
        fxs = jacobian(lambda y: self.reward(y.reshape((-1, 1)), x0, None, None, True)[1], s0)
        fss = hessian(lambda y: self.reward(y.reshape((-1, 1)), x0, None, None), s0)
        gs = jacobian(lambda y: self.transition(y.reshape((-1, 1)), x0, None, None, None, estar), s0)

        # Reshape to ensure conformability
        ds, dx = self.dims['ds', 'dx']

        f0.shape = 1, 1
        s0.shape = ds, 1
        x0.shape = dx, 1
        fs.shape = 1, ds
        fx.shape = 1, dx
        fss.shape = ds, ds
        fxs.shape = dx, ds
        fxx.shape = dx, dx
        g0.shape = ds, 1
        gx.shape = ds, dx
        gs.shape = ds, ds
        fsx = fxs.T

        f0 += - fs @ s0 - fx @ x0 + 0.5 * s0.T @ fss @ s0 + s0.T @ fsx @ x0 + 0.5 * x0.T @ fxx @ x0
        fs += - s0.T @ fss - x0.T @ fxs
        fx += - s0.T @ fsx - x0.T @ fxx
        g0 += - gs @ s0 - gx @ x0

        return LQmodel(f0, fs, fx, fss, fsx, fxx, g0, gs, gx, delta,self.labels.s, self.labels.x)
Example #2
0
    def check_derivatives(self):
        ni, nj, ds, ns = self.dims['ni', 'nj', 'ds', 'ns']
        nij = ni * nj
        idx = pd.MultiIndex.from_product([np.arange(ni), np.arange(nj)], names=['i', 'j'])
        F = pd.DataFrame({
            'fx': np.zeros(nij) + np.nan,
            'fxx': np.zeros(nij) + np.nan},
            index=idx)

        idy = pd.MultiIndex.from_product([['gx', 'gxx'], np.arange(ds)], names=['func', 's'])
        G = pd.DataFrame(np.zeros((nij,2 * ds)), index=idx, columns=idy)

        s = self.Value.nodes
        e = np.tile(self.random.e[:, 0], ns)

        for k, (i, j) in enumerate(indices(ni,nj).T):
            xl, xu = self.bounds(s, i , j)
            xlinf = np.isinf(xl)
            xuinf = np.isinf(xu)
            xm = (xl + xu) / 2
            xm[xlinf] = xu[xlinf]
            xm[xuinf] = xl[xuinf]
            xm[xuinf & xlinf] = 0.0

            f, fx, fxx = self.reward(s,xm,i,j, derivative=True)
            fxa = jacobian(lambda z: self.reward(s, z, i, j), xm)
            fxxa = hessian(lambda z: self.reward(s, z, i, j), xm)

            #FIXME: HESSIAN SEEMS TO BE THROWING WRONG DIMENSIONS!!

            F.values[k, 0] = np.linalg.norm(fx - fxa, np.inf)
            F.values[k, 1] = np.linalg.norm(fxx - fxxa, np.inf)
Example #3
0
def ss2(x, r, tau):
    tmp = lambda x: ss(x, r, tau)
    return jacobian(tmp, x)[0]
def ss2(x, r, tau):
    tmp = lambda x: ss(x, r, tau)
    return jacobian(tmp, x)[0]
Example #5
0
    def lqapprox(self, s0, x0, steady=False):

        assert (
            self.dims.ni * self.dims.nj < 2
        ), 'Linear-Quadratic not implemented for models with discrete state or choice'
        s0, x0 = np.atleast_1d(s0, x0)

        assert s0.size == self.dims.ds, 's0 must have %d values' % self.dims.ds
        assert x0.size == self.dims.dx, 'x0 must have %d values' % self.dims.dx

        s0, x0 = s0.astype(float), x0.astype(float)
        s0.shape = -1, 1
        x0.shape = -1, 1

        delta = self.time.discount

        # Fix shock at mean
        estar = self.random.w @ self.random.e.T
        estar.shape = -1, 1

        # Get derivatives
        f0, fx, fxx = self.reward(s0, x0, None, None, True)
        g0, gx, gxx = self.transition(s0,
                                      x0,
                                      None,
                                      None,
                                      None,
                                      estar,
                                      derivative=True)

        fs = jacobian(
            lambda y: self.reward(y.reshape((-1, 1)), x0, None, None), s0)
        fxs = jacobian(
            lambda y: self.reward(y.reshape((-1, 1)), x0, None, None, True)[1],
            s0)
        fss = hessian(
            lambda y: self.reward(y.reshape((-1, 1)), x0, None, None), s0)
        gs = jacobian(
            lambda y: self.transition(y.reshape(
                (-1, 1)), x0, None, None, None, estar), s0)

        # Reshape to ensure conformability
        ds, dx = self.dims['ds', 'dx']

        f0.shape = 1, 1
        s0.shape = ds, 1
        x0.shape = dx, 1
        fs.shape = 1, ds
        fx.shape = 1, dx
        fss.shape = ds, ds
        fxs.shape = dx, ds
        fxx.shape = dx, dx
        g0.shape = ds, 1
        gx.shape = ds, dx
        gs.shape = ds, ds
        fsx = fxs.T

        f0 += -fs @ s0 - fx @ x0 + 0.5 * s0.T @ fss @ s0 + s0.T @ fsx @ x0 + 0.5 * x0.T @ fxx @ x0
        fs += -s0.T @ fss - x0.T @ fxs
        fx += -s0.T @ fsx - x0.T @ fxx
        g0 += -gs @ s0 - gx @ x0

        # Solve Riccati equation using QZ decomposition
        dx2ds = dx + 2 * ds
        A = np.zeros((dx2ds, dx2ds))
        A[:ds, :ds] = np.identity(ds)
        A[ds:-ds, -ds:] = -delta * gx.T
        A[-ds:, -ds:] = delta * gs.T

        B = np.zeros_like(A)
        B[:ds, :-ds] = np.c_[gs, gx]
        B[ds:-ds, :-ds] = np.c_[fsx.T, fxx]
        B[-ds:] = np.c_[-fss, -fsx, np.identity(ds)]

        S, T, Q, Z = qzordered(A, B)
        C = np.real(np.linalg.solve(Z[:ds, :ds].T, Z[ds:, :ds].T)).T
        X = C[:dx]
        P = C[dx:, :]

        # Compute steady-state state, action, and shadow price
        t0 = np.r_[np.c_[fsx.T, fxx, delta * gx.T],
                   np.c_[fss, fsx,
                         delta * gs.T - np.eye(ds)], np.c_[gs - np.eye(ds), gx,
                                                           np.zeros((ds, ds))]]
        t1 = np.r_[-fx.T, -fs.T, -g0]
        t = np.linalg.solve(t0, t1)
        sstar, xstar, pstar = np.split(t, [ds, ds + dx])
        vstar = (f0 + fs @ sstar + fx @ xstar + 0.5 * sstar.T @ fss @ sstar +
                 sstar.T @ fsx @ xstar + 0.5 * xstar.T @ fxx @ xstar) / (1 -
                                                                         delta)

        # Compute lq-approximation optimal policy and shadow price functions at state nodes
        s = self.Value.nodes.T.copy()
        sstar = sstar.T
        xstar = xstar.T
        pstar = pstar.T
        s -= sstar  # hopefully broadcasting works here  (np.ones(ns,1),:)  #todo make sure!!
        xlq = xstar + s @ X.T  #(np.ones(1,ns),:)
        plq = pstar + s @ P.T  #(np.ones(1,ns),:)
        vlq = vstar + s @ pstar.T + 0.5 * np.sum(
            s * (s @ P.T), axis=1, keepdims=True)

        self.Value[:] = vlq.T[:]
        self.Value_j[:] = vlq.T[:]
        self.Policy[:] = xlq.T[:]
        self.Policy_j[:] = xlq.T[:]

        #MAKE PANDAS DATAFRAME
        ni, nj, dx = self.dims['ni', 'nj', 'dx']

        sr = self.Value.nodes.copy()
        discrete_indices = np.indices(vlq.shape)[:2].reshape(2, -1)
        data = np.vstack((discrete_indices, np.tile(sr,
                                                    ni * nj), vlq.T.flatten()))

        columns = ["i", "j"
                   ] + self.labels.s + ['value_j' if nj > 1 else 'value']

        # Add continuous action
        if dx:
            xlq = np.rollaxis(xlq, -2)
            xlq.shape = (dx, -1)
            data = np.vstack((data, xlq))
            columns = columns + list(self.labels.x)

        data = pd.DataFrame(data.T, columns=columns)

        # Add value
        if nj > 1:
            data['value'] = np.nan
            data.value[data.j == 0] = vlq.flatten()

        # eliminate singleton dimensions, label non-singleton dimensions
        if ni > 1:
            data['i'] = self.__as_categorical(data.i, True)
        else:
            del data['i']

        if nj > 1:
            data['j'] = self.__as_categorical(data.j, False)
        else:
            del data['j']

        if steady:
            ss0 = {a: b for a, b in zip(self.labels.s, sstar)}
            ss0.update({a: b for a, b in zip(self.labels.x, xstar)})
            ss0['value'] = vstar
            ss0['shadow'] = pstar

        return (data, ss0) if steady else data