Пример #1
0
    def get_equation_io(self, eqn_sys: EqnSys):
        # determine all signals present in the set of equations
        all_signal_names = set(signal_names(eqn_sys.get_all_signals()))

        # determine inputs
        input_names = (signal_names(self.get_analog_inputs())
                       | self.assignments.keys()) & all_signal_names
        inputs = self.get_signals(input_names)

        # determine states
        state_names = set(signal_names(eqn_sys.get_states()))
        deriv_names = set(signal_names(eqn_sys.get_derivs()))
        states = self.get_signals(state_names)

        # determine outputs
        output_names = (all_signal_names - input_names - state_names -
                        deriv_names) & self.signals.keys()
        outputs = self.get_signals(output_names)

        # determine sel_bits
        sel_bit_names = set(signal_names(eqn_sys.get_sel_bits()))
        sel_bits = self.get_signals(sel_bit_names)

        # return result
        return inputs, states, outputs, sel_bits
Пример #2
0
def main():
    # equation display
    print(EqnList([AnalogSignal('a') == AnalogSignal('b')]))
    print()

    # signal extraction
    print(signal_names(EqnList([AnalogSignal('a') == AnalogSignal('b')]).get_all_signals()))
    print(signal_names(EqnList([AnalogSignal('a') == Deriv(AnalogSignal('b'))]).get_derivs()))
    print(signal_names(EqnList([AnalogSignal('a') == Deriv(AnalogSignal('b'))]).get_states()))
    print(signal_names(EqnList([AnalogSignal('a') == EqnCase([1, 2], [DigitalSignal('s')])]).get_sel_bits()))
Пример #3
0
    def make_array(self, expr: Array):
        # make the output signal that will hold the results
        output = Signal(name=next(self.namer), format_=expr.format_)
        self.make_signal(output)

        # compile the array elements and address to signals
        elements = [self.expr_to_signal(element) for element in expr.elements]
        address  = self.expr_to_signal(expr.address)

        # extra step for analog signals -- they must be aligned to the output format
        if isinstance(expr.format_, RealFormat):
            # rename the elements list to indicate these values are not aligned to the output format
            non_aligned_elements = elements

            # created an "aligned" version of each signal
            elements = [Signal(name=next(self.namer), format_=expr.format_) for _ in range(len(expr))]
            for element, non_aligned_element in zip(elements, non_aligned_elements):
                self.make_signal(element)
                self.make_assign(input_=non_aligned_element, output=element)

        # now create the array
        case_statment(gen=self, sel=address.name, var=output.name, values=signal_names(elements), default=0)

        # and last return the output signal
        return output
Пример #4
0
    def make_concatenation(self, expr: Concatenate):
        output = Signal(name=next(self.namer), format_=expr.format_)
        self.make_signal(output)

        inputs_ = [self.expr_to_signal(operand) for operand in expr.operands]
        value = '{' + ', '.join(signal_names(inputs_)) + '}'
        self.digital_assignment(signal=output, value=value)

        return output
Пример #5
0
    def make_bitwise_operator(self, expr: BitwiseOperator):
        output = Signal(name=next(self.namer), format_=expr.format_)
        self.make_signal(output)

        inputs = [self.expr_to_signal(operand) for operand in expr.operands]
        value = BITWISE_OP[type(expr)].join(signal_names(inputs))
        self.digital_assignment(signal=output, value=value)

        return output
Пример #6
0
def main():
    from msdsl.eqn.deriv import Deriv
    from msdsl.eqn.cases import eqn_case
    from msdsl.expr.signals import DigitalSignal

    model = MixedSignalModel('test', AnalogInput('x'), dt=1)
    y = AnalogSignal('y')
    z = model.add_signal(AnalogSignal('z', 10))
    s = model.add_signal(DigitalSignal('s'))

    eqn_sys = EqnSys(
        [y == model.x + 1,
         Deriv(z) == (y - z) * eqn_case([2, 3], [s])])

    inputs, states, outputs, sel_bits = model.get_equation_io(eqn_sys)

    print('inputs:', signal_names(inputs))
    print('states:', signal_names(states))
    print('outputs:', signal_names(outputs))
    print('sel_bits:', signal_names(sel_bits))
