def get_derivative(self, s): # Case 1: s is a constant, e.g. MX(5) if ca.MX(s).is_constant(): return 0 # Case 2: s is a symbol, e.g. MX(x) elif s.is_symbolic(): if s.name() not in self.derivative: if len(self.for_loops ) > 0 and s in self.for_loops[-1].indexed_symbols: # Create a new indexed symbol, referencing to the for loop index inside the vector derivative symbol. for_loop_symbol = self.for_loops[-1].indexed_symbols[s] s_without_index = self.get_mx( ast.ComponentRef(name=for_loop_symbol.tree.name)) der_s_without_index = self.get_derivative(s_without_index) if ca.MX(der_s_without_index).is_symbolic(): return self.get_indexed_symbol( ast.ComponentRef( name=der_s_without_index.name(), indices=for_loop_symbol.tree.indices), der_s_without_index) else: return 0 else: der_s = _new_mx("der({})".format(s.name()), s.size()) self.derivative[s.name()] = der_s self.nodes[self.current_class][der_s.name()] = der_s return der_s else: return self.derivative[s.name()] # Case 3: s is an already indexed symbol, e.g. MX(x[1]) elif s.is_op(ca.OP_GETNONZEROS) and s.dep().is_symbolic(): slice_info = s.info()['slice'] dep = s.dep() if dep.name() not in self.derivative: der_dep = _new_mx("der({})".format(dep.name()), dep.size()) self.derivative[dep.name()] = der_dep return der_dep[ slice_info['start']:slice_info['stop']:slice_info['step']] else: return self.derivative[dep.name( )][slice_info['start']:slice_info['stop']:slice_info['step']] # Case 4: s is an expression that requires differentiation, e.g. MX(x2 * x2) # Need to do this sort of expansion: der(x1 * x2) = der(x1) * x2 + x1 * der(x2) else: # Differentiate expression using CasADi orig_deps = ca.symvar(s) deps = ca.vertcat(*orig_deps) J = ca.Function('J', [deps], [ca.jacobian(s, deps)]) J_sparsity = J.sparsity_out(0) der_deps = [ self.get_derivative(dep) if J_sparsity.has_nz(0, j) else ca.DM.zeros(dep.size()) for j, dep in enumerate(orig_deps) ] return ca.mtimes(J(deps), ca.vertcat(*der_deps))
def test_modification_typo(self): with open(os.path.join(MODEL_DIR, 'ModificationTypo.mo'), 'r') as f: txt = f.read() for c in ["Wrong1", "Wrong2"]: with self.assertRaises(tree.ModificationTargetNotFound): ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name=c)) for c in ["Good1", "Good2"]: ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name=c))
def test_inheritance_symbol_modifiers(self): with open(os.path.join(MODEL_DIR, 'Inheritance.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='Sub')) self.assertEqual(flat_tree.classes['Sub'].symbols['x'].max.value, 30.0)
def test_deep_copy_timeout(self): with open(os.path.join(MODEL_DIR, 'DeepCopyTimeout.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) # Start a background thread which will run the flattening, such that # we can kill it if takes to long. # noinspection PyTypeChecker thread = threading.Thread(target=tree.flatten, args=( ast_tree, ast.ComponentRef(name='Test'), )) # Daemon threads automatically stop when the program stops (and do not # prevent the program from exiting) thread.setDaemon(True) thread.start() # Use a timeout of 5 seconds. We check every 100 ms sec, such that the # test is fast to succeed when everything works as expected. for i in range(50): time.sleep(0.1) if not thread.isAlive(): return self.assertFalse(thread.isAlive(), msg='Timeout occurred')
def test_spring(self): with open(os.path.join(MODEL_DIR, 'Spring.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) print('AST TREE\n', ast_tree) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='Spring')) print('AST TREE FLAT\n', flat_tree) self.flush()
def test_extends_modification(self): with open(os.path.join(MODEL_DIR, 'ExtendsModification.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='MainModel')) self.assertEqual( flat_tree.classes['MainModel'].symbols['e.HQ.H'].min.name, "e.H_b")
def test_nested_classes(self): with open(os.path.join(MODEL_DIR, 'NestedClasses.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='C2')) self.assertEqual(flat_tree.classes['C2'].symbols['v1'].nominal.value, 1000.0) self.assertEqual(flat_tree.classes['C2'].symbols['v2'].nominal.value, 1000.0)
def test_inheritance(self): with open(os.path.join(MODEL_DIR, 'InheritanceInstantiation.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='C2')) self.assertEqual( flat_tree.classes['C2'].symbols['bcomp1.b'].value.value, 3.0) self.assertEqual( flat_tree.classes['C2'].symbols['bcomp3.a'].value.value, 1.0) self.assertEqual( flat_tree.classes['C2'].symbols['bcomp3.b'].value.value, 2.0)
def test_spring(self): with open(os.path.join(MODEL_DIR, 'SpringSystem.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='SpringSystem')) print(flat_tree) text = gen_sympy.generate(ast_tree, 'SpringSystem') with open(os.path.join(GENERATED_DIR, 'Spring.py'), 'w') as f: f.write(text) from test.generated.Spring import SpringSystem as SpringSystem e = SpringSystem() e.linearize_symbolic() e.linearize() # noinspection PyUnusedLocal res = e.simulate(x0=[1.0, 1.0]) self.flush()
def test_connector(self): with open(os.path.join(MODEL_DIR, 'Connector.mo'), 'r') as f: txt = f.read() ast_tree = parser.parse(txt) # print(ast_tree) # noinspection PyUnusedLocal flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='Aircraft')) # print(flat_tree) # noinspection PyUnusedLocal walker = tree.TreeWalker() # noinspection PyUnusedLocal classes = ast_tree.classes # noinspection PyUnusedLocal root = ast_tree.classes['Aircraft'] # instantiator = tree.Instantiator(classes=classes) # walker.walk(instantiator, root) # print(instantiator.res[root].symbols.keys()) # print(instantiator.res[root]) # print('INSTANTIATOR\n-----------\n\n') # print(instantiator.res[root]) # connectExpander = tree.ConnectExpander(classes=classes) # walker.walk(connectExpander, instantiator.res[root]) # print('CONNECT EXPANDER\n-----------\n\n') # print(connectExpander.new_class) # text = gen_sympy.generate(ast_tree, 'Aircraft') # print(text) # with open(os.path.join(MODEL_DIR, 'generated/Connect.py'), 'w') as f: # f.write(text) # from generated.Connect import Aircraft as Aircraft # e = Aircraft() # res = e.simulate() self.flush()
def exitExpression(self, tree): if isinstance(tree.operator, ast.ComponentRef): op = tree.operator.name else: op = tree.operator if op == '*': op = 'mtimes' # .* differs from * if op.startswith('.'): op = op[1:] logger.debug('exitExpression') n_operands = len(tree.operands) if op == 'der': v = self.get_mx(tree.operands[0]) src = self.get_derivative(v) elif op == '-' and n_operands == 1: src = -self.get_mx(tree.operands[0]) elif op == 'not' and n_operands == 1: src = ca.if_else(self.get_mx(tree.operands[0]), 0, 1, True) elif op == 'mtimes': assert n_operands >= 2 src = self.get_mx(tree.operands[0]) for i in tree.operands[1:]: src = ca.mtimes(src, self.get_mx(i)) elif op == 'transpose' and n_operands == 1: src = self.get_mx(tree.operands[0]).T elif op == 'sum' and n_operands == 1: v = self.get_mx(tree.operands[0]) src = ca.sum1(v) elif op == 'linspace' and n_operands == 3: a = self.get_mx(tree.operands[0]) b = self.get_mx(tree.operands[1]) n_steps = self.get_integer(tree.operands[2]) src = ca.linspace(a, b, n_steps) elif op == 'fill' and n_operands == 2: val = self.get_mx(tree.operands[0]) n_row = self.get_integer(tree.operands[1]) src = val * ca.DM.ones(n_row) elif op == 'fill' and n_operands == 3: val = self.get_mx(tree.operands[0]) n_row = self.get_integer(tree.operands[1]) n_col = self.get_integer(tree.operands[2]) src = val * ca.DM.ones(n_row, n_col) elif op == 'zeros' and n_operands == 1: n_row = self.get_integer(tree.operands[0]) src = ca.DM.zeros(n_row) elif op == 'zeros' and n_operands == 2: n_row = self.get_integer(tree.operands[0]) n_col = self.get_integer(tree.operands[1]) src = ca.DM.zeros(n_row, n_col) elif op == 'ones' and n_operands == 1: n_row = self.get_integer(tree.operands[0]) src = ca.DM.ones(n_row) elif op == 'ones' and n_operands == 2: n_row = self.get_integer(tree.operands[0]) n_col = self.get_integer(tree.operands[1]) src = ca.DM.ones(n_row, n_col) elif op == 'identity' and n_operands == 1: n = self.get_integer(tree.operands[0]) src = ca.DM.eye(n) elif op == 'diagonal' and n_operands == 1: diag = self.get_mx(tree.operands[0]) n = len(diag) indices = list(range(n)) src = ca.DM.triplet(indices, indices, diag, n, n) elif op == 'cat': axis = self.get_integer(tree.operands[0]) assert axis == 1, "Currently only concatenation on first axis is supported" entries = [] for sym in [self.get_mx(op) for op in tree.operands[1:]]: if isinstance(sym, list): for e in sym: entries.append(e) else: entries.append(sym) src = ca.vertcat(*entries) elif op == 'delay' and n_operands == 2: expr = self.get_mx(tree.operands[0]) duration = self.get_mx(tree.operands[1]) src = _new_mx('_pymoca_delay_{}'.format(self.delay_counter), *expr.size()) self.delay_counter += 1 for f in self.for_loops: syms = set(ca.symvar(expr)) if syms.intersection(f.indexed_symbols): f.register_indexed_symbol(src, lambda i: i, True, tree.operands[0], f.index_variable) self.model.delay_states.append(src.name()) self.model.inputs.append(Variable(src)) delay_argument = DelayArgument(expr, duration) self.model.delay_arguments.append(delay_argument) elif op == '_pymoca_interp1d' and n_operands >= 3 and n_operands <= 4: entered_class = self.entered_classes[-1] if isinstance(tree.operands[0], ast.ComponentRef): xp = self.get_mx(entered_class.symbols[tree.operands[0].name].value) else: xp = self.get_mx(tree.operands[0]) if isinstance(tree.operands[1], ast.ComponentRef): yp = self.get_mx(entered_class.symbols[tree.operands[1].name].value) else: yp = self.get_mx(tree.operands[1]) arg = self.get_mx(tree.operands[2]) if n_operands == 4: assert isinstance(tree.operands[3], ast.Primary) mode = tree.operands[3].value else: mode = 'linear' func = ca.interpolant('interpolant', mode, [xp], yp) src = func(arg) elif op == '_pymoca_interp2d' and n_operands >= 5 and n_operands <= 6: entered_class = self.entered_classes[-1] if isinstance(tree.operands[0], ast.ComponentRef): xp = self.get_mx(entered_class.symbols[tree.operands[0].name].value) else: xp = self.get_mx(tree.operands[0]) if isinstance(tree.operands[1], ast.ComponentRef): yp = self.get_mx(entered_class.symbols[tree.operands[1].name].value) else: yp = self.get_mx(tree.operands[1]) if isinstance(tree.operands[2], ast.ComponentRef): zp = self.get_mx(entered_class.symbols[tree.operands[2].name].value) else: zp = self.get_mx(tree.operands[2]) arg_1 = self.get_mx(tree.operands[3]) arg_2 = self.get_mx(tree.operands[4]) if n_operands == 6: assert isinstance(tree.operands[5], ast.Primary) mode = tree.operands[5].value else: mode = 'linear' func = ca.interpolant('interpolant', mode, [xp, yp], np.array(zp).ravel(order='F')) src = func(ca.vertcat(arg_1, arg_2)) elif op in OP_MAP and n_operands == 2: lhs = ca.MX(self.get_mx(tree.operands[0])) rhs = ca.MX(self.get_mx(tree.operands[1])) lhs_op = getattr(lhs, OP_MAP[op]) src = lhs_op(rhs) elif op in OP_MAP and n_operands == 1: lhs = ca.MX(self.get_mx(tree.operands[0])) lhs_op = getattr(lhs, OP_MAP[op]) src = lhs_op() else: src = ca.MX(self.get_mx(tree.operands[0])) # Check for built-in operations, such as the # elementary functions, first. if hasattr(src, op) and n_operands <= 2: if n_operands == 1: src = ca.MX(self.get_mx(tree.operands[0])) src = getattr(src, op)() else: lhs = ca.MX(self.get_mx(tree.operands[0])) rhs = ca.MX(self.get_mx(tree.operands[1])) lhs_op = getattr(lhs, op) src = lhs_op(rhs) else: try: # Check if there is a component named as the operation. In that case we are dealing with a time access # Should we check for symbol as well? v = self.get_mx(ast.ComponentRef(name=op)) t = self.get_mx(tree.operands[0]) src = self.get_symbol_time_access(v, t) except KeyError: func = self.get_function(op) src = ca.vertcat(*func.call([self.get_mx(operand) for operand in tree.operands], *self.function_mode)) self.src[tree] = src