def test_get_x_coefficient(self): var_x = Symbol("x", REAL) term1 = Plus(var_x, Real(1)) # x + 1 term2 = Plus( Real(1), var_x, ) # 1 + x atom1 = Times(Real(2), var_x) # 2 * x atom2 = Times(var_x, Real(2)) # x * 2 atom3 = Times(Real(3), var_x) # 2 * x term3 = Plus(atom1, Real(1)) # 2 * x + 1 term4 = Plus(atom2, Real(1)) # x * 2 + 1 term5 = Plus(Real(1), atom1) # 1 + 2 * x term6 = Plus(Real(1), atom2) # 1 + x * 2 term7 = Plus([atom1, atom2, atom3, Real(3)]) # 2 * x + 1 + x * 2 + 1 self.assertEqual(get_x_coefficient(term1), Real(1)) self.assertEqual(get_x_coefficient(term2), Real(1)) self.assertEqual(get_x_coefficient(term3), Real(2)) self.assertEqual(get_x_coefficient(term4), Real(2)) self.assertEqual(get_x_coefficient(term5), Real(2)) self.assertEqual(get_x_coefficient(term6), Real(2)) self.assertEqual(get_x_coefficient(term7), Real(7)) # together test get_coefficients self.assertEqual(get_coefficients(term1)[var_x], Real(1)) self.assertEqual(get_coefficients(term2)[var_x], Real(1)) self.assertEqual(get_coefficients(term3)[var_x], Real(2)) self.assertEqual(get_coefficients(term4)[var_x], Real(2)) self.assertEqual(get_coefficients(term5)[var_x], Real(2)) self.assertEqual(get_coefficients(term6)[var_x], Real(2)) self.assertEqual(get_coefficients(term7)[var_x], Real(7))
def create_smt_formula(data, labels, dim, n_weights): weights = [] biases = [] weight_symbols = [Symbol('weight_{}'.format(i), REAL) for i in range(n_weights[0])] weight_symbols2 = Symbol('weight_out', REAL) weights.append(weight_symbols) weights.append([weight_symbols2]) bias_symbol1 = Symbol('bias_{}'.format(1), REAL) bias_symbol2 = Symbol('bias_{}'.format(2), REAL) biases.append([bias_symbol1]) biases.append([bias_symbol2]) layer_domains = [] layer_input = data for i in range(len(layer_input)): # loop over data # \sum <w_ji, x_i> + b_i g = len(weight_symbols)//2 # Layer 1 weight_input1_i = Plus([Times(w_i, Real(int(x_j))) for w_i, x_j in zip(weight_symbols, layer_input[i])]) prod_bias1_i = Plus(weight_input1_i, bias_symbol1) # Layer 2 weight_input2_i = Plus(Times(prod_bias1_i, weight_symbols2)) prod_bias2_i = Plus(weight_input2_i, bias_symbol2) # output weight_output = prod_bias2_i layer_domain = Equals(weight_output, Real(labels[i])) layer_domains.append(layer_domain) network_domain = And(x for x in layer_domains) dnn_problem = network_domain return dnn_problem, weights, biases
def triangle(nvars, rand_gen): # alpha = rand_gen.uniform(0.05, 0.25) alpha = rand_gen.uniform(0.2, 0.25) remain = nvars % 3 n_tris = int(nvars / 3) variables = [Symbol(f"x{i}", REAL) for i in range(1, nvars + 1)] lbounds = [LE(Real(0), x) for x in variables] ubounds = [LE(x, Real(1)) for x in variables] clauses = [] potentials = [] for i in range(n_tris): x, y, z = variables[3 * i], variables[3 * i + 1], variables[3 * i + 2] xc = None if 3 * i + 3 >= nvars else variables[3 * i + 3] # x_i clauses.append(Or(LE(x, Real(alpha)), LE(Real(1 - alpha), x))) # x_i -- y_i clauses.append( Or(LE(y, Plus(x, Real(-alpha))), LE(Plus(x, Real(alpha)), y))) # x_i -- z_i clauses.append(Or(LE(Real(1 - alpha), x), LE(Real(1 - alpha), z))) clauses.append(Or(LE(x, Real(alpha)), LE(z, Real(alpha)))) # z_i -- y_i clauses.append(LE(z, y)) # x_i -- x_i+1 if xc: clauses.append(Or(LE(x, Real(alpha)), LE(Real(1 - alpha), xc))) clauses.append(Or(LE(Real(1 - alpha), x), LE(xc, Real(alpha)))) pot_yz = Ite(LE(z, y), Times([z, y, Real(100)]), Real(1)) pot_xy = Ite(LE(y, Plus(x, Real(-alpha))), Times(Real(100), Plus(x, y)), Real(1)) potentials.append(pot_xy) potentials.append(pot_yz) if remain == 1: x = variables[3 * n_tris] clauses.append(Or(LE(x, Real(alpha)), LE(Real(1 - alpha), x))) if remain == 2: x, y = variables[3 * n_tris], variables[nvars - 1] # x_n clauses.append(Or(LE(x, Real(alpha)), LE(Real(1 - alpha), x))) # x -- y clauses.append( Or(LE(y, Plus(x, Real(-alpha))), LE(Plus(x, Real(alpha)), y))) potentials.append( Ite(LE(y, Plus(x, Real(-alpha))), Times(Real(100), Plus(x, y)), Real(1))) domain = Domain.make( [], # no booleans [x.symbol_name() for x in variables], [(0, 1) for _ in range(len(variables))]) support = And(lbounds + ubounds + clauses) weight = Times(potentials) if len(potentials) > 1 else potentials[0] return Density(domain, support, weight, []), alpha
def test_plus_negatives(self): r0 = Symbol("r0", REAL) r1 = Symbol("r1", REAL) p_1 = Real(1) m_1 = Real(-1) p_2 = Real(2) m_4 = Real(-4) # 4 * r0 + (-1) * r1 + 2 - 4 neg_r1 = Times(m_1, r1) m_4_r0 = Times(Real(4), r0) expr = Plus(m_4_r0, neg_r1, p_2, m_4) res = expr.simplify() self.assertValid(Equals(expr, res)) stack = [res] while stack: curr = stack.pop() if curr.is_plus(): stack.extend(curr.args()) elif curr.is_minus(): stack.extend(curr.args()) elif curr.is_times(): stack.extend(curr.args()) elif curr.is_constant(): self.assertNotEqual(curr, m_1) self.assertNotEqual(curr, p_1) elif not curr.is_symbol(): # unexpected expression type. self.assertTrue(False)
def test_minus_1(self): """walk_minus should not create nested Plus nodes""" x = Symbol("x", INT) y = Symbol("y", INT) oldx = Symbol("oldx", INT) m_1 = Int(-1) i_2 = Int(2) i_4 = Int(4) src = Times(i_2, oldx) src = Plus(src, x) src = Minus(src, Times(i_4, y)) src = Times(m_1, src) td = TimesDistributor() res = td.walk(src) self.assertValid(Equals(src, res)) # root is Plus. self.assertTrue(res.is_plus(), "Expeted summation, got: {}".format(res)) # no other Plus in children: only Times of symbs and constants. stack = list(res.args()) while stack: curr = stack.pop() if curr.is_times(): stack.extend(curr.args()) else: self.assertTrue(curr.is_symbol() or curr.is_constant(), "Expected leaf, got: {}".format(res))
def test_div_pow(self): x = FreshSymbol(REAL) f = Equals(Times(Real(4), Pow(x, Real(-1))), Real(2)) self.assertTrue(is_sat(f)) f = Equals(Div(Real(4), x), Real(2)) self.assertTrue(is_sat(f, solver_name="z3")) f = Equals(Times(x, x), Real(16)) self.assertTrue(is_sat(f))
def test_sum_all_negatives(self): r0 = Symbol("r0", REAL) r1 = Symbol("r1", REAL) m_1 = Real(-1) # -4 * r0 + (-1) * r1 neg_r1 = Times(m_1, r1) m_4_r0 = Times(Real(-4), r0) expr = Plus(m_4_r0, neg_r1) res = expr.simplify() self.assertValid(Equals(expr, res))
def to_smt(self): if len(self.inequality_dict) == 0: return Real(0) <= Real(0) elif len(self.inequality_dict ) == 1 and CONST_KEY in self.inequality_dict: return Real(0) <= Real(-self.inequality_dict.get(CONST_KEY, 0)) result = Plus( Times(Symbol(n, REAL) for n in name) * Real(factor) if factor != 1 else Times( Symbol(n, REAL) for n in name) for name, factor in self.inequality_dict.items() if name != CONST_KEY) < Real(-self.inequality_dict.get(CONST_KEY, 0)) return result
def test_integer(self): x = FreshSymbol(INT) f = Equals(Times(x, x), Int(2)) with Solver(name="z3") as s: self.assertFalse(s.is_sat(f)) # f = Equals(Times(Int(4), Pow(x, Int(-1))), Int(2)) # self.assertTrue(is_sat(f, solver_name="z3")) f = Equals(Div(Int(4), x), Int(2)) self.assertTrue(is_sat(f, solver_name="z3")) f = Equals(Times(x, x), Int(16)) self.assertTrue(is_sat(f))
def sample_comp_literal(self, var): while True: k = int(self.rand_gen.choice([-1, 1])) b = float(self.rand_gen.uniform(0, 1)) f = And(self.primal.get_full_formula(), Equals(Times(Real(k), var), Real(b))) if is_sat(f, solver_name=self.smt_solver): # we should actually check SAT for both (kx <= b) and (kx > b) break return LE(Times(Real(k), var), Real(b))
def feed_data(self, X, Y): formula = [] for x, y in zip(X, Y): x_formula = [] for i, (weight, bias) in self.net_formula.items(): if i == 0: x_hidden = [] for r, w_r in enumerate(weight): tmp = Plus([Plus(Times(w, Real(float(x[c]))), bias[r]) for c, w in enumerate(w_r)]) if self.activation == 'relu': x_hidden.append(Max(tmp, Real(0))) else: x_hidden.append(tmp) """node_output = [] for c, w in enumerate(w_r): var = Plus(Times(w, Real(float(x[c]))), bias[r]) exp_3 = Pow(var, Real(3)) #exp_5 = Pow(var, Real(5)) sen_2 = Times(Real(0.25), var) sen_3 = Times(Real(0.02), exp_3) #sen_4 = Times(Real(0.002), exp_5) node_output.append(Plus(Real(0.5), sen_2, sen_3))#, sen_4)) matrix_addition = Plus(node_output[i] for i in range(len(node_output))) x_hidden.append(matrix_addition)""" x = x_hidden else: x_hidden = [] for r, w_r in enumerate(weight): tmp = Plus([Plus(Times(w, x[c]), bias[r]) for c, w in enumerate(w_r)]) if self.activation == 'relu' and i < len(self.net_formula) - 1: x_hidden.append(Max(tmp, Real(0))) else: x_hidden.append(tmp) """node_output = [] for c, w in enumerate(w_r): var = Plus(Times(w, x[c]), bias[r]) exp_3 = Pow(var, Real(3)) #exp_5 = Pow(var, Real(5)) sen_2 = Times(Real(0.25), var) sen_3 = Times(Real(0.02), exp_3) #sen_4 = Times(Real(0.002), exp_5) node_output.append(Plus(Real(0.5), sen_2, sen_3)) #, sen_4)) matrix_addition = Plus(node_output[i] for i in range(len(node_output))) x_hidden.append(matrix_addition)""" x = x_hidden ## Add activation function if np.argmax(y) == 0: x_formula.append(GE(x[0], x[1])) else: x_formula.append(GE(x[1], x[0])) return And(x_formula)
def test_times_algebraic(self): from pysmt.constants import Numeral env = get_env() mgr = env.formula_manager r0 = Symbol("r0", REAL) p_2 = Real(2) m_5 = mgr._Algebraic(Numeral(-5)) m_10 = mgr._Algebraic(Numeral(-10)) # -5 * r0 * 2 expr = Times(m_5, r0, p_2) res = expr.simplify() self.assertValid(Equals(expr, res)) self.assertIn(m_10, res.args())
def test_misc(self): bool_list = [ And(self.x, self.y), Or(self.x, self.y), Not(self.x), self.x, Equals(self.p, self.q), GE(self.p, self.q), LE(self.p, self.q), GT(self.p, self.q), LT(self.p, self.q), Bool(True), Ite(self.x, self.y, self.x) ] # TODO: FORALL EXISTS real_list = [ self.r, Real(4), Plus(self.r, self.s), Plus(self.r, Real(2)), Minus(self.s, self.r), Times(self.r, Real(1)), Div(self.r, Real(1)), Ite(self.x, self.r, self.s), ] int_list = [ self.p, Int(4), Plus(self.p, self.q), Plus(self.p, Int(2)), Minus(self.p, self.q), Times(self.p, Int(1)), Ite(self.x, self.p, self.q), ] for f in bool_list: t = self.tc.walk(f) self.assertEqual(t, BOOL, f) for f in real_list: t = self.tc.walk(f) self.assertEqual(t, REAL, f) for f in int_list: t = self.tc.walk(f) self.assertEqual(t, INT, f)
def learn(self, domain, data, border_indices): positive_indices = [i for i in range(len(data)) if data[i][1]] real_vars = [ v for v in domain.variables if domain.var_types[v] == REAL ] bool_vars = [ v for v in domain.variables if domain.var_types[v] == BOOL ] d = len(real_vars) hyperplanes = [] for indices in itertools.combinations(positive_indices, d): print(indices) hyperplanes.append( Learner.fit_hyperplane(domain, [data[i][0] for i in indices])) boolean_data = [] for i in range(len(data)): row = [] for v in bool_vars: row.append(data[i][0][v].constant_value()) boolean_data.append(row) hyperplanes_smt = [] for a, c in hyperplanes: lhs_smt = Plus( Times(Real(float(a[j])), domain.get_symbol(real_vars[j])) for j in range(d)) hyperplanes_smt.append(LE(lhs_smt, Real(c))) lhs_smt = Plus( Times(Real(-float(a[j])), domain.get_symbol(real_vars[j])) for j in range(d)) hyperplanes_smt.append(LE(lhs_smt, Real(-c))) for i in range(len(data)): lhs = 0 for j in range(d): lhs += float(a[j]) * float( data[i][0][real_vars[j]].constant_value()) boolean_data[i].append(lhs <= c) boolean_data[i].append(lhs >= c) print(boolean_data) # logical_dnf_indices = [[i] for i in range(len(boolean_data[0]))] logical_dnf_indices = self.learn_logical(boolean_data, [row[1] for row in data]) logical_dnf = [[ domain.get_symbol(bool_vars[i]) if i < len(bool_vars) else hyperplanes_smt[i - len(bool_vars)] for i in conj_indices ] for conj_indices in logical_dnf_indices] print(logical_dnf) return logical_dnf
def sympy2pysmt(expression): """Converts a sympy formula representing a polynomial into a pysmt formula. Args: expression: The sympy formula to convert. Returns: FNode: The pysmt formula. Raises: WMIParsingException: If the method fails to parse the formula. """ if expression.is_Add: return Plus(map(sympy2pysmt, expression.args)) elif expression.is_Mul: return Times(map(sympy2pysmt, expression.args)) elif expression.is_Pow: base, exp = expression.args return Pow(sympy2pysmt(base), sympy2pysmt(exp)) elif expression.is_Symbol: return Symbol(str(expression), REAL) elif expression.is_Number: return Real(float(expression)) else: raise WMIParsingException(WMIParsingException.CANNOT_CONVERT_SYMPY_FORMULA_TO_PYSMT, expression)
def generate_weights_ijcai17(self, pos_only=True): subformulas = [] for a in self.bools: pos = self._random_polynomial() neg = Real(1) if pos_only else self._random_polynomial() subformulas.append(Ite(a, pos, neg)) return Times(subformulas)
def test_get_constants(self): x = Symbol("x", REAL) y = Symbol("y", REAL) z = Symbol("z", REAL) atom1 = Times(Real(2), x) atom2 = Times(Real(3), x) atom3 = Times(Real(2), y) atom4 = Times(Real(7), z) term = Plus( [atom1, atom2, atom3, atom4, Real(4), Real(0.1), Real(7.0001)]) const = get_constants(term) const = float(const.constant_value()) self.assertAlmostEqual(const, 11.1001)
def test_domains_to_intervals(self): # 1 < x < 2, 3 < 2x < 5, 3 < x < 4. var_x = Symbol("x", REAL) atom1 = And(LE(Real(1), var_x), LE(var_x, Real(2))) atom2 = And(LE(Real(3), Times(Real(2), var_x)), LE(Times(Real(2), var_x), Real(5))) atom3 = And(LE(Real(3), var_x), LE(var_x, Real(4))) formula = Or([atom1, atom2, atom3]) interval = domains_to_intervals(formula) self.assertListEqual(interval, [[1, float(1.5)], [1.5, 2], [2, 2.5], [3, 4]]) interval = domains_to_intervals(LE(Real(1), var_x)) self.assertListEqual(interval, [[1, float('inf')]])
def main(): # Example Transition System (SMV-like syntax) # # VAR x: integer; # y: integer; # # INIT: x = 1 & y = 2; # # TRANS: next(x) = x + 1; # TRANS: next(y) = y + 2; x, y = [Symbol(s, INT) for s in "xy"] nx, ny = [next_var(Symbol(s, INT)) for s in "xy"] example = TransitionSystem(variables=[x, y], init=And(Equals(x, Int(1)), Equals(y, Int(2))), trans=And(Equals(nx, Plus(x, Int(1))), Equals(ny, Plus(y, Int(2))))) # A true invariant property: y = x * 2 true_prop = Equals(y, Times(x, Int(2))) # A false invariant property: x <= 10 false_prop = LE(x, Int(10)) for prop in [true_prop, false_prop]: check_property(example, prop) print("")
def test_int(self): p, q = Symbol("p", INT), Symbol("q", INT) f = Or(Equals(Times(p, Int(5)), Minus(p, q)), LT(p, q), LE(Int(6), Int(1))) f_string = self.print_to_string(f) self.assertEqual(f_string, "(or (= (* p 5) (- p q)) (< p q) (<= 6 1))")
def __init__(self, expression, aliases={}): """Default constructor. Takes as input a pysmt formula representing a linear inequality and a dictionary of aliases to be substituted before the parsing. Args: expression (FNode): The pysmt formula representing the inequality. aliases (dict {FNode : FNode}): The dictionary containing the aliases definitions. Raises: WMIParsingException: If the expression is not an inequality or the polynomial has degree more than 1. """ if not (expression.is_le() or expression.is_lt()): raise WMIParsingException(WMIParsingException.NOT_AN_INEQUALITY, expression) left, right = expression.args() if right.is_real_constant(): # Polynomial OP Constant self._parse_expression(left, right, False, aliases) elif left.is_real_constant(): # Constant OP Polynomial self._parse_expression(right, left, True, aliases) else: # Polynomial1 OP Polynomial2 converted into Polynomial1 - Polynomial2 OP 0 self._parse_expression(Plus(left,Times(Real(-1),right)),Real(0), False, aliases)
def dot(self, num_list, pysmt_list): assert (len(num_list) == len(pysmt_list)) res = Real(0) for n in range(len(num_list)): nreal = Real(float(num_list[n])) prod = Times(pysmt_list[n], nreal) res = Plus(res, prod) return res
def to_smt(self): keys = { key: Times(Symbol(n, REAL) for n in key) if key != CONST_KEY else Real(1.0) for key in self.poly_dict.keys() } return Plus(keys[key] * Real(value) if value != 1 else keys[key] for key, value in self.poly_dict.items())
def test_atom_to_bound(self): # univariate atom case x = Symbol("x", REAL) atom1 = Times(Real(2), x) # 2x atom2 = Times(Real(3), x) # 3x lhs = Plus(atom1, Real(3)) rhs = Plus(atom2, Real(5)) atom = LE(lhs, rhs) bound, bound_type = atom_to_bound(atom, x) self.assertEqual(bound, Real(-2)) self.assertEqual(bound_type, LOWER) x = Symbol("x", REAL) atom1 = Times(Real(2), x) lhs = Real(3) rhs = Plus(atom1, Real(1)) atom = LE(lhs, rhs) bound, bound_type = atom_to_bound(atom, x) self.assertEqual(bound, Real(1)) self.assertEqual(bound_type, LOWER) y = Symbol("y", REAL) lhs = Plus([y, 2 * y, Real(5)]) rhs = Plus([y, Real(2), Real(9)]) atom = LE(lhs, rhs) bound, bound_type = atom_to_bound(atom, y) self.assertEqual(bound, Real(3)) self.assertEqual(bound_type, UPPER) # bivariate case lhs = Plus([2 * x, 3 * y, Real(5)]) rhs = Plus([3 * x, 3 * y, Real(4)]) atom = LE(lhs, rhs) bound, bound_type = atom_to_bound(atom, x) self.assertEqual(bound, Real(1)) self.assertEqual(bound_type, LOWER) lhs = Plus([2 * x, 3 * y, Real(5)]) rhs = Plus([3 * x, 4 * y, Real(1)]) atom = LE(lhs, rhs) bound, bound_type = atom_to_bound(atom, x) self.assertEqual(bound_type, LOWER) y_coef = get_x_coefficient(bound) const = get_constants(bound) self.assertEqual(y_coef, Real(-1)) self.assertEqual(const, Real(4))
def cosine_law_crit_angle(self): """Use cosine law to find cos^2(theta) between three points node1---node2---node3 to assert that it is less than cos^2(thetaC) where thetaC is the critical crossing angle :param node1: Outside node :param node2: Middle connecting node :param node3: Outside node :returns: cos^2 as calculated using cosine law (a_dot_b^2/a^2*b^2) """ node1 = self.get_input_nodes().values()[0] node2 = self.get_input_nodes().values()[1] node3 = self.get_output_node() # Lengths of channels aX = Minus(node1.get_x(), node2.get_x()) aY = Minus(node1.get_y(), node2.get_y()) bX = Minus(node3.get_x(), node2.get_x()) bY = Minus(node3.get_y(), node2.get_y()) # Dot products between each channel a_dot_b_squared = Pow(Plus(Times(aX, bX), Times(aY, bY)), Real(2)) a_squared_b_squared = Times( Plus(Times(aX, aX), Times(aY, aY)), Plus(Times(bX, bX), Times(bY, bY)), ) return Div(a_dot_b_squared, a_squared_b_squared)
def shared_hyperplane_problem(): domain = xy_domain() x, y = (domain.get_symbol(v) for v in ["x", "y"]) # y <= -x + 1.25 shared1 = LE(y, Plus(Times(Real(-1.0), x), Real(1.25))) # y >= -x + 0.75 shared2 = GE(y, Plus(Times(Real(-1.0), x), Real(0.75))) # y <= x + 0.5 h1 = LE(y, Plus(x, Real(0.5))) # y >= x + 0.25 h2 = GE(y, Plus(x, Real(0.25))) # y <= x - 0.25 h3 = LE(y, Plus(x, Real(-0.25))) # y >= x - 0.5 h4 = GE(y, Plus(x, Real(-0.5))) return domain, Or(And(shared1, shared2, h1, h2), And(shared1, shared2, h3, h4)), "shared"
def walk_times(self, args): assert len(args) > 0 if self.bool_mode: return Times(*args) else: result = self.pool.one_id for arg in self.walk_smt_multiple(args): result = self.pool.apply(Multiplication, result, arg) return result
def test_get_coefficients(self): x = Symbol("x", REAL) y = Symbol("y", REAL) z = Symbol("z", REAL) atom1 = Times(Real(2), x) atom2 = Times(Real(3), x) atom3 = Times(Real(2), y) atom4 = Times(Real(7), z) term = Plus([atom1, atom2, atom3, atom4, Real(4), Real(0.1)]) coefficients = get_coefficients(term) self.assertEqual(coefficients[x], Real(5)) self.assertEqual(coefficients[y], Real(2)) self.assertEqual(coefficients[z], Real(7)) term2 = Plus(Real(4), Real(0.1)) coefficients2 = get_coefficients(term2) self.assertEqual(coefficients2, {})
def test_div_pow(self): x = FreshSymbol(REAL) f = Equals(Times(Real(4), Pow(x, Real(-1))), Real(2)) try: self.assertTrue(is_sat(f)) except SolverReturnedUnknownResultError: pass f = Equals(Div(Real(4), x), Real(2)) try: self.assertTrue(is_sat(f, solver_name="z3")) except SolverReturnedUnknownResultError: pass f = Equals(Times(x, x), Real(16)) try: self.assertTrue(is_sat(f)) except SolverReturnedUnknownResultError: pass
def test_irrational(self): x = FreshSymbol(REAL) f = Equals(Times(x, x), Real(2)) with Solver(name="z3") as s: self.assertTrue(s.is_sat(f)) model = s.get_model() xval = model[x] self.assertTrue(xval.is_algebraic_constant()) approx = Fraction(-3109888511975, 2199023255552) self.assertEqual(xval.algebraic_approx_value(), approx)
def test_simplify_times(self): a,b = Real(5), Real((1,5)) f = Times(a,b).simplify() self.assertEqual(f.constant_value(), 1)
def test_times_one(self): r = Symbol("r", REAL) f = Times(r, r, Real(1)) f = f.simplify() self.assertNotIn(Real(1), f.args())