Пример #7
0
    def to_lds(self,
               inputs: List[Signal] = None,
               states: List[Signal] = None,
               outputs: List[Signal] = None):
        # set defaults
        inputs = inputs if inputs is not None else []
        states = states if states is not None else []
        outputs = outputs if outputs is not None else []

        # create list of derivatives of state variables
        deriv_dict = {deriv.name: deriv for deriv in self.get_derivs()}
        derivs = list(deriv_dict.values())

        # sanity check: no repeated entries in inputs, states, derivatives, or outputs
        assert len(set(signal_names(inputs))) == len(
            inputs), 'Repeated entries in inputs.'
        assert len(set(signal_names(outputs))) == len(
            outputs), 'Repeated entries in outputs.'
        assert len(set(signal_names(states))) == len(
            states), 'Repeated entries in states.'
        assert len(set(signal_names(derivs))) == len(
            derivs), 'Repeated entries in derivatives.'

        # sanity check: inputs, states, derivatives, and outputs should be disjoint
        assert set(signal_names(inputs)).isdisjoint(
            signal_names(outputs)), 'Inputs and outputs are not disjoint.'
        assert set(signal_names(inputs)).isdisjoint(
            signal_names(states)), 'Inputs and states are not disjoint.'
        assert set(signal_names(inputs)).isdisjoint(signal_names(
            derivs)), 'Inputs and state derivatives are not disjoint.'
        assert set(signal_names(outputs)).isdisjoint(
            signal_names(states)), 'Outputs and states are not disjoint.'
        assert set(signal_names(outputs)).isdisjoint(signal_names(
            derivs)), 'Outputs and state derivatives are not disjoint.'
        assert set(signal_names(states)).isdisjoint(signal_names(
            derivs)), 'States and state derivatives are not disjoint.'

        # sanity check: signal names of derivatives should be the states
        assert set(signal_names(states)) == set(
            signal_names([deriv.signal for deriv in derivs]))

        # create list of all internal signals, then use it to figure out what signals are completely internal
        external_names = set(signal_names(inputs + outputs + states + derivs))
        internal_name_set = set(signal_names(
            self.get_all_signals())) - external_names

        # indices of known and unknown variables
        unknowns = list2dict(
            list(internal_name_set) + signal_names(outputs) +
            signal_names(derivs))
        knowns = list2dict(signal_names(inputs) + signal_names(states))

        # sanity checks
        assert not (
            len(self) > len(unknowns)
        ), f'System of equations is over-constrained with {len(self)} equations and {len(unknowns)} unknowns.'
        assert not (
            len(self) < len(unknowns)
        ), f'System of equations is under-constrained with {len(self)} equations and {len(unknowns)} unknowns.'

        # build up matrices
        U = np.zeros((len(self), len(unknowns)), dtype=float)
        V = np.zeros((len(self), len(knowns)), dtype=float)

        for row, eqn in enumerate(self):
            # prepare equation for analysis
            eqn = eqn.lhs - eqn.rhs
            eqn = distribute_mult(eqn)

            # extract coefficients of signals (note that some signals may be repeated - we deal with this in the next step
            coeffs, others = extract_coeffs(eqn)
            assert len(others) == 0, \
                'The following terms are not yet handled: ['+ ', '.join(str(other) for other in others)+']'

            # sum up all of the coefficients for each signal
            for coeff, signal in coeffs:
                if signal.name in unknowns:
                    U[row, unknowns[signal.name]] += +coeff
                elif signal.name in knowns:
                    V[row, knowns[signal.name]] += -coeff
                else:
                    raise Exception(
                        'Variable is not marked as known vs. unknown: ' +
                        signal.name)

        # solve for unknowns in terms of knowns
        M = np.linalg.solve(U, V)

        # separate into A, B, C, D matrices
        if len(states) > 0:
            A = np.zeros((len(states), len(states)), dtype=float)
            for row, out_state in enumerate(signal_names(states)):
                for col, in_state in enumerate(signal_names(states)):
                    A[row, col] = M[unknowns[deriv_str(out_state)],
                                    knowns[in_state]]
        else:
            A = None

        if len(states) > 0 and len(inputs) > 0:
            B = np.zeros((len(states), len(inputs)), dtype=float)
            for row, out_state in enumerate(signal_names(states)):
                for col, in_input in enumerate(signal_names(inputs)):
                    B[row, col] = M[unknowns[deriv_str(out_state)],
                                    knowns[in_input]]
        else:
            B = None

        if len(outputs) > 0 and len(states) > 0:
            C = np.zeros((len(outputs), len(states)), dtype=float)
            for row, out_output in enumerate(signal_names(outputs)):
                for col, in_state in enumerate(signal_names(states)):
                    C[row, col] = M[unknowns[out_output], knowns[in_state]]
        else:
            C = None

        if len(outputs) > 0 and len(inputs) > 0:
            D = np.zeros((len(outputs), len(inputs)), dtype=float)
            for row, out_output in enumerate(signal_names(outputs)):
                for col, in_input in enumerate(signal_names(inputs)):
                    D[row, col] = M[unknowns[out_output], knowns[in_input]]
        else:
            D = None

        return LDS(A=A, B=B, C=C, D=D)
