def test_evaluate_where_possible_simple(self): two_plus_two = Operation(OperationType.PLUS(), [Variable(2.0), Variable(2.0)]) expression = Operation(OperationType.DIVIDE(), [Variable('x'), two_plus_two]) verify(str(expression.evaluate_where_possible()), self.reporter)
def test_complex_operation(self): two = Operation(OperationType.POSITIVE(), [Variable(2)]) negative_x = Operation(OperationType.NEGATIVE(), [Variable('x')]) op = Operation(OperationType.TIMES(), [two, negative_x]) verify(str(op), self.reporter)
def parse(self, parser): assert isinstance(parser, Parser) if self._operation_type in self.token_map.values(): right = parser.parse(precedence=10) # All prefixes should be evaluated before infixes return Operation(self._operation_type, [right]) else: return Operation(self._operation_type)
def test_evaluation_simple(self): two_plus_two = Operation(OperationType.PLUS(), [Variable(2.0), Variable(2.0)]) two_plus_two_divided_by_four = Operation( OperationType.DIVIDE(), [two_plus_two, Variable(4)]) self.assertTrue(two_plus_two_divided_by_four.is_evaluatable()) self.assertAlmostEqual(two_plus_two_divided_by_four.evaluate(), 1)
def test_collected_terms_subtract(self): x = Term(Operation(Variable('x'))) y = Term(Operation(Variable('y')), 2) z = Term(Operation(Variable('z'))) expression1 = CollectedTerms([x, y], [1, 3]) expression2 = CollectedTerms([y, z], [-3, -2]) result = CollectedTerms.subtract(expression1, expression2) verify(str(result), self.reporter)
def test_equation(self): two = Operation(OperationType.POSITIVE(), [Variable(2)]) negative_x = Operation(OperationType.NEGATIVE(), [Variable('x')]) lhs = Operation(OperationType.TIMES(), [two, negative_x]) rhs = Variable(3.1415) eq = Equation(lhs, rhs) verify(str(eq), self.reporter)
def apply_function(equation): if equation.lhs.operation_type.arity == 0: equation = equation.flip() if (equation.rhs.operation_type.arity == 0) and (equation.rhs.operation_type.symbol == '0'): homogenized = equation.lhs else: homogenized = Operation(OperationType.MINUS(), [equation.lhs, equation.rhs]) collected = CollectedTerms.try_parse_expression(homogenized) if collected is None: return equation collected = collected.as_expression return Equation(collected, Operation(Variable(0)))
def parse(self, parser, left): assert isinstance(parser, Parser) assert isinstance(left, Operation) right = parser.parse(self.precedence) return Operation(self._operation_type, [left, right])
def apply(self, equation): assert isinstance(equation, Equation) assert self.is_applicable_to(equation) assert len(equation.lhs.arguments) == 2 operand = equation.lhs.arguments[1] lhs = equation.lhs.arguments[0] rhs = Operation(self._inverse_type, [equation.rhs, operand]) return Equation(lhs, rhs)
def test_collected_terms_instantiate(self): x = Term(Operation(Variable('x'))) expression1 = CollectedTerms([x], [1]) self.assertEqual(str(expression1), 'x') expression2 = CollectedTerms([x], [5]) self.assertEqual(str(expression2), '5.0x') expression3 = CollectedTerms([x], [-1]) self.assertEqual(str(expression3), '-x') expression4 = CollectedTerms([x], [-12]) self.assertEqual(str(expression4), '-12.0x')
def test_evaluation_trivial(self): two = Operation(OperationType.POSITIVE(), [Variable('2')]) self.assertTrue(two.is_evaluatable()) self.assertAlmostEqual(two.evaluate(), 2) x = Operation(OperationType.POSITIVE(), [Variable('x')]) self.assertFalse(x.is_evaluatable())
def _substitute(self, operation, pattern): if operation.operation_type.arity == 0: if operation.operation_type.symbol in pattern.keys(): return pattern[operation.operation_type.symbol] else: return operation to_return_op_type = operation.operation_type to_return_arguments = list() for sub_op in operation.arguments: to_return_arguments.append(self._substitute(sub_op, pattern)) return Operation(to_return_op_type, to_return_arguments)
def transform(self, expression, pattern=None): assert isinstance(expression, Operation) if pattern is None: assert isinstance(pattern, SubstitutionPattern) for k in pattern.keys(): if type(pattern[k]) == str: pattern[k] = Operation(OperationType.VARIABLE(pattern[k])) substituted_start = self.try_transform(expression, pattern) substituted_end = self._substitute(self._end, pattern) return substituted_end
def test_evaluate_where_possible_complex(self): two_plus_two = Operation(OperationType.PLUS(), [Variable(2.0), Variable(2.0)]) two_plus_two_divided_by_four = Operation( OperationType.DIVIDE(), [two_plus_two, Variable(4)]) three_minus_x = Operation(OperationType.MINUS(), [Variable(3.0), Variable('x')]) seven_plus_five = Operation(OperationType.PLUS(), [Variable(7), Variable(5)]) three_minus_x_over_seven_plus_five = Operation( OperationType.DIVIDE(), [three_minus_x, seven_plus_five]) expression = Operation( OperationType.TIMES(), [two_plus_two_divided_by_four, three_minus_x_over_seven_plus_five]) verify(str(expression.evaluate_where_possible()), self.reporter)
def try_transform(self, expression, pattern=None): assert isinstance(expression, Operation) if pattern is None: pattern = SubstitutionPattern(dict()) assert isinstance(pattern, SubstitutionPattern) substituted_start = self._substitute(self._start, pattern) if not Operation.areEqual(substituted_start, expression): raise TransformException( '{} substituted with {} == {} != {}'.format( str(self._start), str(pattern), str(substituted_start), str(expression))) else: return substituted_start
def flip_transformation(variable_to_force_left): if type(variable_to_force_left) == str: variable_to_force_left = Variable(variable_to_force_left) assert isinstance(variable_to_force_left, Variable) is_applicable_function = lambda x: True subexpression = Operation(variable_to_force_left) def apply_function(equation): if not (equation.rhs.contains(subexpression) ): # If it appears on both sides, default to flipping return equation else: return equation.flip() return Transformation(is_applicable_function, apply_function)
def substitutes(self, expression, pattern): assert isinstance(expression, Operation) assert isinstance(pattern, SubstitutionPattern) substituted_start = self._substitute(self._start, pattern) return Operation.areEqual(substituted_start, expression)
def single_variable(equation, variable, print_out=False, max_iterations=1000): assert isinstance(equation, Equation) if type(variable) == str: variable = Variable(variable) assert isinstance(variable, Variable) assert type(print_out) == bool assert type(max_iterations) == int assert variable.symbol in equation.get_variables() expected_result = Operation(variable) condition = lambda x: ( (Operation.areEqual(x.lhs, expected_result) and variable.symbol not in x.rhs.get_variables())\ or x is None ) distributions = [ ExpressionSubstitution(Parser.parse_expression('a * (b + c)'), Parser.parse_expression('a * b + a * c')), ExpressionSubstitution(Parser.parse_expression('(a + b) * c'), Parser.parse_expression('a * c + b * c')), ExpressionSubstitution(Parser.parse_expression('(a + b) / c'), Parser.parse_expression('a / c + b / c')), ExpressionSubstitution(Parser.parse_expression('a * (b - c)'), Parser.parse_expression('a * b - a * c')), ExpressionSubstitution(Parser.parse_expression('(a - b) * c'), Parser.parse_expression('a * c - b * c')), ExpressionSubstitution(Parser.parse_expression('(a - b) / c'), Parser.parse_expression('a / c - b / c')), ] distributions = [ Transformation.apply_all_substitution_transformations(x) for x in distributions ] distribute = SolverStep( Transformation.each_transformation(distributions, False)) pre_solve = SolverStep( Transformation.collect_like_terms_transformation()) equation = distribute.execute_step(equation) equation = pre_solve.execute_step(equation) branches = [equation] executed_branches = set() iterations = 0 no_regrets_transformations = [ Transformation.apply_all_substitution_transformations(x)\ for x in Solver.no_regrets_substitutions ] solve_step = SolverStep(Transformation.evaluation_transformation(), terminate_on_repeat=True) solve_step_2 = SolverStep( Transformation.each_transformation(no_regrets_transformations, False)) solve_step_3 = SolverStep(Transformation.flip_transformation(variable)) solve_step_4 = SolverStep.cancellations() solve_step.next_step = solve_step_2 solve_step_2.next_step = solve_step_3 solve_step_3.next_step = solve_step_4 solve_step_4.next_step = solve_step while iterations < max_iterations: if len(branches) == 0: raise SolverException('Exhausted possible transformations.') branch = branches[0] branches = branches[1:] if print_out: print('Executing branch: {}'.format(str(branch))) result = solve_step.execute_until(branch, condition, print_out=print_out) if condition(result): final_execute_step = SolverStep( Transformation.evaluation_transformation()) return final_execute_step.execute_step(result) else: executed_branches.add(str(branch)) executed_branches.add( str(result)) # Executed already since steps terminated new_branches = dict() # We don't care about the outputs of flips or cancellations new_branch_strings = solve_step_3.previous_inputs - executed_branches for string in new_branch_strings: new_branches[string] = Parser.parse_equation(string) solve_step.clear_history() solve_step_2.clear_history() solve_step_3.clear_history() solve_step_4.clear_history() for substitution in Solver.substitutions: left_substitution_result = [ x[1] for x in substitution.get_all_substitutions(branch.lhs) ] right_substitution_result = [ x[1] for x in substitution.get_all_substitutions(branch.rhs) ] equations = [ Equation(x, branch.rhs) for x in left_substitution_result ] equations += [ Equation(branch.lhs, x) for x in right_substitution_result ] pairs = [(str(x), x) for x in equations if str(x) not in executed_branches] for k, v in pairs: new_branches[k] = v if print_out: print("New branches from {}:\n{}\n".format( str(branch), '\n'.join(new_branches.keys()))) branches += new_branches.values() branches.sort(key=lambda x: len(str(x))) iterations += 1 raise SolverException( 'Could not solve equation for a single variable. Final result: {}' .format(str(equation)))
def test_single_variable_solver_trivial(self): equation = Equation(Operation(Variable('x')), Operation(Variable(2))) result = Solver.single_variable(equation, 'x') verify(str(result), self.reporter)
def test_simple_operation(self): simple_op = Operation(OperationType.PLUS(), [Variable(2), Variable(2)]) verify(str(simple_op), self.reporter)