def random_polynomial_term(): err = False build_op = tr2narr.sup t1 = random_tok() if 'order2' == nonUniformChoice( ['order1', 'order2'], [0.5, 0.5] ): sign = nonUniformChoice( [+1, -1], [0.5, 0.5] ) if sign < 0: t0 = tr2narr.null_reduce([]) t1 = tr2narr.minus([t0, t1]) t2 = random_tok(only_number=True, small_number=True) t1 = build_op([t1, t2]) try: tex = expression.narr2tex(t1) expression.tex2narr(tex) except Exception as err_msg: err = True return tex, err
def random_exp(complexity=2): """ 按照复杂度指示生成随机数学表达式 """ err = False tex = '' t1 = tr2narr.null_reduce([]) for _ in range(complexity): # null-reduce must be commutative commutative = (len(t1) == 0) n_oprands, build_op = random_operator(commutative=commutative) if n_oprands == 2: always_t12 = False if tr2narr.sup == build_op: t2 = random_tok(only_number=True, small_number=True) always_t12 = True elif 'simple' == nonUniformChoice( ['complex', 'simple'], [0.2, 0.8] ): t2 = random_tok() else: random_complexity = bounded_guassian_sample(3, 5) tex, err = random_exp(complexity=random_complexity) if err: break t2 = expression.tex2narr(tex) if always_t12 or random.choice(['12', '21']) == '12': t1 = build_op([t1, t2]) else: t1 = build_op([t2, t1]) else: t1 = build_op([t1]) try: tex = expression.narr2tex(t1) expression.tex2narr(tex) except Exception as err_msg: err = True #rich.print('[red]invalid random expression') break return tex, err
def random_equations(): """ 生成等号两边由随机表达式组成的等式 """ build_op = tr2narr.eq tex1, err1 = random_terms() tex2, err2 = random_terms() if err1 or err2: return '', True try: t1 = expression.tex2narr(tex1) t2 = expression.tex2narr(tex2) t1 = build_op([t1, t2]) tex = expression.narr2tex(t1) expression.tex2narr(tex) except Exception as err_msg: return '', True return tex, False
def add_rule(self, src, dest, dynamic_procedure=None, animation=None): dests = dest if isinstance(dest, list) else [dest] for i, dest_output in enumerate(dests): A, B, C = self._preprocess(src, dest_output) for (a, b, signs) in zip(A, B, C): if a not in self.rules: self.rules[a] = b elif isinstance(self.rules[a], list): self.rules[a].append(b) else: self.rules[a] = [self.rules[a], b] self.dp[a] = dynamic_procedure self.signs[a] = signs # cache some results for speedup self.narrs[a] = expression.tex2narr(a) self.narrs[b] = expression.tex2narr(b) self.wildcards_idx[a] = expression.get_wildcards_index( self.narrs[a]) if animation: ani_output = animation[i] if isinstance(dest, list) else animation A, B, _ = self._preprocess(src, ani_output) for (a, b) in zip(A, B): if a not in self.animation: self.animation[a] = b elif isinstance(self.animation[a], list): self.animation[a].append(b) else: self.animation[a] = [self.animation[a], b] self.narrs[b] = expression.tex2narr(b) return self
def value_v1(narr, debug=False): """ 计算 表达式的价值(等于各个符号频率的自定义加权和) """ if isinstance(narr, str): return value(expression.tex2narr(narr)) narr = expression.trim_animations_copy(narr) value_dict = { 'VAR': 10, 'NUMBER_integer': 0.1, 'NUMBER_decimal': 2, 'NUMBER_pad_zeros': -0.25, 'NUMBER_one': -0.1, 'NUMBER_zero': 0.1, 'NUMBER_in_sqrt': 0.5, 'eq': 0, 'neg': 0.5, 'add': 2, 'mul': 1, 'div': 10, 'frac': 8, 'ifrac': 20, 'sup': 4, 'abs': 1, 'sqrt': 1, 'n_terms': 0.6, 'n_terms_in_sqrt': 25, 'n_deepest_var_level': 100, 'right_side_of_eq': 200, } stats = token_stats(narr, {}) if debug: print('[value_v1]', expression.narr2tex(narr)) print(stats) accum = 0 # symbol type values for key in stats: if key in value_dict: accum += stats[key] * value_dict[key] # number sum values if 'NUMBER_sum' in stats: accum += math.log(1 + stats['NUMBER_sum']) / 2 if 'NUMBER_in_sqrt' in stats: accum += 1.5 * math.log(1 + stats['NUMBER_in_sqrt']) return -accum
def test(tex, state_value): """ 包装测试函数 """ import expression global g_test_last_val narr = expression.tex2narr(tex) value = state_value(narr, debug=True) print(value) if value > g_test_last_val: rich.print('[green][[pass]][/]') else: rich.print('[red][[failed]][/]') print() g_test_last_val = value
def random_terms(): """ 将随机表达式按项组合,生成随机加和表达式 """ any_err = True build_op = tr2narr.add t1 = tr2narr.null_reduce([]) # the number of terms is sampled from normal distribution n_terms = bounded_guassian_sample(3, 6) for _ in range(n_terms): if 'nested' == nonUniformChoice( ['nested', 'polynomial'], [0.2, 0.8] ): tex_t2, err = random_exp() else: tex_t2, err = random_polynomial_term() if err: continue else: any_err = False t2 = expression.tex2narr(tex_t2) if random.choice(['12', '21']) == '12': t1 = build_op([t1, t2]) else: t1 = build_op([t2, t1]) if len(t1) == 0: return '', True else: tex = expression.narr2tex(t1) return tex, any_err
def test(self, tex=None, debug=False, render=True, printNarr=False, printTrim=False, printJSON=False): # construct test pairs (TeX, expected TeX) tests = self.tests if tex is None else [(tex, None)] if len(tests) == 0: print('[no test case]') # test through each testcase for this axiom ... for test, expect in tests: expr = test if isinstance(test, str) else expression.narr2tex(test) narr = expression.tex2narr(expr) if isinstance(test, str) else test results = self.apply(narr, debug=debug) # render texs is for HTML preview render_texs = [expr] rich.print('[bold cyan][[test]][/]', end=" ") print(expr) #if printNarr: # expression.narr_prettyprint(narr) for applied_narr, ani_narr in results: # print transition animations if ani_narr: ani_tex = expression.narr2tex(ani_narr) rich.print('[bold cyan][[transition]][/]', end=" ") print(ani_tex) else: rich.print('[bold cyan][[transition]][/]', None) # print result expression applied_tex = expression.narr2tex(applied_narr) rich.print('[bold cyan][[result]][/]', end=" ") print(applied_tex, end=" ") if expect is not None: if applied_tex in expect: rich.print('[bold green]pass[/]') else: rich.print('[bold red]failed[/]') else: print() # render texs is for HTML preview render_texs.append(applied_tex) if printNarr: rich.print("[red]narr[/]:") expression.narr_prettyprint(applied_narr) if printTrim: rich.print("[red]trim[/]:") expression.trim_animations(applied_narr) expression.narr_prettyprint(applied_narr) if printJSON: rich.print('[red]JSON[/]:') json = mathjs.tex2json(applied_tex, indent=4) print(json) if render: import render_math render_math.render_equations(render_texs)
#testcases += test_case_from_log('./rational_8000.txt') #testcases += test_case_from_log('./full_random_1000.txt') #n_sample_times = 220 n_sample_times = 440 start = 0 always_use_MCTS = True open('fallback.log', 'w') for i, expr in enumerate(testcases[-1:]): #for i, expr in enumerate(testcases[:]): if i < start: continue try: narr = expression.tex2narr(expr) err = None if not always_use_MCTS: # use DFS or mcts (as fallback) to generate steps steps, err = dfs(narr, basic_axioms, debug=True, maxsteps=150) steps = [(n, a, ai) for n, an, a, ai in steps] if err or always_use_MCTS: with open('fallback.log', 'a') as fh: fh.write(f'#{i}: ' + expr + '\n') steps = mcts(narr, all_axioms, debug=False, n_sample_times=n_sample_times, nn_models=None, force_single_thread=False) # make data pair
def test(all_axioms): from render_math import render_steps from test_cases import test_cases_x3_rational, test_cases_wiki131278697 testcases = [] tmp, _ = test_cases_x3_rational() testcases += tmp tmp, _ = test_cases_wiki131278697() testcases += tmp testcases += [ '\\frac{12a}{3a + a + 20a} - \\frac{1}{4}', '1 + \\frac{7}{3}', '4 -3 \\frac{1}{2}', '\\frac{(-3)^{3}}{2 \cdot \\frac{1}{4} \cdot (-\\frac{2}{3})^{2}} + 4 -4 \cdot \\frac{1}{3}', '\\frac{11}{2} (- \\frac{1}{6}) \\frac{3}{11} \\frac{4}{3}', 'a - x^{2} + x^{2} \\times 0.609 + 1 = 0', "25 \cdot 48 + 103 \cdot 25 - 25 \cdot 51", "-13 \\times \\frac{2}{3} - 0.34 \\frac{2}{7} + \\frac{1}{3}(-13) - \\frac{5}{7} 0.34", '(-3\\frac{1}{3})\div2\\frac{1}{3}\\times\\frac{7}{10}', "(-18) \div ((2\\frac{1}{4}) \\times (1 - \\frac{3}{4}))", #"(-3 - \\frac{4}{17}) (14\\frac{13}{15}) - (3\\frac{4}{17}) (2 + \\frac{2}{15})", "b + 3x^{2} +2b + 3b + x^{2}= 0", "(3 + \\frac{4}{17}) (-14\\frac{13}{15} - \\frac{2}{15}) - 2 \times 3 - 2 \\times \\frac{4}{17}", "-(3 + \\frac{4}{17}) \\times (14\\frac{13}{15}) - (3 + \\frac{4}{17}) \\times (2\\frac{2}{15})", "\\frac{-1}{\\frac{2}{3} \cdot \\frac{7}{10}}", "\\frac{ 60 (1 - \\frac{2}{5}) + 3}{2}" ## some animation testcases #"1 + 0 + 0 + 0 + 0", #'-3 \\frac{-2}{4}', #'\\frac{2}{3} \div \\frac{4}{5}', #'(\sqrt{2})^{2}', #'0+1+2', #'-\\frac{1}{-2} \div \\frac{-3}{4}', #'\\frac{x}{3xy}', #'-x x', #'-\\frac{8}{-2}', #"a + b = 3 - c", #"x + b = 12", #'\\frac{2}{x} + y = a + b', #'\\frac{1}{y} \\frac{x}{1}', #'-7(a-b)', #'-(-2-3)^{2}', #"\left| -(5+\\frac{1}{2})\\right| (\\frac{1}{3} - \\frac{1}{2}) \\frac{3}{11} \\div (1 - \\frac{1}{4})", #"2 + 7 + 8", #"3x + 3 = 2x - 1", #'2(a + b) + 3', #'2(a + b)', #'(-7) + 10 + (-3) + 6 + (-6)', #'-(3 - 2)x', #"(-3 - \\frac{4}{17}) \\times (14\\frac{13}{15}) - (3\\frac{4}{17}) \\times (2\\frac{2}{15})", #"\\frac{1}{2} \\times 10.2 - (\\frac{5}{4} + 1 - 9 \\frac{1}{7})^{2}" ] begin_from = 0 n_steps = 0 timer = Timer() #for i, test in enumerate(testcases): for i, test in enumerate(testcases[-1:]): if i < begin_from: continue test_narr = expression.tex2narr(test) with timer: steps, err = dfs(test_narr, all_axioms, debug=True, animation_mode=True, printTrim=False) if err: print('DFS error:', err) for narr, ani_narr, axiom, axiom_idx in steps: rich.print(f'[red]{axiom.name()}') narr = expression.trim_animations_copy(narr) tex = expression.narr2tex(narr) if ani_narr: ani_tex = expression.narr2tex(ani_narr) print('\t', ani_tex) print('\t', tex) #animation_json = mathjs.tex2json(tex) #print('\t', animation_json) render_steps(steps) n_steps += len(steps) print(f'steps: {len(steps)}') print(f'test case: {i} / {len(testcases)}') #input('Enter to continue...') timer.show_stats(n_steps=n_steps)
strict_simplify=True, disable=False).add_rule( '-(x + *{1}) *{2} ', '(-x - *{1}) *{2}', animation='`-(x + *{1})`[replace]{-x - *{1}} *{2}'). add_test('-(3 - 2)x', '(-3 + 2) \\times x') ] else: from common_axioms import common_axioms all_axioms = common_axioms() args = sys.argv[1:] if len(args) > 0: tex = args[0] narr = expression.tex2narr(tex) steps, err = dfs(narr, all_axioms, debug=False, animation_mode=True) if err: print(err, file=sys.stderr) quit() ret_arr = [] for i, (narr, ani_narr, axiom, axiom_idx) in enumerate(steps): trim_narr = expression.trim_animations_copy(narr) trim_tex = expression.narr2tex(trim_narr) animate_tex = expression.narr2tex(narr) animate_json = mathjs.tex2json(animate_tex) if ani_narr:
#narr1 = expression.tex2narr('-\\sqrt{x}^{2}') #narr2 = expression.tex2narr('-(\\sqrt{x})^{2}') #narr1 = expression.tex2narr('0 (-n)') #narr2 = expression.tex2narr('- 0 n') #narr1 = expression.tex2narr('-x \\times *{1} + x \\times *{2}') #narr2 = expression.tex2narr('-25 \\times 51 + 25 \\times 48') #narr1 = expression.tex2narr('+(-X)(-X)') #narr2 = expression.tex2narr('-yy') #narr1 = expression.tex2narr('a + *{1}') #narr1 = expression.tex2narr('- a - *{1}') narr1 = expression.tex2narr('-a') narr2 = [ NarrRoot(1, 'add'), [NarrRoot(1, 'NUMBER'), 30.0], [ NarrRoot(1, 'add'), [NarrRoot(1, 'NUMBER'), 1.0], [NarrRoot(1, 'NUMBER'), 3.0] ] ] is_equiv, rewrite_rules = test_alpha_equiv(narr1, narr2, debug=True) if is_equiv: rich.print('[bold green]Is alpha-equivalent') alpha = rewrite_rules[0] alpha_prettyprint(alpha) rewritten_narr = rewrite_by_alpha(narr1, alpha) rich.print('[[rewritten]]', expression.narr2tex(rewritten_narr))
testcase = ( "(-3 - \\frac{4}{17}) \\times (14 + \\frac{13}{15}) - (3 + \\frac{4}{17}) \\times (2 + \\frac{2}{15})" ) debug_NN = True if debug_NN: global nn_models nn_models = nn.NN_models('model-policy-nn.pretrain.pt', 'model-value-nn.pretrain.pt', 'bow.pkl') state_value = nn_value if debug_NN else state.value_v2 try: narr = expression.tex2narr(testcase) val0 = state_value(narr) steps = [(narr, None, Axiom(name='原式'), -1)] choices = [0] values = [val0] while len(steps) > 0: narr = steps[-1][0] value = state_value(narr) values.append(value) # only used in animation_mode expression.trim_animations(narr) if debug_NN: rules, probs, _ = nn.predict_policy(testcase, nn_models, k=4) rules = rules.tolist()
def test(): from test_cases import test_cases_x3_rational, test_cases_wiki131278697, test_case_from_log from common_axioms import common_axioms axioms = common_axioms(full=True) testcases = [] tmp, _ = test_cases_x3_rational() testcases += tmp #testcases += [ # '\\frac{12a}{3a + a + 20a} - \\frac{1}{4}', # '1 + \\frac{7}{3}', # '4 -3 \\frac{1}{2}', # '\\frac{(-3)^{3}}{2 \cdot \\frac{1}{4} \cdot (-\\frac{2}{3})^{2}} + 4 -4 \cdot \\frac{1}{3}', # '\\frac{11}{2} (- \\frac{1}{6}) \\frac{3}{11} \\frac{4}{3}', # '(-3\\frac{1}{3})\div2\\frac{1}{3}\\times\\frac{7}{10}', # 'a - x^{2} + x^{2} \\times 0.609 + 1 = 0', # '1.609 \\times x^{2} + x^{2} + x^{2} \\times 2 \\times x = 0', # '-x \\times 0.391 - 629 - x^{2} \\times 2 + y^{2} + x \\times \\frac{50}{x + y} = 0', # # some tests for extracting common factors # "25 \cdot 48 + 103 \cdot 25 - 25 \cdot 51", # "-13 \\times \\frac{2}{3} - 0.34 \\frac{2}{7} + \\frac{1}{3}(-13) - \\frac{5}{7} 0.34", # "-x 0.391 - 629 - 2 x^{2} + y^{2} + \\frac{50x}{x+y} = 0", # "- (3\\frac{4}{17}) (2\\frac{2}{15}) - (7\\frac{4}{17}) (14 \\frac{13}{15}) - 4 (-14 \\frac{13}{15})", # "(-3 - \\frac{4}{17}) \\times (14 + \\frac{13}{15}) - (3 + \\frac{4}{17}) \\times (2 + \\frac{2}{15})", # #"-200.9 + 28 + 0.9 + (-8)", # #"3+5\\times6-6\div3", # #"\\frac{(-3)^{3}}{2 \\times \\frac{1}{4} (-\\frac{2}{3})^{2}} +4 -4 \\times\\frac{1}{3}", # #"6 \div 3" #] nn_models = True debug = True force_single_thread = False n_steps = 0 timer = Timer() open(rollout_logfile, 'w') #for i, expr in enumerate(testcases[-1:]): for i, expr in enumerate(testcases[:]): narr = expression.tex2narr(expr) n_sample_times = 22 if nn_models or force_single_thread else 440 with timer: steps = mcts(narr, axioms, debug=debug, n_sample_times=n_sample_times, nn_models=nn_models, force_single_thread=force_single_thread) for j, (narr, axiom, axiom_idx) in enumerate(steps): val = state_value(narr) expr = expression.narr2tex(narr) axiom_name = axiom.name() if axiom is not None else '原式' rich.print(f'step{j} {axiom_name} [blue]val={val:.2f}[/]', expr) render_steps(steps) n_steps += len(steps) print(f'steps: {len(steps)}') print(f'Test case: {i} / {len(testcases) - 1}') #print('Enter to continue') #input() timer.show_stats(n_steps=n_steps)