Пример #8
0
    def add_eqn_sys(self, eqns: List[ModelExpr], extra_outputs=None):
        """
        Accepts a list of equations that can contain derivatives of analog state variables.  The approach used is
        to convert the system of differential equations into a standard-form linear dynamical system (reference:
        http://ee263.stanford.edu/lectures/linsys.pdf).  If there are several eqn_cases to be considered, we construct
        a different LDS for each one.  These LDS's are discretized using the given timestep by using the exponential
        matrix function assuming piecewise-constant input (sometimes known as the zero-order hold approach).

        :param eqns:            List of equations.
        :param extra_outputs:   List of internal variables in the system of equations that should be bound to analog signals.
        """

        # set defaults
        extra_outputs = extra_outputs if extra_outputs is not None else []

        # create object to hold system of equations
        eqn_sys = EqnSys(eqns)

        # analyze equation to find out knowns and unknowns
        inputs, states, outputs, sel_bits = self.get_equation_io(eqn_sys)

        # add the extra outputs as needed
        for extra_output in extra_outputs:
            if not isinstance(extra_output, Signal):
                print('Skipping extra output ' + str(extra_output) +
                      ' since it is not a Signal.')
            elif extra_output.name in signal_names(outputs):
                print('Skipping extra output ' + extra_output.name + \
                      ' since it is already included by default in the outputs of the system of equations.')
            else:
                outputs.append(extra_output)

        # initialize lists of matrices
        collection = LdsCollection()

        # iterate over all of the bit combinations
        for k in range(2**len(sel_bits)):
            # substitute values for this particular setting
            sel_bit_settings = address_to_settings(k, sel_bits)
            eqn_sys_k = eqn_sys.subst_case(sel_bit_settings)

            # convert system of equations to a linear dynamical system
            lds = eqn_sys_k.to_lds(inputs=inputs,
                                   states=states,
                                   outputs=outputs)

            # discretize linear dynamical system
            lds = lds.discretize(dt=self.dt)

            # add to collection of LDS systems
            collection.append(lds)

        # construct address for selection
        if len(sel_bits) > 0:
            sel = concatenate(sel_bits)
        else:
            sel = None

        # add the discrete-time equation
        self.add_discrete_time_lds(collection=collection,
                                   inputs=inputs,
                                   states=states,
                                   outputs=outputs,
                                   sel=sel)