def print_steps(steps): for i, (narr, ani_narr, axiom, axiomIdx) in enumerate(steps): tex = expression.narr2tex(narr) value = state_value(narr) rich.print(f'[red]{i + 1}[/red] [blue]{value:.2f}[/]', end=" ") print(axiom.name()) if ani_narr: ani_tex = expression.narr2tex(ani_narr) print('\t', ani_tex) print('\t', tex)
def _uniq_append(result_narrs, new_narr, max_results): full = False if len(result_narrs) + 1 > max_results: return True, False applied_set = set([expression.narr2tex(narr) for narr in result_narrs]) new_tex = expression.narr2tex(new_narr) append = False if new_tex not in applied_set: result_narrs.append(new_narr) append = True return full, append
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 alpha_prettyprint(alpha): print('rewrite rules:') if len(alpha) == 0: print('\t(empty)') else: for key in alpha: print(f'\t[{key}]:', end=' ') print(expression.narr2tex(alpha[key]))
def dfs(narr, axioms, debug=False, maxsteps=150, animation_mode=False, printTrim=False): any_err = None try: next_steps = [(narr, None, Axiom(name='原式'), -1)] return_steps = [] cnt = 0 while len(next_steps) > 0: narr, ani_narr, axiom, axiom_idx = next_steps[0] output_narr = deepcopy(narr) #print('[output narr]', narr) if animation_mode: if printTrim: rich.print('[blue]before trim[/]') expression.narr_prettyprint(narr) print('[tex]', expression.narr2tex(narr)) expression.trim_animations(narr) if printTrim: rich.print('[blue]after trim[/]') expression.narr_prettyprint(narr) print('[tex]', expression.narr2tex(narr)) return_steps.append((output_narr, ani_narr, axiom, axiom_idx)) next_steps = possible_next_steps(narr, axioms, state.value_v1, animation_mode=animation_mode, fast_return=True, debug=debug) if cnt > maxsteps: any_err = "maximum steps reached." break cnt += 1 except KeyboardInterrupt: pass return return_steps, any_err
def best_child_of(father, c_param=None, debug=False): """ 根据 UCT 选择最好的儿子节点 """ q, N, narr, _, _, _, children = father if c_param is None: weights = children_weights(father, debug=debug) else: weights = children_weights(father, c_param=c_param, debug=debug) argmax_idx = argmax(weights) if debug: print(f"\nfather", expression.narr2tex(narr)) for i, ((q, n, narr, _, a, ai, _), UCT) in enumerate(zip(children, weights)): print() print(f"child [[{i}]] of axiom#{ai}", a.name()) print( f"[UCT={UCT:.4f}, q/n={q:.2f}/{n}, N={N}, c_param={c_param}]", expression.narr2tex(narr)) print('\n[choice index]', f"[[{argmax_idx}]]") print() return children[argmax_idx], weights[argmax_idx], argmax_idx
def print_UCT(father, detailed=False): q, n, f_narr, _, _, _, children = father children_visits = [c[1] for c in children] children_narrs = [c[2] for c in children] zip_arr = zip(children, children_weights(father, c_param=.0), children_visits, children_narrs) if detailed: arr = [(c[4].name(), round(w, 3), f'{visits}/{n}', narr) for c, w, visits, narr in zip_arr] else: arr = [(c[4].name(), round(w, 3), f'{visits}/{n}') for c, w, visits, _ in zip_arr] arr.sort(key=lambda x: x[1], reverse=True) if detailed: print(expression.narr2tex(f_narr)) for axiom_name, UCT, visits, narr in arr: rich.print('[green]UCT:[/]', end=" ") print(UCT, visits, axiom_name, expression.narr2tex(narr)) else: print('[UCT]', arr)
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 _fraction_cancel(narr, debug=False): sign, Type = narr[0].get() if Type != 'frac': return narr # extract weights numerator_weights = Axiom()._extract_weights(narr[1]) if any([x is None for x in numerator_weights]): return narr denominator_weights = Axiom()._extract_weights(narr[2]) if any([x is None for x in denominator_weights]): return narr # cancel weights L = len(numerator_weights) weights = np.array(numerator_weights + denominator_weights, dtype=int) gcd = np.gcd.reduce(weights) weights = (weights // gcd).tolist() # restore weights numerator_weights = weights[:L] denominator_weights = weights[L:] if debug: rich.print('[yellow]cancel fraction:', expression.narr2tex(narr)) print('numerator:', numerator_weights) print('denominator:', denominator_weights) Axiom()._restore_weights(numerator_weights, narr[1]) Axiom()._restore_weights(denominator_weights, narr[2]) if debug: rich.print('[yellow]after:', expression.narr2tex(narr)) expression.narr_prettyprint(narr) return narr
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 render_steps(steps, output='./render-tex.html', show_index=False): display_str = '\\begin{align}' for i, step in enumerate(steps): if len(step) == 4: narr, _, axiom, axiom_idx = step else: narr, axiom, axiom_idx = step tex = expression.narr2tex(narr) if show_index: display_str += '' if i == 0 else ('\\text{step %d}' % i) display_str += '&' if i == 0 else '=&' display_str += tex if axiom_idx >= 0: text = axiom.name() + f' (规则 {axiom_idx})' display_str += ('\\qquad %s' % latex_text(text)) display_str += '\\\\' display_str += '\\end{align}' output_html(output, display_str)
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 value_v2(narr, level=0, debug=False): stats = { 'right_side_of_eq': 0, 'neg': 0, 'NUMBER_level_cnt': 0, 'NUMBER_sum': 0, 'NUMBER_in_sqrt': 0, 'NUMBER_one_zero': 0, 'NUMBER_other_ints': 0, 'NUMBER_pad_zeros': 0, 'NUMBER_decimal': 0, 'VAR_max_level': 0, 'VAR_level_cnt': 0 } narr = expression.trim_animations_copy(narr) collect_stats(narr, stats, 0, None, False) if debug: tex = expression.narr2tex(narr) print('[value_v2]', tex) complexity = [ (2.0 * stats['right_side_of_eq'])**3, 1.0 * math.log(1 + math.log(1 + stats['NUMBER_sum'])), 5.0 * math.log(1 + stats['NUMBER_in_sqrt']), 1.0 * (0 + 0.9 * stats['NUMBER_one_zero'] + 1.0 * stats['NUMBER_other_ints'] + 0.1 * stats['neg'] + 3.0 + stats['NUMBER_decimal'] - 0.2 * stats['NUMBER_pad_zeros'] + 3.0 * stats['VAR_level_cnt'] + 1.0 * stats['NUMBER_level_cnt']), (2.0 * stats['VAR_max_level'])**2 ] if debug: print(stats) print(complexity) return -sum(complexity)
def back_off_step(steps, debug=False): """ 裁剪 MCTS 最后几步的探索步骤,确保得到的 value 比较小 """ max_val = max([state_value(narr) for narr, a, ai in steps]) while len(steps) > 1: cur_step = steps[-1] narr, _, _ = cur_step val = state_value(narr) if val >= max_val: break else: if debug: expr = expression.narr2tex(narr) val = state_value(narr) rich.print(f'[magenta]back-off[/] [blue]val={val:.3f}[/]', end=' ') print(expr) steps.pop() return steps
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 _exact_apply(self, pattern, narr, debug=False): # refuse to apply when rule is not animation compatible in animation mode. if self.animation_mode and pattern not in self.animation: return narr, False # apply pattern transformation to nested array pattern_narr = self.narrs[pattern] is_match, rewrite_rules = test_alpha_equiv(pattern_narr, narr, debug=False) if debug: # Example: # Axiom: (a+b)(a-b) => (a)^{2} - (b)^{2} # pattern => destination # narr: (-xy - z)(-xy + z) # rewrite_rules: # a -> -xy # b -> -z print() if False: print('pattern:', pattern_narr) print('subject:', narr) else: print('pattern:', expression.narr2tex(pattern_narr)) print('subject:', expression.narr2tex(narr)) rich.print('match:', is_match) if is_match: if debug: alpha_prettyprint(rewrite_rules[0]) dest = self.animation[ pattern] if self.animation_mode else self.rules[pattern] dest_narr = [self.narrs[d] for d in dest] if isinstance( dest, list) else self.narrs[dest] if debug: print('dest:', dest) call = self.dp[pattern] if call is not None: # dynamical axiom signs = self.signs[pattern] rewritten_narr, is_applied = call(pattern_narr, signs, narr, rewrite_rules[0], dest_narr) else: rewritten_narr, is_applied = rewrite_by_alpha( dest_narr, rewrite_rules[0]), True if debug: rich.print('applied:', is_applied, end=': ') if False: print(rewritten_narr) else: print(expression.narr2tex(rewritten_narr)) # if a rule with higher priority get applied, later ones are ignored if is_applied: # post processes rewritten_narr = self._fraction_cancel(rewritten_narr) return rewritten_narr, True return narr, False
def policy_steps(narr, all_axioms, k=3, debug=False, nn_models=False, lock=None): """ 结合 policy 网络预测的 prior 生成 steps 以及其每一种可能的概率 """ if nn_models: # get NN predictions expr = expression.narr2tex(narr) if lock: lock.acquire() j = nn_request({'req': 'rule', 'tex': expr}) rules, probs, = j['rules'], j['probs'] if lock: lock.release() if debug: rich.print('[[restrict apply]]', end=" ") rich.print([all_axioms[r].name() for r in rules]) steps = possible_next_steps(narr, all_axioms, state_value, restrict_rules=rules) if len(steps) == 0: steps = possible_next_steps(narr, all_axioms, state_value, restrict_rules=None) steps = [(n, a, ai) for n, _, a, ai in steps] # combine NN priors base_prob = min(probs) / 2.0 step_probs = [ base_prob if ai not in rules else probs[rules.index(ai)] for s, a, ai in steps ] step_probs = np.array(step_probs) # normalize step probabilities sum_probs = step_probs.sum() if sum_probs != 0: step_probs = step_probs / sum_probs if debug: for prob, (s, a, ai) in zip(step_probs, steps): prob_percent = round(prob * 100, 2) rich.print( f'NN Policy: axiom#[red]{ai}[/red] {a.name()} prob=[blue]{prob_percent}%[/blue]' ) print(expression.narr2tex(s)) print() return steps, step_probs else: # default steps without prior steps = possible_next_steps(narr, all_axioms, state_value) steps = [(n, a, ai) for n, _, a, ai in steps] return steps, [0 for _ in steps]
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)
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 data = [] for j in range(1, len(steps)): last_narr, _, _ = steps[j - 1] narr, axiom, axiom_idx = steps[j] last_expr = expression.narr2tex(last_narr) expr = expression.narr2tex(narr) axiom_name = axiom.name() rich.print(f'step{j} axiom#{axiom_idx} {axiom_name}', expr) data.append((last_expr, axiom_idx + 1, expr)) data.append((expr, 0, expr)) # write data pair print(f'Test case: {i} / {len(testcases) - 1}') generate_corpus(i, data, steps, DIV=20) #input('pause') except KeyboardInterrupt:
def rollout(node, idx, all_axioms, n_times, visited, debug=False, nn_models=False, k=3, lock=None): """ Monte-Carlo 树的 roll-out 操作 """ q, n, narr, father, axiom, axiom_idx, children = node cnt = 0 values = [state_value(father[2])] choices = [idx + 1] root_tex = expression.narr2tex(father[2]) if debug: print('[roll-out origin]', end=' ') rich.print(f'[blue]{values[0]:.2f}[/]', end=' ') print(root_tex) while True: q, n, narr, father, axiom, axiom_idx, children = node expr = expression.narr2tex(narr) if nn_models: # use NN to estimate the number of left-over steps if lock: lock.acquire() j = nn_request({'req': 'value', 'tex': expr}) expr_val = j['value'] if lock: lock.release() else: # use rule-based value function to indicate complexity expr_val = state_value(narr) values.append(expr_val) if debug: axiom_name = axiom.name() print(f'[roll-out depth={cnt}]', end=' ') rich.print(f'[blue]{expr_val:.2f}[/]', end=' ') print(axiom_name, expr) if expr in visited: if debug: rich.print(f'[[roll-out]] [red]visited![/]') reward, rewardless_len = 0, 0 break steps, step_probs = policy_steps(narr, all_axioms, k=k, debug=False, nn_models=nn_models, lock=lock) if len(steps) == 0: if debug: print('[roll-out reach leaf]') reward, rewardless_len = reward_calc( values, relative_value=(nn_models is None)) break elif cnt >= n_times: if debug: print(f'[roll-out stop early (max times reached)]') reward, rewardless_len = reward_calc( values, relative_value=(nn_models is None)) break # randomly select index rollout_idx = random.randint(0, len(steps) - 1) choices.append(rollout_idx + 1) # dive to deeper node specified by index if lock: lock.acquire() fully_expand(node, steps, prior_arr=step_probs) _, _, _, _, _, _, children = node next_node = children[rollout_idx] if lock: lock.release() node = next_node cnt += 1 # write to roll-out log if lock: lock.acquire() with open(rollout_logfile, 'a') as fh: fh.write(json.dumps(choices)) fh.write(json.dumps([f'{reward:.3f}'])) fh.write(' ' + root_tex) fh.write('\n') if lock: lock.release() return node, reward, rewardless_len
all_axioms, state_value, debug=True, restrict_rules=rules) else: next_steps = possible_next_steps(narr, all_axioms, state_value, debug=True) if len(next_steps) == 0: break # print choices rich.print(f'[bold red]current[/] [blue]{value:.2f}[/]:', end=" ") print(expression.narr2tex(narr)) #expression.narr_prettyprint(narr) reward, _ = reward_calc(values) print('\033[91m', end='') print( f'origin value: {val0:.2f}, cur value: {value:.2f}, reward = {reward:.2f}' ) print('\033[0m') while True: print_steps(next_steps) render_steps(steps[-1:] + next_steps, show_index=True, output='./debug.html') j = input('Enter choice (0 to print past steps): ')
def possible_next_steps(narr, axioms, state_value, animation_mode=False, debug=False, restrict_rules=None, fast_return=False): return_steps = [] cur_value = state_value(narr) if debug: tex = expression.narr2tex(narr) rich.print(f'[light]{cur_value:.2f}', end=' ') print(tex) for axiom_idx, axiom in enumerate(axioms): if restrict_rules and axiom_idx not in restrict_rules: if debug: rich.print('[grey50][[N]]', end=' ') print(axiom.name(), end=' ') print(tex) continue axiom.animation_mode = animation_mode #print('restrict apply', [axioms[r].name() for r in restrict_rules if r >= 0]) possible_applied_tuples = axiom.apply(narr) #print('done apply', axiom.name()) value_constrain_narrs = [] for applied_narr, ani_narr in possible_applied_tuples: value = state_value(applied_narr) if not axiom.allow_complication: if ((axiom.strict_simplify and value <= cur_value) or value < cur_value): if debug: rich.print('[grey50][[x]]', end=' ') tex = expression.narr2tex(applied_narr) print(axiom.name(), end=' ') rich.print(f'[light]{value:.2f}', end=' ') print(tex) continue value_constrain_narrs.append( (applied_narr, ani_narr, axiom, axiom_idx, value)) return_steps += value_constrain_narrs if fast_return and len(value_constrain_narrs) > 0: break return_steps.sort(key=lambda x: (x[-2], -x[-1])) if debug: for i, (applied_narr, ani_narr, axiom, axiom_idx, value) in enumerate(return_steps): # print axiom name if i == 0: rich.print(f'[bright_green][[✓]]', end=' ') else: rich.print(f'[grey50][[ ]]', end=' ') print(axiom.name(), end=" ") # print value rich.print(f'[light]{value:.2f}', end=' ') # print tex tex = expression.narr2tex(applied_narr) print(tex) print() # trim value from tuples return [s[:-1] for s in return_steps]
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: ani_tex = expression.narr2tex(ani_narr) ani_json = mathjs.tex2json(ani_tex) ret_arr.append({ 'tex': '\\text{transition step ...}', 'animate_tex': ani_tex, 'animate_json': ani_json, 'axiom': '移项', 'axiom_idx': -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)) else: rich.print('[bold red]Not alpha-equivalent')
def mcts(narr0, all_axioms, sample_depth=4, n_sample_times=200, n_maxsteps=100, k=3, debug=False, nn_models=False, training=False, force_single_thread=False): # q n narr father axiom axiomIdx children root = [0, 1, narr0, None, None, -1, []] moves = [root] render_steps([(narr0, None, -1)]) global manager if not force_single_thread: # prepare proxy structure for parallel processes root[6] = manager.list([]) root = manager.list(root) moves = [root] else: manager = None node = root visited = set([expression.narr2tex(narr0)]) final_steps = [] while True: q, n, narr, father, axiom, axiom_idx, children = node # debug print if True: #if debug: print('\033[94m', end='') expr_val = state_value(narr) print(f'[current] step={len(moves)}, val={expr_val:.1f}:', expression.narr2tex(narr), end='') print('\033[0m', end='\n') expression.narr_prettyprint(narr) steps, step_probs = policy_steps(narr, all_axioms, k=k, debug=debug, nn_models=nn_models) if debug: rich.print(f'[magenta]Candidate steps: {len(steps)}[/]') for i, (n, a, ai) in enumerate(steps): val = state_value(n) rich.print(f'[red]#{i+1}[/]', a.name(), ':', end=' ') rich.print(f'val={val:.2f}', end=' ') print(expression.narr2tex(n), end='\n\n') if False: from axiom import Axiom render_steps([(narr, Axiom(), -1)] + steps, show_index=True) choices = input('Limit choices: ') choices = [ i for i in map(lambda x: int(x), choices.split(',')) ] rich.print(choices) steps = [steps[i - 1] for i in choices] if len(steps) == 0: if debug: print('[no more candidate steps]') if nn_models and training: policy = 0 #policy_fine_tuning(nn_models, expr, policy, debug=debug, all_axioms=all_axioms) break if manager and not force_single_thread: evaluate_parallel(node, all_axioms, steps, n_sample_times, sample_depth, visited, debug=debug, nn_models=nn_models, k=k, step_probs=step_probs) else: evaluate(node, all_axioms, steps, n_sample_times, sample_depth, visited, debug=debug, nn_models=nn_models, k=k, step_probs=step_probs) # selection move_choice, w, _ = best_child_of(node, c_param=.0, debug=debug) move_to_expr = expression.narr2tex(move_choice[2]) if w == 0 or move_to_expr in visited: print( f'[abort] best w={w:.2f}, visited: {move_to_expr in visited}') break else: if nn_models and training: policy = move_choice[5] + 1 #policy_fine_tuning(nn_models, expr, policy, debug=debug, all_axioms=all_axioms) moves.append(move_choice) node = move_choice # construct steps to be returned final_steps = [(e, a, ai) for q, n, e, f, a, ai, c in moves] render_steps(final_steps) visited.add(move_to_expr) #if debug: print('[visited]', visited) if len(moves) >= n_maxsteps: if debug: print('[exceed max steps]') break if len(final_steps) > 0: final_steps = back_off_step(final_steps, debug=True) if nn_models and training: # fine-tune value network for i, (e, _, _) in enumerate(final_steps): value = -(len(final_steps) - i - 1) #value_fine_tuning(nn_models, e, value, debug=debug) return final_steps
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)
def _level_apply(self, narr, debug=False): ret_narrs = [] # store possible results ani_narrs = [] # store transition animations root_sign, root_type = narr[0].get() # ignore terminal tokens (no rule has a single terminal token as pattern) if root_type in expression.terminal_tokens(): return [], [] for pattern in self.rules: wildcards_idx = self.wildcards_idx[pattern] no_permute = (wildcards_idx != None) or root_type in expression.no_permute_tokens() if debug: rich.print( f'\n[red]level apply[/] {pattern} wildcards_idx={wildcards_idx},' + f' no_permute={no_permute} [red]to[/]', expression.narr2tex(narr)) # extract and try partial one or two operands to match against pattern for construct_tree, brothers, ani_tree in self._children_permutation( narr, no_permute=no_permute): rewritten_narr, is_applied = self._exact_apply(pattern, construct_tree, debug=debug) if is_applied: new_narr = [] # construct a new father if len(brothers) > 0: if self.root_sign_reduce: # always positive new father, in case the negative # sign of father is also reduced, e.g. -abc => (ab)c new_root = NarrRoot(+1, root_type) else: # in addition case, we will need to keep father sign, # e.g. -(1+2+3) => -(3+3) new_root = NarrRoot(root_sign, root_type) new_narr = [new_root, None, *brothers] expression.replace_or_pass_children( new_narr, 0, rewritten_narr) else: # the entire expression in this level gets reduced new_narr = rewritten_narr if not self.root_sign_reduce and root_sign < 0: new_narr[0].apply_sign(-1) #print('[animat]', expression.narr2tex(ani_tree)) #print('[result]', expression.narr2tex(new_narr)) #print() if False: rich.print('[red]level apply[/]:') print('[origin]', expression.narr2tex(narr)) print('[pattern]', pattern) print('[result]', expression.narr2tex(new_narr)) print() _, append = Axiom()._uniq_append(ret_narrs, new_narr, self.max_results) if append: ani_narrs.append(ani_tree) return ret_narrs, ani_narrs
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)
def _print_results_in_tex(narrs): for n in narrs: print(expression.narr2tex(n)) print()
def nn_value(narr): tex = expression.narr2tex(narr) expr_val, _ = nn.predict_value(tex, nn_models) return expr_val