def reweighAndPrint(): toPrint = [] toAddToLeftSide=[] toAddToRightSide=[] for i in range(len(equalities)): leftSideLeftOvers=[] leftSide = functools.reduce(lambda x,y:x^y, [False]+InitialWallaceWeights('a',n,'a',n,i,toAddToLeftSide,leftSideLeftOvers)) # EXCEPTION - MOVING -1 TO LEFT SIDE OF EQUATION TO SUBTRACT: if i==0: leftSide ^= 1 #print("left side:") #print(leftSide) rightSideLeftOvers=[] rightSide = functools.reduce(lambda x,y:x^y, [False]+InitialWallaceWeights('n',n,'k',n,i,toAddToRightSide,rightSideLeftOvers)) #print("right side") #print(rightSide) for j in equalities[:i+1]: leftSide = leftSide.subs(j) rightSide = rightSide.subs(j) toAddToLeftSide = leftSideLeftOvers toAddToRightSide = rightSideLeftOvers equations.append((leftSide,rightSide)) for i,j in enumerate(equations): if str(sympy.simplify_logic(j[0])) != str(sympy.simplify_logic(j[1])): toPrint.append(("w:{:>2}{:>"+str(math.floor(shutil.get_terminal_size((80,20))[0]/2)-2-2-10)+"}").format(i,sympy.pretty(sympy.simplify_logic(j[0])))+" = "+sympy.pretty(sympy.simplify_logic(j[1]))) print("\n".join(toPrint))
def symbolic_equality(test_expr, target_expr): """Test if two expressions are symbolically equivalent. Use the sympy 'simplify_logic' function to simplify the two boolean expressions as much as possible. Two equilvalent expressions MUST simplify to the same thing, and then they can be tested for equivalence again. Returns True if sympy can determine that the two expressions are equal, and returns False if they are not equal. - 'test_expr' should be the untrusted sympy expression to check. - 'target_expr' should be the trusted sympy expression to match against. """ print("[SYMBOLIC TEST]") try: simplified_target = sympy.simplify_logic(target_expr) simplified_test = sympy.simplify_logic(test_expr) if simplified_target == simplified_test or sympy.srepr(simplified_target) == sympy.srepr(simplified_test): print("Symbolic match.") print("INFO: Adding known pair ({0}, {1})".format(target_expr, test_expr)) KNOWN_PAIRS[(target_expr, test_expr)] = EqualityType.SYMBOLIC return True else: return False except NotImplementedError as e: print("{0}: {1} - Can't check symbolic equality!".format(type(e).__name__, str(e).capitalize())) return False
def _recursive_simplify(expr): """Simplify the expression as much as possible based on domain knowledge.""" input_expr = expr # Simplify even further, based on domain knowledge: # windowses = ('WIN32', 'WINRT') apples = ("MACOS", "UIKIT", "IOS", "TVOS", "WATCHOS") bsds = ("FREEBSD", "OPENBSD", "NETBSD") androids = ("ANDROID", ) unixes = ( "APPLE", *apples, "BSD", *bsds, "LINUX", *androids, "HAIKU", "INTEGRITY", "VXWORKS", "QNX", "WASM", ) unix_expr = simplify_logic("UNIX") win_expr = simplify_logic("WIN32") false_expr = simplify_logic("false") true_expr = simplify_logic("true") expr = expr.subs(Not(unix_expr), win_expr) # NOT UNIX -> WIN32 expr = expr.subs(Not(win_expr), unix_expr) # NOT WIN32 -> UNIX # UNIX [OR foo ]OR WIN32 -> ON [OR foo] expr = _simplify_expressions(expr, Or, (unix_expr, win_expr), true_expr) # UNIX [AND foo ]AND WIN32 -> OFF [AND foo] expr = _simplify_expressions(expr, And, (unix_expr, win_expr), false_expr) expr = _simplify_flavors_in_condition("WIN32", ("WINRT", ), expr) expr = _simplify_flavors_in_condition("APPLE", apples, expr) expr = _simplify_flavors_in_condition("BSD", bsds, expr) expr = _simplify_flavors_in_condition("UNIX", unixes, expr) expr = _simplify_flavors_in_condition("ANDROID", (), expr) # Simplify families of OSes against other families: expr = _simplify_os_families(expr, ("WIN32", "WINRT"), unixes) expr = _simplify_os_families(expr, androids, unixes) expr = _simplify_os_families(expr, ("BSD", *bsds), unixes) for family in ("HAIKU", "QNX", "INTEGRITY", "LINUX", "VXWORKS"): expr = _simplify_os_families(expr, (family, ), unixes) # Now simplify further: expr = simplify_logic(expr) while expr != input_expr: input_expr = expr expr = _recursive_simplify(expr) return expr
def _simplify_flavors_in_condition(base: str, flavors, expr): """Simplify conditions based on the knowledge of which flavors belong to which OS.""" base_expr = simplify_logic(base) false_expr = simplify_logic("false") for flavor in flavors: flavor_expr = simplify_logic(flavor) expr = _simplify_expressions(expr, And, (base_expr, flavor_expr), flavor_expr) expr = _simplify_expressions(expr, Or, (base_expr, flavor_expr), base_expr) expr = _simplify_expressions(expr, And, (Not(base_expr), flavor_expr), false_expr) return expr
def _simplify_os_families(expr, family_members, other_family_members): for family in family_members: for other in other_family_members: if other in family_members: continue # skip those in the sub-family f_expr = simplify_logic(family) o_expr = simplify_logic(other) expr = _simplify_expressions(expr, And, (f_expr, Not(o_expr)), f_expr) expr = _simplify_expressions(expr, And, (Not(f_expr), o_expr), o_expr) expr = _simplify_expressions(expr, And, (f_expr, o_expr), simplify_logic("false")) return expr
def simp_guarantee_conjunct(Gset, Gvars): """ Convert a conjunction of guarantees to an equivalent an Boolean expression input: Gset - a list of guarantee conjuncts Gvars - set of all variables involved in the conjuncts output: an equivalent Boolean expression """ ldict = {} G_all = [] for idx in Gset: # picks out guarantees from a particular fixpoint G_all.append(Gi[idx]) for var in Gvars: exec(var + '= Symbol(\'' + var + '\')', globals(), ldict) B = None for disj in G_all: A = None for conj in disj: if A == None: exec('A = ' + conj, locals(), ldict) A = ldict['A'] else: exec('A = A | ' + conj, locals(), ldict) # TODO: check A = ldict['A'] if B == None: exec('B = A', locals(), ldict) B = ldict['B'] else: exec('B = B & A', locals(), ldict) B = ldict['B'] if B != None: B = simplify_logic(B, force=True) return str(B)
def _convert_to_dnf(self): self._debug(f'Converting to DNF ...', header=self._middle_code.full_name) exp = simplify_logic(self._middle_code.exp, form='dnf', force=self._deep_simplify) self._middle_code.exp = to_dnf(exp) self._debug(f'Converted to DNF', header=self._middle_code.full_name)
def simplify_condition(cond, depth_limit=8, variables_limit=8): memo = {} if cond.depth > depth_limit or len(cond.variables) > variables_limit: return cond sympy_expr = ConditionProcessor.claripy_ast_to_sympy_expr(cond, memo=memo) r = ConditionProcessor.sympy_expr_to_claripy_ast( sympy.simplify_logic(sympy_expr, deep=False), memo) return r
def _aggregate_explanations(local_explanations_accuracy, topk_explanations, target_class, x, y, max_accuracy, val_mask, threshold): """ Sort explanations by accuracy and then aggregate explanations which increase the accuracy of the aggregated formula. :param local_explanations_accuracy: dictionary of explanations and related accuracies. :param topk_explanations: limits the number of explanations to be aggregated. :param target_class: target class. :param x: observations in validation set. :param y: labels in validation set. :param max_accuracy: if True a formula is simplified only if the simplified formula gets 100% accuracy. :return: """ if len(local_explanations_accuracy) == 0: return "" else: # get the topk most accurate local explanations local_explanations_sorted = sorted( local_explanations_accuracy.items(), key=lambda x: -x[1])[:topk_explanations] explanations = [] best_accuracy = 0 best_explanation = "" for explanation_raw, accuracy in local_explanations_sorted: explanation = _simplify_formula(explanation_raw, x, y, target_class, max_accuracy, val_mask, threshold) if not explanation: continue explanations.append(explanation) # aggregate example-level explanations aggregated_explanation = " | ".join(explanations) aggregated_explanation_simplified = simplify_logic( aggregated_explanation, "dnf", force=True) aggregated_explanation_simplified = f"({aggregated_explanation_simplified})" if aggregated_explanation_simplified in [ "", "False", "True", "(False)", "(True)", ]: continue accuracy, _ = test_explanation(aggregated_explanation_simplified, x, y, target_class, val_mask, threshold) if accuracy > best_accuracy: best_accuracy = accuracy best_explanation = aggregated_explanation_simplified explanations = [best_explanation] return best_explanation, best_accuracy
def to_truth_table(func): """ Translates Python code into truth table (represented as integer) """ sfunc, args = _parse(func) symbols = sympy.symbols(args) return sum( (1 if sympy.simplify_logic(sfunc.subs(zip(symbols, reversed(bs))) ) else 0) << i for i, bs in enumerate( itertools.product([False, True], repeat=len(symbols))))
def to_hex_truth_table(func): """ Translates Python code into truth table (represented as hex string) """ sfunc, args = _parse(func) symbols = sympy.symbols(args) nargs = len(symbols) v = sum( (1 if sympy.simplify_logic(sfunc.subs(zip(symbols, reversed(bs))) ) else 0) << i for i, bs in enumerate(itertools.product([False, True], repeat=nargs))) return "{:0{}x}".format(v, max(1, 2**(nargs - 2)))
def to_binary_truth_table(func): """ Translates Python code into truth table (represented as binary string) """ sfunc, args = _parse(func) symbols = sympy.symbols(args) return ''.join( reversed([ ('1' if sympy.simplify_logic(sfunc.subs(zip( symbols, reversed(bs)))) else '0') for bs in itertools.product([False, True], repeat=len(symbols)) ]))
def simp_assume_disjunct(Aset, Avars): """ Convert a disjunction oof assumptions to an equivalent an Boolean expression input: Aset - a list of assumptions disjuncts Avars - set of all variables involved in the disjuncts output: an equivalent Boolean expression """ ldict = {} # local dictionary has to be created because of a bug in exec() A_all = [] # for idx in Aset: A_all.append(Ai[idx]) for var in Avars: exec(var + '= Symbol(\'' + var + '\')', globals(), ldict) B = None for conj in A_all: A = None for disj in conj: if A == None: exec('A = ' + disj, locals(), ldict) A = ldict['A'] else: exec('A = A & ' + disj, locals(), ldict) A = ldict['A'] if B == None: exec('B = A', locals(), ldict) B = ldict['B'] else: exec('B = B | A', locals(), ldict) B = ldict['B'] if B != None: B = simplify_logic(B, force=True) # simplify init conditions # TODO: define init to automate this process exp1 = ldict['r1_far'] | ldict['r1_near'] | ldict['r1_home'] exp2 = ldict['r2_far'] | ldict['r2_near'] | ldict['r2_home'] B = B.subs(exp1, True) B = B.subs(exp2, True) B = simplify_logic(B, force=True) return str(B)
def test_invert(): from sympy import simplify_logic k0 = (K.Egap > 6) & (K.PV_cell < 13) kn0 = ~k0 # The not simplifies version assert _expr_to_strings(kn0) == "!(Egap(!*6),PV_cell(!13*))" kn0 = simplify_logic(kn0) strings = _expr_to_strings(kn0) assert ":" in strings for itm in strings.split(":"): assert "!" not in itm # reset() k1 = (K.Egap >= 6) & (K.PV_cell <= 13) kn1 = simplify_logic(~k1) strings = _expr_to_strings(kn1) assert ":" in strings for itm in strings.split(":"): assert "!" in itm
def checkCmpExpression(s1, s2): """ 验证 两个式子是否一致 :param s1: 标准参考答案 :param s2: 用户输入结果 :return: """ if ':' in s1: answer_type, answer = s1.split(':') # 默认添加 : 的都是 矩阵类型的, 因为其他类型的貌似 sympy simplify 能处理 # 一般矩阵乘法不满足交换律, 所以一般来说, 矩阵的表达式不需要化简 # 貌似可以去掉空格而不影响式子, 所以处理方式是去掉空格后比较。 answer = answer.replace(' ', '') s2 = s2.replace(' ', '') if s2 == answer: return True else: return False if is_expression_cmp(s1): return u'代数式方可简化,但不是方程,不能带=><' else: try: correct_answer = simplify_logic(s1) except: return u'作者编写的参考答案有bug,请联系管理员' try: input_answer = simplify_logic(s2) except: return u'你输入的代数式 %s ' % s2 if input_answer != correct_answer: return False return True
def get_sympy(self, query: str, simplify: bool = True) -> sp.Basic: """Converts the given string query into a SymPy expression. :param query: The query to convert. :param simplify: If true (default) the result will be simplified using boolean logic. :return: A SymPy expression. """ parsed_query = self._parser.parse(query) # noinspection PyUnresolvedReferences bool_expr = self.transform(parsed_query) if simplify: bool_expr = sp.simplify_logic(bool_expr) return bool_expr
def longest_subexpression(self) -> SympyLogicMiddleCode: return SympyLogicMiddleCode(exp=simplify_logic( self._middle_code.exp.replace(sympy.Or, sympy.And)))
def simplify_condition(cond): memo = {} sympy_expr = ConditionProcessor.claripy_ast_to_sympy_expr(cond, memo=memo) return ConditionProcessor.sympy_expr_to_claripy_ast( sympy.simplify_logic(sympy_expr), memo)
def _aggregate_explanations_try_all(local_explanations_accuracy, topk_explanations, target_class, x, y, max_accuracy, val_mask, c_threshold): """ Sort explanations by accuracy and then aggregate explanations which increase the accuracy of the aggregated formula. :param local_explanations_accuracy: dictionary of explanations and related accuracies. :param topk_explanations: limits the number of explanations to be aggregated. :param target_class: target class. :param x: observations in validation set. :param y: labels in validation set. :param max_accuracy: if True a formula is simplified only if the simplified formula gets 100% accuracy. :return: """ if len(local_explanations_accuracy) == 0: return "" else: # get the topk most accurate local explanations local_explanations_sorted = sorted( local_explanations_accuracy.items(), key=lambda x: -x[1])[:topk_explanations] predictions = [] explanations = [] best_accuracy = 0.0 best_explanation = "" for explanation_raw, accuracy in local_explanations_sorted: explanation = _simplify_formula(explanation_raw, x, y, target_class, max_accuracy, val_mask, c_threshold) if not explanation: continue predictions.append( get_predictions(explanation, x, target_class, c_threshold)) explanations.append(explanation) predictions = np.array(predictions) explanations = np.array(explanations) y = y[:, target_class] y = y.detach().numpy() for i in range(1, 1 << len(predictions)): include = i & (1 << np.arange(len(predictions))) > 0 pred = predictions[np.nonzero(include)] pred = np.sum(pred, axis=0) pred = pred > 0.5 accuracy = np.sum(pred == y) if accuracy > best_accuracy: # aggregate example-level explanations explanation = explanations[np.nonzero(include)] aggregated_explanation = " | ".join(explanation) aggregated_explanation_simplified = simplify_logic( aggregated_explanation, "dnf", force=True) aggregated_explanation_simplified = ( f"({aggregated_explanation_simplified})") if aggregated_explanation_simplified in [ "", "False", "True", "(False)", "(True)", ]: continue else: best_accuracy = accuracy best_explanation = aggregated_explanation_simplified return best_explanation, best_accuracy
def simplify_condition(condition: str) -> str: input_condition = condition.strip() # Map to sympy syntax: condition = " " + input_condition + " " condition = condition.replace("(", " ( ") condition = condition.replace(")", " ) ") tmp = "" while tmp != condition: tmp = condition condition = condition.replace(" NOT ", " ~ ") condition = condition.replace(" AND ", " & ") condition = condition.replace(" OR ", " | ") condition = condition.replace(" ON ", " true ") condition = condition.replace(" OFF ", " false ") # Replace dashes with a token condition = condition.replace("-", "_dash_") # SymPy chokes on expressions that contain two tokens one next to # the other delimited by a space, which are not an operation. # So a CMake condition like "TARGET Foo::Bar" fails the whole # expression simplifying process. # Turn these conditions into a single token so that SymPy can parse # the expression, and thus simplify it. # Do this by replacing and keeping a map of conditions to single # token symbols. # Support both target names without double colons, and with double # colons. pattern = re.compile(r"(TARGET [a-zA-Z]+(?:::[a-zA-Z]+)?)") target_symbol_mapping = {} all_target_conditions = re.findall(pattern, condition) for target_condition in all_target_conditions: # Replace spaces and colons with underscores. target_condition_symbol_name = re.sub("[ :]", "_", target_condition) target_symbol_mapping[target_condition_symbol_name] = target_condition condition = re.sub(target_condition, target_condition_symbol_name, condition) # Do similar token mapping for comparison operators. pattern = re.compile( r"([a-zA-Z_0-9]+ (?:STRLESS|STREQUAL|STRGREATER) [a-zA-Z_0-9]+)") comparison_symbol_mapping = {} all_comparisons = re.findall(pattern, condition) for comparison in all_comparisons: # Replace spaces and colons with underscores. comparison_symbol_name = re.sub("[ ]", "_", comparison) comparison_symbol_mapping[comparison_symbol_name] = comparison condition = re.sub(comparison, comparison_symbol_name, condition) try: # Generate and simplify condition using sympy: condition_expr = simplify_logic(condition) condition = str(_recursive_simplify(condition_expr)) # Restore the target conditions. for symbol_name in target_symbol_mapping: condition = re.sub(symbol_name, target_symbol_mapping[symbol_name], condition) # Restore comparisons. for comparison in comparison_symbol_mapping: condition = re.sub(comparison, comparison_symbol_mapping[comparison], condition) # Map back to CMake syntax: condition = condition.replace("~", "NOT ") condition = condition.replace("&", "AND") condition = condition.replace("|", "OR") condition = condition.replace("True", "ON") condition = condition.replace("False", "OFF") condition = condition.replace("_dash_", "-") except (SympifyError, TypeError, AttributeError): # sympy did not like our input, so leave this condition alone: condition = input_condition return condition or "ON"
leftSideKnowns = { D['a0']:True, # we assume a is odd - even can be calculated once we have odd } rightSideKnowns = { D['n0']:True, # if we substitute n or not it always will be odd so we might as well } substitutes = { } equations = [] toAddToLeftSide=[] toAddToRightSide=[] satEquation = True i = 0 while i < 2*n: leftSideLeftOvers=[] leftSide = sympy.simplify_logic(functools.reduce(lambda x,y:x^y, [False]+InitialWallaceWeights('a',n,'a',n,i,toAddToLeftSide,leftSideLeftOvers)).subs(leftSideKnowns)) # EXCEPTION - MOVING -1 TO LEFT SIDE OF EQUATION TO SUBTRACT: if i==0: leftSide ^= 1 leftSideLeftOvers = [sympy.simplify_logic(leftOverTerm.subs(leftSideKnowns)) for leftOverTerm in leftSideLeftOvers] rightSideLeftOvers=[] rightSide = functools.reduce(lambda x,y:x^y, [False]+InitialWallaceWeights('n',n,'k',n,i,toAddToRightSide,rightSideLeftOvers)).subs(rightSideKnowns).subs(substitutes) rightSideLeftOvers = [rightSideTerm.subs(rightSideKnowns).subs(substitutes) for rightSideTerm in rightSideLeftOvers] # replacing n digits if shouldSubN: rightSide = rightSide.subs(n_Subs) rightSideLeftOvers = [rightSideTerm.subs(n_Subs) for rightSideTerm in rightSideLeftOvers] toAddToLeftSide = leftSideLeftOvers toAddToRightSide = rightSideLeftOvers
def _expr_to_strings(expr, symbol_prefix="x_", simplify=False, root=True): """Use expression tree to parse the symbols and return a aflow-like api-reference If the provided expression has only one known keyword, group all the conditions in one bracket. Other, use the sequency given by sympy parameter `root` controls at which level the keyword should be added """ if not hasattr(expr, "func"): raise TypeError( f"Expect to take boolean expression but get {type(expr)} instead") if root: num_symbols, valid_symbols = _num_symbols_in_expr( expr, symbol_prefix=symbol_prefix) # convert keyword kw = symb_to_keyword[valid_symbols[0].name] if num_symbols > 1: # Use fallback mode expression convertion # TODO warning return _fallback_expr_to_strings(expr, symbol_prefix=symbol_prefix) # simplify should only be called at root level and 1 keyword # expected simplications are if simplify: expr = simplify_logic(expr) func = expr.func args = expr.args # Symbol or numerical are always leaf nodes # use expr.is_Xxx to determine type instead of func (not working for type One) if expr.is_Symbol: # Is current expression the keyword? if expr.name.startswith(symbol_prefix): return None else: # Wrap expression using single brackets return f"'{str(expr)}'" elif expr.is_Float: return str(float(expr)) elif expr.is_Integer: return str(expr) else: pass # if func in (Symbol, Integer, Float): # if func == Symbol: # # Is current expression the keyword? # if expr.name.startswith(symbol_prefix): # return None # else: # # Wrap expression using single brackets # return f"'{str(expr)}'" # else: # # Reduce the precision of float to default # if func == Float: # return str(float(expr)) # else: # return str(expr) child_strings = [] for arg in args: # get a partial string from each child # Handle only relational nodes cstr = _expr_to_strings(arg, symbol_prefix=symbol_prefix, root=False) if cstr is None: # encounters target continue # determine the priority of operations. wrap lower priority with brackets # if _expr_priority(arg) < _expr_priority(expr): # cstr = "(" + cstr + ")" if arg.func in (And, Or): cstr = "(" + cstr + ")" child_strings.append(cstr) # all child calls are fallback mode! # import pdb; pdb.set_trace() current_string = _join_children(expr, child_strings) if root: current_string = f"{kw}({current_string})" return current_string
def simplify(expression): simplified_expr = sp.simplify_logic( parse_expr(expression, evaluate=False)) return srepr(simplified_expr)