From 6136b8a55323b327554b68623613b7c1ae10c434 Mon Sep 17 00:00:00 2001 From: ZhouTimeMachine <99057383+ZhouTimeMachine@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:33:46 +0800 Subject: [PATCH] test: tikzautomata --- .github/workflows/ci.yml | 1 + .gitignore | 3 +- docs/courses/imgs/toc/4.1.drawio.png | Bin 13136 -> 13703 bytes docs/courses/toc/turing_machine.md | 815 +++++++++++++++++- mkdocs.yml | 1 + tools/mkdocs-tikzautomata-plugin/README.md | 1 + .../mkdocs_tikzautomata_plugin/__init__.py | 0 .../markdown_utils.py | 18 + .../lib/mkdocs_tikzautomata_plugin/plugin.py | 83 ++ .../mkdocs_tikzautomata_plugin/renderer.py | 59 ++ .../lib/mkdocs_tikzautomata_plugin/tex.py | 84 ++ .../PKG-INFO | 18 + .../SOURCES.txt | 13 + .../dependency_links.txt | 1 + .../entry_points.txt | 2 + .../requires.txt | 1 + .../top_level.txt | 1 + .../mkdocs_tikzautomata_plugin/__init__.py | 0 .../css/svg_extra.css | 15 + .../markdown_utils.py | 18 + .../mkdocs_tikzautomata_plugin/plugin.py | 83 ++ .../mkdocs_tikzautomata_plugin/renderer.py | 59 ++ .../mkdocs_tikzautomata_plugin/tex.py | 84 ++ tools/mkdocs-tikzautomata-plugin/setup.py | 51 ++ 24 files changed, 1409 insertions(+), 2 deletions(-) create mode 100644 tools/mkdocs-tikzautomata-plugin/README.md create mode 100644 tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/__init__.py create mode 100644 tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/markdown_utils.py create mode 100644 tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/plugin.py create mode 100644 tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/renderer.py create mode 100644 tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/tex.py create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/PKG-INFO create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/SOURCES.txt create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/dependency_links.txt create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/entry_points.txt create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/requires.txt create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/top_level.txt create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/__init__.py create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/css/svg_extra.css create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/markdown_utils.py create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/plugin.py create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/renderer.py create mode 100644 tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/tex.py create mode 100644 tools/mkdocs-tikzautomata-plugin/setup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40b0e46..3ddc294 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,5 @@ jobs: - run: pip install mkdocs-material - run: pip install mkdocs-heti-plugin - run: pip install mkdocs-git-revision-date-localized-plugin + - run: pip install tools/mkdocs-tikzautomata-plugin - run: mkdocs gh-deploy --force \ No newline at end of file diff --git a/.gitignore b/.gitignore index a2e9270..e3a4cec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store .vscode site/ -todo/ \ No newline at end of file +todo/ +cache/ \ No newline at end of file diff --git a/docs/courses/imgs/toc/4.1.drawio.png b/docs/courses/imgs/toc/4.1.drawio.png index 3f2f15a80889ac01e09f8659b7b86d5f0e8b5701..328fb8aa8ea7a41535465ffcf59ed3dfa05078f3 100644 GIT binary patch delta 5422 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 diff --git a/docs/courses/toc/turing_machine.md b/docs/courses/toc/turing_machine.md index c379dcb..98a52a3 100644 --- a/docs/courses/toc/turing_machine.md +++ b/docs/courses/toc/turing_machine.md @@ -4,4 +4,817 @@ !!! info "Part of note taken on ZJU *Introductiion to Theoretical Computer Science*, 2022 Fall & Winter" -!!! warning "本页面还在建设中" \ No newline at end of file +!!! warning "本页面还在建设中" + +## The Definition of a Turing Machine + +- 图灵机: + - 头可读写,双向移动 + - 带无限长,带上默认字符为空格 ($\sqcup$) +- 实际计算过程中,带上只有有限的空格被读取 + +### Turing Machine and its Configuration + +!!! info "图灵机 (Turing Machine, TM)" + 图灵机即 quintuple $(K, \Sigma, \delta, s, H)$,有所特别的唯 $\Sigma, \delta, H$: + + - $\Sigma$: 要求 $\{\sqcup,\triangleright\}\subseteq \Sigma$; $\leftarrow, \rightarrow\notin \Sigma$ + - $H$: 停机状态集合 (halting states),不同于先前的 $F$ 被称为末态,到达末态之后也可以再转移,到达停机状态之后就不能再转移了(可以从下面 $\delta$ 的定义看出) + - $\delta$: 转移**函数**,要求 $\forall \delta(q, a)=(p, b)$,$(K-H)\times \Sigma\to K\times(\Sigma\cup \{\leftarrow, \rightarrow\})$ + - $b\neq \triangleright$,即不可写左端标记 + - $a=\triangleright$ 时,$b=\to$。即读到左端标记则右移。 + +!!! info "格局 (configuration)" + 图灵机 $M=(K, \Sigma, \delta, s, H)$ 的格局 $\in K\times \triangleright\Sigma^*\times(\Sigma^*(\Sigma-\{\sqcup\})\cup \left\{ e \right\})$ + + - 首先状态肯定是必要的 + - 然后是两段字符串,是读写头左右侧的字符串 + - 左侧边缘为左端标记 $\triangleright$(左侧字符串包括当前字符) + - 右侧边缘为最右侧的非 $\sqcup$ 字符,或者如果右侧没有非 $\sqcup$ 字符则取 $e$ + +!!! tip "Remark" + 特别地,格局中状态 $\in H$ 时该格局称为停机格局 (halted configuration)。 + +格局符号也存在简写: + +$$ + (q, wa, u)\Rightarrow (q, w\underline{a}u) +$$ + + +### Turing Machines Computation and Notation + +图灵机 $M=(K, \Sigma, \delta, s, H)$,考虑格局的单步变换: + +$$ + (q_1, w_1\underline{a_1}u_1)\vdash_M + (q_2, w_2\underline{a_2}u_2) +$$ + + +需要 $\:b\in \Sigma\cup \{\leftarrow, \rightarrow\}$, $\delta(q_1, a_1)=(q_2, b)$,包括以下情况: + +- $b\in \Sigma$: 写操作,头不动,$w_1=w_2$, $a_2=b$ +- $b=\leftarrow$: 左移操作,$w_1=w_2a_2$ + - $a_1= \sqcup, u_1=e$: $u_2=e$ + - otherwise: $u_2=a_1u_1$ +- $b=\rightarrow$: 右移,同理分析。 + + +!!! tip "Remark" + 除了 $\vdash_M$ 的自反传递闭包 $\vdash_M^*$ 之外,还有 $n$ 步计算/长度为 $n$ 的计算 $\vdash_M^n$。 + +根据图灵机的基本计算操作类型,定义图灵机的基本图灵机 (basic machines) + +!!! info "基本图灵机 (basic machines)" + + $$ + M_a=(\{s, h\}, \Sigma, \delta, s, \{h\}) + $$ + + + 其中 $\delta$ 定义为 + + $$ + \delta(s, \triangleright)=(s, \rightarrow),\quad + \delta(s, b)=(h, a), \forall b\in\Sigma-\{\triangleright\} + $$ + + - Symbol-writing Machines: $M_a$,简写作 $a$ ($a\notin \{\leftarrow, \rightarrow\}$) + - Hand-moving Machines: $M_{\leftarrow}$,简写作 $L$;$M_{\rightarrow}$,简写作 $R$ + + +根据基本图灵机可以组合得到复杂图灵机。中间的基本图灵机把停机状态改成普通状态,然后根据基本图灵机之间的关系在原停机状态和指向的图灵机初态之间建立转移。 + + +- $R^2$: 右移两格 +- $R_{\sqcup}$: 移到右侧第一个空格$\sqcup$ +- $R_{\overline{\sqcup}}$: 移到右侧第一个非空格字符 +- 同理$L_{\sqcup}, L_{\overline{\sqcup}}$ + +
+ 4.1 +
+ +\automata[->,>={Stealth[round]},auto,node distance=4em,on grid,semithick,inner sep=2pt,bend angle=50,initial text=,every state/.style={draw=none,minimum size=0pt,inner sep=1pt}] + \node[initial,state] (q_0) {$L\sqcup$}; + \node[state] at (1, 0) (q_1) {$R$}; + \node[state] at (3, 0) (q_2) {$\sqcup LaR$}; + \node[state] at (1, -1) (q_3) {$L$}; + + \path + (q_0) edge node {} (q_1) + (q_1) edge node {$a\neq\sqcup$} (q_2) + edge node {$\sqcup$} (q_3) + (q_2) edge [bend right] node {} (q_1); + +上图中,从左到右分别为 $R_{\sqcup}, R_{\overline{\sqcup}}, L_{\sqcup}, L_{\overline{\sqcup}}$。注意,第一步的右移/左移是必然发生的,也就是至少移动一步。 + +!!! example "拷贝机 The Copying Machine" + The Copying Machine, 从 $\underline{\sqcup} w\sqcup$ 得到 $\sqcup w\sqcup w \underline{\sqcup}$ (要求 $w$ 中无 $\sqcup$) + + $$ + \begin{aligned} + & =———————————&=\\ + & \downarrow &\uparrow \\ +\rightarrow & R \xrightarrow[a\neq \sqcup]{} \underbrace{\sqcup}_{\text{标记}} R_{\sqcup}^2aL_ {\sqcup}^2\underbrace{a}_{\text{恢复}} ——&=\\ + & \downarrow\sqcup\\ + & R_{\sqcup} + \end{aligned} + $$ + + +!!! example "左移机 The Left-shifting Machine" + The Left-shifting Machine $S_{\leftarrow}$,从 $\sqcup w\underline{\sqcup}$ 得到 $w \underline{\sqcup}$ (要求 $w$ 中无 $\sqcup$) + + $$ + \begin{aligned} + & =———————&=\\ + & \downarrow &\uparrow \\ +\rightarrow L_{\sqcup} \rightarrow& R \xrightarrow[a\neq \sqcup]{} \sqcup LaR_ {\sqcup} ——&=\\ + & \downarrow\sqcup\\ + & L_{\sqcup} + \end{aligned} + $$ + + +我认为,这里用 $\sqcup$ 标记没有必要,每次都只走一步感觉就够了。 + +!!! example "example" + 擦除右侧第一段连续的 $a$ + + $$ + \begin{aligned} + & =——&=\\ + & \downarrow &\uparrow \\ +\rightarrow & R \xrightarrow[a]{} \sqcup —&= + \end{aligned} + $$ + + +## Computing with Turing Machines} + +为方便,定义$\Sigma_0=\Sigma-\{\sqcup, \triangleright\}$。 + +### Deciding Languages} + +图灵机可用于判定是否有$w\in L$。给定TM $M=(K, \Sigma, \delta, s, H)$,$w\in \Sigma_0^*$,为判定$w\in L$,需要有初始格局$(s, \triangleright\underline{\sqcup}w)$。当然,要求$w$中没有$\sqcup$。 + +令$H=\{y, n\}$,然后根据格局中状态为$y, n$分别定义\textbf{接受格局}(accepeting configuration)和\textbf{拒绝格局}(rejecting configuration) + +根据初始格局运行后得到接受/拒绝格局判断$M$\textbf{接受/拒绝}$w$。 + +$M$\textbf{判定}(decide)$L\subseteq \Sigma_0^*$当且仅当$\forall w\in \Sigma_0^*$有 + +- $w\in L\iff L$接受$w$ +- $w\notin L\iff L$拒绝$w$ + +当存在 TM 判定 $L$ 时,称 $L$ 是递归 (recursive) 的。递归、可判定、可计算是一致的概念。 + +!!! example "example" + 写出判定 $L=\{a^nb^nc^n:\; n\geqslant 0\}$ 的 TM。 + +
+ 4.2 +
+ + +### Computing Functions} + +给定 TM $M=(K, \Sigma, \delta, s, H)$,$w\in \Sigma_0^*$。 + +设 $M$ 对输入 $w$ 停机,输出为 $y\in \Sigma_0^*$,即有 + +$$ + (s, \triangleright\underline{\sqcup}w) + \vdash_M^* + (h, \triangleright\underline{\sqcup}y) +$$ + + +这样,$y$ 就可以表示为 $M$ 对输入 $w$ 的输出 $M(w)$。 + +!!! info "definition" + $M$ compute $f$ + + - $f:\; \Sigma_0^*\to \Sigma_0^*$,则 $M$ **计算** $f$ 当且仅当 + + $$ + \forall w\in \Sigma_0^*,\quad M(w)=f(w) + $$ + + - $f:\; \N^k\to \N$,则 $M$ **计算** $f$ 当且仅当 $\forall w_1,\cdots,w_k\in 0\cup 1\{0, 1\}^*$ + + $$ + \operatorname{num}(M(w_1;\cdots;w_k))=f(\operatorname{num}(w_1),\cdots, \operatorname{num}(w_k)) + $$ + + $\operatorname{num}$是将二进制串转换为十进制数的函数。 + + 可计算 $\iff$ 递归,存在 TM $M$ 计算 $f$ 时称 $f$ **递归**。 + +!!! example "example" + $\kappa:\; \Sigma^*\to\Sigma^*,\kappa(w)=ww$,则$\kappa$被$CS_{\leftarrow}$计算,即拷贝+左移。 + +!!! example "example" + 设计图灵机计算后继函数(successor function): $\operatorname{suc}(n)=n+1$ + +
+ 4.3 +
+ +### Semideciding and Properties} + +给定 TM $M=(K, \Sigma, \delta, s, H)$,$H$ 不一定就是 $\{y, n\}$ 了。 + +!!! info "definition" + 若 $\forall w\in \Sigma_0^*$, + + $$ + w\in L\iff M\text{ 在输入 }w\text{ 上停机} + $$ + + 则称 $M$ **半判定** (semidecide)$L$。 + + 存在 TM 半判定 $L$ 时,称 $L$ 是**递归可枚举** (recursive enumerable, r.e.)的。 + +!!! tip "Remark" + 半判定(递归可枚举)情形下,TM 只能判定 $w\in L$ 情况(能停机),而 $w\notin L$ 情况通过不停机逃避了回答。现在给不停机一个符号表示: + + $$ + M(w)=\uparrow\iff M\text{ 对输入 }w\text{ 不停机} + $$ + + 即 + + $$ + M(w)=\uparrow\iff w\notin L + $$ + + +!!! example "example" + 对 $L=\{w\in\{a, b\}^*:\; w\text{含有至少 1 个 }a\}$,可以设计图灵机遇到非 $a$ 字符永远右移,遇到 $a$ 停机,这样就半判定了。 + + 设计永远向右寻找空格的图灵机,以及永远循环的图灵机(如卡在左端标记和右移的a之间的图灵机),都可以让图灵机永不停机。 + +!!! abstract "theorem" + 递归语言的**交、并、补**还是递归语言。递归语言一定是递归可枚举语言。 + +!!! tip "Remark" + 关于补的封闭,将 $y, n$ **交换**就可证明。交、并的封闭性需要后续的图灵机模拟和多带图灵机的知识。 + + 证明递归语言也是递归可枚举的,只需要把$n$从停机状态中删去,到达$n$状态后让图灵机一直复写(循环)就行。 + + 递归可枚举语言关于**交、并**封闭,但是**关于补不封闭**。如果递归可枚举语言的补语言也是递归可枚举语言,那么可以证明他们都是递归语言。 + +## Extensions of the Turing Machine + +### $k$-tape TM + +简单地拓展图灵机,考虑多带、双向无限带、多头、多维带、不确定性。事实上这些都没能真正扩展图灵机的计算能力,但是提供了 feature,对同一问题可以做出简化。后续将主要阐述多带图灵机和不确定性图灵机。 + +$k$ 带图灵机 ($k$-tape TM) 关键的是 $\delta$: $(K-H)\times \Sigma^k\to K\times(\Sigma\cup \{\leftarrow, \rightarrow\})^k$ + +格局也变化为$\in K\times(\triangleright \Sigma^*\times(\Sigma^*(\Sigma-\{\sqcup\})\cup \{e\}))^k$ + +我们规定输入和输出都在第一条带,其他带起始全空,停机时忽略带上内容。 + +!!! example "example" + 拷贝机。目标:$\triangleright \underline{\sqcup}w\sqcup \to \triangleright \sqcup w\sqcup w \underline{\sqcup}$。2-tape 图灵机比较自然。 + +2-tape 拷贝机 + +
+ 4.4 +
+ +- 1 带到 2 带同位置拷贝 $w$ +- 2 带移动读写头到最左 $\sqcup$ +- 2 带到 1 带错位拷贝 $w$ + + + + +!!! example "example" + 计算 $x+y, x, y\in 0\cup 1\{0, 1\}^*$。 + +选用分号分割输入的 $x, y$。 + + + +下图最后细节上可能不太对,但是整体思路是对的。 + +
+ 4.5 +
+ +2-tape $x+y$ + +### $k$-tape TM/Standard TM Equivalence} + +$k$-tape TM $M=(K, \Sigma, \delta, s, H)$, $\exists$ STM $M'=(K', \Sigma', \delta', s', H)$, $\Sigma\subseteq \Sigma'$,满足 + +- 对相同的输入,$M, M'$能到达相同的停机状态,且输出相同。 +- $M$对输入$x$在$t$步后停机,则$M'$对$x$停机前所需步数为$O(t(|x|+t))$ + + +把$M'$的单带分割为$2k$份,记录$k$条带的内容和$k$个读写头的位置。 + +$\Sigma'=\Sigma\cup (\Sigma\times\{0, 1\})^k$,左端标记和空格依然存在,中间都是$2k$元组。 + +
+ 4.6 +
+ +2-tape TM $\to$ STM + + +- 初态:写$\triangleright$, 然后$(\sqcup, 1, \cdots, \sqcup, 1)$,接着$(a, 0, \sqcup, 0, \cdots, \sqcup, 0)$,直到第1条带上$a$到达最右侧的非$\sqcup$,后续就写$\sqcup$。 +- 模拟$M$计算:每步模拟开始时带头总在右侧第一个“真正空格”$\sqcup$处(下称“右标记”) + + - \textbf{向左扫描全带},获取$k$带带头信息。随后\textbf{返回右标记},通过\textbf{改变状态}反映$k$带上的字符内容。 + - 根据$M$的模拟结果更新$k$带 + +- 停机格局:$2k$元组只取第1元,带头和状态与$M$保持一致 + + +$M'$需要的步数为$O(t(|x|+t))$,因为 + +- 初始化需要$O(|x|)$ +- $M$上有$2k$元组的部分长度,初始为$|x|+2$,后续模拟的每步长度增加不超过1,则$t$步时长度至多为$|x|+2+t$,因此$M$的每步可以用$M'$的$O(|x|+t)$步模拟 + + +简单提一提其他图灵机扩展: + +- Two-way infinite tape: 从某处切开,可以被2-tape TM模拟 +- Multiple heads: 可以用$k$-tape TM模拟,除了第1条带其他带只用来记录带头位置。 +- Two dimensional tape: “带”变成了无穷二维网格。有利于解决铺砖问题等,但是也可以被STM模拟。 + + +## Nondeterministic Turing Machines + +### NTM + +非确定性图灵机(nondeterministic Turing Machine, NTM)$M=(K, \Sigma, \Delta, s, H)$的关键是从STM的$\delta$变成了$\Delta\subseteq ((K-H)\times \Sigma)\times(K\times(\Sigma\cup\{\leftarrow, \rightarrow\}))$,即\textbf{函数}到\textbf{关系}。 + +$\vdash_M, \vdash_M^*$定义类似STM,但$\vdash_M$不一定单值。 + +!!! info "definition" + 考虑 NTM $M=(K, \Sigma, \Delta, s, H)$, $\Sigma_0=\Sigma-\{\triangleright, \sqcup\}$ + + - $M$\textbf{接受}$w\in \Sigma_0^*$: 指能对$w$停机,即$\exists h\in H, a\in \Sigma, u, v\in \Sigma^*\text{ s.t. }$ + + $$ + (s, \triangleright\underline{\sqcup}w)\vdash_M^* (h, u\underline{a}v) + $$ + + - $M$\textbf{半判定}$L\subseteq \Sigma_0^*$:属于$L$就停机,即$\forall w\in \Sigma_0^*$有 + + $$ + w\in L\iff M\text{接受}w + $$ + + - $M$\textbf{判定}$L\subseteq \Sigma_0^*$: 属于$L$就停机于$y$,要求有限步停机,$H=\{y, n\}$,$\exists \; a\in \Sigma, u, v\in \Sigma^*$ + + $$ + w\in L \iff + (s, \triangleright\underline{\sqcup}w)\vdash_M^* (y, u\underline{a}v) + $$ + + - $M$\textbf{计算}$f: \Sigma_0^*\to \Sigma_0^*$: 要求有限步停机,且输出就是$v=f(w)$,即 + + $$ + (s, \triangleright\underline{\sqcup}w)\vdash_M^* (h, u\underline{a}v)\iff + ua=\triangleright \sqcup, v=f(w) + $$ + + - \textbf{有限步停机}的刻画: $\exists\; N(M, w)\in \N\text{ s.t. }$不存在格局$C$满足$(s, \rightarrow\underline{\sqcup}w)\vdash_M^N C$ + + +!!! tip "Remark" + 半判定时,只要有一种停机格局就行;判定时,只要有一种达到$y$就行。但是计算$f$时要求所有可能的计算的输出结果一致。 + +!!! example "example" + 设计 NTM 半判定$C=\{\operatorname{num}(p\cdot q):\; p,q\geqslant 2\}$(合数集) + +NTM可以随机选择$p, q$尝试$p, q$相乘是否等于该合数,如果是合数就能在有限步内终止。当然,如果随机选择变成按序遍历,就可以实现判定。 + +### NTM/STM Equivalence + +!!! abstract "theorem" + 如果NTM $M$半判定/判定一个语言,或能计算一个函数,那么存在一个STM $M'$也半判定/判定同一语言,也能计算同一函数。 + +!!! general "proof" + 下面以半判定为例进行证明。注意到$\Delta\in ((K-H)\times \Sigma)\times(K\times (\Sigma\cup \{\leftarrow, \rightarrow\}))$,因此给定当前的状态和读到的字符,次态和操作的不同分支最多只有$|K|\cdot(|\Sigma|+2)$种。 + + 因此可以按照层序遍历整个NTM可能产生的计算树,按照字典序遍历同样计算步数能够达到的格局,观察是否能够停机,来实现半判定的效果。 + + 遍历所有可能的状态-输入二元组($q$, $a$),可以确定最大可能的转移数量为$r\leqslant |K|(|\Sigma|+2)$。那么给定$(q, a)$,可以通过编码使得一个1位$r$进制数能够表示下一步可能的转移分支。 + + 定义选好分支的NTM $M$的确定性版本$M_d$:$M_d$是2-tape TM,和$M$有相同的状态。$M_d$1带就是$M$的带,2带上面一个$n$位$r$进制数,表示第1到第n步要选择的转移分支。 + + 接下来构建3-tape TM $M'$(由于3-tape TM和STM等价,所以直接构造3-tape TM)。1带放置$M$的最初输入,整个模拟过程中保持不变。2带是模拟带,3带是分支选择带,2、3带相当于$M_d$。 + +
+ 4.7 +
+ + 3-tape TM $M'$ 模拟 NTM + + $M'$按照字典序更改3带,尝试1步计算到$n$步计算的所有$M_d$,从而层序遍历$M$的计算树。每个$M_d$模拟完毕停机时,擦除2带,拷贝1带到2带,然后更改3带以准备下一个$M_d$的模拟。 + +
+ 4.8 +
+ + 3-tape TM $M'$ 模拟 NTM + + 如果NTM $M$能停机,那么它一定在某个计算树$n$步(取最小的$n$)的分支停机,那么$M'$模拟的某一个选择了$n$个分支的$M_d$也一定能够模拟进入停机状态,从而实现半判定。 + + 但是 $M$ 需要$n$步停机时,$M'$ (worst case)就需要 $r + r^2+\cdots +r^n$步停机,即 $M$ 步数的指数倍。P类和NP类的差别可见一斑。 + +## Grammars} + +### Grammar} + +!!! info "definition" + 文法(grammar, or unrestricted grammar)是一个quadruple $G=(V, \Sigma, R, S)$,和CFG不同之处只有$R\subseteq (V^*(V-\Sigma)V^*)\times V^*$。 + +!!! tip "Remark" + $\to G, \Rightarrow_G, \Rightarrow_G^*, L(G)$以及推导的概念都类似CFG。CFG就是一种文法。 + +!!! example "example" + 以下给出的 $G=(V, \Sigma, R, S)$ 生成语言 $\{a^nb^nc^n: n\geqslant 1\}$ + +
+ 4.9 +
+ + grammar for $\{a^nb^nc^n: n\geqslant 1\}$ + +### Grammar/TM Equivalence + +!!! abstract "theorem" + $L$ 由文法 $G$ 生成 $\iff$ $L$ 递归可枚举 + +!!! general "proof" + $\Rightarrow$: 构造3-tape TM。1带输入$w$,保持不变。2带模拟$G$从$S$开始的推导。3带包含的是状态转移的分支选择信息。 + + 每一步推导有$|R|+1$种可能的状态转移,其中$|R|$种都来自于$R$,如果选择这$R$种中的一种,就在2带中搜索可以应用该规则的地方,“非确定性”地应用该规则进行推导。 + + 第$|R|+1$种选择则是判断当前2带模拟结果是否与1带一致,若一致则停机并接受。 + + $\Leftarrow$: 指定$L$被$M-(K, \Sigma, \delta, s, \{h\})$半判定。令$\Sigma\cap K=\emptyset$,且都不包含右端标记$\triangleleft$(新引入的一个记号)。再规定停机格局总是$(h, \triangleright\underline{\sqcup})$(即原停机后还要再删除带上内容,并到达唯一停机状态$h$)。 + + 这样,基于从规定的停机格局到初始格局的\textbf{反向计算}(backwards computation)方法,构造生成$M$半判定的语言$L\subseteq \Sigma_0^*=(\Sigma-\{\sqcup, \triangleright\})^*$的文法$G=(V, \Sigma_0, R, S)$。($V=\Sigma\cup K\cup \{S, \triangleleft\}$) + + 反向计算的关键是用字符串模拟格局,即用$\triangleright uaqw\triangleleft$模拟$(q, \triangleright u\underline{a}w)$。这里利用了状态$q$标识了带头所在的位置$q$。$\forall q\in K, a\in \Sigma$,根据$\delta(q, a)$,$G$建立如下规则: + + - 写操作:$\exists p\in K, b\in \Sigma \text{ s.t. } \delta(q, a)=(p, b)$, 有$bp\to aq$ + - 右移操作:$\exists p\in K \text{ s.t. } \delta(q, a)=(p, \rightarrow)$, 有$abp\to aqb$ + - 左移操作:$\exists p\in K \text{ s.t. } \delta(q, a)=(p, \leftarrow)$,要考虑删除空格操作 + + $$ + \begin{cases} + pa\to aq, & a\neq \sqcup\\ + p\sqcup b\to \sqcup qb(\forall b\in \Sigma), p\triangleleft\to \sqcup q\triangleleft, &a=\sqcup + \end{cases} + $$ + + - 初始和终止:$S\to \triangleright\sqcup h\triangleleft$, $\triangleright\sqcup s\to e$, $\triangleleft\to e$ + + +Claim: 对$M$ 的$\forall$格局$(q_1, u_1\underline{a_1}w_1), (q_2, u_2\underline{a_2}w_2)$,有 + +$$ + (q_1, u_1\underline{a_1}w_1)\vdash_M(q_2, u_2\underline{a_2}w_2) + \iff + u_2a_2q_2w_2\triangleleft \Rightarrow_G u_1a_1q_1w_1\triangleleft +$$ + + +有这个claim成立就能证明构造等价了。证明见课本练习。 + +但至此文法生成的语言等价的是递归可枚举语言,与递归函数等价的是文法可计算函数。 +!!! info "definition" + $G=(V, \Sigma, R, S)$是文法,$f: \Sigma^*\to\Sigma^*$是函数 + + - $G$\textbf{计算}$f$: $\forall w, v\in \Sigma^*$,有 + + $$ + SwS\Rightarrow_G^* v\iff v=f(w) + $$ + + - 当$\exists G$计算$f$时,$f$称为\textbf{文法可计算}(grammatically computable)的。 + + +!!! abstract "theorem" + $f: \Sigma^*\to \Sigma^*$是递归的 $\iff$ $f$是文法可计算的 + +## Numerical Functions} + +### Primitice Recusive Functions} + +!!! info "definition" + 给出三个基本函数 + + - $\forall k\geqslant 0$,定义$k$元\textbf{零函数}($k$-ary zero function) + + $$ + \operatorname{zero}_k(n_1, \cdots, n_k)=0,\quad \forall n_1, \cdots, n_k\in \N + $$ + + - $\forall k\geqslant j>0$,定义第 $j$ 个 $k$ 元**恒等函数**($j$-th $k$-ary identity function) + + $$ + \operatorname{id}_{k, j}(n_1, \cdots, n_k)=n_j,\quad \forall n_1, \cdots, n_k\in \N + $$ + + - 定义**后继函数**(successor function) + + $$ + \operatorname{suc}(n)=n+1,\quad \forall n\in\N + $$ + + + +!!! tip "Remark" + 恒等函数可以理解为投影函数。 + +有两种合成较复杂函数的方法: + +- $\forall k, l \geq 0$, $g: \mathbb{N}^k \rightarrow \mathbb{N}$, $h_1, \ldots, h_k$ 是 $l$元函数。则$g$和$h_1, \ldots, h_k$的\textbf{复合}(composition)为如下$l$元函数 + + $$ + f(n_1,\cdots, n_l)=g(h_1(n_1,\cdots, n_l), \cdots, h_k(n_1,\cdots, n_l)) + $$ + +- $\forall k \geq 0$, $g$是$k$元函数,$h$是$(k+2)$元函数。则用$g, h$\textbf{递归定义}的$k+1$元函数$f$为 + + $$ + \begin{aligned} + & f\left(n_1, \ldots, n_k, 0\right) = g\left(n_1, \ldots, n_k\right) \\ + & f\left(n_1, \ldots, n_k, m+1\right) = h\left(n_1, \ldots, n_k, m, f\left(n_1, \ldots, n_k, m\right)\right) + \end{aligned} + $$ + + + +!!! info "definition" + 原始递归函数(primitive recursive functions)就是基本函数及所有由基本函数通过若干次复合/递归定义得到的函数。 + +!!! example "example" + 下列函数都是原始递归的 + + - $\operatorname{plus}(m, n)=m+n$ + - $\operatorname{mult}(m, n)=m \cdot n$ + - $\exp (m, n)=m^n$ + + +!!! general "proof" + + $$ + \begin{aligned} + & \operatorname{plus}(m, 0)=m \\ + & \operatorname{plus}(m, n+1)=\operatorname{succ}(\operatorname{plus}(m, n)) \\ + & \operatorname{mult}(m, 0)=\operatorname{zero}(m) \\ + & \operatorname{mult}(m, n+1)=\operatorname{plus}(m, \operatorname{mult}(m, n)) \\ + & \exp (m, 0)=\operatorname{succ}(z \operatorname{ero}(m)) \\ + & \exp (m, n+1)=\operatorname{mult}(m, \exp (m, n)) + \end{aligned} + $$ + + +!!! tip "Remark" + 不直接写$\exp (m, 0)=1$是因为还没证明常数函数也是原始递归的。 + +!!! example "example" + 常数函数、符号函数都是原始递归的。常数函数可以通过$\operatorname{zero}$加上若干次$\operatorname{suc}$得到,符号函数则直接定义0的时候是0,正数的时候是1。 + +!!! example "example" + $m\sim n=\max\{m-n, 0\}$也是原始递归的。 + +!!! general "proof" + 定义原始递归的前驱函数(predecessor function): + + $$ + \begin{aligned} + & \mathrm{pred}(0) = 0\\ + & \mathrm{pred}(n+1) = n + \end{aligned} + $$ + + + 根据前驱函数递归定义 + + $$ + \begin{aligned} + & m\sim 0 = m\\ + & m\sim (n+1) = \mathrm{pred}(m\sim n) + \end{aligned} + $$ + + +!!! info "definition" + 原始递归谓词(primitive recursive predicate): 只取值0, 1的原始递归函数 + +!!! example "example" + + - 零判定iszero($n$): iszero(0)=1, iszero(m+1)=0 + - 正判定positive($n$) = sgn($n$) + - 大于等于greater-than-or-equal($m, n$) = iszero($n\sim m$) + - 小于less-than($m, n$) = 1 $\sim$ greater-than-or-equal($m, n$) + - 取反negation: $\neg p(m)=1\sim p(m)$ + - 析取合取disjunction and conjunction: + + $$ + \begin{gathered} + p(m, n) \vee q(m, n) = \text{sgn}(p(m, n) + q(m, n))\\ + p(m, n) \wedge q(m, n) = \text{sgn}(p(m, n)\cdot q(m, n)) + \end{gathered} + $$ + + - 条件语句: $f(n_1,\cdots, n_k)=\begin{cases} + g(n_1,\cdots, n_k), & p(n_1,\cdots, n_k)\\ + h(n_1,\cdots, n_k), & \neg p(n_1,\cdots, n_k) + \end{cases}$, 因为 + + $$ + f(n_1,\cdots, n_k)= p(n_1,\cdots, n_k)\cdot g(n_1,\cdots, n_k)+ \neg p(n_1,\cdots, n_k)\cdot h(n_1,\cdots, n_k) + $$ + + + +!!! example "example" + 定义 div 和 rem: + + $$ + \begin{aligned} + \text{rem}(0, n) &= 0\\ + \text{rem}(m+1, n) &= \begin{cases} + 0, &\text{equal}(\text{rem}(m, n), \text{pred}(n))\\ + \text{rem}(m, n)+1, & \text{otherwise} + \end{cases}\\ + \text{div}(0, n) &= 0\\ + \text{div}(m+1, n) &= \begin{cases} + \text{div}(m, n) + 1, & \text{equal}(\text{rem}(m, n), \text{pred}(n))\\ + \text{div}(m, n), & \text{otherwise} + \end{cases} + \end{aligned} + $$ + + +!!! tip "Remark" + equal($m, n$) = iszero($n\sim m$) $\wedge$ iszero($m\sim n$) + +!!! example "example" + digit($m, n, p$): $n$在$p$进制表示下的第$m$位(低位向高位数) + + $$ + \text{digit}(m, n, p)=\text{div}(\text{rem}(n, p^ m), p^{m\sim 1}) + $$ + + + 特别地,判断奇数的odd$(n)$=digit($1, n, 2$)。 + +!!! example "example" + $f(n, m)$原始递归,则sum + + $$ + \text{sum}_f(n, m)=\sum_{k=0}^m f(n, k) + $$ + + 也原始递归。因为它被递归定义为 + + $$ + \begin{aligned} + \text{sum}(n, 0) &= 0\\ + \text{sum}(n, m+1) &= \text{sum}_f(n, m) + f(n, m+1) + \end{aligned} + $$ + + + 同理,mult $_f(n, m)=\displaystyle\prod_{k=0}^m f(n, k)$ 也是原始递归的。 + +!!! example "example" + 由于 + + $$ + \begin{gathered} + \exists t_{(\leqslant m)}p(n_1, \cdots, n_k, t)\iff \sum_{t=0}^m p(n_1, \cdots, n_k, t)\neq 0\\ + \forall t_{(\leqslant m)}p(n_1, \cdots, n_k, t)\iff \prod_{t=0}^m p(n_1, \cdots, n_k, t)\neq 0 + \end{gathered} + $$ + + + 所以考虑$\leqslant m$范围的存在和任意谓词也是原始递归的。 + +!!! example "example" + 有了存在和任意,很多就好搞了。 + + - 整除$y | x\iff \exists t_{(\leqslant x)}(y\cdot t=x)$ + - 质数判定prime$(x)\iff(x\geqslant 2)\wedge\forall t_{( str: + return re.sub(f"\\b{word}\\b", target, string) + +def replace_indented_block_start_with_options(target, handle, string): + return re.sub(rf"(?P[ \t]*?)({target}(\[(?P.*)\])?(\\zoom{{(?P.*)}})?.*\n(?P(((?P=leading)(\t|( )).*)|\n)*))", handle, string) + +def get_indentation_level(str): + return (len(str) - len(str.lstrip())) // 4 + +def _set_line_indentation_level(line, level): + prev_level = get_indentation_level(line) + return(" " * 4 * (prev_level + level)) + line.lstrip() + +def return_to_indentation_level(str, level): + lines = str.split("\n") + return "\n".join([_set_line_indentation_level(line, level) for line in lines]) diff --git a/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/plugin.py b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/plugin.py new file mode 100644 index 0000000..9f0e2aa --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/plugin.py @@ -0,0 +1,83 @@ +import os +import re + +from mkdocs.plugins import BasePlugin +from mkdocs.structure.pages import Page +from mkdocs.config import config_options +from mkdocs.utils import copy_file + +from typing import Dict, Any + +from .renderer import TikZAutomataRenderer +from .markdown_utils import replace_indented_block_start_with_options, get_indentation_level + +PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__)) + +class TikzAutomataPlugin(BasePlugin): + config_scheme = ( + ("enabled", config_options.Type(bool, default=True)), + ("cachefile", config_options.Type(bool, default=True)), + ) + + enabled = True + + def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: + if not self.enabled: + return config + if not self.config.get('enabled'): + return config + config["extra_css"] = ["css/svg_extra.css"] + config["extra_css"] + + def on_page_markdown( + self, markdown: str, page: Page, config: config_options.Config, files, **kwargs + ) -> str: + if not self.enabled: + return markdown + if not self.config.get("enabled"): + return markdown + def _replace_automata(matched: re.Match) -> str: + options = matched.group("options") + contents = matched.group("contents") + zoom = matched.group("zoom") + first_line_indentation_level = get_indentation_level(matched.group("contents")) + + # print(first_line_indentation_level) + + contents = [i for i in contents.splitlines()] + + contents_remain = [] + + for idx, i in enumerate(contents): + if get_indentation_level(i) < first_line_indentation_level: + contents_remain = contents[idx:] + contents = contents[:idx] + break + + contents = "\n".join(contents) + tikzcd = TikZAutomataRenderer(options, contents) + + # The string should not be splitted into lines, since markdown parser won't recognize it + svg_str = "".join(tikzcd.write_to_svg(self.config.get("cachefile")).replace("", "").splitlines()) + + # bolden the stroke + svg_str = svg_str.replace("stroke-width='0.6'", "stroke-width='0.7'") + svg_str = svg_str.replace("stroke='none'", "stroke='#000' stroke-width='0.2'") + + return matched.group("leading") + f"
{svg_str}
" + "\n" + "\n".join(contents_remain) + + markdown = replace_indented_block_start_with_options(r"(? None: + if not self.enabled: + return + if not self.config.get('enabled'): + return + files = ["css/svg_extra.css"] + for file in files: + dest_file_path = os.path.join(config["site_dir"], file) + src_file_path = os.path.join(PLUGIN_DIR, file) + assert os.path.exists(src_file_path) + copy_file(src_file_path, dest_file_path) \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/renderer.py b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/renderer.py new file mode 100644 index 0000000..f9f2819 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/renderer.py @@ -0,0 +1,59 @@ +from .tex import TeXWriter + +from hashlib import sha256 +from mkdocs.utils import log +import os + +class TikZAutomataRenderer: + def __init__(self, options: str, contents: str) -> None: + self.options = options + self.contents = contents + + def write_to_svg(self, cachefile: bool) -> str: + filename = sha256(self.contents.encode()).hexdigest() + + if cachefile == True: + try: + os.chdir("cache") + except OSError: + log.error("[tikzautomata] cache directory not found!") + + if cachefile == True and os.path.exists(f"{filename}.svg"): + log.debug("[tikzautomata] load from existing file...") + with open(f"{filename}.svg", "r", encoding="utf-8") as f: + svg_str = f.read(None) + os.chdir("..") + return svg_str + + writer = TeXWriter() + writer.config.preamble = r''' +\documentclass[dvisvgm]{standalone} +\usepackage{tikz} + +\usetikzlibrary {arrows.meta,automata,positioning,shapes.geometric} + ''' + begin_command = r"\begin{tikzpicture}[%s]" % self.options if self.options else r"\begin{tikzpicture}[->,>={Stealth[round]},shorten >=1pt,auto,node distance=2cm,on grid,semithick,inner sep=2pt,bend angle=50,initial text=]" + writer.create_tex_file("\n".join(( + begin_command, + self.contents.strip(), + r'''\end{tikzpicture} +''' + )), filename) + + writer.create_svg_from_tex(filename) + + with open(f"{filename}.svg", "r", encoding="utf-8") as f: + svg_str = f.read(None) + + # clean up + if cachefile == False : + try: + os.remove(filename + ".svg") + except FileNotFoundError: + pass + + os.chdir("..") + + return svg_str + + \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/tex.py b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/tex.py new file mode 100644 index 0000000..dd2fb80 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/build/lib/mkdocs_tikzautomata_plugin/tex.py @@ -0,0 +1,84 @@ +import os +import re + +from mkdocs.utils import log + +class TeXError(BaseException): + pass + +class TeXWriterConfig: + def __init__(self) -> None: + self.compiler = "xelatex" + self.preamble = "" + +class TeXWriter: + def __init__(self, config=TeXWriterConfig()) -> None: + self.config = config + + def create_tex_file(self, content: str, tex_name: str) -> None: + """ + Write content into tex_name, with preamble set by config + """ + full_tex = "\n\n".join(( + self.config.preamble, + "\\begin{document}", + content, + "\\end{document}" + )) + "\n" + + try: + with open(f"{tex_name}.tex", "w", encoding="utf-8") as tex_file: + tex_file.write(full_tex) + except OSError: + log.error("[tikzautomata] unable to create tex file!") + + def create_svg_from_tex(self, tex_name: str) -> None: + """ + Generate svg from tex file + """ + if self.config.compiler == "xelatex": + program = "xelatex -no-pdf" + else: + raise NotImplementedError(f"Compiler {self.config.compiler} is not implemented!") + + log.info(f"rendering {tex_name}.svg") + + # use compiler to transform tex to pdf + tex2xdv_cmd = " ".join(( + program, + "-halt-on-error", + "-interaction=batchmode", + f"\"{tex_name}.tex\"", + ">", + os.devnull + )) + log.debug(f"running {tex2xdv_cmd}") + if os.system(tex2xdv_cmd): + log.error( + "LaTeX Error! Not a worry, it happens to the best of us." + ) + raise TeXError("LaTeX Error! Look into log file for detail") + + # use dvisvgm to transform xdv to svg + xdv2svg_cmd = " ".join(( + "dvisvgm", + f"\"{tex_name}.xdv\"", + "-n", + "-v 0", + f"-o \"{tex_name}.svg\"", + ">", + os.devnull + )) + log.debug(f"running {xdv2svg_cmd}") + if os.system(xdv2svg_cmd): + log.error( + "dvisvgm Error!" + ) + raise TeXError("dvisvgm Error!") + + # clean up + for ext in (".log", ".aux", ".xdv", ".tex"): + try: + os.remove(tex_name + ext) + except FileNotFoundError: + pass \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/PKG-INFO b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/PKG-INFO new file mode 100644 index 0000000..019bad5 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 2.1 +Name: mkdocs-tikzautomata-plugin +Version: 0.0.1 +Author: TonyCrane +Author-email: me@tonycrane.cc +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Requires-Python: >=3.5 diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/SOURCES.txt b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/SOURCES.txt new file mode 100644 index 0000000..4dd0a49 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/SOURCES.txt @@ -0,0 +1,13 @@ +README.md +setup.py +mkdocs_tikzautomata_plugin/__init__.py +mkdocs_tikzautomata_plugin/markdown_utils.py +mkdocs_tikzautomata_plugin/plugin.py +mkdocs_tikzautomata_plugin/renderer.py +mkdocs_tikzautomata_plugin/tex.py +mkdocs_tikzautomata_plugin.egg-info/PKG-INFO +mkdocs_tikzautomata_plugin.egg-info/SOURCES.txt +mkdocs_tikzautomata_plugin.egg-info/dependency_links.txt +mkdocs_tikzautomata_plugin.egg-info/entry_points.txt +mkdocs_tikzautomata_plugin.egg-info/requires.txt +mkdocs_tikzautomata_plugin.egg-info/top_level.txt \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/dependency_links.txt b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/entry_points.txt b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/entry_points.txt new file mode 100644 index 0000000..55f07b9 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[mkdocs.plugins] +tikzautomata = mkdocs_tikzautomata_plugin.plugin:TikzAutomataPlugin diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/requires.txt b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/requires.txt new file mode 100644 index 0000000..016bb16 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/requires.txt @@ -0,0 +1 @@ +mkdocs diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/top_level.txt b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/top_level.txt new file mode 100644 index 0000000..a8b96ee --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin.egg-info/top_level.txt @@ -0,0 +1 @@ +mkdocs_tikzautomata_plugin diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/__init__.py b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/css/svg_extra.css b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/css/svg_extra.css new file mode 100644 index 0000000..8faf97b --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/css/svg_extra.css @@ -0,0 +1,15 @@ +path[stroke="#000"], g[stroke="#000"] { + stroke: var(--md-default-fg-color); +} + +path[stroke="#fff"], g[stroke="#fff"] { + stroke: var(--md-default-bg-color); +} + +path[fill="#fff"] { + fill: var(--md-default-bg-color); +} + +path:not([fill]) { + fill: var(--md-default-fg-color); +} \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/markdown_utils.py b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/markdown_utils.py new file mode 100644 index 0000000..7392ab7 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/markdown_utils.py @@ -0,0 +1,18 @@ +import re + +def replace_standalone_words(word: str, target: str, string: str) -> str: + return re.sub(f"\\b{word}\\b", target, string) + +def replace_indented_block_start_with_options(target, handle, string): + return re.sub(rf"(?P[ \t]*?)({target}(\[(?P.*)\])?(\\zoom{{(?P.*)}})?.*\n(?P(((?P=leading)(\t|( )).*)|\n)*))", handle, string) + +def get_indentation_level(str): + return (len(str) - len(str.lstrip())) // 4 + +def _set_line_indentation_level(line, level): + prev_level = get_indentation_level(line) + return(" " * 4 * (prev_level + level)) + line.lstrip() + +def return_to_indentation_level(str, level): + lines = str.split("\n") + return "\n".join([_set_line_indentation_level(line, level) for line in lines]) diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/plugin.py b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/plugin.py new file mode 100644 index 0000000..9f0e2aa --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/plugin.py @@ -0,0 +1,83 @@ +import os +import re + +from mkdocs.plugins import BasePlugin +from mkdocs.structure.pages import Page +from mkdocs.config import config_options +from mkdocs.utils import copy_file + +from typing import Dict, Any + +from .renderer import TikZAutomataRenderer +from .markdown_utils import replace_indented_block_start_with_options, get_indentation_level + +PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__)) + +class TikzAutomataPlugin(BasePlugin): + config_scheme = ( + ("enabled", config_options.Type(bool, default=True)), + ("cachefile", config_options.Type(bool, default=True)), + ) + + enabled = True + + def on_config(self, config: config_options.Config, **kwargs) -> Dict[str, Any]: + if not self.enabled: + return config + if not self.config.get('enabled'): + return config + config["extra_css"] = ["css/svg_extra.css"] + config["extra_css"] + + def on_page_markdown( + self, markdown: str, page: Page, config: config_options.Config, files, **kwargs + ) -> str: + if not self.enabled: + return markdown + if not self.config.get("enabled"): + return markdown + def _replace_automata(matched: re.Match) -> str: + options = matched.group("options") + contents = matched.group("contents") + zoom = matched.group("zoom") + first_line_indentation_level = get_indentation_level(matched.group("contents")) + + # print(first_line_indentation_level) + + contents = [i for i in contents.splitlines()] + + contents_remain = [] + + for idx, i in enumerate(contents): + if get_indentation_level(i) < first_line_indentation_level: + contents_remain = contents[idx:] + contents = contents[:idx] + break + + contents = "\n".join(contents) + tikzcd = TikZAutomataRenderer(options, contents) + + # The string should not be splitted into lines, since markdown parser won't recognize it + svg_str = "".join(tikzcd.write_to_svg(self.config.get("cachefile")).replace("", "").splitlines()) + + # bolden the stroke + svg_str = svg_str.replace("stroke-width='0.6'", "stroke-width='0.7'") + svg_str = svg_str.replace("stroke='none'", "stroke='#000' stroke-width='0.2'") + + return matched.group("leading") + f"
{svg_str}
" + "\n" + "\n".join(contents_remain) + + markdown = replace_indented_block_start_with_options(r"(? None: + if not self.enabled: + return + if not self.config.get('enabled'): + return + files = ["css/svg_extra.css"] + for file in files: + dest_file_path = os.path.join(config["site_dir"], file) + src_file_path = os.path.join(PLUGIN_DIR, file) + assert os.path.exists(src_file_path) + copy_file(src_file_path, dest_file_path) \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/renderer.py b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/renderer.py new file mode 100644 index 0000000..f9f2819 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/renderer.py @@ -0,0 +1,59 @@ +from .tex import TeXWriter + +from hashlib import sha256 +from mkdocs.utils import log +import os + +class TikZAutomataRenderer: + def __init__(self, options: str, contents: str) -> None: + self.options = options + self.contents = contents + + def write_to_svg(self, cachefile: bool) -> str: + filename = sha256(self.contents.encode()).hexdigest() + + if cachefile == True: + try: + os.chdir("cache") + except OSError: + log.error("[tikzautomata] cache directory not found!") + + if cachefile == True and os.path.exists(f"{filename}.svg"): + log.debug("[tikzautomata] load from existing file...") + with open(f"{filename}.svg", "r", encoding="utf-8") as f: + svg_str = f.read(None) + os.chdir("..") + return svg_str + + writer = TeXWriter() + writer.config.preamble = r''' +\documentclass[dvisvgm]{standalone} +\usepackage{tikz} + +\usetikzlibrary {arrows.meta,automata,positioning,shapes.geometric} + ''' + begin_command = r"\begin{tikzpicture}[%s]" % self.options if self.options else r"\begin{tikzpicture}[->,>={Stealth[round]},shorten >=1pt,auto,node distance=2cm,on grid,semithick,inner sep=2pt,bend angle=50,initial text=]" + writer.create_tex_file("\n".join(( + begin_command, + self.contents.strip(), + r'''\end{tikzpicture} +''' + )), filename) + + writer.create_svg_from_tex(filename) + + with open(f"{filename}.svg", "r", encoding="utf-8") as f: + svg_str = f.read(None) + + # clean up + if cachefile == False : + try: + os.remove(filename + ".svg") + except FileNotFoundError: + pass + + os.chdir("..") + + return svg_str + + \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/tex.py b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/tex.py new file mode 100644 index 0000000..dd2fb80 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/mkdocs_tikzautomata_plugin/tex.py @@ -0,0 +1,84 @@ +import os +import re + +from mkdocs.utils import log + +class TeXError(BaseException): + pass + +class TeXWriterConfig: + def __init__(self) -> None: + self.compiler = "xelatex" + self.preamble = "" + +class TeXWriter: + def __init__(self, config=TeXWriterConfig()) -> None: + self.config = config + + def create_tex_file(self, content: str, tex_name: str) -> None: + """ + Write content into tex_name, with preamble set by config + """ + full_tex = "\n\n".join(( + self.config.preamble, + "\\begin{document}", + content, + "\\end{document}" + )) + "\n" + + try: + with open(f"{tex_name}.tex", "w", encoding="utf-8") as tex_file: + tex_file.write(full_tex) + except OSError: + log.error("[tikzautomata] unable to create tex file!") + + def create_svg_from_tex(self, tex_name: str) -> None: + """ + Generate svg from tex file + """ + if self.config.compiler == "xelatex": + program = "xelatex -no-pdf" + else: + raise NotImplementedError(f"Compiler {self.config.compiler} is not implemented!") + + log.info(f"rendering {tex_name}.svg") + + # use compiler to transform tex to pdf + tex2xdv_cmd = " ".join(( + program, + "-halt-on-error", + "-interaction=batchmode", + f"\"{tex_name}.tex\"", + ">", + os.devnull + )) + log.debug(f"running {tex2xdv_cmd}") + if os.system(tex2xdv_cmd): + log.error( + "LaTeX Error! Not a worry, it happens to the best of us." + ) + raise TeXError("LaTeX Error! Look into log file for detail") + + # use dvisvgm to transform xdv to svg + xdv2svg_cmd = " ".join(( + "dvisvgm", + f"\"{tex_name}.xdv\"", + "-n", + "-v 0", + f"-o \"{tex_name}.svg\"", + ">", + os.devnull + )) + log.debug(f"running {xdv2svg_cmd}") + if os.system(xdv2svg_cmd): + log.error( + "dvisvgm Error!" + ) + raise TeXError("dvisvgm Error!") + + # clean up + for ext in (".log", ".aux", ".xdv", ".tex"): + try: + os.remove(tex_name + ext) + except FileNotFoundError: + pass \ No newline at end of file diff --git a/tools/mkdocs-tikzautomata-plugin/setup.py b/tools/mkdocs-tikzautomata-plugin/setup.py new file mode 100644 index 0000000..241f7b1 --- /dev/null +++ b/tools/mkdocs-tikzautomata-plugin/setup.py @@ -0,0 +1,51 @@ +import os +from setuptools import setup, find_packages + + +def read(fname): + file_path = os.path.join(os.path.dirname(__file__), fname) + with open(file_path) as file: + content = file.read() + return content if content else 'no content read' + + +setup( + name='mkdocs-tikzautomata-plugin', + version='0.0.1', + author='TonyCrane', + author_email='me@tonycrane.cc', + # description='A MkDocs plugin that uses heti to improve Chinese typesetting', + # long_description=read('README.md'), + # long_description_content_type='text/markdown', + # keywords='mkdocs python markdown typesetting', + # url='https://github.com/TonyCrane/mkdocs-heti-plugin', + license='MIT', + python_requires='>=3.5', + install_requires=[ + 'mkdocs', + ], + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10' + ], + entry_points={ + 'mkdocs.plugins': [ + 'tikzautomata = mkdocs_tikzautomata_plugin.plugin:TikzAutomataPlugin' + ] + }, + include_package_data=True, + package_data={ + 'mkdocs_heti_plugin': [ + 'css/*.css' + ] + } +)