<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Ping Zhou’s Personal Blog</title>
    <link>/</link>
    <description>Recent content on Ping Zhou’s Personal Blog</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 18 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>基于 Emacs org-mode 和 hugo 的 Blog 工作流</title>
      <link>/notes/20260418_emacs_org_hugo_workflow/</link>
      <pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
      <guid>/notes/20260418_emacs_org_hugo_workflow/</guid>
      <description>&lt;div id=&#34;outline-container-org2ee75ba&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org2ee75ba&#34;&gt;Introduction&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org2ee75ba&#34;&gt;
&lt;p&gt;
我经常使用 Emacs Org-mode 来写blog。虽然有很多其他笔记工具和方案，但我一直觉得没法取代 Org-mode，比如Org-mode里的 Babel 代码块，内嵌LaTex公式，GUI下图表预览，表格自动求值等功能，实在是很方便。Emacs Org-mode 也天然支持将众多 org 文件笔记通过链接组织成个人知识库。
&lt;/p&gt;

&lt;p&gt;
不过，现有的工作流基于标准的 &lt;code&gt;ox-publish&lt;/code&gt; 导出 HTML，样式略显陈旧，如果支持更美观的主题，需要很多手动配置。
&lt;/p&gt;

&lt;p&gt;
最近对orgmode工作进行了一次升级：引入 Hugo 渲染引擎（搭配 PaperMod 主题），同时保留了纯粹的 Org-mode 创作体验。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org2f869ab&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org2f869ab&#34;&gt;工作流的演进&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org2f869ab&#34;&gt;
&lt;/div&gt;
&lt;div id=&#34;outline-container-org23cf43b&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org23cf43b&#34;&gt;过去：Org -&amp;gt; HTML (ox-publish)&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org23cf43b&#34;&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;优点：流程简单，完全受 Emacs 控制。&lt;/li&gt;
&lt;li&gt;缺点：网页样式极其基本，移动端适配差，缺乏搜索和标签功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7ef909b&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org7ef909b&#34;&gt;现在：Org -&amp;gt; HTML Fragment -&amp;gt; Hugo (PaperMod)&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org7ef909b&#34;&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;改进：采用 Hugo 作为渲染引擎，利用 PaperMod 提供现代化的 UI。&lt;/li&gt;
&lt;li&gt;核心逻辑：利用 Emacs 的 &lt;code&gt;ox-html&lt;/code&gt; 将 Org 导出为纯净的 HTML 片段，再交给 Hugo 进行最终装配。&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org671ad1d&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org671ad1d&#34;&gt;关键技术点&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org671ad1d&#34;&gt;
&lt;/div&gt;
&lt;div id=&#34;outline-container-orgc29a37d&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;orgc29a37d&#34;&gt;1. 自动化导出脚本：publish-hugo.el&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-orgc29a37d&#34;&gt;
&lt;p&gt;
这是整个流程的“心脏”。它是一个 Emacs Lisp 脚本，通过 Org 内置的 &lt;code&gt;ox-html&lt;/code&gt; 导出器实现精准渲染，同时负责元数据注入和路径修正。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-elisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;pz/export-all-to-hugo&lt;/span&gt; ()
  (&lt;span style=&#34;font-weight: bold;&#34;&gt;dolist&lt;/span&gt; (section &#39;(&lt;span style=&#34;font-style: italic;&#34;&gt;&#34;notes&#34;&lt;/span&gt; &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;quantum&#34;&lt;/span&gt; &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;me&#34;&lt;/span&gt;))
    (&lt;span style=&#34;font-weight: bold;&#34;&gt;dolist&lt;/span&gt; (file (directory-files (expand-file-name section) t &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;\\.org$&#34;&lt;/span&gt;))
      (&lt;span style=&#34;font-weight: bold;&#34;&gt;with-current-buffer&lt;/span&gt; (find-file-noselect file)
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#23548;&amp;#20986;&amp;#20026; HTML &amp;#29255;&amp;#27573;
&lt;/span&gt;        (&lt;span style=&#34;font-weight: bold;&#34;&gt;let&lt;/span&gt; ((html-content (org-export-as &#39;html nil nil t)))
          &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#20462;&amp;#22797;&amp;#36164;&amp;#28304;&amp;#36335;&amp;#24452;
&lt;/span&gt;          (&lt;span style=&#34;font-weight: bold;&#34;&gt;setq&lt;/span&gt; html-content (replace-regexp-in-string &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;\\.\\./images/&#34;&lt;/span&gt; &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;/images/&#34;&lt;/span&gt; html-content))
          &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#20889;&amp;#20837; Hugo content &amp;#30446;&amp;#24405;
&lt;/span&gt;          (&lt;span style=&#34;font-weight: bold;&#34;&gt;with-temp-file&lt;/span&gt; dest-file
            (insert &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;---\n&#34;&lt;/span&gt; (format &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;title: \&#34;%s\&#34;\n&#34;&lt;/span&gt; title) &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;math: true\n---\n\n&#34;&lt;/span&gt;)
            (insert html-content)))))))
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org37929d0&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org37929d0&#34;&gt;2. 构建与部署：build.sh &amp;amp; deploy.sh&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org37929d0&#34;&gt;
&lt;p&gt;
实现“一键发布”的两个 Bash 脚本：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>LlamaPi 初步试用 RWKV, Ollama</title>
      <link>/notes/20250621_llamapi_rwkv/</link>
      <pubDate>Sat, 21 Jun 2025 00:00:00 +0000</pubDate>
      <guid>/notes/20250621_llamapi_rwkv/</guid>
      <description>&lt;p&gt;
最近RWKV有不少进展，RWKV-7出了思考模型 &lt;code&gt;rwkv-7-world-g1&lt;/code&gt; ，RWKV-8也即将发布。
&lt;/p&gt;

&lt;p&gt;
RWKV的模型架构，在计算量和内存上相比Transformer有很大的优势，对于LlamaPi这种在边缘设备上运行的应用很有吸引力。因此尝试在LlamaPi上适配RWKV，看看效果如何。
&lt;/p&gt;

&lt;p&gt;
目前初步尝试，总的来说不太成功，可能是使用方法或适配上还有些问题，模型始终不能很好的遵循系统指令，生成符合要求的响应。
&lt;/p&gt;

&lt;div id=&#34;outline-container-orga0c8feb&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orga0c8feb&#34;&gt;llama.cpp运行&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orga0c8feb&#34;&gt;
&lt;p&gt;
按照&lt;a href=&#34;https://rwkv.cn/tutorials/intermediate/llamacpp&#34;&gt;文档&lt;/a&gt;步骤：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;编译最新的llama.cpp&lt;/li&gt;
&lt;li&gt;下载 &lt;code&gt;rwkv7-g1-1.5b-20250429-ctx4096&lt;/code&gt; ，转换成gguf，Q8&lt;sub&gt;0量化&lt;/sub&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
用 &lt;code&gt;llama-cli&lt;/code&gt; 命令行运行，提示词参考LlamaPi：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;./build/bin/llama-cli -m models/rwkv7-g1-1.5b-20250429-ctx4096-Q8_0.gguf -p &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;Your name is Skyler. You are a helpful assistant.\n\n&#34;&lt;/span&gt; -cnv -t 4 -ngl 99 -n 500
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
启动后本来应该就进入对话的，但它先进入了自嗨模式，输出了一大通不相关的东西，然后才停下进入提示符。这可能还是因为我设了n=500的参数，否则不知道它什么时候会停下：
&lt;/p&gt;


&lt;div id=&#34;orgca43088&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2025/2025-06-21_14-37-03_screenshot.png&#34; alt=&#34;2025-06-21_14-37-03_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
然后检查一下提示词的有效性，结果是完全没用：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;org9a55b9b&#34;&gt;
&amp;gt; hello, what&#39;s your name?
 My name is [My name]

&amp;gt; who are you?
 My name is [My name]

&amp;gt; 
&lt;/pre&gt;


&lt;p&gt;
命令行不行，那试试Web界面？
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;/build/bin/llama-server -m models/rwkv7-g1-1.5b-20250429-ctx4096-Q8_0.gguf 
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
先设置一下系统提示：
&lt;/p&gt;


&lt;div id=&#34;orgc1d1b6c&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2025/2025-06-21_14-43-42_screenshot.png&#34; alt=&#34;2025-06-21_14-43-42_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
试一下，这次系统提示词似乎有效了。
&lt;/p&gt;


&lt;div id=&#34;orgf1c0f69&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2025/2025-06-21_14-45-16_screenshot.png&#34; alt=&#34;2025-06-21_14-45-16_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
试试LlamaPi的完整提示词：
&lt;/p&gt;


&lt;div id=&#34;orgaddeb5f&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2025/2025-06-21_14-47-58_screenshot.png&#34; alt=&#34;2025-06-21_14-47-58_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <title>LlamaPi: Experiments with VideoCore GPU</title>
      <link>/notes/20241116_llamapi_update_rpi_videocore/</link>
      <pubDate>Sat, 16 Nov 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20241116_llamapi_update_rpi_videocore/</guid>
      <description>&lt;p&gt;
As mentioned in my previous posts, I&#39;ve been trying to get LlamaPi to run with VideoCore GPU on Raspberry Pi, hoping to further boost generation speed.
&lt;/p&gt;

&lt;p&gt;
Well, that effort might have just come to a conclusion&amp;#x2026; TL;DR is that VideoCore on Raspberry Pi is not well suited for such computation - in fact, it is even much slower than the ARM CPUs on Raspberry Pi.
&lt;/p&gt;

&lt;p&gt;
In case I need them again, here are some of the records of my experiments.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>LlamaPi Update - Gemini Support</title>
      <link>/notes/20241027_llamapi_gemini/</link>
      <pubDate>Sun, 27 Oct 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20241027_llamapi_gemini/</guid>
      <description>&lt;p&gt;
LlamaPi now supports Gemini as its backing LLM, in addition to local LLM and Coze.
&lt;/p&gt;


&lt;div id=&#34;org9cdce84&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2024/2024-10-27_22-35-35_screenshot.png&#34; alt=&#34;2024-10-27_22-35-35_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
On Gemini side:
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Test the prompt (system instruction) for using with LlamaPi.&lt;/li&gt;
&lt;li&gt;Create an API key (may need to create a project first).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
On LlamaPi side:
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Create a wrapper using Gemini Python SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
In order to support multiple backing LLMs, major refactor was done to the LlamaPi code.
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Create a common base class &lt;code&gt;LlamaPiBase&lt;/code&gt; for all three scenarios (local, Coze, Gemini). The base class includes the common functionalities like the UI, audio, ASR, etc.&lt;/li&gt;
&lt;li&gt;Created subclasses for local LLM, Coze and Gemini respectively. They extend the base class and implement the logic for interacting with backing LLMs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
With this refactor, it will be easier for me to add support for other LLMs in the furture.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>LlamaPi Update - Llama-3.2 3B</title>
      <link>/notes/20241001_llamapi_updated/</link>
      <pubDate>Tue, 01 Oct 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20241001_llamapi_updated/</guid>
      <description>&lt;p&gt;
I just updated LlamaPi with Llama-3.2 3B as its default local LLM.
&lt;/p&gt;

&lt;p&gt;
Similar to Llama-3.1, I need to convert the model into gguf format, and then quantize it into different sizes. For 5-bit quantization, memory usage was reduced to ~2.7GB and generation speed reached 3.3 tokens/second. Compared to Llama-3.1 8B (4-bit quantized), the speedup is 1.83x.
&lt;/p&gt;

&lt;p&gt;
Here is a comparison using llama.cpp CLI:
&lt;/p&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;


&lt;colgroup&gt;
&lt;col  class=&#34;org-left&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-left&#34;&gt;&amp;#xa0;&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;tokens/second&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-left&#34;&gt;Llama-3.1 8B (4-bit quantized)&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1.8&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-left&#34;&gt;Llama-3.2 3B (8-bit quantized)&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;2.5&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-left&#34;&gt;Llama-3.2 3B (5-bit quantized)&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;3.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Generation quality seemed to be similar as Llama-3.1 8B, but I haven&#39;t had time to compare them extensively yet.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>LlamaPi Robot - Voice chatbot with LLM and robot arm</title>
      <link>/notes/20240920_llamapi/</link>
      <pubDate>Fri, 20 Sep 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20240920_llamapi/</guid>
      <description>&lt;div id=&#34;outline-container-org6931315&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org6931315&#34;&gt;Intro&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org6931315&#34;&gt;
&lt;p&gt;
Recently I built a prototype demonstrating the possibilities of &lt;b&gt;&lt;b&gt;Voice + LLM + Robotics&lt;/b&gt;&lt;/b&gt;. It is a voice chatbot running on Raspberry Pi 5 backed by the latest LLM (e.g. Llama-3.1), allowing the user to control robot arm gestures through voice interactions.
&lt;/p&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Backed by local LLM (Llama-3.1 8B) or cloud-based LLM.&lt;/li&gt;
&lt;li&gt;Local ASR (&lt;code&gt;faster_whisper&lt;/code&gt;) and TTS (&lt;code&gt;piper&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Robot arm commands generated by LLM based on the context of the conversation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The prototype won 1st and 3nd prizes at the recent &lt;a href=&#34;https://lf-edge.atlassian.net/wiki/spaces/IA/pages/28311564/Day0+Hackathon#Results&#34;&gt;InfiniEdge AI Hackathon&lt;/a&gt;.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>[论文解读] DistServe分离式架构优化大模型推理服务性能</title>
      <link>/notes/20240505_distserve_inference/</link>
      <pubDate>Sun, 05 May 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20240505_distserve_inference/</guid>
      <description>&lt;p&gt;
这是一篇北大和UCSD合作的论文，主题是优化大模型推理服务的性能。
&lt;/p&gt;

&lt;p&gt;
在前文（RWKV）中提到过，对于大语言模型（LLM）来说，更大的挑战是推理。因为推理成本属于OpEx，用户使用越多，花费就越大。降低LLM的推理服务成本，是LLM应用在商业上可持续的关键之一。
&lt;/p&gt;

&lt;p&gt;
LLM推理服务对性能的要求，也与训练有所不同。推理服务更注重响应的延迟，由于直接面向用户，响应延迟直接影响用户体验。例如对于聊天机器人类型的应用，需要尽快的开始回答，也就是说要尽快生成输出第一个token，之后的token，需要能跟上人的阅读速度；而对于代码生成类的应用，则需要更快的端到端生成速度，以支持实时的代码提示。
&lt;/p&gt;

&lt;p&gt;
目前的大模型推理服务系统，都是以吞吐（throughput）为标准来优化的，也就是单位时间服务的用户请求数（request per second, rps）。这个指标，也被用作推理成本优化的一个目标，因为更高的吞吐，意味着可以用更少的GPU时间服务更多的用户请求。
&lt;/p&gt;

&lt;p&gt;
作者提出，简单的用吞吐作为指标是不够的。在实际场景中，应用对推理服务有不同的质量目标（service level objectives, SLO），对于LLM推理服务而言，最重要的有两个SLO：
&lt;/p&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;TTFT (Time To First Token): LLM生成第一个token需要的时间&lt;/li&gt;
&lt;li&gt;TPOT (Time Per Output Token): LLM生成两个token之间的平均延迟&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
显然，TTFT表示LLM多久开始回答，而TPOT表示LLM的语速。
&lt;/p&gt;

&lt;p&gt;
作者认为，LLM推理服务的质量应该看Goodput，可以理解为“有效吞吐”。Goodput的意思是：
对于每个分配的GPU，在满足SLO目标的前提下（例如 90% TTFT &amp;lt; 200ms，90% TPOT &amp;lt; 50ms），所能达到的最大吞吐。更高的Goodput，意味着每个请求的服务成本更低。
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
maximum request rate that can be served adhering to the SLO attainment goal (say, 90%) for each GPU provisioned – higher per-GPU goodput directly translates into lower cost per query.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
更高的throughput，并不一定意味着更高的goodput。
&lt;/p&gt;


&lt;div id=&#34;orgf82aff6&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2024/20240505-162112_screenshot.png&#34; alt=&#34;20240505-162112_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
为什么生成第一个token会花费更久呢？
&lt;/p&gt;

&lt;p&gt;
因为在服务用户的时候，LLM需要把用户的历史对话作为上下文，加上用户当前的问题（请求），作为prompt去推理，因此生成第一个token需要计算很长的prompt。而后续的token，因为LLM推理普遍采用KV cache缓存前一次生成的中间结果，避免重复计算，每次生成token计算量要比第一个token小得多。
&lt;/p&gt;

&lt;p&gt;
关于KV cache，简单说几句：不要把它混淆为Key Value，这里的KV指的是Transformer的K，V矩阵。KV cache的原理是，自回归的LLM在生成时，每次生成的上下文，和上一次生成只差一个token，因此有大量计算是重复的。如果把上一次生成时的中间结果（主要是K，V矩阵）记下来，那么下一次生成的时候就可以避免重复计算，大大降低推理成本。这个技术目前已经是标配了，基本上所有的LLM推理都会用到它。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>谨慎使用C语言里的联合(union)和位域(bit field)！</title>
      <link>/notes/20240121_c_union_bitfield/</link>
      <pubDate>Sun, 21 Jan 2024 00:00:00 +0000</pubDate>
      <guid>/notes/20240121_c_union_bitfield/</guid>
      <description>&lt;p&gt;
对于内核、驱动、嵌入式系统等底层开发来说，C语言的bit field（位域）和union（联合）都是常用的特性。
位域可以让我们在结构体中指定某些成员占多少位，这在同硬件打交道的时候特别有用。例如硬件要求某个32位的消息里，第31位是flag，其余是value，用位域定义的数据结构：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_t&lt;/span&gt; {
&amp;#8203;
  &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;flag&lt;/span&gt; : 1;
  &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;value&lt;/span&gt; : 31;
} &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_t&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
在程序里可以直接操作结构体成员那样访问flag和value，而不用手动去对32位消息进行位操作，这些编译器都给我们做了。
我们还可以加上联合（union），使得我们既可以访问里面的成员，也可以按照一个32位数访问整个消息：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_fields_t&lt;/span&gt; {
  &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;flag&lt;/span&gt; : 1;
  &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;value&lt;/span&gt; : 31;
} &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_fields_t&lt;/span&gt;;
&amp;#8203;
&lt;span style=&#34;font-weight: bold;&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_t&lt;/span&gt; {
  &lt;span style=&#34;font-weight: bold;&#34;&gt;union&lt;/span&gt; {
    &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;raw&lt;/span&gt;;
    &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_fields_t&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;fields&lt;/span&gt;;
  };
} &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;msg_t&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
union告诉编译器，raw和fields这两个成员在结构体里占用同样的内存地址。因为这两个成员都是32位，因此raw就是整个32位的消息，而通过fields可以访问该消息的flags和value。
但是，在同时使用union和bit field的时候要注意，union和bit field如果互相套在一起，编译器产生的内存排列可能和你想的不一样！
&lt;/p&gt;</description>
    </item>
    <item>
      <title>index</title>
      <link>/me/</link>
      <pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate>
      <guid>/me/</guid>
      <description>&lt;div id=&#34;orgef372ec&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2023/20230727-104449_screenshot.png&#34; alt=&#34;20230727-104449_screenshot.png&#34; width=&#34;120p&#34; style=&#34;margin-left: auto; margin-right: auto;x&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
I&#39;m currently with Google, working on &lt;b&gt;&lt;b&gt;TPU ASIC Software &amp;amp; Firmware&lt;/b&gt;&lt;/b&gt;.
&lt;/p&gt;

&lt;p&gt;
Previously I was a Senior Researcher/Architect at &lt;b&gt;ByteDance&lt;/b&gt;. I worked on &lt;b&gt;next-gen infra for AI/Machine Learning&lt;/b&gt; (AIGC, LLM, Recommendation and stuff), &lt;i&gt;software/hardware co-design&lt;/i&gt;, &lt;i&gt;data &amp;amp; storage accelerators&lt;/i&gt;, &lt;i&gt;disaggregated &amp;amp; hybrid memory architecture&lt;/i&gt; and cloud infrastructure.
&lt;/p&gt;

&lt;p&gt;
Before joining ByteDance, I worked as Senior System Integration Engineer/Technical Solution Engineer at &lt;b&gt;Google&lt;/b&gt;. I worked with XFN teams and partners to integrate Google products/services (Google Assistant, Chromecast, Project Matter) into partner devices. I also collaborated with &lt;b&gt;Google Brain&lt;/b&gt; on a moonshot project for &lt;b&gt;Edge TPU&lt;/b&gt;. The work was &lt;a href=&#34;https://research.google/blog/improved-on-device-ml-on-pixel-6-with-neural-architecture-search/&#34;&gt;featured by Google AI&lt;/a&gt; and published in CVPR-22 (ECV22) with patents pending.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>My Little Reflections on Optane</title>
      <link>/notes/20231210_my_reflections_on_optane/</link>
      <pubDate>Sun, 10 Dec 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20231210_my_reflections_on_optane/</guid>
      <description>&lt;p&gt;
In early 2023, Intel &lt;a href=&#34;https://www.intel.com/content/www/us/en/support/articles/000091826/../memory-and-storage/&#34;&gt;announced the discontinuity of Optane products&lt;/a&gt; (including SSD and memory). While not quite surprising considering their business environment, it’s still a bit of disappointment to me. As we are closing to the end of 2023, I decided to take some time and write down some of my reflections on Optane’s journey.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;[Disclaimer]&lt;/b&gt; All contents &amp;amp; discussions in this article are just my personal opinions and do not represent any organization or institution.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>三国GPT (SanGuo GPT)  v0.1</title>
      <link>/notes/20230924_sanguo_gpt_v0.1/</link>
      <pubDate>Sun, 24 Sep 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230924_sanguo_gpt_v0.1/</guid>
      <description>&lt;div id=&#34;outline-container-orgfe6ce49&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgfe6ce49&#34;&gt;Overview&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgfe6ce49&#34;&gt;
&lt;p&gt;
SanGuo GPT is a Large Language Model trained on 三国演义 (San1 Guo2 Yan3 Yi4, Romance of the Three Kindoms), an ancient Chinese novel based on historical events happened ~1800 years ago during the late Han dynasty. It is a Transformer-based model with about 13.778M parameters in current version.
&lt;/p&gt;

&lt;p&gt;
I created this project for learning and exploration purpose.
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;I&#39;d like to try out the LLM application building process end-to-end, including major steps like data ingestion &amp;amp; preprocessing, shuffling/sampling, model building &amp;amp; training, visualization, model checkpointing and model serving.&lt;/li&gt;
&lt;li&gt;I want to explore the idea of &#34;书读千遍，其义自现&#34; (something like &#34;if you read a book a thousand times, the meaning and implications will emerge by itself&#34;). This idea popped up when I chat with a friend, and I found it very interesting. What if I train the model with data from just one book and iterate many steps? How would the model behave after intensive training on a single book?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I also plan to use this project as a vehicle for playing with other new ideas - stay tuned!
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Some Math Notes About Self-Attention</title>
      <link>/notes/20230729_math_notes_self_attention/</link>
      <pubDate>Sat, 29 Jul 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230729_math_notes_self_attention/</guid>
      <description>&lt;p&gt;
If you are familiar with the recent frenzy about &lt;a href=&#34;https://chat.openai.com&#34;&gt;ChatGPT&lt;/a&gt;, &lt;a href=&#34;https://bard.google.com&#34;&gt;Bard&lt;/a&gt; and other large language models, you probably already know about &lt;a href=&#34;https://arxiv.org/abs/1706.03762&#34;&gt;Transformer&lt;/a&gt;, the key building block behind these models.
&lt;/p&gt;

&lt;p&gt;
The key algorithm inside Transformer block is &lt;a href=&#34;https://en.wikipedia.org/wiki/Self-attention&#34;&gt;Self Attention&lt;/a&gt;, which is commonly described as the following equation:
&lt;/p&gt;

\begin{matrix}
Attn(Q, K, V) = softmax(QK^T)V
\end{matrix}

&lt;p&gt;
(NOTE: the scaling part in the softmax is omitted for brevity.)
&lt;/p&gt;

&lt;p&gt;
Sometimes this equation is also written in an iterative way like this:
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Google AIGC开发平台MakerSuite - 用MakerSuite构建聊天机器人</title>
      <link>/notes/20230715_google_makersuite_chatbot/</link>
      <pubDate>Sat, 15 Jul 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230715_google_makersuite_chatbot/</guid>
      <description>&lt;div id=&#34;outline-container-org5aa4f2d&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org5aa4f2d&#34;&gt;MakerSuite 介绍&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org5aa4f2d&#34;&gt;
&lt;p&gt;
Google 在推出 PaLM 大语言模型和 Bard 聊天机器人之后，很快就推出了面向开发者的 MakerSuite，这是一个基于浏览器的云端 IDE，可以帮助用户快速试验大模型，以及开发 AIGC 应用原型。今天我们就来以聊天机器人为例，试验一下 MakerSuite 的开发体验。
&lt;/p&gt;

&lt;p&gt;
MakerSuite 的整个开发流程和 OpenAI 类似，先是在平台上试验提示，然后将试验结果导出成代码，变成可部署的产品。
&lt;/p&gt;

&lt;p&gt;
MakerSuite 目前支持 3 种场景：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Text prompt：最通用的一种，自由测试 PaLM 的各种提示&lt;/li&gt;
&lt;li&gt;Data prompt：将提示样本组织成表格形式&lt;/li&gt;
&lt;li&gt;Chat prompt：用于交互式聊天的模板&lt;/li&gt;
&lt;/ul&gt;


&lt;div id=&#34;org80438cc&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2023/20230716-144105_screenshot.png&#34; alt=&#34;20230716-144105_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;


&lt;div id=&#34;outline-container-org8ba3fe5&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org8ba3fe5&#34;&gt;用 MakerSuite 构建聊天机器人&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org8ba3fe5&#34;&gt;
&lt;p&gt;
在主界面中选择“Chat Prompt”，创建一个新的聊天提示。
&lt;/p&gt;


&lt;div id=&#34;orgf357d0d&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2023/20230716-144230_screenshot.png&#34; alt=&#34;20230716-144230_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;
PaLM 提供的是通用的语言模型，而 Bard 则是基于 PaLM 构建的通用聊天机器人。而在实际应用中，我们经常会需要根据自己的需要构建定制化的聊天机器人，例如希望机器人以某个角色（e.g. 客服，老师）或某种风格（e.g. 亲切，专业）同用户交谈。这样的需求，可以在通用语言模型的基础上，通过小样本学习（few-shot prompting）来实现。
&lt;/p&gt;

&lt;p&gt;
首先我们要提供一些样本（Example），每个样本包含用户输入（提问）和模型的回答，在上下文（context）中告诉模型自己在对话中的角色和风格。这样通过上下文和少量的样本，PaLM 就能以期望的方式同用户交谈。
&lt;/p&gt;

&lt;p&gt;
假设我们想要构建的聊天机器人叫做 Amy，是一只生活在欧罗巴（木星的卫星之一）上的外星猫，聊天的对象是 K-12 的小朋友。
&lt;/p&gt;

&lt;p&gt;
在左边的 prompt example 里输入 context 和样本：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;org6e467b2&#34;&gt;
Context: Your name is Amy, a friendly cat that lives on
Europa, one of Jupiter&#39;s moons.

User: Hello

Model: Hi! This is Amy the cat, I live on Europa, one of
Jupiter&#39;s moons. It&#39;s cold down here!
&lt;/pre&gt;

&lt;p&gt;
然后在右边测试一个提问：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>颠覆Transformer？新语言模型算法RWKV解读</title>
      <link>/notes/20230701_rwkv_paper/</link>
      <pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230701_rwkv_paper/</guid>
      <description>&lt;p&gt;
RWKV 是 Peng Bo 提出的一个新的语言模型算法，它融合了 Transformer 和 RNN 的优点，在训练时能够像 Transformer 那样并行计算，又能在推理时像 RNN 那样高效，因此在 2022 年项目开源后热度迅速上升，吸引了全球众多开发者的参与。本文基于 RWKV 论文，讨论一下这个算法的原理，以及它为什么能够融合两者的优点。
&lt;/p&gt;


&lt;div id=&#34;org532e23d&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2023/20230707-171611_screenshot.png&#34; alt=&#34;20230707-171611_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
先看看 RWKV 的效果。
&lt;/p&gt;

&lt;p&gt;
RWKV 聊天：
&lt;/p&gt;

&lt;p width=&#34;800px&#34;&gt;
&lt;img src=&#34;./images/2023/20230707-173422_screenshot.png&#34; alt=&#34;20230707-173422_screenshot.png&#34; width=&#34;800px&#34; /&gt;
(图源：&lt;a href=&#34;https://github.com/BlinkDL/ChatRWKV&#34;&gt;https://github.com/BlinkDL/ChatRWKV&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
RWKV 文本生成的效果：
&lt;/p&gt;

&lt;p width=&#34;640px&#34;&gt;
&lt;img src=&#34;./images/2023/20230707-173735_screenshot.png&#34; alt=&#34;20230707-173735_screenshot.png&#34; width=&#34;640px&#34; /&gt;
(图源：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/619721229&#34;&gt;https://zhuanlan.zhihu.com/p/619721229&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
RWKV 代码生成的效果：
&lt;/p&gt;

&lt;p width=&#34;640px&#34;&gt;
&lt;img src=&#34;./images/2023/20230707-173919_screenshot.png&#34; alt=&#34;20230707-173919_screenshot.png&#34; width=&#34;640px&#34; /&gt;
(图源：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/619721229&#34;&gt;https://zhuanlan.zhihu.com/p/619721229&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
顺便提一句，RWKV 的作者 Peng Bo 也是一位神人，大家有兴趣可以关注一下这位的 Zhihu。
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgb12f434&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgb12f434&#34;&gt;1. 一些背景&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgb12f434&#34;&gt;
&lt;p&gt;
最近以 GPT 为代表的大语言模型（LLM）的爆火，让 Transformer 这个算法广为人知。这个源自 2017 年「Attention Is All You Need」论文的算法，是几乎所有大模型的基础，GPT 里的 T 就是 Transformer。可以说正是 Transformer 算法造就了现在的 AIGC 的大突破。
&lt;/p&gt;

&lt;p&gt;
Transformer 的成功，我认为关键是两点：一是 Transformer 的注意力机制，对 token 之间关系的建模表达能力极强；二是 Transformer 算法训练时可以并行。
&lt;/p&gt;

&lt;p&gt;
在 Transformer 之前，语言建模的主流是 RNN/LSTM。Transformer 里的注意力机制，其概念实际上在 RNN/LSTM 时期就提出来了，但 RNN 的一个致命弱点就是训练很难并行，导致训练效率太低，很大程度上限制了模型规模。因此 Transformer 出现后，不仅在自然语言理解领域迅速占领了原本 RNN/LSTM 模型的份额，还扩展到了视觉等其他领域。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>超长上下文&#43;无分词器，新一代大模型架构？Meta最新论文MegaByte解读</title>
      <link>/notes/20230604_megabyte_llm/</link>
      <pubDate>Sat, 10 Jun 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230604_megabyte_llm/</guid>
      <description>&lt;p&gt;
今天来解读一下 Meta 的这篇论文：MEGABYTE: Predicting Million-byte Sequences with Multiscale Transformers
&lt;/p&gt;

&lt;div id=&#34;outline-container-org6da917f&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org6da917f&#34;&gt;大模型的上下文窗口瓶颈&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org6da917f&#34;&gt;
&lt;p&gt;
基于 Transformer  的大语言模型，有个很重要的指标就是上下文长度。这是因为模型本身是无状态的，它在推理时只能“记住”上下文窗口里的内容。因此我们在和模型对话时，需要把对话的历史（上下文）和我们的提示一起发给模型，否则模型就只能看到提示本身。随着对话的进行，这个上下文会越来越长，当超过模型的上下文窗口长度时，模型就只能“忘记”最早的对话内容了。
&lt;/p&gt;

&lt;p&gt;
例如在下面这个会话里，如果我们把上下文窗口的长度设为 10 个 token，可以看到模型就不能记住之前对话的内容了：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;org439d9bf&#34;&gt;
User&amp;gt; Hi, my name is Ping
AI&amp;gt; Hello Ping, it&#39;s nice to meet you! My name is OpenAI.
    How can I assist you today?

User&amp;gt; What is 1+1?
AI&amp;gt; The answer to 1+1 is 2.

User&amp;gt; What is my name?
AI&amp;gt; I&#39;m sorry, I don&#39;t have access to that information.
    Could you please tell me your name?
&lt;/pre&gt;

&lt;p&gt;
上面这个例子只是文本对话，对于多模态大模型而言（例如视觉大模型），输入的序列长度可以达到几百万个 token，因此上下文长度对于多模态大模型有极其重要的意义。但是，目前基于 Transformer 的大模型，普遍都只支持几千个 token 的上下文长度。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Variable Length Array in C&#43;&#43;</title>
      <link>/notes/20230423_cpp_variable_length_array/</link>
      <pubDate>Sun, 23 Apr 2023 00:00:00 +0000</pubDate>
      <guid>/notes/20230423_cpp_variable_length_array/</guid>
      <description>&lt;p&gt;
经常用 C/C++的朋友都知道，C/C++里面声明变量或者数组，是由编译器在编译时分配在堆栈上的，例如这个例子：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;main&lt;/span&gt;() {
    &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a&lt;/span&gt;;          &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#22768;&amp;#26126;&amp;#19968;&amp;#20010;&amp;#21464;&amp;#37327;
&lt;/span&gt;    &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;arr&lt;/span&gt;[10];    &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#22768;&amp;#26126;&amp;#19968;&amp;#20010;&amp;#25968;&amp;#32452;
&lt;/span&gt;
    &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; 0;
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
这个例子里的变量 &lt;code&gt;a&lt;/code&gt; 和数组 &lt;code&gt;arr&lt;/code&gt; 都是在编译时在栈上已经分配好了空间，这个和我们用 &lt;code&gt;malloc()&lt;/code&gt;, &lt;code&gt;new&lt;/code&gt; 在运行时分配空间不同，后者是分配在堆(heap)上。
&lt;/p&gt;

&lt;p&gt;
因为这些声明的变量/数组是在编译时分配的，它们的大小必须在编译时就已知，也就是说不能在运行时才决定数组的大小：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;r&lt;/span&gt; = 1 + (rand() % 10);

&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;arr&lt;/span&gt;[r]; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;C++&amp;#35821;&amp;#35328;&amp;#19981;&amp;#20801;&amp;#35768;&amp;#36825;&amp;#20040;&amp;#22768;&amp;#26126;&amp;#65292;&amp;#22240;&amp;#20026;r&amp;#32534;&amp;#35793;&amp;#26102;&amp;#26410;&amp;#30693;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;
但是最近用 C++的时候注意到一个神奇的事情，像上面这样不符合 C++语言规范的用法，也能编译通过并正常运行！例如：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>关于 =decltype，remove_pointer= ，别名和函数指针类型</title>
      <link>/notes/20221112_decltype_func_ptrs/</link>
      <pubDate>Sat, 12 Nov 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20221112_decltype_func_ptrs/</guid>
      <description>&lt;p&gt;
C++里的 &lt;code&gt;decltype&lt;/code&gt; 是个神奇的存在，你给它一个名字或表达式，它会返回这个名字或表达式的类型。并且这个检查是在编译时进行的，所以你给它的表达式不会真的运行，而只是用作类型推导，例如：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;f&lt;/span&gt;();
&lt;span style=&#34;font-weight: bold;&#34;&gt;decltype&lt;/span&gt;(f()) &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a&lt;/span&gt;;  &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a is bool&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
这里 &lt;code&gt;decltype&lt;/code&gt; 在编译时用 &lt;code&gt;f()&lt;/code&gt; 推导出类型 bool（因为函数 f 返回 bool），但实际运行时并不会真的执行这个 &lt;code&gt;f()&lt;/code&gt; 。
&lt;/p&gt;

&lt;p&gt;
大部分时间， &lt;code&gt;decltype&lt;/code&gt; 都能返回你想要的结果。
&lt;/p&gt;

&lt;p&gt;
但是遇到别名和函数指针类型，情况就会绕一些，咱们分别讨论一下。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;II&lt;/span&gt; = &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt;* ();
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;This is equivalent to declaring an external function (not a pointer):
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;extern int* a1();
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;II&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a1&lt;/span&gt;;
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;This will cause compile error unless a function int* a1() is implemented.
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a1();&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
类型别名 &lt;code&gt;II&lt;/code&gt; 是一个返回 &lt;code&gt;int*&lt;/code&gt; 的函数类型（注意，不是函数指针哈）。于是用 &lt;code&gt;II&lt;/code&gt; 声明的 &lt;code&gt;a1&lt;/code&gt; 就是一个返回 &lt;code&gt;int*&lt;/code&gt; 的函数。但是这里只是申明了函数 &lt;code&gt;a1&lt;/code&gt; 而已，并没有它的实现，所以调用 &lt;code&gt;a1&lt;/code&gt; 是会编译出错的。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Emacs里如何把某个字符替换为“换行”？</title>
      <link>/notes/20220904_emacs_replace_with_newline/</link>
      <pubDate>Sun, 04 Sep 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220904_emacs_replace_with_newline/</guid>
      <description>&lt;p&gt;
Emacs 的 &lt;code&gt;query-replace&lt;/code&gt; 是一个很常用的查找替换命令（快捷键 &lt;code&gt;M-%&lt;/code&gt; ）。这个命令会提示你输入要查找的字符串，再输入要替换的字符串，然后就会从当前位置逐个查找和确认是否替换。
&lt;/p&gt;

&lt;p&gt;
但是，如果我想要把某个字符替换为特殊字符，例如想把字符“;”替换为“换行”，就不能直接输入了，因为如果你在第二步输入了回车，它会认为你想要把查找的字符串替换为空字符串（相当于查找＋删除）。怎么办呢？
&lt;/p&gt;

&lt;p&gt;
查了一下，有这样几个办法。
&lt;/p&gt;

&lt;p&gt;
一是在输入要替换的字符串时，输入 &lt;code&gt;C-q C-j&lt;/code&gt; ，然后回车。
&lt;/p&gt;


&lt;div id=&#34;org23650bb&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022/20220904_emacs_replace_newline1.png&#34; alt=&#34;20220904_emacs_replace_newline1.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
这里的 &lt;code&gt;C-q&lt;/code&gt; 表示 &lt;code&gt;quoted-insert&lt;/code&gt; ，意思是告诉 Emacs 输入后面的控制字符。而 &lt;code&gt;C-j&lt;/code&gt; 代表的是 ASCII 码 0x0a，也就是 newline。然后按下回车，就会用 ASCII 码 0x0a 来替换字符串“;”。
&lt;/p&gt;


&lt;div id=&#34;org81cbcb1&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022/20220904_emacs_replace_newline2.png&#34; alt=&#34;20220904_emacs_replace_newline2.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
还有种方法，就是在 &lt;code&gt;C-q&lt;/code&gt; 之后输入 ASCII 码的八进制数，例如 0x0a 的八进制数就是 12 。
&lt;/p&gt;

&lt;p&gt;
另一种办法可能更直观，就是用 &lt;code&gt;C-k&lt;/code&gt; 去删除一个空行，把空行加入到 kill-ring 里，然后在输入要替换的字符串时，用 &lt;code&gt;C-y&lt;/code&gt; 把它粘贴进来。这个就有点类似图形界面编辑器（例如VSCode）里的操作了。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Emacs集成开发环境和远程开发配置</title>
      <link>/notes/20220814_emacs_remote_development/</link>
      <pubDate>Sun, 14 Aug 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220814_emacs_remote_development/</guid>
      <description>&lt;p&gt;
一个经典问题，如何把 Emacs 配置出 IDE 的效果？
&lt;/p&gt;

&lt;p&gt;
对于本地开发来说，可以用 projectile+lsp 配合相应语言的 language server（例如 C++的 clangd），基本上就能满足需要。
&lt;/p&gt;

&lt;p&gt;
如果 clangd 不够智能，例如有些头文件或者符号找不到，可以用 &lt;code&gt;compile_commands.json&lt;/code&gt; 文件来指导 clangd。 &lt;code&gt;compile_commands.json&lt;/code&gt; 文件，你可以把它理解为编译时产生的 trace，记录了整个 project 的编译步骤，通过它 clangd 就能知道每个文件是如何编译的，包括头文件在哪里，链接库在哪里，乃至各个符号的定义等。有了这个，你的 Emacs 基本上就具备了集成开发环境的主要能力。
&lt;/p&gt;

&lt;p&gt;
如何生成 &lt;code&gt;compile_commands.json&lt;/code&gt; 文件呢？取决于项目的构建工具。如果是 cmake，ninja 等比较现代的工具，都内置了导出 &lt;code&gt;compile_commands.json&lt;/code&gt; 文件的功能，比如 cmake 里只要加上这个环境变量就行了：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;org464fd46&#34;&gt;
CMAKE_EXPORT_COMPILE_COMMANDS
&lt;/pre&gt;

&lt;p&gt;
如果工具不支持自动导出 &lt;code&gt;compile_commands.json&lt;/code&gt; ，可以在编译时用 bear 来记录。bear 相当于一个命令的 wrapper，后面的参数就是你的编译命令。例如：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;./configure
bear make
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;bear&lt;/code&gt; 会把 &lt;code&gt;make&lt;/code&gt; 过程中执行的步骤全都记录在 &lt;code&gt;compile_commands.json&lt;/code&gt; 里。
&lt;/p&gt;

&lt;p&gt;
但是还有个问题：如果项目代码在远程机器上呢？Emacs 能否有类似 VSCode 的远程开发能力？
&lt;/p&gt;

&lt;p&gt;
答案是可以的，但是需要你自己配置。:-)
&lt;/p&gt;

&lt;p&gt;
Emacs 内置有远程连接的客户端 tramp，支持 SSH 等多种连接方式。我们可以通过 tramp 来访问远程机器上的文件。例如我们在打开文件时输入这样的地址：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;orgf6b059c&#34;&gt;
/ssh:user@192.168.0.100#1234:/home/user/test.c
&lt;/pre&gt;

&lt;p&gt;
这个地址告诉 tramp，我们用 ssh 方式打开主机 192.168.0.100 上的文件 &lt;code&gt;/home/user/test.c&lt;/code&gt; ，登录用户名 user，连接端口是 1234 (如果 ssh 用的是默认配置，端口可以省略)。如果需要，tramp 会提示你输入密码登录。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>用Emacs做PPT演示</title>
      <link>/notes/20220709_emacs_presentation/</link>
      <pubDate>Sat, 09 Jul 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220709_emacs_presentation/</guid>
      <description>&lt;div id=&#34;org5879a3d&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022/2022-07-09_10-56-47_screenshot.png&#34; alt=&#34;2022-07-09_10-56-47_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgac9fef4&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgac9fef4&#34;&gt;为什么要用 Emacs 做 PPT 演示&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgac9fef4&#34;&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;我也不知道为啥，看起来酷呗 :-)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
按照大佬的说法：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;不需要复杂的 presentation 软件&lt;/li&gt;
&lt;li&gt;可以让你专注在演讲的内容上&lt;/li&gt;
&lt;li&gt;给你的同事留下牛 X 的印象
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;当然，也可能把他们搞糊涂 :-)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgbfa7de2&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgbfa7de2&#34;&gt;安装 org-present 包&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgbfa7de2&#34;&gt;
&lt;p&gt;
如果是 Doom Emacs，它已经自带了 org-present 支持，只需要打开即可。打开 &lt;code&gt;.doom.d/init.el&lt;/code&gt; ，在 org 那栏加上 present 即可：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-emacs-lisp&#34;&gt;(doom! &lt;span style=&#34;font-weight: bold;&#34;&gt;:input&lt;/span&gt;
       &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;......
&lt;/span&gt;       &lt;span style=&#34;font-weight: bold;&#34;&gt;:lang&lt;/span&gt;
       &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;......
&lt;/span&gt;       (org                &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;organize your plain life in plain text
&lt;/span&gt;        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;......
&lt;/span&gt;        +present
        +pretty
        +roam2)
       &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;......&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
如果是用自己的配置，用 use-package 或者手动安装即可。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Doom Emacs中文字体显示问题的解决</title>
      <link>/notes/20220703_chinese_display_in_doom_emacs/</link>
      <pubDate>Sun, 03 Jul 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220703_chinese_display_in_doom_emacs/</guid>
      <description>&lt;div id=&#34;outline-container-org538572c&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org538572c&#34;&gt;问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org538572c&#34;&gt;
&lt;p&gt;
Doom Emacs 别的都挺好，就是有个奇怪的小问题，在显示中文的时候，会出现下面这样字体不一致的情况：
&lt;/p&gt;


&lt;div id=&#34;orgf6294a0&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022/20220703_151615screenshot.png&#34; alt=&#34;20220703_151615screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
可以看到，同一行文字里的中文字符，它似乎用了不同的字体来显示，导致显示出来的文字大小样式不一，显得很奇怪。这种情况，我在以前的 Emacs 配置里没有遇到过，感觉似乎是 Doom 里某些编码没有指定字体？
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org0d266f9&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org0d266f9&#34;&gt;解决&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org0d266f9&#34;&gt;
&lt;p&gt;
开始我以为只是 org-mode 的问题，后来发现，这个问题在其他模式（例如代码注释）里也存在，因此是 Doom Emacs 本身的问题。
&lt;/p&gt;

&lt;p&gt;
经过一番研究，找到这个帖子：
&lt;a href=&#34;https://emacs.stackexchange.com/questions/63850/doom-emacs-how-to-align-chinese-characters-in-org-tables&#34;&gt;https://emacs.stackexchange.com/questions/63850/doom-emacs-how-to-align-chinese-characters-in-org-tables&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
这个帖子是关于 org-mode 表格显示的，但其中对中文字体的设置也有借鉴意义：我们可以用 &lt;code&gt;set-font-set&lt;/code&gt; 这个函数，对指定的字符集设置字体。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-emacs-lisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;pz/setup-cjk-fonts&lt;/span&gt; ()
  (&lt;span style=&#34;font-weight: bold;&#34;&gt;interactive&lt;/span&gt;)
  (&lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; (display-graphic-p)
      (&lt;span style=&#34;font-weight: bold;&#34;&gt;dolist&lt;/span&gt; (charset &#39;(kana han cjk-misc bopomofo))
        (set-fontset-font (frame-parameter nil &#39;font)
                          charset (font-spec &lt;span style=&#34;font-weight: bold;&#34;&gt;:family&lt;/span&gt; &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;Noto Sans Mono CJK SC&#34;&lt;/span&gt;)))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;set-font-set&lt;/code&gt; 函数的定义如下：
&lt;/p&gt;

&lt;pre class=&#34;example&#34; id=&#34;orgda66814&#34;&gt;
(set-fontset-font NAME TARGET FONT-SPEC &amp;amp;optional FRAME ADD)
&lt;/pre&gt;

&lt;p&gt;
第一个是要设置的窗口参数，我们用 &lt;code&gt;frame-parameter&lt;/code&gt; 加 nil 可以获取当前窗口的字体参数。然后对这个参数，设置各个字符集的字体。这里字符集参数 &lt;code&gt;charset&lt;/code&gt; ：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;可以是字符集名称（例如 chinese-gbk，可以通过命令 &lt;code&gt;list-character-sets&lt;/code&gt; 查看）；&lt;/li&gt;
&lt;li&gt;也可以是 script symbol，可以查看变量 &lt;code&gt;script-representative-chars&lt;/code&gt; 获取所有的 script symbol 名字。&lt;/li&gt;
&lt;li&gt;也可以是单个字符，即为单个字符指定字体；&lt;/li&gt;
&lt;li&gt;如果是 nil，则表示给所有未设置字体的字符集设置字体。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
对我来说，就是给 CJK 字符集设置中文字体，这里用的是 script symbol，包括 kana, han, cjk-misc, bopomofo 这几个。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Doom Emacs on Ubuntu Linux 尝鲜</title>
      <link>/notes/20220521_doom_emacs_on_linux/</link>
      <pubDate>Sat, 21 May 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220521_doom_emacs_on_linux/</guid>
      <description>&lt;div id=&#34;outline-container-org55053f5&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org55053f5&#34;&gt;Doom Emacs简介&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org55053f5&#34;&gt;
&lt;p&gt;
Doom Emacs 是一个 Emacs 的配置框架，相比原厂的 Emacs 配置，Doom Emacs 提供了更方便完善的默认设置，预装了常用的包，并且提供了一个很容易自定义的框架。同时，Doom Emacs 启动速度也很快。最近在 Ubuntu 上试用了一下，感觉不错，正在考虑将我原有的配置移到 Doom Emacs 上去。
&lt;/p&gt;


&lt;div id=&#34;org05c8718&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022/2022-05-21_19-14-11_screenshot.png&#34; alt=&#34;2022-05-21_19-14-11_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
注意，Doom Emacs 不是 Emacs 发行版，只是一组 Emacs 配置，你运行 Doom Emacs 时，运行的还是系统上安装的 Emacs。Doom Emacs 需要 Emacs 27.1 以上，如果你的 Emacs 版本太低，需要先升级才能使用。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org88b34b8&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org88b34b8&#34;&gt;在 Ubuntu 上安装 Doom Emacs&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org88b34b8&#34;&gt;
&lt;p&gt;
首先你需要 Emacs 27.1 以上，个人推荐最新的 28.1 ，并带上 native-compilation 。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;./configure --prefix=$&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;HOME&lt;/span&gt;/local &lt;span style=&#34;font-style: italic;&#34;&gt;\&lt;/span&gt;
            --with-native-compilation &lt;span style=&#34;font-style: italic;&#34;&gt;\&lt;/span&gt;
            --with-x --with-json &lt;span style=&#34;font-style: italic;&#34;&gt;\&lt;/span&gt;
            --with-jpeg --with-tiff --with-png
make -j4
make install
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
根据Doom Emacs的建议，加上 &lt;code&gt;--with-json&lt;/code&gt; 可以提高运行速度。安装完成后确保 &lt;code&gt;~/.local/bin&lt;/code&gt; 在PATH里。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Paxos 讨论：Phase-1 收到更高的 proposal number 该怎么办?</title>
      <link>/notes/20220408_paxos_note_prepare_higher_proposal_number/</link>
      <pubDate>Fri, 08 Apr 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220408_paxos_note_prepare_higher_proposal_number/</guid>
      <description>&lt;p&gt;
最近研究了一下Paxos，这个协议本身并不难懂，但 Lamport 的文章比较抽象，在具体实现上有一些细节没有充分讨论到，这里就讨论其中之一。
&lt;/p&gt;

&lt;p&gt;
先聊一下背景：我们知道，每次运行Paxos（也就是一个Paxos实例，Paxos Instance）都由两个阶段（phase）组成。在phase-1，proposer 向 acceptor 发送 prepare 消息，并根据收到的回应决定是否可以进入下一阶段 phase-2 。
&lt;/p&gt;

&lt;p&gt;
这个prepare消息主要内容是 proposer 的 proposal number （有些地方也称为 ballot number），例如在 Lamport 的 &lt;a href=&#34;https://www.microsoft.com/en-us/research/publication/paxos-made-simple/&#34;&gt;Paxos Made Simple&lt;/a&gt; 文章里：
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
(a) A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
当一个 acceptor 收到这个 prepare 请求时，它会把这个 proposal number 与它之前见过（承诺过） 的值比较，如果小于它之前见过的，则拒绝这个 prepare 请求，否则就接受这个 proposal ，并承诺 (promise) 它不再接受比这个 proposal number 更小的 proposal 。同样还是 Lamport 的这篇文章：
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
(b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered proposal (if any) that it has accepted.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Emacs 28.1  正式发布，elisp原生编译支持来了！</title>
      <link>/notes/20220404_emacs_28_1_released/</link>
      <pubDate>Mon, 04 Apr 2022 00:00:00 +0000</pubDate>
      <guid>/notes/20220404_emacs_28_1_released/</guid>
      <description>&lt;p&gt;
期待已久的 Emacs 28.1 版本来了！这个版本中引入了一系列新特性，其中最引人注目，也最受期待的恐怕就是对Emacs Lisp（elisp）的原生编译支持（native compilation）。
&lt;/p&gt;

&lt;p&gt;
目前 Emacs 28.1 还没有apk等二进制包，需要从源代码安装，方法和以前版本类似。但需要注意的是，原生elisp编译特性默认是不打开的，需要在 &lt;code&gt;configure&lt;/code&gt; 时候加上 &lt;code&gt;--with-native-compilation&lt;/code&gt; 参数。
&lt;/p&gt;

&lt;p&gt;
另外，这个特性还依赖 &lt;code&gt;libgccjit&lt;/code&gt; 和 &lt;code&gt;gcc-10&lt;/code&gt; 包，需要先安装这些依赖包，否则 &lt;code&gt;--with-native-compilation&lt;/code&gt; 参数会被拒绝：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;sudo apt install -y libgccjit0 libgccjit-10-dev gcc-10
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
安装完依赖包，就可以编译 Emacs 28.1 了：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;CC&lt;/span&gt;=&lt;span style=&#34;font-style: italic;&#34;&gt;&#34;gcc-10&#34;&lt;/span&gt;
./autogen.sh
./configure --prefix=$&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;HOME&lt;/span&gt;/local/ --with-imagemagick --with-native-compilation
make -j2
make install
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
安装完成！
&lt;/p&gt;


&lt;div id=&#34;org38ae954&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2022-04-04_14-26-54_screenshot.png&#34; alt=&#34;2022-04-04_14-26-54_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
试试看这个 native elisp compilation ，随便写个简单的 elisp 文件然后用 &lt;code&gt;native-compile&lt;/code&gt; 函数编译：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-emacs-lisp&#34;&gt;(native-compile &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;serve.el&#34;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
生成的原生二进制文件会保存在 &lt;code&gt;~/.emacs.d/eln-cache/&lt;/code&gt; 目录下。用 &lt;code&gt;file&lt;/code&gt; 命令查看，可以看到它被编译成了原生的ELF文件：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;file ~/.emacs.d/eln-cache/28.1-18b24ab0/serve-902d1548-7e0cb452.eln 
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class=&#34;example&#34; id=&#34;orgb7241b1&#34;&gt;
.../serve-902d1548-7e0cb452.eln: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=92a81b6c71e5cfee84e9763d9ac0625a0164875c, not stripped
&lt;/pre&gt;

&lt;p&gt;
除了原生elisp编译外，Emacs 28.1 还有很多新特性，以后有空慢慢探索。 :-)
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子信息的不可克隆性 (2)</title>
      <link>/quantum/no-clone-theorem-2/</link>
      <pubDate>Fri, 07 Jan 2022 00:00:00 +0000</pubDate>
      <guid>/quantum/no-clone-theorem-2/</guid>
      <description>&lt;p&gt;
接前文，继续聊量子信息的不可克隆性这个话题。在前文中，我们用最简单的CNOT门为例子，演示了为什么CNOT量子电路只能复制经典信息，而并不能复制处于一般态的量子位。今天把这个讨论扩展到一般的情况，来证明 &lt;b&gt;&lt;b&gt;复制量子态的机器不可能存在&lt;/b&gt;&lt;/b&gt; 。
&lt;/p&gt;

&lt;p&gt;
基本思路是用反证法。假设我们有个能复制一个量子位的机器，那么这个机器构成的系统，至少包含这些部分：
&lt;/p&gt;
&lt;ol class=&#34;org-ol&#34;&gt;
&lt;li&gt;被克隆的量子位， \(|\psi\rangle = \alpha|0\rangle + \beta|1\rangle\)&lt;/li&gt;
&lt;li&gt;辅助量子位， \(|\phi\rangle\)&lt;/li&gt;
&lt;li&gt;机器本身的状态 \(|A\rangle\) ，初始状态用 \(|A_i\rangle\) 表示&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
既然这个机器是量子电路构成的，这个“克隆”过程也必然是一个幺正变换。把这个幺正变换记作U，那么我们要达到的是这样的一个变换过程：
&lt;/p&gt;

\begin{matrix}
U |\psi\rangle |\phi\rangle |A_i\rangle \rightarrow |\psi\rangle |\psi\rangle |A_{f\psi}\rangle
\end{matrix}

&lt;p&gt;
也就是输入的 \(|\psi\rangle\) 被复制成了2个，而机器的状态由初始的 \(|A_i\rangle\) 变成了某个终态，这个终态必然是依赖于输入 \(|\psi\rangle\) ，所以记作 \(|A_{f\psi}\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
显然，如果输入是 \(|0\rangle\) 或 \(|1\rangle\) 的话，我们有：
&lt;/p&gt;

\begin{matrix}
U |0\rangle |\phi\rangle |A_i\rangle \rightarrow |0\rangle |0\rangle |A_{f0}\rangle \\
U |1\rangle |\phi\rangle |A_i\rangle \rightarrow |1\rangle |1\rangle |A_{f1}\rangle \\
\end{matrix}

&lt;p&gt;
那么如果输入是一般的量子态呢？也就是
&lt;/p&gt;

&lt;p&gt;
\(|\psi\rangle = \alpha|0\rangle + \beta|1\rangle\) 
&lt;/p&gt;

&lt;p&gt;
把它代入到前面的变换过程里：
&lt;/p&gt;

\begin{matrix}
U |\psi\rangle |\phi\rangle |A_i\rangle \\
= U (\alpha|0\rangle + \beta|1\rangle) |\phi\rangle |A_i\rangle \\
\rightarrow \alpha U |0\rangle |\phi\rangle |A_{f0}\rangle + \beta U |1\rangle |\phi\rangle |A_{f1}\rangle \\
= \alpha |0\rangle |0\rangle |A_{f0}\rangle + \beta |1\rangle |1\rangle |A_{f1}\rangle \\
= \alpha |00\rangle |A_{f0}\rangle + \beta |11\rangle |A_{f1}\rangle \\
\end{matrix}

&lt;p&gt;
但是，这是不是我们想要的“克隆”效果呢？显然不是啊！我们要的“克隆”变换，变换后的状态应该是这样的：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子信息的不可克隆性 (1)</title>
      <link>/quantum/no-clone-theorem-1/</link>
      <pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate>
      <guid>/quantum/no-clone-theorem-1/</guid>
      <description>&lt;p&gt;
如果你关注过量子通信或量子信息的动态，应该会经常听到量子信息的“不可克隆性”这个概念。那么这个概念是怎么来的？今天就来聊一聊。
&lt;/p&gt;

&lt;p&gt;
先从一个简单的例子出发。从以前对CNOT门的讨论，它似乎可以用来“复制”信息：
&lt;/p&gt;


&lt;div id=&#34;org656ed6e&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/no-clone-cnot.jpg&#34; alt=&#34;no-clone-cnot.jpg&#34; width=&#34;240px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
看起来当辅助量子位是 \(|0\rangle\) 的时候，输出就得到了两个 \(|x\rangle\) ，真的是这样吗？
&lt;/p&gt;

&lt;p&gt;
实际上，CNOT门只能复制经典信息（也就是输入为 \(|0\rangle\) 或 \(|1\rangle\) 的情况）。如果输入是一般状态 \(|\psi\rangle=a|0\rangle+b|1\rangle\) ，CNOT并不能复制它！
&lt;/p&gt;

&lt;p&gt;
咱们来具体推导一下。首先在输入端，要复制的量子位是 \(a|0\rangle+b|1\rangle\) ，辅助量子位 \(|0\rangle\) ，合起来输入端状态就是
&lt;/p&gt;

\begin{matrix}
(a|0\rangle + b|1\rangle)|0\rangle = a|00\rangle + b|10\rangle
\end{matrix}

&lt;p&gt;
CNOT的作用是当第一个量子位为1的时候翻转第二个量子位，所以我们在输出端得到的状态是
&lt;/p&gt;

\begin{matrix}
a|00\rangle + b|11\rangle &amp; (1) \\
\end{matrix}

&lt;p&gt;
但是，我们要的是在输出端复制 \(|\psi\rangle\) ，也就是在输出端得到 \(|\psi\rangle|\psi\rangle\) ，这个状态展开写就是这样：
&lt;/p&gt;

\begin{matrix}
|\psi\rangle|\psi\rangle = (a|0\rangle+b|1\rangle)(a|0\rangle+b|1\rangle) &amp; \\
= a^2|00\rangle + ab|01\rangle + ab|10\rangle + b^2|11\rangle &amp; (2) \\
\end{matrix}

&lt;p&gt;
比较一下(1)和(2)，只有当 \(ab=0\) ，也就是要复制的量子位为 \(|0\rangle\) 或 \(|1\rangle\) 时，这两个式子才有可能相等，而在一般情况下，(1)和(2)不可能相等。
&lt;/p&gt;

&lt;p&gt;
这个推导告诉我们，CNOT组成的“复制”电路，只能复制经典比特，不能复制处于一般态的量子位。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>C&#43;&#43; vector容器强制释放内存</title>
      <link>/notes/20211127_cpp_container_force_free_memory/</link>
      <pubDate>Sat, 27 Nov 2021 00:00:00 +0000</pubDate>
      <guid>/notes/20211127_cpp_container_force_free_memory/</guid>
      <description>&lt;p&gt;
换个话题，聊聊使用C++ STL vector容器时的一个内存管理问题。
&lt;/p&gt;

&lt;p&gt;
C++用户经常会用STL里的vector容器来管理一组动态对象。STL vector的使用和C语言的数组类似，很方便：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;std&lt;/span&gt;::&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;vector&lt;/span&gt;&amp;lt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;buf&lt;/span&gt; = { 0, 1, 2, 3, 4, 5 };
buf[0]++;
buf[1] = 10;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
并且内存的使用和释放都由STL管理，用户不用像C语言那样手动malloc和free管理内存，确实省心不少。
&lt;/p&gt;

&lt;p&gt;
但是在有些情况下，我们需要对容器占用的内存进行手动管理。
&lt;/p&gt;

&lt;p&gt;
举个例子，假如我们有个数据管道，每次迭代生成一个样本池，里面有几百万个对象放在一个vector里，从中抽取一万个样本，然后交给管道作下一步处理，并且这个过程会反复进行。
&lt;/p&gt;

&lt;p&gt;
那么在这个反复迭代过程中，我们希望样本池在每次采样完后就从内存里释放掉。否则下一次迭代时，我们再次生成样本池，而前一次占用的内存的还没释放掉，就有可能因为内存不够导致程序崩溃（Out Of Memory）。
&lt;/p&gt;

&lt;p&gt;
你可能会问，这种情况用容器的clear方法不就好了？
&lt;/p&gt;

&lt;p&gt;
但是，clear方法只是把这个容器的size变成了0，如果它的capacity其实没变，也就是说，clear方法并没有真的释放容器占用的内存。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;buf.clear();

&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;std&lt;/span&gt;::cout &amp;lt;&amp;lt; buf.size();      &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;size&amp;#21464;&amp;#25104;&amp;#20102;0
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;std&lt;/span&gt;::cout &amp;lt;&amp;lt; buf.capacity();  &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;capacity&amp;#36824;&amp;#26159;6&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
那么在C++ STL里，是可以手动强制释放容器占用的内存的。方法不止一个，这里举个简单可靠的，就是用swap方法：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-c++&#34;&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;std&lt;/span&gt;::&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;vector&lt;/span&gt;&amp;lt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt;&amp;gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;buf&lt;/span&gt; = { 0, 1, 2, 3, 4, 5 };
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;......
&lt;/span&gt;
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;// &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24378;&amp;#21046;&amp;#37322;&amp;#25918;buf&amp;#21344;&amp;#29992;&amp;#30340;&amp;#20869;&amp;#23384;
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;std&lt;/span&gt;::vector&amp;lt;&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;int&lt;/span&gt;&amp;gt;().swap(buf);
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
解释一下这个方法的原理：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>脆弱的量子位：聊聊为什么量子计算机这么难造</title>
      <link>/quantum/why-quantum-is-hard/</link>
      <pubDate>Sat, 13 Nov 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/why-quantum-is-hard/</guid>
      <description>&lt;p&gt;
之前聊了不少量子计算的算法，今天换个话题，回到量子计算的一个根本问题，聊聊为什么量子计算机这么难制造。
&lt;/p&gt;

&lt;p&gt;
经典计算机里的晶体管，工作时状态是确定的0或1，这样的可靠性是因为晶体管的状态并不取决于单个电子，而是许多电子的平均。这样一来，即使有少数电子的状态受到干扰翻转了（例如电子在线路中逃逸了），也不影响整个晶体管的状态。这也是经典计算机能可靠工作的关键。
&lt;/p&gt;

&lt;p&gt;
举个例子来说，假如一个经典计算机里的比特由3个电子的状态表示，每个电子可能处于两种状态之一，记作 \(|\uparrow\rangle\) 和 \(|\downarrow\rangle\) 。在工作的时候，假如某个电子发生了翻转：
&lt;/p&gt;

\begin{matrix}
|\uparrow\uparrow\uparrow\rangle \Rightarrow |\uparrow\downarrow\uparrow\rangle
\end{matrix}

&lt;p&gt;
这种情况并不会造成经典比特的翻转。实际情况里，一个经典比特会由多得多的电子状态来表示，少数电子造成翻转的可能性就更小了，由此保证了经典计算机的可靠性。
&lt;/p&gt;

&lt;p&gt;
但是，这个性质也决定了经典计算机的『经典』特性，使它无法表现出『量子』的一面。
&lt;/p&gt;

&lt;p&gt;
设想一下，假如我们想用两个经典比特构造一个量子相干态：
&lt;/p&gt;

\begin{matrix}
|\uparrow\uparrow\uparrow\rangle |\downarrow\downarrow\uparrow\rangle
 + |\downarrow\downarrow\downarrow\rangle |\uparrow\uparrow\uparrow\rangle 
\end{matrix}

&lt;p&gt;
在逻辑上，这个状态可以写成
&lt;/p&gt;

\begin{matrix}
|L\rangle = |1\rangle |0\rangle + |0\rangle |1\rangle
\end{matrix}

&lt;p&gt;
假如某个电子翻转了，例如第一个晶体管的状态从 \(|\uparrow\uparrow\uparrow\rangle\) 变成了 \(|\uparrow\uparrow\downarrow\rangle\) ，物理状态发生了变化，但逻辑状态没有变：
&lt;/p&gt;

\begin{matrix}
|P\rangle = |\uparrow\uparrow\uparrow\rangle |\downarrow\downarrow\uparrow\rangle
 + |\downarrow\downarrow\downarrow\rangle
 |\uparrow\uparrow\uparrow\rangle \\
\Rightarrow
|\uparrow\uparrow\downarrow\rangle |\downarrow\downarrow\uparrow\rangle
 + |\downarrow\downarrow\downarrow\rangle |\uparrow\uparrow\uparrow\rangle \\
\\
|L\rangle = |1\rangle |0\rangle + |0\rangle |1\rangle \\
\Rightarrow |1\rangle |0\rangle + |0\rangle |1\rangle
\end{matrix}

&lt;p&gt;
您可能会问：这不挺好么？物理上虽然有电子发生了翻转，但逻辑上就像什么也没发生一样！
&lt;/p&gt;</description>
    </item>
    <item>
      <title>用量子搜索加速『NP完全』问题？</title>
      <link>/quantum/quantum_search_np/</link>
      <pubDate>Tue, 26 Oct 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/quantum_search_np/</guid>
      <description>&lt;p&gt;
最近和量子搜索干上了 :-) 聊聊另一个量子搜索的应用——加速NP完全问题的求解。
&lt;/p&gt;

&lt;p&gt;
学计算机朋友的应该都听说过『NP完全问题』。
&lt;/p&gt;

&lt;p&gt;
所谓NP完全问题（NP-complete problems），不是指某一个，而是指一类问题，它们的共同特征是，可以快速验证给定的答案是否正确（verify the solution），但是要找到正确的答案（find the solutions）则很难，目前还没有找到高效（多项式复杂度）的算法。并且这一类问题是可以互相转化的，也就是说如果某一个NP完全问题找到了高效的求解算法，那么所有其他的NP完全问题也都能够高效求解了。
&lt;/p&gt;

&lt;p&gt;
哈密尔顿回路问题（Hamilton cycle problem）就是一个NP完全问题。这个问题是说，给你一个图（有向或无向都可），请你找到一个访问图中所有顶点各一次的回路。这个问题又称为“旅行推销员问题”。
&lt;/p&gt;

&lt;p&gt;
用数学语言来分析，假设这个图有n个顶点 \(v_1, v_2, \dots, v_n\) ，我们要找的是一个包含n个顶点，也就是长度为n的路径。为方便讨论，我们假设允许顶点重复，那么把所有可能的长度为n的路径列出来，总共有 \(n^n\) 种可能，所以整个搜索空间有 \(n^n = 2^{n \log n}\) 个状态。哈密尔顿回路问题，就是要在这个搜索空间里找出符合条件的答案。
&lt;/p&gt;

&lt;p&gt;
显然，给定一个路径，我们很容易就能判断它是否是哈密尔顿回路，但是要从给定的图找到一个哈密尔顿回路，目前还没有发现高效（多项式）的算法。在经典计算机上，寻找哈密尔顿回路的复杂度是：
&lt;/p&gt;

\begin{matrix}
O(2^{n \lceil \log n \rceil})
\end{matrix}

&lt;p&gt;
那么在量子计算机上，是否可以做的更好呢？答案是肯定的。量子计算机可以对这个问题实现平方根（square root）加速：
&lt;/p&gt;

\begin{matrix}
O(2^{n \lceil \log n \rceil / 2})
\end{matrix}

&lt;p&gt;
接下来看看量子计算机是怎么做到的。
&lt;/p&gt;

&lt;p&gt;
首先我们得有个函数判断给定的路径是否是哈密尔顿回路：
&lt;/p&gt;

\begin{matrix}
f(v_1 v_2 \dots v_n) = \left\{
\begin{array}{ll}
1 &amp;&amp; v_1 v_2 \dots v_n \verb= is a Hamilton cycle= \\
0 &amp;&amp; otherwise
\end{array}
\right.
\end{matrix}

&lt;p&gt;
然后，把路径 \(v_1 v_2 \dots v_n\) 看作是量子电路的一个状态 \(|v\rangle\) 。比如，给某个顶点编号1到n，那么每个状态就是由n个1到n的数字来组成。显然要用二进制表示这样的状态，需要的肯定不止n比特，而是需要 \(n\log n\) 比特。因此在量子电路里需要 \(n\log n\) 个量子位来表示状态。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子搜索问题的扩展：量子计数</title>
      <link>/quantum/quantum_counting/</link>
      <pubDate>Tue, 19 Oct 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/quantum_counting/</guid>
      <description>&lt;p&gt;
前文&lt;a href=&#34;../grovers_algorithm_1/&#34;&gt;《量子搜索》&lt;/a&gt;讨论了Grover算法，今天来讨论量子搜索的一个扩展问题——量子计数。
&lt;/p&gt;

&lt;p&gt;
在量子搜索里，我们有一个搜索空间以及一个判断函数f，给定一个状态，f告诉我们它是不是要找的目标状态。Grover算法把f包装成一个G变换，经过多次迭代后测量，可以以任意高的概率得到答案（目标状态）。
&lt;/p&gt;

&lt;p&gt;
量子计数的问题和搜索有点类似，但它关注的是答案（目标状态）的数量：已知在N个项的搜索空间里有若干个（M个）答案，但是答案的数量M未知，如何高效的确定有多少个答案（也就是M的值）？
&lt;/p&gt;

&lt;p&gt;
如果用经典计算机来解决这个问题，就必须遍历整个搜索空间，也就是要查询 \(O(N)\) 次。
&lt;/p&gt;

&lt;p&gt;
如果用量子计算机的话，我们只需要用 \(O(\sqrt N)\) 次查询。相对经典计算机，这是一个多项式级别的加速（polynomial improvement）。之前讨论的&lt;a href=&#34;../shor_algorithm_1_rsa/&#34;&gt;Shor算法&lt;/a&gt;对因式分解问题有指数级加速，量子计数相比没那么耀眼，但也是一个显著的进步了。
&lt;/p&gt;

&lt;p&gt;
量子计数算法的思路其实很简单：把Grover算法中的G变换看作是相位估计里的U，放到量子相位估计电路里，根据估计出来的相位，就能得到目标状态的数量（M的值）。
&lt;/p&gt;

&lt;p&gt;
在&lt;a href=&#34;../grovers_algorithm_2/&#34;&gt;《Grover算法的可视化》&lt;/a&gt;一文中，我们知道G变换其实是一个旋转变换，画个图理解一下：
&lt;/p&gt;


&lt;div id=&#34;orgd92629e&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/quantum_counting_grover.jpg&#34; alt=&#34;quantum_counting_grover.jpg&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
在这个图里， \(|\alpha\rangle\) 表示所有非目标状态的叠加，而 \(|\beta\rangle\) 是所有目标状态的叠加。显然它们互相是正交的，如果用它们作为两个轴张成一个平面，那么Grover算法里的G变换就是把输入向量 \(|\psi\rangle\) 朝着 \(|\beta\rangle\) 轴旋转一个角度 \(\theta\) 。因此G变换可以写成这样的矩阵：
&lt;/p&gt;

\begin{bmatrix}
\cos\theta &amp;&amp; -sin\theta \\
\sin\theta &amp;&amp; \cos\theta
\end{bmatrix}

&lt;p&gt;
不难证明， \(|\alpha\rangle\) 和 \(|\beta\rangle\) 是G的两个本征向量，并且它们对应的本征值分别是 \(e^{i\theta}\) 和 \(e^{i(2\pi-\theta)}\) 。如果你有兴趣，可以拿纸笔验证一下。:-) 总之，这个G变换可以作为量子相位估计里的U来使用。
&lt;/p&gt;

&lt;p&gt;
把Grover算法里的G变换放到量子相位估计里：
&lt;/p&gt;


&lt;div id=&#34;org52fbffd&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/quantum_counting_circuit.jpg&#34; alt=&#34;quantum_counting_circuit.jpg&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
假设搜索空间大小N可以用n位二进制数表示，这里输入用了n+1位，也就是把搜索空间扩大了一倍。为什么要这么做呢？
&lt;/p&gt;

&lt;p&gt;
还记得前文讨论Grover算法有个特殊情况吗？就是目标状态（答案）的数量M超过N的一半，这种情况下Grover算法的性能反而会变差，因为每次旋转的角度太大了，转过头了。解决的办法，就是把搜索空间扩大一倍，确保目标状态的数量少于搜索空间的一半。这里用了同样的思路，因为我们不知道M是多少，干脆先在输入端把N扩大成2N，确保M少于搜索空间的一半。
&lt;/p&gt;

&lt;p&gt;
另一个寄存器需要的量子位数目t，是由我们需要的精度和成功率来决定。例如，假如我们需要相位估计达到m比特的精度，并且成功率大于 \(1-\epsilon\) ，那么t可以这样估算：
&lt;/p&gt;

\begin{matrix}
t = m + \lceil \log (2+\frac{1}{2\epsilon}) \rceil
\end{matrix}

&lt;p&gt;
运行这个电路，在右边测量得到相位估计的结果，也就是 \(\theta\) 的近似值。
&lt;/p&gt;

&lt;p&gt;
而根据Grover算法的原理和G变换的性质，我们知道：
&lt;/p&gt;

\begin{matrix}
\sin \frac{\theta}{2} = \sqrt \frac{M}{2N}
\end{matrix}

&lt;p&gt;
因此：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子相位估计的应用：求解幺正变换的平方根</title>
      <link>/quantum/square_root_of_unitary/</link>
      <pubDate>Fri, 27 Aug 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/square_root_of_unitary/</guid>
      <description>&lt;p&gt;
在前文&lt;a href=&#34;../phase-estimation/&#34;&gt;《量子相位估计》&lt;/a&gt;里，我们讨论了量子相位估计算法，它也是很多量子算法能有指数级加速的关键。今天来讨论量子相位估计的另一个应用：求解给定幺正变换的平方根。
&lt;/p&gt;

&lt;p&gt;
假如我们有一个幺正变换 \(U\) ，求解它的k次方 \(U^k\) 很容易，只要把k个 \(U\) 连起来就行，但是如果要求它的平方根 \(U^{1/2}\) 呢？事情就没那么简单了。今天来讨论一下，如何利用量子相位估计来求给定幺正变换的平方根，或者说，构造一个新的幺正变换，使它的平方等于给定的幺正变换。
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;先回顾一下什么是量子相位估计。&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
假如我们有一个幺正算符 \(U\) ， \(|u\rangle\) 是它的一个本征矢量，对应的本征值是 \(e^{i\phi}\) （ \(\phi\) 在0到 \(2\pi\) 之间）：
&lt;/p&gt;

\begin{matrix}
U|u\rangle = e^{i\phi}|u\rangle
\end{matrix}

&lt;p&gt;
&#34;量子相位估计&#34;问题，就是从幺正算符 \(U\) 和 \(|u\rangle\) ，估算相应的本征值 \(e^{i\phi}\) 里的相位 \(\phi\) 。
&lt;/p&gt;

&lt;p&gt;
量子相位估计的电路如下，输入 \(|u\rangle\) 是U的一个本征向量，在输出端测量，就能得到其本征值的相位 \(\psi=\phi/2\pi\) 的 &lt;b&gt;n位二进制近似&lt;/b&gt; 。
&lt;/p&gt;


&lt;div id=&#34;org072ec78&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/phase_estimation_simplified.jpg&#34; alt=&#34;phase_estimation_simplified.jpg&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
你可能注意到，这里的输入 \(|u\rangle\) 是U的 &lt;b&gt;一个&lt;/b&gt; 本征向量。但是，U可能有很多个本征向量，对不对？
&lt;/p&gt;

&lt;p&gt;
如果这个相位估计电路输入的不是某一个本征向量，而是U所有本征向量的叠加呢？右边输出会得到什么？也就是说，如果左边的输入是U的所有本征向量 \(|u_j\rangle\) 的叠加：
&lt;/p&gt;

\begin{matrix}
|\psi\rangle = \sum_j \alpha_j |u_j\rangle
\end{matrix}

&lt;p&gt;
猜猜右边会得到啥？
&lt;/p&gt;


&lt;div id=&#34;orgca53643&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/phase_estimation_simplified_generic.jpg&#34; alt=&#34;phase_estimation_simplified_generic.jpg&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
答案很容易猜到，既然左边的输入是本征向量的叠加，那么右边的输出，就是对应的相位估计结果的叠加：
&lt;/p&gt;

\begin{matrix}
U|\psi\rangle = \sum_j \alpha_j e^{i\phi_j} |u_j\rangle
\end{matrix}

&lt;p&gt;
也就是说，如果在右边测量，会得到某一个本征向量 \(|u_j\rangle\) 的相位估计，其概率取决于它在输入的叠加态里的强度 \(|\alpha_i|^2\) 。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算里用到的一些数学概念</title>
      <link>/quantum/theory-and-concepts/</link>
      <pubDate>Sat, 14 Aug 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/theory-and-concepts/</guid>
      <description>&lt;div id=&#34;outline-container-org490c832&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org490c832&#34;&gt;线性独立&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org490c832&#34;&gt;
&lt;p&gt;
如果一组矢量 \(|\alpha_1\rangle, \dots, \alpha_m\rangle\) 满足：
&lt;/p&gt;

\begin{matrix}
c_1|\alpha_1\rangle + c_2|\alpha_2\rangle + \dots + c_m|\alpha_m\rangle = 0
\end{matrix}

&lt;p&gt;
当且仅当
&lt;/p&gt;
\begin{matrix}
c_1 = c_2 = \dots = c_m = 0
\end{matrix}

&lt;p&gt;
那么称这组矢量就是线性独立的。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orge59fc26&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orge59fc26&#34;&gt;内积&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org41177cb&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org41177cb&#34;&gt;模&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org6ac3171&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org6ac3171&#34;&gt;柯西-施瓦茨不等式&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgf84e054&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgf84e054&#34;&gt;正交归一&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7f5d1bf&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7f5d1bf&#34;&gt;维数&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org69620db&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org69620db&#34;&gt;Gram-Schmidt分解，构造正交归一基方法&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org46e0339&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org46e0339&#34;&gt;线性算符&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7c50986&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7c50986&#34;&gt;线性算符之积&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org97b4127&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org97b4127&#34;&gt;完备性关系&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org34cc317&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org34cc317&#34;&gt;线性算符的矩阵表示&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org9335b58&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org9335b58&#34;&gt;Pauli矩阵&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org647d2c6&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org647d2c6&#34;&gt;投影算符&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgc59f7e9&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgc59f7e9&#34;&gt;多维子空间中的投影算符&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org97bd8d9&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org97bd8d9&#34;&gt;本征值，本征矢量&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org3be5f31&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org3be5f31&#34;&gt;厄米算符(Hermitian?)&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org32ce75a&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org32ce75a&#34;&gt;逆算符&lt;/h2&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7973c34&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7973c34&#34;&gt;幺正算符&lt;/h2&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Lisp里的延迟执行</title>
      <link>/notes/20210731_deferred_execution_lisp/</link>
      <pubDate>Sat, 31 Jul 2021 00:00:00 +0000</pubDate>
      <guid>/notes/20210731_deferred_execution_lisp/</guid>
      <description>&lt;p&gt;
在图计算和协程里经常会用到的“延迟执行”，在Lisp里，只需要十几行代码就能实现！
&lt;/p&gt;

&lt;div id=&#34;outline-container-org944b796&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org944b796&#34;&gt;创建和使用延迟执行对象&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org944b796&#34;&gt;
&lt;p&gt;
首先我们来定义一个宏 &lt;code&gt;defer&lt;/code&gt; ，它的作用是把输入的代码块变成一个延迟执行的对象（其实是一个闭包）：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defmacro&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;defer&lt;/span&gt; (&lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;&amp;amp;body&lt;/span&gt; body)
  (&lt;span style=&#34;font-weight: bold;&#34;&gt;let&lt;/span&gt; ((executed (gensym))
        (result (gensym)))
    `(&lt;span style=&#34;font-weight: bold;&#34;&gt;let&lt;/span&gt; ((,executed nil)
           (,result nil))
       (&lt;span style=&#34;font-weight: bold;&#34;&gt;lambda&lt;/span&gt; ()
         (&lt;span style=&#34;font-weight: bold;&#34;&gt;unless&lt;/span&gt; ,executed
           (setf ,result (&lt;span style=&#34;font-weight: bold;&#34;&gt;progn&lt;/span&gt; ,@body))
           (setf ,executed t))
         ,result))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
而如果要执行之前被推迟的代码，可以用这个函数：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;defer-execute&lt;/span&gt; (deferred)
  (funcall deferred))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
例如，我们有这样一块代码：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(princ &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;Hello, world.&#34;&lt;/span&gt;)
(princ (1+ 2))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
用 &lt;code&gt;defer&lt;/code&gt; 来包装它：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(defer
  (princ &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;Hello, world.&#34;&lt;/span&gt;)
  (princ (1+ 2)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;defer&lt;/code&gt; 会把输入的代码块包装成一个 &lt;b&gt;闭包&lt;/b&gt; 返回给我们。所以通常我们要把这个闭包存在一个变量里：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defvar&lt;/span&gt; &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;*deferred-hello*&lt;/span&gt;
  (defer
    (princ &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;Hello, world.&#34;&lt;/span&gt;)
    (princ (1+ 2))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
等我们想要执行这个代码块的时候，只需要用 &lt;code&gt;defer-execute&lt;/code&gt; 来调用这个闭包：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子相位估计 Phase Estimation</title>
      <link>/quantum/phase-estimation/</link>
      <pubDate>Mon, 26 Jul 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/phase-estimation/</guid>
      <description>&lt;p&gt;
之前的Shor算法系列，其实还有个相关的坑，就是“量子相位估计” (Phase Estimation)，今天也来填一下。:-)
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgfdf32e5&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgfdf32e5&#34;&gt;什么是“量子相位估计”？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgfdf32e5&#34;&gt;
&lt;p&gt;
假如我们有一个幺正算符 \(U\) ，他的本征矢量是 \(|u\rangle\) ，本征值是 \(e^{i\phi}\) （ \(\phi\) 在0到 \(2\pi\) 之间）。
&lt;/p&gt;

&lt;p&gt;
&#34;量子相位估计“问题，就是从幺正算符 \(U\) 和它的本征矢量 \(|u\rangle\) ，估算它的本征值 \(e^{i\phi}\) 里的相位 \(\phi\) ：
&lt;/p&gt;

&lt;p&gt;
Estimate phase \(\phi\) of an eigenvalue of an unitary operator \(U\), given its eigenvector \(|u\rangle\).
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgb6aaa96&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgb6aaa96&#34;&gt;量子相位估计算法&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgb6aaa96&#34;&gt;
&lt;p&gt;
既然幺正算符 \(U\) 的本征矢量是 \(|u\rangle\) ，并且其对应的本征值是 \(e^{i\phi}\) ，那么根据本征矢量的定义：
&lt;/p&gt;

\begin{matrix}
U|u\rangle = e^{i\phi}|u\rangle
\end{matrix}

&lt;p&gt;
那么如果对 \(|u\rangle\) 进行多次U变换，不难推导出：
&lt;/p&gt;

\begin{matrix}
U^k|u\rangle = e^{ik\phi}|u\rangle
\end{matrix}

&lt;p&gt;
所以多次对 \(|u\rangle\) 进行 \(U\) 变换，得到的状态仍然是 \(|u\rangle\) ，只不过给它增加了一个全局相位。
&lt;/p&gt;

&lt;p&gt;
我们可以从 \(U\) 制备出一组（n个）这样的幺正变换： \(U, U^2, U^4, U^8, \dots, U^{2^{n-1}}\)
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子傅里叶变换详解 (3) 电路实现</title>
      <link>/quantum/qft-3/</link>
      <pubDate>Mon, 12 Jul 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/qft-3/</guid>
      <description>&lt;p&gt;
今天来填个坑 :-) 前面Shor算法系列里大量用到的“量子傅立叶变换”，具体要怎么实现？这次来详细讨论一下。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org338c92b&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org338c92b&#34;&gt;量子傅立叶变换&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org338c92b&#34;&gt;
&lt;p&gt;
&lt;a href=&#34;../qft-1/&#34;&gt;前文&lt;/a&gt;说过，量子傅立叶变换QFT是这么一块电路：
&lt;/p&gt;


&lt;div id=&#34;orgef810d9&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/qft_box.jpg&#34; alt=&#34;qft_box.jpg&#34; width=&#34;560px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
同样根据&lt;a href=&#34;../qft-1/&#34;&gt;前文&lt;/a&gt;，当输入n个量子位的基态 \(|j\rangle\) 时，输出可以写成：
&lt;/p&gt;

\begin{matrix}
QFT|j\rangle = \\
\frac{1}{\sqrt{2^n}} \otimes_{l=1}^n \left[ |0\rangle + e^{2\pi ij/2^l} |1\rangle \right] \\
= \frac{1}{\sqrt{2^n}}
\left( |0\rangle + e^{2\pi i 0.j_0}|1\rangle \right)
\left( |0\rangle + e^{2\pi i 0.j_1j_0}|1\rangle \right)
\dots
\left( |0\rangle + e^{2\pi i 0.j_{n-1}j_{n-2}\dots j_0}|1\rangle \right) \\
\end{matrix}

&lt;p&gt;
我们的任务是用基本量子电路门构造这样的变换。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org2214a2e&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org2214a2e&#34;&gt;基本构件：受控旋转门&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org2214a2e&#34;&gt;
&lt;p&gt;
就像搭积木一样，量子傅立叶变换的电路是由很多基本组件构成的。其中最重要的一个组件是“受控旋转门” \(R_k\) ：
&lt;/p&gt;


&lt;div id=&#34;orgf20cc67&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/qft_rk.jpg&#34; alt=&#34;qft_rk.jpg&#34; width=&#34;160px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
这个量子门和CNOT门差不多，有一个控制输入，当控制输入是 \(|0\rangle\) 的时候，输入状态原样输出；而如果控制输入是 \(|1\rangle\) ，就对输入状态绕Z轴旋转一定的角度。旋转多少角度呢？这是由门的参数k决定的，具体来说就是 \(2\pi/2^k\) 。换句话说， \(R_k\) 的作用就是当控制输入为 \(|1\rangle\) 时，给输入作这样的一个变换：
&lt;/p&gt;

\begin{bmatrix}
1 &amp; 0 \\
0 &amp; e^{\frac{2\pi i}{2^k}}
\end{bmatrix}
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7dbb425&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7dbb425&#34;&gt;量子傅立叶变换电路&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org7dbb425&#34;&gt;
&lt;p&gt;
有了受控旋转门 \(R_k\) ，接下来我们就可以来搭建量子傅里叶变换电路了！
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子算法之Simon问题</title>
      <link>/quantum/simons_problem/</link>
      <pubDate>Wed, 07 Jul 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/simons_problem/</guid>
      <description>&lt;p&gt;
随手翻到一个Simon问题，今天就来聊聊它。Simon问题和之前讨论的Deutsch问题有点类似，也是一个用量子计算机能够达到指数级加速的问题。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org466d1ff&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org466d1ff&#34;&gt;什么是Simon问题？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org466d1ff&#34;&gt;
&lt;p&gt;
和Deutsch问题类似，假设有人给我们一个函数f(x)，内部实现未知，只知道它输入n位二进制数，输出也是n位二进制数。用数学语言描述就是这样：
&lt;/p&gt;

\begin{matrix}
f: \{ 0,1 \}^n \rightarrow \{ 0,1 \}^n
\end{matrix}

&lt;p&gt;
然后又知道存在某个数a，使得 \(f(y)=f(x)\) 当且仅当 \(y=x \oplus a\) ，这个a也称作函数f(x)的周期。为方便讨论，假设a不是0。
&lt;/p&gt;

&lt;p&gt;
Simon问题要解决的，就是求解这个周期a的值。
&lt;/p&gt;

&lt;p&gt;
在经典计算机上，这是一个困难的问题。因为f(x)是未知的，我们能做的就是从0到 \(2^n-1\) 中，每次选一个数作为输入计算f(x)，直到发现重复的输出。最坏情况下，需要遍历超过一半的输入，即 \(2^{n-1}+1\) 次。根据Birthday Paradox，这个问题在经典计算机上需要的查询次数（时间复杂度）是 \(O(2^{n/2})\) 。
&lt;/p&gt;

&lt;p&gt;
而在量子计算机上，我们只需要运行电路 \(O(n)\) 次就能以任意高的成功率得到结果。相对经典计算机，这是 &lt;b&gt;指数级别&lt;/b&gt; 的加速！
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgae168f1&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgae168f1&#34;&gt;Simon问题的量子算法&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgae168f1&#34;&gt;
&lt;p&gt;
和之前的其他量子算法一样，首先我们得把函数f(x)包装成可逆变换 \(U_f\) ：
&lt;/p&gt;


&lt;div id=&#34;org2a3e2d0&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/simon_uf.jpg&#34; alt=&#34;simon_uf.jpg&#34; width=&#34;240px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
然后放到Simon问题的量子电路里：
&lt;/p&gt;

&lt;div id=&#34;org3c1c036&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/simon_circuit.jpg&#34; alt=&#34;simon_circuit.jpg&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
接下来我们来详细分析一下这个量子算法的过程。
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org8c8e485&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org8c8e485&#34;&gt;t0时刻&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org8c8e485&#34;&gt;
&lt;p&gt;
第一个寄存器经过n位的H变换，变成 \(|0\rangle\) 到 \(|2^n-1\rangle\) 的叠加态，即 \(\frac{1}{2^{n/2}}\sum_{x=0}^{2^n-1}|x\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
第二个寄存器状态是 \(|0\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
所以合起来，t0时刻的状态是 \(\frac{1}{2^{n/2}}\sum_{x=0}^{2^n-1}|x\rangle|0\rangle\)
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgde1d063&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;orgde1d063&#34;&gt;t1时刻&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-orgde1d063&#34;&gt;
&lt;p&gt;
经过 \(U_f\) 变换，两个寄存器进入纠缠态，第一个寄存器还是 \(\frac{1}{2^{n/2}}\sum_{x=0}^{2^n-1}|x\rangle\) ，第二个寄存器变成了 \(|f(x)\rangle\) 。所以系统状态是：
&lt;/p&gt;

&lt;p&gt;
\(\frac{1}{2^{n/2}}\sum_{x=0}^{2^n-1}|x\rangle|f(x)\rangle\) 
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgf6d728e&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;orgf6d728e&#34;&gt;t2时刻&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-orgf6d728e&#34;&gt;
&lt;p&gt;
对第二个寄存器进行测量，使它坍缩到某个值 \(z=f(x_0)\) 。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>实战：用Cirq实现量子因式分解算法</title>
      <link>/quantum/shor_impl_cirq/</link>
      <pubDate>Fri, 02 Jul 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_impl_cirq/</guid>
      <description>&lt;p&gt;
前面的“量子因式分解系列”讨论了Shor算法和它的量子电路，今天切换到编程模式，用谷歌的Cirq框架来实现Shor算法，并用它来解决简单的因式分解问题。
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgf5e259a&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgf5e259a&#34;&gt;导入依赖包&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgf5e259a&#34;&gt;
&lt;p&gt;
首先还是导入需要的包：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; fractions
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; math
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; random
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; sympy
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; cirq
&lt;span style=&#34;font-weight: bold;&#34;&gt;from&lt;/span&gt; cirq.contrib.svg &lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; SVGCircuit
&lt;span style=&#34;font-weight: bold;&#34;&gt;from&lt;/span&gt; typing &lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; Callable, List, Optional, Sequence, Union
%matplotlib inline
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org343ecb1&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org343ecb1&#34;&gt;指数取模运算&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org343ecb1&#34;&gt;
&lt;p&gt;
还记得Shor算法的量子电路吗？
&lt;/p&gt;

&lt;div id=&#34;orgece4048&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf3.png&#34; alt=&#34;shor2_uf3.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
首先，我们需要一个“指数取模”的量子变换 \(U_f\) :
&lt;/p&gt;

\begin{matrix}
U_f |x\rangle|y\rangle \rightarrow |x\rangle|ya^x \mod N\rangle
\end{matrix}

&lt;p&gt;
它的输入 \(|x\rangle|y\rangle\) ，输出是 \(|x\rangle|ya^x \mod N\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
在Cirq里面怎么实现这个变换呢？一种办法是按照我之前的文章《聊聊量子因式分解算法的实现》，从加法器和乘法器开始，逐层往上实现。这个工程量就大了，我们也没有真的量子计算机可以跑不是？另一种方法是，利用Cirq框架提供的ArithmeticOperation接口，用经典计算来模拟这个变换：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;mod_pow&lt;/span&gt;(a:&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, x:&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, N:&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, y:&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;=1) -&amp;gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;:
    &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;p&lt;/span&gt; = y
    &lt;span style=&#34;font-weight: bold;&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;font-weight: bold;&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;range&lt;/span&gt;(x):
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;p&lt;/span&gt; = (p * a) % N
        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; p

&lt;span style=&#34;font-weight: bold;&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ModularExp&lt;/span&gt;(cirq.ArithmeticOperation):
    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;__init__&lt;/span&gt;(
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;,
        y: Sequence[cirq.Qid],
        x: Union[&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, Sequence[cirq.Qid]],
        a: &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;,
        N: &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;,
    ) -&amp;gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;None&lt;/span&gt;:
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;len&lt;/span&gt;(y) &amp;lt; N.bit_length():
            &lt;span style=&#34;font-weight: bold;&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ValueError&lt;/span&gt;(
                f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;Register with &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;len&lt;/span&gt;(y)}&lt;span style=&#34;font-style: italic;&#34;&gt; qubits is too small for N &lt;/span&gt;{N}&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;&lt;/span&gt;
            )
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;y&lt;/span&gt; = y
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;x&lt;/span&gt; = x
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a&lt;/span&gt; = a
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;N&lt;/span&gt; = N

    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;registers&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;) -&amp;gt; Sequence[Union[&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, Sequence[cirq.Qid]]]:
        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.y, &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.x, &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.a, &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.N

    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;with_registers&lt;/span&gt;(
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;,
        *new_registers: Union[&lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;, Sequence[&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;cirq.Qid&#39;&lt;/span&gt;]],
    ) -&amp;gt; &lt;span style=&#34;font-style: italic;&#34;&gt;&#39;ModularExp&#39;&lt;/span&gt;:
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;len&lt;/span&gt;(new_registers) != 4:
            &lt;span style=&#34;font-weight: bold;&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ValueError&lt;/span&gt;(
                f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;Expected 4 registers (y, x, a, &#39;&lt;/span&gt;
                f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;N), but got &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;len&lt;/span&gt;(new_registers)}&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;&lt;/span&gt;
            )
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;y&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;N&lt;/span&gt; = new_registers
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;isinstance&lt;/span&gt;(y, Sequence):
            &lt;span style=&#34;font-weight: bold;&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ValueError&lt;/span&gt;(f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;y must be a qubit register, got &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;type&lt;/span&gt;(y)}&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;&lt;/span&gt;)
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;isinstance&lt;/span&gt;(a, &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;):
            &lt;span style=&#34;font-weight: bold;&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ValueError&lt;/span&gt;(f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;a must be a classical constant, got &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;type&lt;/span&gt;(a)}&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;&lt;/span&gt;)
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;isinstance&lt;/span&gt;(N, &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;):
            &lt;span style=&#34;font-weight: bold;&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;ValueError&lt;/span&gt;(f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;N must be a classical constant, got &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;type&lt;/span&gt;(N)}&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;&lt;/span&gt;)
        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; ModularExp(y, x, a, N)

    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;apply&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;, *register_values: &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;) -&amp;gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;int&lt;/span&gt;:
        &lt;span style=&#34;font-weight: bold;&#34;&gt;assert&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;len&lt;/span&gt;(register_values) == 4
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;y&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;N&lt;/span&gt; = register_values
        &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; y &amp;gt;= N:
            &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; y
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;return (y * a ** x) % N
&lt;/span&gt;        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; mod_pow(a=a, x=x, N=N, y=y)

    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;_circuit_diagram_info_&lt;/span&gt;(
        &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;,
        args: cirq.CircuitDiagramInfoArgs,
    ) -&amp;gt; cirq.CircuitDiagramInfo:
        &lt;span style=&#34;font-weight: bold;&#34;&gt;assert&lt;/span&gt; args.known_qubits &lt;span style=&#34;font-weight: bold;&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;None&lt;/span&gt;
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;wire_symbols&lt;/span&gt;: List[&lt;span style=&#34;font-weight: bold;&#34;&gt;str&lt;/span&gt;] = []
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;e&lt;/span&gt; = 0, 0
        &lt;span style=&#34;font-weight: bold;&#34;&gt;for&lt;/span&gt; qubit &lt;span style=&#34;font-weight: bold;&#34;&gt;in&lt;/span&gt; args.known_qubits:
            &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; qubit &lt;span style=&#34;font-weight: bold;&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.y:
                &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; t == 0:
                    &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.x, Sequence):
                        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;e_str&lt;/span&gt; = &lt;span style=&#34;font-style: italic;&#34;&gt;&#39;e&#39;&lt;/span&gt;
                    &lt;span style=&#34;font-weight: bold;&#34;&gt;else&lt;/span&gt;:
                        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;e_str&lt;/span&gt; = &lt;span style=&#34;font-weight: bold;&#34;&gt;str&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.x)
                    wire_symbols.append(f&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;ModularExp(t*&lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.a}&lt;span style=&#34;font-style: italic;&#34;&gt;**&lt;/span&gt;{e_str}&lt;span style=&#34;font-style: italic;&#34;&gt; % &lt;/span&gt;{&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.N}&lt;span style=&#34;font-style: italic;&#34;&gt;)&#39;&lt;/span&gt;)
                &lt;span style=&#34;font-weight: bold;&#34;&gt;else&lt;/span&gt;:
                    wire_symbols.append(&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;t&#39;&lt;/span&gt; + &lt;span style=&#34;font-weight: bold;&#34;&gt;str&lt;/span&gt;(t))
                &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;t&lt;/span&gt; += 1
            &lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;isinstance&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.x, Sequence) &lt;span style=&#34;font-weight: bold;&#34;&gt;and&lt;/span&gt; qubit &lt;span style=&#34;font-weight: bold;&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;.x:
                wire_symbols.append(&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;e&#39;&lt;/span&gt; + &lt;span style=&#34;font-weight: bold;&#34;&gt;str&lt;/span&gt;(e))
                &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;e&lt;/span&gt; += 1
        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; cirq.CircuitDiagramInfo(wire_symbols=&lt;span style=&#34;font-weight: bold;&#34;&gt;tuple&lt;/span&gt;(wire_symbols))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
利用cirq.ArithmeticOperation接口，模拟实现一个指数取模变换ModularExp，这其中的核心就是apply函数：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>聊聊量子因式分解算法的实现</title>
      <link>/quantum/shor_impl_thoughts/</link>
      <pubDate>Tue, 22 Jun 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_impl_thoughts/</guid>
      <description>&lt;p&gt;
在前面的Shor算法系列中，我讨论了用量子计算进行因式分解的思路。但是这些基本上都是理论上的推导，实际要实现量子因式分解，有哪些难点？解决的思路又是什么？今天就来聊聊我的实现方法。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org46e806b&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org46e806b&#34;&gt;Shor算法实现的难点：指数取模&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org46e806b&#34;&gt;
&lt;p&gt;
在Shor算法中，我们用量子计算来寻找函数 \(f(x)=a^x \mod N\) 的周期（order），并且这一步也是Shor算法中唯一的『量子』部分。
&lt;/p&gt;


&lt;div id=&#34;org9fed0f4&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf3.png&#34; alt=&#34;shor2_uf3.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
为什么Shor算法这么设计？因为“order finding”这一步目前还没有找到高效的经典算法，而量子计算机则可以用多项式代价求解。
&lt;/p&gt;

&lt;p&gt;
问题来了，这个电路看起来也太『简单』了吧！这么难的一个问题，这样几个电路块就解决了？
&lt;/p&gt;

&lt;p&gt;
实际上，为了方便讨论算法，上面的这个电路图作了大量的抽象，其中关键的一步，就是把函数 \(f(x)\) 包装成了一个计算『指数取模』的可逆变换 \(U_f\) 。有了这样一个可逆变换，我们才能给它输入 \(2^n\) 个基态的叠加态，实现对经典算法的指数级加速。
&lt;/p&gt;

&lt;p&gt;
这个 \(U_f\) 变换有2个寄存器，分别都是n个量子位：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;第一个是寄存器 \(|x\rangle\) ，就是要对a取的指数，&lt;/li&gt;
&lt;li&gt;第二个是寄存器是辅助的 \(|1\rangle\) （注意，这也是个n量子位的寄存器）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
在另一端， \(U_f\) 输出 \(|x\rangle\) ，以及 \(|a^x \mod N\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
那这个『指数取模』可逆变换 \(U_f\) 怎么实现呢？
&lt;/p&gt;

&lt;p&gt;
一个偷懒的方法是，用经典电路实现这样一个变换，把它接入到量子电路里。但是，我们给它的输入是 \(2^n\) 个基态的叠加态，而这个经典电路一次只能计算一个f(x)值，遇到这种叠加态的输入就傻眼了：这不还是 \(O(2^n)\) 的时间复杂度么？运行这样的电路，等于只是用经典电路模拟了Shor算法而已。
&lt;/p&gt;

&lt;p&gt;
所以，这个『指数取模』可逆变换 \(U_f\) 必须是『量子』的，不能用经典电路来实现。这也是Shor算法实现中的一个主要的挑战和难点。
&lt;/p&gt;

&lt;p&gt;
接下来，我会用自顶向下的方式，逐个分解讨论。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgbf97dc2&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgbf97dc2&#34;&gt;量子指数取模&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgbf97dc2&#34;&gt;
&lt;p&gt;
这个『量子指数取模』变换的实现，其实方法不止一种，这里结合我对相关论文的理解，提出我的一个实现方法。
&lt;/p&gt;

&lt;p&gt;
首先， \(a^x \mod N\) 里面，N是给定的数，a是每次运行电路前随机选定的一个数，所以运行时候也是已知的，这两个都可以看作是已知的常量。
&lt;/p&gt;

&lt;p&gt;
然后，变量x是n位二进制数，而我们知道n位二进制数可以表示成这样的形式：
&lt;/p&gt;

\begin{matrix}
x = \left( x_{n-1} x_{n-2} \dots x_0 \right)_2  \\
 = x_{n-1}2^{n-1} + x_{n-2}2^{n-2} \dots x_02^0 \\
\end{matrix}

&lt;p&gt;
其中的 \(x_{n-1}, x_{n-2}, \dots x_0\) 都是0或1。因为x是用在a的指数上， \(a^x \mod N\) 也可以写成：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>用Lisp可视化量子因式分解Shor算法的例子</title>
      <link>/quantum/lisp_shor_example/</link>
      <pubDate>Fri, 11 Jun 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/lisp_shor_example/</guid>
      <description>&lt;p&gt;
在前文中我初步尝试了Lisp的数值计算和绘图功能，本着“拿着锤子找钉子”的精神，这次我们来搞个实际的应用，用Lisp来演示和可视化量子因式分解算法（Shor​算法）。
&lt;/p&gt;

&lt;p&gt;
首先还是引用numcl和vgplot包：
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(ql:quickload &lt;span style=&#34;font-weight: bold;&#34;&gt;:numcl&lt;/span&gt;)
(ql:quickload &lt;span style=&#34;font-weight: bold;&#34;&gt;:vgplot&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
假设我们要分解的数是N（前文的例子中N=21），Shor算法需要先在1到N-1之间随机选一个与N互质的数a，然后用量子计算求解函数 \(f(x)=a^x \mod N\) 的周期r。要可视化这个求解的过程，我们得先知道r，所以先写个辅助函数：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-lisp&#34;&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;a-x-mod-N&lt;/span&gt; (a x N)
  (mod (expt a x) N))

&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;Find order for function f(x)=a^x mod N
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;also returns the &#34;modulo sequence&#34; of the function.
&lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;E.g. a=11, N=21 -&amp;gt; returns order r=6 and sequence (1 11 16 8 4 2)
&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;shor-find-mods&lt;/span&gt; (a N)
  (&lt;span style=&#34;font-weight: bold;&#34;&gt;labels&lt;/span&gt; ((next-mod (a x N mods)
             (&lt;span style=&#34;font-weight: bold;&#34;&gt;let&lt;/span&gt; ((r (a-x-mod-n a x N)))
               (&lt;span style=&#34;font-weight: bold;&#34;&gt;if&lt;/span&gt; (equal r (car mods))  &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;; check if the mod sequence is repeating
&lt;/span&gt;                   mods                  &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;; stop when mod sequence starts repeating
&lt;/span&gt;                   (next-mod a           &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;; otherwise continue with a^(x+1) mod N
&lt;/span&gt;                             (+ 1 x)
                             N
                             (append mods (list r)))))))
    (&lt;span style=&#34;font-weight: bold;&#34;&gt;let&lt;/span&gt; ((mod-seq (next-mod a 0 N nil)))
      &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&#34;values&#34; returns multiple values
&lt;/span&gt;      &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;By default the first value is used, but caller can get other values using
&lt;/span&gt;      &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;multiple-value-bind:
&lt;/span&gt;      &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;;; &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;(multiple-value-bind (a b) (foo))
&lt;/span&gt;      (values (length mod-seq) mod-seq))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;code&gt;a-x-mod-N&lt;/code&gt; 函数很简单，就是返回 \(a^x \mod N\) 的值。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(6) 实战篇</title>
      <link>/quantum/shor_algorithm_6/</link>
      <pubDate>Mon, 07 Jun 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_6/</guid>
      <description>&lt;p&gt;
量子计算机解决因式分解问题的Shor算法，前面讨论了许多背后的数学原理，未免有点抽象，今天用一个简单的例子来实际演示一下，相信可以对Shor算法有更直观的认识。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org219f682&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org219f682&#34;&gt;因式分解问题：N=21&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org219f682&#34;&gt;
&lt;p&gt;
人肉计算，数字不能用的太大 :-) 这里就假设我们要分解N=21这个数，以它为例子。
&lt;/p&gt;

&lt;p&gt;
根据Shor算法，首先我们要在1到N之间选个随机数a，然后看a和N是否互质：
&lt;/p&gt;

&lt;p&gt;
如果a和N不互质， \(GCD(a, N) \ne 1\) ，那么我们很走运， \(GCD(a, N)\) 就是N的因子之一，问题解决。
&lt;/p&gt;

&lt;p&gt;
如果a和N互质，比如说a=11，继续下一步，用量子计算机寻找函数 \(f(x)=a^x \mod N\) 的周期r，也就是使得 \(1=a^r \mod N\) 的最小正整数r。
&lt;/p&gt;

&lt;p&gt;
在这个例子里，假设我们挑选的随机数a=11，所以我们要找的是这个函数的周期：
&lt;/p&gt;

\begin{matrix}
f(x)=11^x \mod 21
\end{matrix}

&lt;p&gt;
我们简单算一下就可以知道，这个函数的周期是6:
&lt;/p&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;


&lt;colgroup&gt;
&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;\(x\)&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;\(f(x)=11^x \mod 21\)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;0&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;11&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;2&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;16&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;3&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;8&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;4&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;4&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;5&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;2&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;6&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;&amp;#x2026;&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;&amp;#x2026;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
但是在这个例子中，函数周期是未知的，需要用量子计算机来求解。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgf711af0&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgf711af0&#34;&gt;找函数周期&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgf711af0&#34;&gt;
&lt;p&gt;
前文已经讨论过，量子计算寻找函数周期的电路长这样：
&lt;/p&gt;


&lt;div id=&#34;org528cc5f&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf3.png&#34; alt=&#34;shor2_uf3.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
这里面的2个寄存器，分别需要多少个量子位呢？
&lt;/p&gt;

&lt;p&gt;
一般的规则是，寄存器能表达的最大二进制数q，应该不小于N的平方： \(N^2 \le q \le 2N^2\) （这个规则是从概率推导出来的，具体可参见相关教科书）。因此，量子位的数目n，应该不小于 \(\log N^2\) 。在这个例子里面，N=21 ，所以每个寄存器需要9个量子位， \(q=2^9=512\) 。
&lt;/p&gt;

&lt;p&gt;
经过 \(U_f\) 后，我们其实就不关心第2个寄存器 \(|\beta\rangle\) 的状态了，换句话说我们『丢弃』了第2个寄存器。但在量子计算中，『丢弃』实际上也意味着一种『隐性的测量』（implicit measurement），所以，可以认为 \(|\beta\rangle\) 隐性的坍缩到了某个值z。
&lt;/p&gt;

&lt;p&gt;
而这时候的第1个寄存器 \(|\alpha\rangle\) ，必然处于使得 \(11^x \mod 21 = z\) 的所有 \(|x\rangle\) 的叠加态。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(5.3) 补充证明</title>
      <link>/quantum/shor_algorithm_5.3/</link>
      <pubDate>Fri, 04 Jun 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_5.3/</guid>
      <description>&lt;p&gt;
在前文『&lt;a href=&#34;shor_algorithm_5.../2/&#34;&gt;(5.2) Shor算法详解&lt;/a&gt;』中，我留了一个尾巴需要补充证明，今天就来填上这个坑，把补丁补上。
&lt;/p&gt;

&lt;p&gt;
在前文讨论概率的推导过程中，有一步看起来不起眼，就是我们需要保证 \(\alpha\) 在0到 \(\pi/2\) 之间（其中 \(\alpha = \frac{A+1}{2} |\theta|\) ）。
&lt;/p&gt;

&lt;p&gt;
但这一步其实很重要，为什么呢？因为只有保证 \(\alpha\) 在0到 \(\pi/2\) 之间，我们才能说 \(\sin \alpha \ge 2\alpha / \pi\) ：
&lt;/p&gt;


&lt;div id=&#34;org1d2c816&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/sin_x.png&#34; alt=&#34;sin_x.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
如果 \(\alpha\) 超过了 \(\pi/2\) ，这个性质就不成立了。而这个不等式，是后面我们推导出 \(|\tilde f(y)|^2\) ，也就是Shor算法成功概率的前提。
&lt;/p&gt;

&lt;p&gt;
回顾一下， \(\theta = 2\pi \frac{ry \mod q}{q}\) ，并且y满足不等式 \(|ry \mod q| \le r/2\) 。
&lt;/p&gt;

&lt;p&gt;
我们想要的不等式是：
&lt;/p&gt;

\begin{matrix}
\alpha = \frac{A+1}{2} \vert \theta \vert \le \frac{\pi}{2}
\end{matrix}

&lt;p&gt;
怎样才能证明这个不等式呢？
&lt;/p&gt;

&lt;p&gt;
我自己研究了一下，应该可以这样证明。
&lt;/p&gt;

&lt;p&gt;
首先来看一下A是怎么计算的。如果我们把 \(l, l+r, \dots, l+Ar\) 画在数轴上，大致是这样的：
&lt;/p&gt;


&lt;div id=&#34;org1bc6cab&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor_l_Ar_q.png&#34; alt=&#34;shor_l_Ar_q.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
所以A的意义是在0到q-1之间，去掉开头的offset和最后的余数t，中间能容纳A个完整的周期（A+1个数据点）。不难得出： \(A=\lfloor \frac{q-l-1}{r} \rfloor - 1\)  。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(5.2) Shor算法详解</title>
      <link>/quantum/shor_algorithm_5.2/</link>
      <pubDate>Mon, 31 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_5.2/</guid>
      <description>&lt;p&gt;
前文『&lt;a href=&#34;shor_algorithm_5.../1/&#34;&gt;5.1 Shor算法详解&lt;/a&gt;』讨论了在简化条件下，QFT如何提取函数周期信息，但是如果简化条件不满足呢？今天接着这个话题继续讨论。推导过程较长，涉及的知识点也比较多，我会根据我的学习经验，尽可能写的简明易懂。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org5549726&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org5549726&#34;&gt;回顾：简化条件&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org5549726&#34;&gt;
&lt;p&gt;
在前文中，我们构造周期函数 \(f(x)\) 的可逆变换 \(U_f\) ，给它制备适当的输入：
&lt;/p&gt;


&lt;div id=&#34;orgb237942&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf2.png&#34; alt=&#34;shor2_uf2.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
在输出端测量 \(|\beta\rangle\) ，使得它坍缩到某个值z，这时 \(\alpha\) 就会坍缩到 \(|l\rangle, |l+r\rangle, |l+2r\rangle, \dots, |l+Ar\rangle\) 的叠加态：
&lt;/p&gt;

\begin{matrix}
|\alpha\rangle = \frac{1}{\sqrt{A+1}} \sum_{j=0}^{A}|jr+l\rangle
\end{matrix}

&lt;p&gt;
其中\(l\) 称为offset，是满足 \(a^l \mod N = z\) 的最小整数。假设 \(|\alpha\rangle\) 有n个量子位，那么A就是在0到 \(2^n-1\) 之间有多少个周期。
&lt;/p&gt;

&lt;p&gt;
接下来的关键一步，是对 \(|\alpha\rangle\) 作量子傅立叶变换，从中提取函数周期信息。
&lt;/p&gt;

&lt;p&gt;
在前文中，我们假设了一个简化条件，即 \(q=2^n\) 能被函数周期 \(r\) 整除。【注：这里勘误一下，前文中把简化条件写成了“ \(2^n-1\) 能被 \(r\) 整除”，这是笔误】
&lt;/p&gt;

&lt;p&gt;
在这个简化条件下， \(|\alpha\rangle\) 经过量子傅里叶变换(QFT)后再测量，会得到某个值 \(y=k\frac{q}{r}\) ，而这个y值与 \(q=2^n\) 之间的比值，等于某个整数k与周期r的比值，即 \(\frac{y}{q} = \frac{k}{r}\) 。这里y和q都是已知的，化简后得到的分数，其分母就是可能的 \(r\) 值。
&lt;/p&gt;

&lt;p&gt;
那么，如果这个简化条件不成立呢？
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org60c3f03&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org60c3f03&#34;&gt;简化条件不成立？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org60c3f03&#34;&gt;
&lt;p&gt;
还是从QFT前的状态开始推导，QFT的作用是什么？
&lt;/p&gt;

&lt;p&gt;
和前文一样，我们知道把QFT作用在任意态 \(|\psi\rangle\) 上：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(5.1) Shor算法详解</title>
      <link>/quantum/shor_algorithm_5.1/</link>
      <pubDate>Thu, 27 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_5.1/</guid>
      <description>&lt;p&gt;
在前文“回到Shor算法”中我们用QFT提取函数周期信息，但是​这个过程的原理是什么？本文接着详解……
&lt;/p&gt;

&lt;div id=&#34;outline-container-orge1a0d5d&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orge1a0d5d&#34;&gt;Shor算法关键：如何用QFT提取函数周期信息？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orge1a0d5d&#34;&gt;
&lt;p&gt;
用量子计算机求解Order Finding问题，实质上是要找到函数 \(f(x)=a^x \mod N\) 的周期。
&lt;/p&gt;

&lt;p&gt;
Shor算法的第一步，是构造函数f(x)对应的可逆变换 \(U_f\) （这里的 \(|x\rangle, |y\rangle\) 都是多个量子位组成的寄存器）：
&lt;/p&gt;


&lt;div id=&#34;org081553e&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf.png&#34; alt=&#34;shor2_uf.png&#34; width=&#34;280px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
然后给它制备输入： 
&lt;/p&gt;


&lt;div id=&#34;org5e7ea0b&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf2.png&#34; alt=&#34;shor2_uf2.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
在输出端测量 \(|\beta\rangle\) ，使得它坍缩到某个值z，这时 \(\alpha\) 就会坍缩到 \(|l\rangle, |l+r\rangle, |l+2r\rangle, \dots, |l+Ar\rangle\) 的叠加态：
&lt;/p&gt;

\begin{matrix}
|\alpha\rangle = \frac{1}{\sqrt{A+1}} \sum_{j=0}^{A}|jr+l\rangle
\end{matrix}

&lt;p&gt;
其中\(l\) 称为offset，是满足 \(a^l \mod N = z\) 的最小整数。假设 \(|\alpha\rangle\) 有n个量子位，那么A就是在0到 \(2^n-1\) 之间有多少个周期。
&lt;/p&gt;

&lt;p&gt;
这里面， \(l, j\) 都是未知的，并且每次运行这个电路都有可能不同，不能直接从中导出周期r。所以下一步我们要做的，就是用量子傅立叶变换从中提取出有用的周期信息。
&lt;/p&gt;

&lt;p&gt;
接下来关键一步： &lt;b&gt;如果我们对 \(|\alpha\rangle\) 做量子傅立叶变换，会发生什么？&lt;/b&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7c7d04f&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7c7d04f&#34;&gt;先考虑一个简化情况&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org7c7d04f&#34;&gt;
&lt;p&gt;
先考虑一个简化情况：
&lt;/p&gt;

&lt;p&gt;
如果 \(2^n\) 能被 \(r\) 整除，那么我们定义 \(m=2^n/r\) ，显然 \(A=m-1\) ，并且 \(A+1=m=\frac{2^n}{r}\) 。
&lt;/p&gt;

&lt;p&gt;
那么这时的 \(|\alpha\rangle\) 可以写成：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子傅里叶变换详解 (2) 逆傅里叶变换</title>
      <link>/quantum/qft-2/</link>
      <pubDate>Wed, 26 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/qft-2/</guid>
      <description>&lt;p&gt;
既然有『量子傅立叶变换』，那么自然也有它的逆变换，今天就来聊聊这个话题。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org4d68a61&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org4d68a61&#34;&gt;量子傅立叶变换和逆变换&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org4d68a61&#34;&gt;
&lt;p&gt;
前文说过，对于任意状态 \(|\psi\rangle\) ：
&lt;/p&gt;

\begin{matrix}
|\psi\rangle = \sum_{j=0}^{N-1}f(j)|j\rangle &amp; (N=2^n)
\end{matrix}

&lt;p&gt;
对它进行量子傅立叶变换QFT后，会得到一个新状态：
&lt;/p&gt;

\begin{matrix}
|\tilde\psi\rangle = QFT|\psi\rangle = \sum_{k=0}^{N-1}\tilde f(k)|k\rangle, \\\
\\\
\tilde f(k) = \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} e^{2\pi ijk/N} f(j) \\\
= \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} \omega^{jk} f(j) &amp; (\omega=e^{2\pi i/N}) \\\
\end{matrix}

&lt;p&gt;
那么量子傅里叶逆变换 \(QFT^{-1}\) 是什么样的？
&lt;/p&gt;

&lt;p&gt;
很简单，其实就是把e的指数加个负号：
&lt;/p&gt;

\begin{matrix}
|\phi\rangle = \sum_{k=0}^{N-1} \tilde f(k) |k\rangle \\\
\\\
QFT^{-1} |\phi\rangle = \sum_{j=0}^{N-1} g(j) |j\rangle \\\
\\\
g(j) = \frac{1}{\sqrt{N}} \sum_{k=0}^{N} \omega^{-jk} \tilde f(k) \\\
\end{matrix}

&lt;p&gt;
可以看到，量子傅立叶逆变换和QFT非常相似，唯一的区别就是e的指数上多了个负号。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>试用国产超导量子计算云平台ScQ</title>
      <link>/quantum/scq-getting-started/</link>
      <pubDate>Fri, 21 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/scq-getting-started/</guid>
      <description>&lt;p&gt;
最近中科院物理所发布了一个量子计算云平台ScQ，是一个10量子位的超导量子计算机，可以在云端免费用。看到这个新闻很兴奋，忍不住马上去试用了一下。
&lt;/p&gt;

&lt;p&gt;
点击云平台的链接（&lt;a href=&#34;http://q.iphy.ac.cn/&#34;&gt;http://q.iphy.ac.cn/&lt;/a&gt;），进去以后是这样的：
&lt;/p&gt;


&lt;div id=&#34;orge2f3aa1&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_09-43-59_screenshot.png&#34; alt=&#34;2021-05-21_09-43-59_screenshot.png&#34; width=&#34;720px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
点击&#34;Try ScQ Cloud&#34;，进入量子电路页面：
&lt;/p&gt;


&lt;div id=&#34;orgf6ce444&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_09-57-27_screenshot.png&#34; alt=&#34;2021-05-21_09-57-27_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
先来试试精度，用2个量子比特，测量它们的初始状态（应该都是0）：
&lt;/p&gt;


&lt;div id=&#34;org329f785&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_09-59-09_screenshot.png&#34; alt=&#34;2021-05-21_09-59-09_screenshot.png&#34; width=&#34;600px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;


&lt;div id=&#34;org4044efb&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_09-59-52_screenshot.png&#34; alt=&#34;2021-05-21_09-59-52_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
点击Submit，让我耐心等待结果：
&lt;/p&gt;

&lt;div id=&#34;orgb3255c2&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-00-25_screenshot.png&#34; alt=&#34;2021-05-21_10-00-25_screenshot.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
然后结果是这样：
&lt;/p&gt;


&lt;div id=&#34;orgbfd37ac&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-01-35_screenshot.png&#34; alt=&#34;2021-05-21_10-01-35_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
测量100次，96%情况下符合预期（00 ），有3%的情况下第2个量子位测到1…
&lt;/p&gt;

&lt;p&gt;
试试H门产生叠加态：
&lt;/p&gt;


&lt;div id=&#34;org173671b&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-10-09_screenshot.png&#34; alt=&#34;2021-05-21_10-10-09_screenshot.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;



&lt;div id=&#34;org22391dd&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-09-49_screenshot.png&#34; alt=&#34;2021-05-21_10-09-49_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
预期是4个状态各25%，这个误差有点高……感觉可能我的取样次数（100）太少，增加到1000次，结果就好了很多：
&lt;/p&gt;


&lt;div id=&#34;orgcf851cc&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-14-30_screenshot.png&#34; alt=&#34;2021-05-21_10-14-30_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;


&lt;p&gt;
再来试试Deutsch算法：f(x)=x, 预期输出1 (balanced)
&lt;/p&gt;


&lt;div id=&#34;org5182a52&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-43-29_screenshot.png&#34; alt=&#34;2021-05-21_10-43-29_screenshot.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
取样1000次，92%的情况下得到正确的结果。
&lt;/p&gt;

&lt;div id=&#34;org673c238&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-19-50_screenshot.png&#34; alt=&#34;2021-05-21_10-19-50_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
取样10000次，正确率达到接近97%：
&lt;/p&gt;


&lt;div id=&#34;orga8f1344&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2021-05-21_10-30-10_screenshot.png&#34; alt=&#34;2021-05-21_10-30-10_screenshot.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
第一次试用国产量子计算云平台的感受：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;用户体验不错，界面直观易用，一键就可开始搭建电路&lt;/li&gt;
&lt;li&gt;目前只支持10个量子位和基本的量子逻辑门，功能还比较有限&lt;/li&gt;
&lt;li&gt;从我运行的结果看，误差还是有点高&lt;/li&gt;
&lt;li&gt;提交电路后，有时候会返回运行错误，需要重试&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
为国产量子计算平台的进展感到高兴，希望将来能看到更多的消息！
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子傅里叶变换详解 (1) 输入输出解读</title>
      <link>/quantum/qft-1/</link>
      <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/qft-1/</guid>
      <description>&lt;p&gt;
之前的Shor算法系列，讲了利用量子傅立叶变换QFT来提取函数周期信息。这次歪个楼，聊聊量子傅立叶变换。
&lt;/p&gt;

&lt;p&gt;
假如我们用n个量子位搭了一个QFT电路（后文会讲到如何实现），如何解读它的输入和输出呢？
&lt;/p&gt;


&lt;div id=&#34;orgc041aa2&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/qft_nbit.png&#34; alt=&#34;qft_nbit.png&#34; width=&#34;520px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
假设我们的电路输入状态是某个基态 \(|j\rangle\) ，它是由n个量子位组成的，我们把它展开成二进制表示的话：
&lt;/p&gt;

\begin{matrix}
|j\rangle = |j_{n-1}j_{n-2}\dots j_0\rangle \\\
j = j_{n-1}2^{n-1} + j_{n-2}2^{n-2} + \dots + j_0 2^0
\end{matrix}

&lt;p&gt;
经过QFT，我们得到一个新的状态：
&lt;/p&gt;

\begin{matrix}
QFT|j\rangle = \frac{1}{\sqrt{2^n-1}} \sum_{k=0}^{2^n-1} \omega^{jk} |k\rangle \\\
= \frac{1}{\sqrt{2^n}} \sum_{k=0}^{2^n-1} e^{2\pi ijk/2^n} |k\rangle
\end{matrix}

&lt;p&gt;
其中每个分量k的相位  \(e^{2\pi ijk/2^n}\) 。而因为其中的 \(k\) 也是一个n位二进制数，也就是说：
&lt;/p&gt;

\begin{matrix}
k = k_{n-1}2^{n-1} + k_{n-2}2^{n-2} + \dots + k_0 2^0
\end{matrix}

&lt;p&gt;
那么这个相位里，是把k除以 \(2^n\) ，我们把k的二进制展开代入进去就变成了这样：
&lt;/p&gt;

\begin{matrix}
k / 2^n = \frac{k_{n-1}}{2^1} + \frac{k_{n-2}}{2^2} + \dots + \frac{k_0}{2^n}
\end{matrix}

&lt;p&gt;
等一下，这个式子是不是似曾相识？
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(5) 回到Shor算法</title>
      <link>/quantum/shor_algorithm_5/</link>
      <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_5/</guid>
      <description>&lt;div id=&#34;outline-container-org422dc20&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org422dc20&#34;&gt;上文回顾：量子傅立叶变换&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org422dc20&#34;&gt;
&lt;p&gt;
量子傅立叶变换是Shor算法的核心步骤之一。今天我们回到Shor算法，看看如何用量子傅立叶变换来求解Order Finding和函数周期问题。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org65eb077&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org65eb077&#34;&gt;Shor算法第一步&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org65eb077&#34;&gt;
&lt;p&gt;
用量子计算机求解Order Finding问题，实质上是要找到函数 \(f(x)=a^x \mod N\) 的周期。
&lt;/p&gt;

&lt;p&gt;
Shor算法的第一步，是构造函数f(x)对应的可逆变换 \(U_f\) （这里的 \(|x\rangle, |y\rangle\) 都是多个量子位组成的寄存器）：
&lt;/p&gt;


&lt;div id=&#34;orgb0312d3&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf.png&#34; alt=&#34;shor2_uf.png&#34; width=&#34;280px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
然后给它制备输入： 
&lt;/p&gt;


&lt;div id=&#34;orgac78b98&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf2.png&#34; alt=&#34;shor2_uf2.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
在输出端测量 \(|\beta\rangle\) ，使得它坍缩到某个值z，这时 \(\alpha\) 就会坍缩到 \(|l\rangle, |l+r\rangle, |l+2r\rangle, \dots, |l+Ar\rangle\) 的叠加态：
&lt;/p&gt;

\begin{matrix}
|\alpha\rangle = \frac{1}{\sqrt{A+1}} \sum_{j=0}^{A}|jr+l\rangle
\end{matrix}

&lt;p&gt;
其中\(l\) 称为offset，是满足 \(a^l \mod N = z\) 的最小整数。假设 \(|\alpha\rangle\) 有n个量子位，那么A就是在0到 \(q=2^n\) 之间有多少个周期。
&lt;/p&gt;

&lt;p&gt;
这里面， \(l, j\) 都是未知的，并且每次运行这个电路都有可能不同，不能直接从中导出周期r。所以下一步我们要做的，就是用量子傅立叶变换从中提取出有用的周期信息。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgaadb9fd&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgaadb9fd&#34;&gt;Shor算法的关键：用量子傅立叶变换提取周期信息&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgaadb9fd&#34;&gt;
&lt;p&gt;
接着上一步：如果我们对 \(|\alpha\rangle\) 做量子傅立叶变换，会发生什么？
&lt;/p&gt;


&lt;div id=&#34;org1df3b24&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf3.png&#34; alt=&#34;shor2_uf3.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
节省大家时间，先把答案放在这里，如果你有兴趣可以看我后文的推导。
&lt;/p&gt;

&lt;p&gt;
如果我们对 \(|\alpha\rangle\) 做量子傅立叶变换，然后对它测量，得到某个值y，那么这个y值与 \(q=2^n\) 之间的比值，会非常接近某个整数k与周期r的比值。
&lt;/p&gt;

\begin{matrix}
\frac{y}{q} = \frac{y}{2^n} \approx \frac{k}{r} &amp;&amp; (q=2^n) \\\
\end{matrix}

&lt;p&gt;
好了，到了这一步，q是已知的（n个量子位， \(q=2^n\) ），y是测量得到的，也就是说左边这个分数是已知的。这个不等式告诉我们：测量得到的y，与q的比值，是 \(\frac{k}{r}\) 的一个 &lt;b&gt;近似估计&lt;/b&gt; 。我们知道r小于N（N是要分解的数），所以右边是一个分母小于N的分数。如果我们能从左边 \(\frac{y}{q}\) 估计出一个和它很接近，并且分母小于N的分数，那这个分数的分母就有可能是我们要找的周期r！然后我们要做的就是对这个候选的r进行验证，如果它确实是函数f(x)的周期，那么问题解决，否则就重新运行Shor算法电路。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(4) 量子傅立叶变换</title>
      <link>/quantum/shor_algorithm_4_qft/</link>
      <pubDate>Mon, 03 May 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_4_qft/</guid>
      <description>&lt;div id=&#34;outline-container-orgb11a93e&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgb11a93e&#34;&gt;上文回顾：Shor算法初探&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgb11a93e&#34;&gt;
&lt;p&gt;
用量子计算机求解Order Finding问题，其实就是要找到函数 \(f(x)=a^x \mod N\) 的周期。在上文中我们已经看到，如果我们把f(x)包装成量子变换 \(U_f\) ，然后把它的输入x制备成叠加态，在输出端就能得到 \(|l\rangle, |l+r\rangle, |l+2r\rangle, \dots\) 的叠加态。但是这个叠加态并不能直接告诉我们周期（r）是什么，我们还需要对它做更多的处理才能提取出有用的信息。这个处理过程，就是 &lt;b&gt;量子傅里叶变换&lt;/b&gt; 。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org322fbb2&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org322fbb2&#34;&gt;离散傅里叶变换（DFT）&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org322fbb2&#34;&gt;
&lt;p&gt;
先复习一下傅里叶变换…
&lt;/p&gt;

&lt;p&gt;
我们有一个N维的矢量 \(\{f(0), f(1), \dots, f(N-1)\}\) ， 其中每个分量f(j)都是复数。
&lt;/p&gt;

&lt;p&gt;
离散傅里叶变换会把它变成一个新的矢量： \(\{ \tilde{f}(0), \tilde{f}(1), \dots, \tilde{f}(N-1) \}\)
&lt;/p&gt;

&lt;p&gt;
其中每个分量 \(\tilde{f}(k)\) 是这样计算的：
&lt;/p&gt;

\begin{matrix}
\tilde{f}(k) = \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} e^{2\pi ijk/N}f(j)
\end{matrix}

&lt;p&gt;
如果我们记 \(\omega = e^{2 \pi i/N}\) ，那么上面的定义可以简写成：
&lt;/p&gt;

\begin{matrix}
\tilde{f}(k) = \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} \omega^{jk}f(j)
\end{matrix}

&lt;p&gt;
学过信号处理的朋友应该很熟悉这个吧！
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;其他相关性质&lt;/b&gt; 也复习一下：
&lt;/p&gt;

&lt;p&gt;
根据定义， \(e^{ix} = \cos x + i\sin x\) 。
&lt;/p&gt;

&lt;p&gt;
所以 \(\omega^N = e^{2 \pi i} = \cos 2\pi + i\sin 2\pi = 1\) 。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(3) Shor算法初探</title>
      <link>/quantum/shor_algorithm_3/</link>
      <pubDate>Thu, 29 Apr 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_3/</guid>
      <description>&lt;div id=&#34;outline-container-orgaab5bcb&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgaab5bcb&#34;&gt;上文回顾：因式分解问题可以转化为Order Finding&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgaab5bcb&#34;&gt;
&lt;p&gt;
在&lt;a href=&#34;../shor_algorithm_2_order_finding/&#34;&gt;上文&lt;/a&gt;中，我们分析了为什么因式分解问题可以转化为Order Finding，因此如果能高效求解Order Finding，也就能高效解决因式分解问题。
&lt;/p&gt;

&lt;p&gt;
所谓Order Finding问题，就是已知互质的两个数a和N，求满足 \(a^r=1 \mod N\) 的最小正整数r。
&lt;/p&gt;

&lt;p&gt;
这个问题可以进一步归纳为一个周期求解问题：假如我们定义函数 \(f(x)=a^x \mod N\) ，那么 \(f(x)=f(x+r)\) ，因此这个函数的值是以r为周期的。我们要找的r，就是这个函数的周期。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org745c008&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org745c008&#34;&gt;用量子计算机解决因式分解问题：Shor算法&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org745c008&#34;&gt;
&lt;p&gt;
Shor算法是Peter Shor在1994年提出的一种量子计算算法，可以在多项式时间内求解因式分解问题。
&lt;/p&gt;

&lt;p&gt;
要完整理解这个算法涉及的知识面很多，容易迷失在细节里。我的经验是，先从高层次出发，理解它的总体思路，然后对其中的重要步骤逐一分解研究。
&lt;/p&gt;

&lt;p&gt;
Shor算法求解因式分解问题的过程，分为『经典』部分和『量子』部分：
&lt;/p&gt;

&lt;ol class=&#34;org-ol&#34;&gt;
&lt;li&gt;『经典部分』假设要分解的数为N；&lt;/li&gt;
&lt;li&gt;『经典部分』如果N是偶数，或者N是某个质数的幂，那么已经找到了它的因子，问题解决；&lt;/li&gt;
&lt;li&gt;『经典部分』在2到N-1之间，随机选一个与N互质的数，即 \(GCD(a, N)=1\) ；&lt;/li&gt;
&lt;li&gt;&lt;b&gt;『量子部分』&lt;/b&gt; 计算a对N模的order，即满足 \(a^r \mod N=1\) 的最小正整数r；&lt;/li&gt;
&lt;li&gt;『经典部分』如果r是奇数，回到步骤3重新选a；&lt;/li&gt;
&lt;li&gt;『经典部分』如果r是偶数，计算 \(GCD(a^{r/2}+1, N)\), \(GCD(a^{r/2}-1, N)\), 两者中必有N的非平凡因子。&lt;/li&gt;
&lt;/ol&gt;


&lt;div id=&#34;org0a1e246&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor1.png&#34; alt=&#34;shor1.png&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
这就是Shor算法解决因式分解问题的总体思路！
&lt;/p&gt;

&lt;p&gt;
这其中，只有第4步Order Finding需要用到量子计算，因此Shor算法的核心，就是用量子计算机找到a对N模的order。接下来我们来看一下，量子计算机是如何解决Order Finding问题的。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org465c84d&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org465c84d&#34;&gt;用量子计算机求解Order Finding问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org465c84d&#34;&gt;
&lt;p&gt;
用量子计算机求解Order Finding问题，实质上是要找到函数 \(f(x)=a^x \mod N\) 的周期。看过我之前文章的朋友可能会想到，首先我们要考虑如何将这个函数整合到我们的量子电路里，也就是把它转换成一个量子电路能用的变换 \(U_f\) 。
&lt;/p&gt;

&lt;p&gt;
为了避免迷失在实现细节里（后文会讨论 \(U_f\) 的实现），咱们先假设已经有了这样一个可逆变换 \(U_f\) ：
&lt;/p&gt;


&lt;div id=&#34;orgc7dab4c&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/shor2_uf.png&#34; alt=&#34;shor2_uf.png&#34; width=&#34;280px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
这个变换使用2个量子寄存器x和y。这两个寄存器都是由多个量子位构成的，可以把它们看作是多个量子位构成的二进制数。
\(U_f\) 的作用是：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;输入 \(|x\rangle\) 和 \(|y\rangle\)&lt;/li&gt;
&lt;li&gt;输出 \(|x\rangle\) 和 \(|y f(x)\rangle = |y a^x \mod N\rangle\)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
还记得我们是如何解决Deutsch问题的吗？我们给 \(U_f\) 输入叠加态，然后在输出端经过一些变换就能得到函数的性质。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(2) 因式分解和Order Finding问题</title>
      <link>/quantum/shor_algorithm_2_order_finding/</link>
      <pubDate>Mon, 26 Apr 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_2_order_finding/</guid>
      <description>&lt;div id=&#34;outline-container-org956d49f&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org956d49f&#34;&gt;上文回顾：RSA公钥加密算法&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org956d49f&#34;&gt;
&lt;p&gt;
在&lt;a href=&#34;../shor_algorithm_1_rsa/&#34;&gt;上文&lt;/a&gt;中我们讲了以RSA为代表的公钥加密算法，以及为什么其安全性的核心是因式分解问题。
&lt;/p&gt;


&lt;div id=&#34;orgf410b0a&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/RSA.png&#34; alt=&#34;RSA.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
那么为什么量子计算机能高效求解因式分解问题呢？这是因为，因式分解可以转化为Order Finding或函数周期求解问题，而这类问题，正是量子计算机所能高效求解的！
&lt;/p&gt;

&lt;p&gt;
下面我们就来看一下，如何将因式分解问题转化为Order Finding问题。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org08c4968&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org08c4968&#34;&gt;Order Finding问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org08c4968&#34;&gt;
&lt;p&gt;
给定两个互质的数N和a，求使得 \(a^r=1 \mod N\) 的最小整数r，这就是所谓的Order Finding问题。求出的r，称为&#34;the order of a modulo N&#34;。
&lt;/p&gt;

&lt;p&gt;
如果 \(a^r=1 \mod N\) ，那么很显然 \(a^{2r}, a^{3r}, \dots\) 也对N取模为1。
&lt;/p&gt;

&lt;p&gt;
进一步推论，如果a的幂取任意整数 \(x=jr+l\) ，那么 \(a^x=a^{jr+l}=a^{jr}a^l=a^l \mod N\) ， 同理可知 \(a^{x+r} = a^xa^r = a^x \mod N\) 。
&lt;/p&gt;

&lt;p&gt;
因此如果我们定义一个函数 \(f(x)=a^x \mod N\) ，那么这个函数的值其实是 &lt;b&gt;以r为周期的&lt;/b&gt; ，即 \(f(x)=f(x+r)\) 。因此Order Finding问题就相当于找到这个函数f(x)的周期。
&lt;/p&gt;

&lt;p&gt;
例如N=15，a=7，那么 \(f(x)=7^x \mod 15\) 函数的值：
&lt;/p&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;
&lt;caption class=&#34;t-above&#34;&gt;&lt;span class=&#34;table-number&#34;&gt;Table 1:&lt;/span&gt; \(f(x)=7^x \mod 15\)&lt;/caption&gt;

&lt;colgroup&gt;
&lt;col  class=&#34;org-left&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-left&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-left&#34;&gt;\(x\)&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;0&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;1&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;2&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;3&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;4&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;5&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;6&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-left&#34;&gt;\(\dots\)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-left&#34;&gt;\(f(x)\)&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;7&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;4&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;13&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;7&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;4&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;\(\dots\)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
不难看出，7对15模的order是4，因此这个函数的值是以4为周期不断重复。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算会终结现在的密码体系吗？(1) RSA公钥算法</title>
      <link>/quantum/shor_algorithm_1_rsa/</link>
      <pubDate>Tue, 20 Apr 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/shor_algorithm_1_rsa/</guid>
      <description>&lt;div id=&#34;outline-container-org249abfb&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org249abfb&#34;&gt;公钥加密系统&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org249abfb&#34;&gt;
&lt;p&gt;
关于量子计算，很多人可能都听说过这个耸人听闻的说法：有了量子计算机之后，现在的密码就都不安全了，因为量子计算机可以轻易破解它们。真的有这么夸张吗？实际上，量子计算机威胁的主要是基于『大数分解』的一类加密算法，其中最广泛使用的就是RSA。
&lt;/p&gt;

&lt;p&gt;
RSA (Rivest–Shamir–Adleman) 是目前广泛使用的一种公钥加密系统（RSA是三位设计者的名字首字母的组合）。所谓公钥加密系统，就是说它里面的密码是分为可以公开的『公钥』和必须保密的『私钥』。公钥和私钥是成对的，用一个公钥加密的信息，只能用它对应的私钥来解密。为什么要这么设计呢？
&lt;/p&gt;

&lt;p&gt;
我们知道，两个人之间要实现保密通信，最简单直接的办法是互相约定一个密码，用这个密码来加密他们之间传递的信息，甲方用它来加密，乙方用同样的密码来解密。这种密码又叫做『对称密码』，因为加密和解密使用相同的密码。但是问题来了，甲方和乙方怎样才能『约定』这个共享的通信密码呢？如果直接把这个通信密码发过去，中间被人窃听了，别人知道了通信密码，那后面的通信岂不就是在网上裸奔了吗？
&lt;/p&gt;

&lt;p&gt;
这时候就需要公钥加密算法来帮忙了：
&lt;/p&gt;
&lt;ol class=&#34;org-ol&#34;&gt;
&lt;li&gt;甲方生成一对公钥和私钥，然后把公钥发给乙方。&lt;/li&gt;
&lt;li&gt;乙方生成要共享的通信密码，用甲方提供的公钥来加密，然后把这个加密后的通信密码发回给甲方。&lt;/li&gt;
&lt;li&gt;甲方收到乙方的回复，用它的私钥来解密，得到共享的通信密码。&lt;/li&gt;
&lt;li&gt;甲方和乙方都有了通信密码，就可以放心的通信了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
这个过程中，即使有人在中间窃听，因为信息是用甲方的公钥加密的，只有拥有私钥的甲方能解密，窃听的人就是截获了信息也无法得到通信密码，所以不用担心通信密码泄露的问题。
&lt;/p&gt;

&lt;p&gt;
可见，公钥加密系统保证了通信双方『约定通信密码』这个步骤的安全。没有这个保证，通信的双方就无法安全的建立联系，更不用说安全的通信了。除此之外，公钥加密系统还是电子签名，电子证书等许多网络安全技术的基础。这也是为什么说以RSA为代表的公钥加密系统是当前网络通信和安全的基石之一。如果RSA的安全性受到量子计算机的挑战，那么基于RSA的很多通信协议，认证服务等等都会变得不安全，这个影响还是挺大的。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org23fa711&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org23fa711&#34;&gt;RSA的工作原理&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org23fa711&#34;&gt;
&lt;p&gt;
为什么量子计算机被认为会威胁到RSA公钥加密体系呢？要回答这个问题，需要先了解一下RSA的工作原理。
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org5388a25&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org5388a25&#34;&gt;生成公钥私钥&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org5388a25&#34;&gt;
&lt;p&gt;
RSA生成公钥和私钥的流程大致是这样：
&lt;/p&gt;

&lt;div id=&#34;org54be60d&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/RSA.png&#34; alt=&#34;RSA.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
首先随机选两个很大的质数（p, q），然后用它们计算两个乘积：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;\(N=pq\)&lt;/li&gt;
&lt;li&gt;\(\phi(N)=(p-1)(q-1)\)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
其中的 \(\phi(N)\) 也被称为『欧拉函数』，它的意思是“小于等于N并且与N互质的正整数的个数”。例如，如果某个数p是质数，那么显然有 \(\phi(p)=p-1\) 。为什么要用这个欧拉函数，后面会看到。
&lt;/p&gt;

&lt;p&gt;
然后，随机选一个与 \(\phi(N)\) 互质的奇数e，计算它对于 \(\phi(N)\) 的『模逆』d，也就是找到某个正整数d，使得\(ed = 1 \mod \phi(N)\) ，这个数d可以用欧几里德算法高效计算出来。
&lt;/p&gt;

&lt;p&gt;
最后，把e和N拼在一起，组成公钥P，而d和N拼在一起，组成私钥S。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org8a8b5ae&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org8a8b5ae&#34;&gt;加密和解密过程&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org8a8b5ae&#34;&gt;
&lt;p&gt;
有了公钥和私钥后，可以用公钥加密，然后用对应的私钥解密（下图左边是加密过程，右边是解密过程）：
&lt;/p&gt;


&lt;div id=&#34;org820d1e2&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/RSA2.png&#34; alt=&#34;RSA2.png&#34; width=&#34;640px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
我们把要加密的消息看作是一个固定长度的二进制数M，加密的时候，我们用公钥里的e和N，对M求e次幂，然后对N取模，得到加密后的消息E(M)：
&lt;/p&gt;

&lt;p&gt;
\[
M^e \mod N \to E(M)
\]
&lt;/p&gt;

&lt;p&gt;
解密的方式和加密类似，但使用的是私钥里的d和N，对收到的消息E(M)求d次幂，然后对N取模，就能解密出原来的消息M。
&lt;/p&gt;

&lt;p&gt;
\[
E(M)^d \mod N \to M
\]
&lt;/p&gt;

&lt;p&gt;
如果你对RSA算法的数学原理感兴趣，可以参阅本文最后的分析。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org4236628&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org4236628&#34;&gt;RSA安全性的关键：因式分解&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org4236628&#34;&gt;
&lt;p&gt;
RSA如何保证加密后信息的安全性？或者说，它安全性的关键点在哪里？从上面的过程不难看出，RSA安全性的关键点在于N是否被分解。想象一下：假如我们能够把N分解回p和q，那么就可以知道 \(\phi(N)=(p-1)(q-1)\) ，再加上公钥里的e，用欧几里德算法把d也算出来，这样私钥也有了，整个加密过程就被攻破了！
&lt;/p&gt;

&lt;p&gt;
好在，因式分解问题目前在经典计算机上还没有找到高效的算法（现有的算法都是指数级时间复杂度），因此只要N足够大，就可以保证加密后的消息无法在合理的时间内被破解，使得破解失去意义。
&lt;/p&gt;

&lt;p&gt;
量子计算机对RSA的威胁就在于，它能实现高效的因式分解（多项式时间复杂度），这就是为什么我们说量子计算机对现有网络通信和安全会构成一定的挑战。当然，凡事有矛必有盾，一方面现在的量子计算机还远远没有达到能破解RSA的程度，另一方面，也已经有不基于因式分解的公钥加密算法，以及抗量子计算的密码研究。因此可以说，挑战是存在的，但过于夸大量子计算的作用也没有必要。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgaa21aca&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgaa21aca&#34;&gt;总结&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgaa21aca&#34;&gt;
&lt;p&gt;
现在我们理解了RSA的工作原理，以及为什么它的安全性取决于因式分解的难度。那么接下来的问题是，为什么量子计算能高效计算因式分解呢？实际上，量子计算机解决的是Order Finding问题（或者说函数周期求解问题），在下一篇里，我们来看看因式分解是如何转化为Order Finding/函数周期求解问题的。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Understanding the Power of Quantum Computing 1: Deutsch Problem</title>
      <link>/quantum/understand-quantum-computing-1bit-deutsch/</link>
      <pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/understand-quantum-computing-1bit-deutsch/</guid>
      <description>&lt;div id=&#34;outline-container-orgc224b09&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgc224b09&#34;&gt;Introduction&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgc224b09&#34;&gt;
&lt;p&gt;
Quantum computing is one of the hottest topic these days. You might have already heard about hypes like &#34;quantum supremacy&#34;. But why quantum computers can be faster on solving some problems? And how does it work behind the scene?
&lt;/p&gt;

&lt;p&gt;
In this article I&#39;m going to use a simple problem (the Deutsch Problem) as an example and walk you through the process.
&lt;/p&gt;

&lt;p&gt;
Ok, let&#39;s get started.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org9d8fef8&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org9d8fef8&#34;&gt;Terms and Basic Concepts&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org9d8fef8&#34;&gt;
&lt;p&gt;
Before we start discussing the problem, let&#39;s first review some basic terms and concepts.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Understanding the Power of Quantum Computing 2: Extending Deutsch Problem to 2-bits</title>
      <link>/quantum/understand-quantum-computing-2bit-deutsch/</link>
      <pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/understand-quantum-computing-2bit-deutsch/</guid>
      <description>&lt;div id=&#34;outline-container-org2ab4354&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org2ab4354&#34;&gt;Two-Bit Deutsch Problem&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org2ab4354&#34;&gt;
&lt;p&gt;
My &lt;a href=&#34;../understand-quantum-computing-1bit-deutsch/&#34;&gt;previous article&lt;/a&gt; discussed Deutsch Problem in its simplest form - determine attribute of an unknown 1-bit function. Now what if we extend the function to having 2-bit input?
&lt;/p&gt;

&lt;p&gt;
\[
f(x_0, x_1) \to \{0, 1\}
\]
&lt;/p&gt;

&lt;p&gt;
For 2-bit functions, there are 4 possible inputs: 00, 01, 10, 11. Outputs of the function are still 1-bit. So the truth table of the function will be like this:
&lt;/p&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;


&lt;colgroup&gt;
&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-left&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;input&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-left&#34;&gt;output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;00&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;01&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;10&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;11&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
We define the attribute of the function in a similar way as 1-bit Deutsh Problem.
If the function returns a constant value regardless of the input, we call it &lt;b&gt;constant&lt;/b&gt;. Apparently there are 2 &lt;b&gt;constant&lt;/b&gt; functions (always return 0, or always return 1).
&lt;/p&gt;</description>
    </item>
    <item>
      <title>聊聊量子搜索算法(Grover算法) - 2</title>
      <link>/quantum/grovers_algorithm_2/</link>
      <pubDate>Tue, 26 Jan 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/grovers_algorithm_2/</guid>
      <description>&lt;p&gt;
在&lt;a href=&#34;../grovers_algorithm_1/&#34;&gt;上文&lt;/a&gt;中我们讨论了量子搜索算法的实现，其中的关键是用G变换多次迭代，逐渐逼近答案，最后在输出端测量得到结果。
那么为什么多次重复G变换能达到这样的效果呢？Grover算法的查询次数是\(O(\sqrt{N})\)，这个\(O(\sqrt{N})\)又是怎么来的呢？本文接着上一篇继续讨论这些问题。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org8c71905&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org8c71905&#34;&gt;Grover算法的图像化&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org8c71905&#34;&gt;
&lt;p&gt;
要理解Grover算法的原理，最直观的方法是把它图像化，从几何意义来理解。
&lt;/p&gt;

&lt;p&gt;
回顾一下上文的量子搜索电路：
&lt;/p&gt;


&lt;div id=&#34;org508a8e5&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/Quantum Search - Grover&#39;s Algorithm.png&#34; alt=&#34;Quantum Search - Grover&#39;s Algorithm.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
我们的电路有n个搜索用的量子比特，可以表示\(N=2^{n}\)个基态，对应搜索空间0到N-1的编号。为方便讨论，我们用\(|0\rangle, |1\rangle, |2\rangle, |3\rangle, \dots, |N-1\rangle\)来表示这些基态。
&lt;/p&gt;

&lt;p&gt;
假定在这N个编号中，有M个是符合条件的答案。在上文的讨论中我们假定M=1，也就是只有1个符合条件的编号，这里我们把它扩展到更普遍的情况，M可以大于1，也就是可以有多个符合条件的答案。因此不符合条件（也就是让判断函数f返回0）的编号有N-M个。
&lt;/p&gt;

&lt;p&gt;
我们把所有 &lt;b&gt;&lt;b&gt;不符合条件&lt;/b&gt;&lt;/b&gt; 的N-M个基态叠加在一起，组成一个叠加态的状态向量，把它记作\(|\alpha\rangle\)。
&lt;/p&gt;

&lt;p&gt;
类似的，把所有 &lt;b&gt;&lt;b&gt;符合条件&lt;/b&gt;&lt;/b&gt; 的M个状态叠加在一起，组成一个状态向量，把它记作\(|\beta\rangle\)。
&lt;/p&gt;

&lt;p&gt;
显然，上面这两个向量\(|\alpha\rangle\)，\(|\beta\rangle\)是正交的。因此我们可以用它们作轴，张成一个二维平面。
&lt;/p&gt;

&lt;p&gt;
然后我们在这个平面里，考察前面G变换公式里的\(|\psi\rangle\)，这是一个所有基态无差别叠加的状态，那么这个\(|\psi\rangle\)在这个二维平面里，可以写成：
&lt;/p&gt;

\begin{matrix}
|\psi\rangle=\sqrt{\frac{N-M}{N}}|\alpha\rangle + \sqrt{\frac{M}{N}}|\beta\rangle
\end{matrix}

&lt;p&gt;
这个\(|\psi\rangle\)向量，和\(|\alpha\rangle\)轴之间的夹角，我们记作\(\frac{\theta}{2}\)，如下图所示：
&lt;/p&gt;

&lt;div id=&#34;orgcdefa41&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/Quantum Search - Grover&#39;s Algorithm Visualized1.png&#34; alt=&#34;Quantum Search - Grover&#39;s Algorithm Visualized1.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
根据简单的三角函数，我们很容易得到：
&lt;/p&gt;

\begin{matrix}
\cos\frac{\theta}{2} = \sqrt{\frac{N-M}{N}} &amp;&amp;
\sin\frac{\theta}{2} = \sqrt{\frac{M}{N}}
\end{matrix}

&lt;p&gt;
继续分析G变换。从上文我们知道，G变换可以写成：
&lt;/p&gt;

&lt;p&gt;
\[
G=(2|\psi\rangle \langle\psi|-I)O
\]
&lt;/p&gt;

&lt;p&gt;
我们先从O（Oracle）变换看起。O变换干了什么呢？它是把输入的状态\(|x\rangle\)变成\((-1)^{f(x)} |x\rangle\)。如果f(x)=1，那么它就给输出加上一个-1的相位，否则不变。那么这个操作在二维平面图中，实际上就是把输入的向量对\(|\alpha\rangle\)轴做了一个 &lt;b&gt;&lt;b&gt;反射&lt;/b&gt;&lt;/b&gt; 。
&lt;/p&gt;

&lt;div id=&#34;org4d6d924&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/Quantum Search - Grover&#39;s Algorithm Visualized2.png&#34; alt=&#34;Quantum Search - Grover&#39;s Algorithm Visualized2.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <title>聊聊量子搜索算法(Grover算法) - 1</title>
      <link>/quantum/grovers_algorithm_1/</link>
      <pubDate>Mon, 25 Jan 2021 00:00:00 +0000</pubDate>
      <guid>/quantum/grovers_algorithm_1/</guid>
      <description>&lt;p&gt;
今天来聊一下量子计算里面的一个经典算法：量子搜索算法（Grover算法）。
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgf4fd029&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgf4fd029&#34;&gt;搜索问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgf4fd029&#34;&gt;
&lt;p&gt;
假设我们有N个无结构的元素，比如很多指纹信息，我们的任务是从中找出符合某些要求的元素。为方便讨论，我们给这些元素编号，从0开始一直到N-1，并且其中只有1个符合我们要找的条件。如果我们用经典计算机来搜索的话，只能一个一个元素的试，看它是否符合要求，因此需要试的次数为\(O(N)\)。但是如果使用量子计算机，只需要试\(O(\sqrt{N})\)次数就可以找到。和经典计算机相比，虽然这不是指数级别的加速，但也相当可观了，尤其是如果“判断元素是否符合要求”这个操作很费力的话，减少判断次数的收益也会很大。
&lt;/p&gt;

&lt;p&gt;
这个问题可以进一步抽象：我们有N个数（元素编号），这里为了方便讨论，假设\(N=2^{n}\)，即编号可以用一个n位二进制数来表示。然后我们有一个判断函数f，给它输入编号x（n位二进制数），它判断该编号对应的元素是否符合要求，如果符合就返回1，否则返回0。
&lt;/p&gt;

&lt;p&gt;
\[
f : \{0, 1\}^{n} \rightarrow \{0, 1\}
\]
&lt;/p&gt;

&lt;p&gt;
你可能会问：我都知道判断函数f了，为什么还需要查询呢？这里需要分清“找到符合条件的元素”和“判断元素是否符合条件”的区别。前者是“搜索”，而后者是“检验”。还是拿指纹做例子：警察在犯罪现场发现了嫌疑人的指纹，但是光有指纹并不能找到对应的嫌疑人，需要拿着指纹去和指纹库里的样本比对。这个“找到对应嫌疑人”的过程就是搜索，而“和指纹库里的样本进行比对”的过程就是检验，也就是前面说的判断函数f。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgfec9176&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgfec9176&#34;&gt;量子搜索算法(Grover算法)&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgfec9176&#34;&gt;
&lt;p&gt;
定义了搜索问题，接下来讨论量子搜索算法。
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org23b2745&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org23b2745&#34;&gt;量子搜索电路&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org23b2745&#34;&gt;
&lt;p&gt;
量子搜索算法（Grover算法）是一个迭代的过程，主要思路是从初始状态出发，重复进行多次变换，让系统越来越接近目标状态，最后进行测量，就能以很高的概率得到正确结果。
&lt;/p&gt;

&lt;p&gt;
量子搜索的电路图长这样：
&lt;/p&gt;


&lt;div id=&#34;org7fab32a&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/Quantum Search - Grover&#39;s Algorithm.png&#34; alt=&#34;Quantum Search - Grover&#39;s Algorithm.png&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
整个算法大致分成这样几步：
&lt;/p&gt;

&lt;ol class=&#34;org-ol&#34;&gt;
&lt;li&gt;在输入端，我们需要准备一组量子比特，包括n个用来搜索的\(|0\rangle\)，以及用于操作辅助量子比特的工作区（workspace）。这些辅助量子比特相当于程序里的临时变量，因此我们不用关心工作区的内部操作，只需要讨论前面的n个搜索用的量子比特。&lt;/li&gt;

&lt;li&gt;计算的第一步，用H门把这n个量子比特从初始的\(|0\rangle\)变成无差别叠加态，也就是把\(|0\dots0\rangle\)变成\(\frac{1}{N}\sum_{x=0}^{N-1}|x\rangle\)（其中\(N=2^{n}\)）。&lt;/li&gt;

&lt;li&gt;然后，对这些量子比特进行G变换（后面会详细介绍），这个过程反复迭代多次。&lt;/li&gt;

&lt;li&gt;最后在输出端测量，得到结果（n位二进制数）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
你可能会问，这个算法里的第3步里，需要多少次迭代（重复多少次G变换）呢？
&lt;/p&gt;

&lt;p&gt;
答案是重复\(O(\sqrt{N})\)次后，我们就能以很高的概率得到正确结果。这个结果是怎么来的？后文分析原理的时候会详细讨论。现在先看看G变换里面是啥。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org64baf22&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org64baf22&#34;&gt;G变换&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org64baf22&#34;&gt;
&lt;p&gt;
我们把量子搜索算法里每次迭代的过程称为G变换，这个G变换内部又分成4个步骤。
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;&lt;b&gt;G变换的第一步：Oracle&lt;/b&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
首先要做什么？看过我之前文章的读者应该能猜到，要把判断函数f包装成量子计算机能用的可逆变换。套路和Deutsch问题类似，在输入的量子比特\(|x\rangle\)之外，增加一个辅助量子比特\(|y\rangle=\frac{1}{\sqrt{2}}(|0\rangle - |1\rangle)\)，然后在输出端，原样输出\(|x\rangle\)，外加输出\(|y \oplus f(x)\rangle\)。我们把这个变换称作Oracle，简称O变换：
&lt;/p&gt;

&lt;p&gt;
\[
O|x\rangle|y\rangle = |x\rangle |y \oplus f(x)\rangle =  |x\rangle \frac{1}{\sqrt{2}}(|0 \oplus f(x)\rangle - |1 \oplus f(x)\rangle)
\]
&lt;/p&gt;

&lt;p&gt;
(其中的\(\oplus\)表示异或)
&lt;/p&gt;

&lt;p&gt;
我们知道判断函数f(x)的输出是0或者1，那么如果f(x)=1，上面右边就是\(|x\rangle \frac{1}{\sqrt{2}}(|0\rangle - |1\rangle)\)，也就是\(|x\rangle |y\rangle\)；而如果f(x)=1的话，上面右边就变成了\(|x\rangle \frac{1}{\sqrt{2}}(|1\rangle - |0\rangle)\)，也就是\(-|x\rangle |y\rangle\)。这样一来，f(x)的输出就变成了相位：\(|x\rangle |y\rangle\)前面的正负号。所以Oracle变换可以写成：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子神经网络</title>
      <link>/quantum/20201213_quantum_neural_network_mnist/</link>
      <pubDate>Sun, 13 Dec 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20201213_quantum_neural_network_mnist/</guid>
      <description>&lt;p&gt;
上次讨论了量子机器学习框架TensorFlow Quantum的Hello World，这次看一下另一个应用：量子神经网络。
&lt;/p&gt;

&lt;p&gt;
注：本文根据TensorFlow Quantum的MNIST示例整理：&lt;a href=&#34;https://www.tensorflow.org/quantum/tutorials/mnist&#34;&gt;https://www.tensorflow.org/quantum/tutorials/mnist&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
这个示例是基于谷歌在2018年发表的一篇论文&#34;Classification with Quantum Neural Networks on Near Term Processors&#34;。
在这篇论文中，作者Farhi等人提出了将量子电路和神经网络的结合起来实现二元分类的一种方案，主要思路是这样：
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;将训练输入看作是二进制的字串，把其中每个比特转换成量子比特，例如0对应 \(|0\rangle\) ，1对应 \(|1\rangle\) ；&lt;/li&gt;
&lt;li&gt;创建一个量子电路，输入端有N+1个量子比特，其中N个量子比特对应输入的字串长度，另有一个read out量子比特，用来输出分类结果；&lt;/li&gt;
&lt;li&gt;量子电路内部，用一组带参数的门连接起来，具体用什么门以及如何连接，似乎没有特别的要求，在文章里作者经过试验选用了带参数的XX和ZZ门。每个门带有一个控制参数（例如 \({XX}^\alpha\) ）。如果量子电路有32个这样的门，那么就有32个参数需要学习；&lt;/li&gt;
&lt;li&gt;把量子电路作为神经网络的一层，输入训练集和标签，学习控制参数。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
整个训练过程大致如下：
&lt;/p&gt;

&lt;p&gt;
把量子电路看作神经网络的一层，那么输入训练集里的字串，从它的read out那里读出分类结果，和训练标签比较，产生loss，然后反馈给神经网络进行梯度下降。经过训练，模型会学习到一组量子电路的参数，用这组参数控制量子电路，就能对未知的输入作出准确的分类预测。
&lt;/p&gt;


&lt;div id=&#34;org4f505df&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/12/QNN-MNIST.png&#34; alt=&#34;QNN-MNIST.png&#34; width=&#34;500px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
论文原文在这里：&lt;a href=&#34;https://arxiv.org/pdf/1802.06002.pdf&#34;&gt;https://arxiv.org/pdf/1802.06002.pdf&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
这个思路看起来不难，可以用TensorFlow Quantum实现，实际上整个示例，模型部分是很简单的，主要工作在于数据处理。
&lt;/p&gt;

&lt;p&gt;
因为目前的量子计算机和模拟器，对于量子比特的数量和量子电路的深度都有一定的限制，例如MNIST里的图片都是28x28，即使转换成黑白图像，也需要784个量子比特来代表输入，这显然是不现实的。所以在这个示例里，MNIST图像先要降低分辨率，然后要去掉重复，再去掉矛盾的标签，等等，经过一番处理，才能把输入送入模型进行训练。
&lt;/p&gt;

&lt;p&gt;
接下来我们来看一下具体的代码。首先还是先引入要用的包。
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; tensorflow &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; tf
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; tensorflow_quantum &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; tfq

&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; cirq
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; sympy
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; numpy &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; np
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; seaborn &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; sns
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; collections

&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;visualization tools
&lt;/span&gt;%matplotlib inline
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; plt
&lt;span style=&#34;font-weight: bold;&#34;&gt;from&lt;/span&gt; cirq.contrib.svg &lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; SVGCircuit
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
读入数据，把每个像素从0-255转换为0-1之间的浮点数：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子机器学习：TensorFlow Quantum入门</title>
      <link>/quantum/20201127_quantum_machine_learning_tensorflow_quantum_cn/</link>
      <pubDate>Fri, 27 Nov 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20201127_quantum_machine_learning_tensorflow_quantum_cn/</guid>
      <description>&lt;p&gt;
注：转载请注明作者，本文也发布在作者的个人公众号上。
&lt;/p&gt;

&lt;p&gt;
相关代码请见&lt;a href=&#34;https://github.com/zhoupingjay/quantum/blob/main/Getting-Started-With-TensorFlow-Quantum-CN.ipynb&#34;&gt;作者的Github&lt;/a&gt;。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org80c9b43&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org80c9b43&#34;&gt;TensorFlow Quantum&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org80c9b43&#34;&gt;
&lt;p&gt;
&lt;a href=&#34;https://www.tensorflow.org/quantum&#34;&gt;TensorFlow Quantum&lt;/a&gt; (TFQ) 是谷歌在2020年发布的 &lt;b&gt;量子机器学习框架&lt;/b&gt; ，它能够帮助开发者方便的创建经典+量子结合的混合机器学习模型。量子+机器学习，两个热门的名词结合在一起，听上去很酷！但是你可能会问，这个框架是如何把经典和量子世界结合起来的呢？所谓的”经典-量子混合模型“又是如何工作的？在这个笔记里，我会结合谷歌的&#34;&lt;a href=&#34;https://www.tensorflow.org/quantum/tutorials/hello_many_worlds&#34;&gt;Hello, many world&lt;/a&gt;&#34;示例，实际讲解一下在TFQ下量子机器学习模型的搭建和工作原理。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orged7291b&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orged7291b&#34;&gt;基本概念&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orged7291b&#34;&gt;
&lt;p&gt;
要理解TFQ的代码，首先需要了解一些量子计算的基本概念。
&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7e653e9&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org7e653e9&#34;&gt;量子比特和状态向量&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org7e653e9&#34;&gt;
&lt;p&gt;
你可能在很多地方已经听到过量子比特（Qubit）的概念。一个经典比特在任意时刻只能处于两个状态中的一个，而量子比特则可以处于两个状态的叠加态：
&lt;/p&gt;

&lt;p&gt;
\(|\psi\rangle = \alpha|0\rangle + \beta|1\rangle\) 
&lt;/p&gt;

&lt;p&gt;
其中， \(\alpha\) 和 \(\beta\) 都是复数，并且 \(|\alpha|^2+|\beta|^2=1\) 。如果我们对这个量子比特进行测量，得到两个状态的概率分别是 \(|\alpha|^2\) 和 \(|\beta|^2\) 。
&lt;/p&gt;

&lt;p&gt;
因此，一个量子比特的状态可以用一个**状态向量**来表示：
&lt;/p&gt;

\begin{matrix}
\begin{pmatrix}
\alpha \\
\beta
\end{pmatrix} 
\end{matrix}

&lt;p&gt;
如果我们的系统里有多个量子比特，每个量子比特有相应的状态向量，那么这个系统的状态向量就是这些量子比特的**张量积**（tensor product）。例如一个系统有两个量子比特 \(\begin{pmatrix}\alpha_1 \\ \beta_1\end{pmatrix}\)  和  \(\begin{pmatrix}\alpha_2 \\ \beta_2\end{pmatrix}\) ，那么这个系统的状态向量就是：
&lt;/p&gt;

&lt;p&gt;
\(\begin{pmatrix}\alpha_1 \\ \beta_1\end{pmatrix}
\otimes
\begin{pmatrix}\alpha_2 \\ \beta_2\end{pmatrix} =
\begin{pmatrix}
\alpha_1\alpha_2 \\
\alpha_1\beta_2 \\
\beta_1\alpha_2 \\
\beta_1\beta_2
\end{pmatrix}\) 
&lt;/p&gt;

&lt;p&gt;
在谷歌的量子计算框架[&lt;a href=&#34;https://github.com/quantumlib/Cirq&#34;&gt;Cirq&lt;/a&gt;]里，模拟器运行的结果里有一个 &lt;code&gt;final_state&lt;/code&gt; 成员，这就是系统的状态向量。详细信息请参见我的[&lt;a href=&#34;https://github.com/zhoupingjay/quantum/blob/main/Quantum-Teleportation.ipynb&#34;&gt;另一篇笔记&lt;/a&gt;]。
&lt;/p&gt;

&lt;p&gt;
量子比特另外一种重要的表示方法是 &lt;b&gt;Bloch球&lt;/b&gt; （Bloch Sphere）。每一个量子比特都可以表示为Bloch球面上的一个向量：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Machine Learning Meets Quantum: Getting Started with TensorFlow Quantum</title>
      <link>/quantum/20201123_machine_learning_meets_quantum_getting_started_with_tensorflow_quantum/</link>
      <pubDate>Mon, 23 Nov 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20201123_machine_learning_meets_quantum_getting_started_with_tensorflow_quantum/</guid>
      <description>&lt;p&gt;
Source: &lt;a href=&#34;https://github.com/zhoupingjay/quantum/blob/main/Getting-Started-With-TensorFlow-Quantum.ipynb&#34;&gt;My notebook&lt;/a&gt;
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgedd2036&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgedd2036&#34;&gt;Introduction&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgedd2036&#34;&gt;
&lt;p&gt;
Earlier this year, Google announced &lt;a href=&#34;https://www.tensorflow.org/quantum&#34;&gt;TensorFlow Quantum&lt;/a&gt;. It&#39;s a framework for building &lt;b&gt;Hybrid Quantum-Classical Machine Learning models&lt;/b&gt;. So now we are entering the era of &lt;b&gt;Quantum Machine Learning&lt;/b&gt; - sounds exciting!
&lt;/p&gt;

&lt;p width=&#34;480px&#34;&gt;
&lt;a href=&#34;https://github.com/tensorflow/quantum/blob/master/docs/images/logo/tf_quantum_circle.jpg?raw=true&#34; width=&#34;480px&#34;&gt;TensorFlow Quantum logo&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
You may wonder how these two very different worlds work together, and what these hybrid ML models look like.
In this notebook, I&#39;m going to walk you through the &#34;&lt;a href=&#34;https://www.tensorflow.org/quantum/tutorials/hello_many_worlds&#34;&gt;Hello, many world&lt;/a&gt;&#34; example to explain the basic ideas of building quantum-classical hybrid ML model and how it works on a real problem.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Markdown in Emacs: Live Preview, Math and Images</title>
      <link>/notes/20201111_markdown_in_emacs_live_preview_math_and_images/</link>
      <pubDate>Wed, 11 Nov 2020 00:00:00 +0000</pubDate>
      <guid>/notes/20201111_markdown_in_emacs_live_preview_math_and_images/</guid>
      <description>&lt;div id=&#34;outline-container-orgf41bfed&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgf41bfed&#34;&gt;Introduction&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgf41bfed&#34;&gt;
&lt;p&gt;
Emacs has a Markdown mode, but I need a more comprehensive setup with features like:
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;Live preview window&lt;/li&gt;
&lt;li&gt;Rendering of math equations&lt;/li&gt;
&lt;li&gt;Rendering of local and remote images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Basically I need a setup that provides similar functionality as VS Code + Markdown extensions.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgef86f5f&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgef86f5f&#34;&gt;Install dependencies&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgef86f5f&#34;&gt;
&lt;p&gt;
Emacs packages needed:
&lt;/p&gt;
&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;markdown-mode&lt;/li&gt;
&lt;li&gt;markdown-mode&lt;/li&gt;
&lt;li&gt;simple-httpd&lt;/li&gt;
&lt;li&gt;impatient-mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Install pandoc:
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;brew install pandoc
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Get MathJax:
&lt;/p&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;git clone https://github.com/mathjax/MathJax.git mathjax
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org7242e02&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org7242e02&#34;&gt;Configure Emacs&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org7242e02&#34;&gt;
&lt;/div&gt;
&lt;div id=&#34;outline-container-org8feef9b&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org8feef9b&#34;&gt;Markdown settings&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org8feef9b&#34;&gt;
&lt;p&gt;
Add these customization to Emacs config:
&lt;/p&gt;</description>
    </item>
    <item>
      <title>用Cirq模拟量子隐形传态过程</title>
      <link>/quantum/20201028_cirq_quantum_teleportation/</link>
      <pubDate>Wed, 28 Oct 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20201028_cirq_quantum_teleportation/</guid>
      <description>&lt;p&gt;
这里我们用谷歌的Cirq框架来模拟量子隐形传态的过程：Alice这里有一个量子比特 \(|\psi\rangle\) ，通过量子隐形传态电路，把状态传送到Bob那里。
&lt;/p&gt;

&lt;p&gt;
关于量子隐形传态的原理，请参见&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzI4ODYxNDY5NA==&amp;amp;mid=2247484394&amp;amp;idx=1&amp;amp;sn=e167ed2e9427fe35ed5a0bc320b19a45&amp;amp;chksm=ec3af3a3db4d7ab5281f3d17ce86cd56e9043316667c9f51d23643cbd444b02535f2faf26cc1&amp;amp;scene=178&amp;amp;cur_album_id=1504407355969617921#rd&#34;&gt;我的相关文章&lt;/a&gt;。
&lt;/p&gt;

&lt;div id=&#34;outline-container-orgab0d979&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgab0d979&#34;&gt;制备纠缠态的量子比特&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgab0d979&#34;&gt;
&lt;p&gt;
要在Alice和Bob之间实现量子隐形传态，首先需要用到一对纠缠态的量子比特。这可以通过一个H门和一个CNOT门来实现：
&lt;/p&gt;


&lt;div id=&#34;orgf5ffa5f&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/Entanglement.svg&#34; alt=&#34;Entanglement.svg&#34; class=&#34;org-svg&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
为简化后面的量子电路，可以把这里的H门和CNOT门包装成一个自定义的复合门，这样也方便以后重用。我们把这个自定义门起名叫“纠缠门”（EntangleGate）。
&lt;/p&gt;


&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; cirq
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; numpy &lt;span style=&#34;font-weight: bold;&#34;&gt;as&lt;/span&gt; np

&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#21019;&amp;#24314;&amp;#19968;&amp;#20010;&amp;#33258;&amp;#23450;&amp;#20041;&amp;#38376; - &amp;#8220;&amp;#32416;&amp;#32544;&amp;#38376;&amp;#8221;&amp;#65292;&amp;#36755;&amp;#20837;2&amp;#20010;&amp;#37327;&amp;#23376;&amp;#27604;&amp;#29305;
&lt;/span&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;font-weight: bold; text-decoration: underline;&#34;&gt;EntangleGate&lt;/span&gt;(cirq.ops.gate_features.TwoQubitGate):
    &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#20869;&amp;#37096;&amp;#23601;&amp;#26159;&amp;#19968;&amp;#20010;H&amp;#38376;&amp;#21644;&amp;#19968;&amp;#20010;CNOT&amp;#38376;
&lt;/span&gt;    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;_decompose_&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;, qubits):
        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;q0&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;q1&lt;/span&gt; = qubits
        &lt;span style=&#34;font-weight: bold;&#34;&gt;yield&lt;/span&gt; cirq.H(q0),
        &lt;span style=&#34;font-weight: bold;&#34;&gt;yield&lt;/span&gt; cirq.CNOT(q0, q1)

    &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#25171;&amp;#21360;&amp;#30340;&amp;#26102;&amp;#20505;&amp;#22914;&amp;#20309;&amp;#26174;&amp;#31034;&amp;#36825;&amp;#20010;&amp;#38376;&amp;#65311;
&lt;/span&gt;    &lt;span style=&#34;font-weight: bold;&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;font-weight: bold;&#34;&gt;_circuit_diagram_info_&lt;/span&gt;(&lt;span style=&#34;font-weight: bold;&#34;&gt;self&lt;/span&gt;, args):
        &lt;span style=&#34;font-weight: bold;&#34;&gt;return&lt;/span&gt; cirq.protocols.CircuitDiagramInfo(wire_symbols=(&lt;span style=&#34;font-style: italic;&#34;&gt;&#39;E&#39;&lt;/span&gt;, &lt;span style=&#34;font-style: italic;&#34;&gt;&#39;E&#39;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
试验一下这个自定义门：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>瞬间移动？传送门？“量子隐形传态”是怎么回事？</title>
      <link>/quantum/20201015_quantum_teleportation/</link>
      <pubDate>Thu, 15 Oct 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20201015_quantum_teleportation/</guid>
      <description>&lt;p&gt;
&lt;b&gt;&lt;b&gt;注：&lt;/b&gt;&lt;/b&gt; 本文也发布在作者的个人公众号上，转载请注明作者。
&lt;/p&gt;

&lt;p&gt;
说起“ &lt;b&gt;量子隐形传态&lt;/b&gt; ”（ &lt;b&gt;Quantum Teleportation&lt;/b&gt; ），你会想到什么？传送门？瞬间移动？是不是感觉很科幻？其实，量子隐形传态是已经被实验证实的现象，所以理论上说是可以用来制造传送门的……今天我们就来聊聊，量子隐形传态到底是怎么回事。
&lt;/p&gt;

&lt;p&gt;
既然叫“隐形传态”，那么必然有发送方和接收方。这里我们给发送方起名叫Alice，接收方起名叫Bob。假设Alice那边需要传送的量子比特是 \(|\Psi\rangle\) = \((\alpha|0\rangle + \beta |1\rangle)\) 。我们的目标是在Bob那端得到一个和 \(|\Psi\rangle\) 状态完全一样的量子比特。
&lt;/p&gt;

&lt;p&gt;
量子隐形传态需要用到 &lt;b&gt;&lt;b&gt;量子纠缠&lt;/b&gt;&lt;/b&gt; ，我在&lt;a href=&#34;https://www.yuque.com/order66/xu52cg/lct5do&#34;&gt;这篇专栏文章&lt;/a&gt;里有介绍。简单的说，Alice和Bob之间要准备一对纠缠态的量子比特 \(|\Phi_0\Phi_1\rangle\) ：
 \(|\Phi\rangle\) = \(|\Phi_0\Phi_1\rangle\) = \((\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle)\) 
这两个纠缠态的量子比特，Alice和Bob各拿其中一个，Bob可以在很远的地方，只要两个量子比特仍然处于纠缠态，隐形传态就能进行。
&lt;/p&gt;

&lt;p&gt;
好了，准备工作完成，我们可以来搭电路了！
&lt;/p&gt;

&lt;p&gt;
Alice这边的电路长这样：
&lt;/p&gt;


&lt;div id=&#34;orgbc2c076&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/10/image.png&#34; alt=&#34;image.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
老规矩，我们分时序来分析：
&lt;/p&gt;

&lt;p&gt;
t0:
&lt;/p&gt;

&lt;p&gt;
\(|\Psi\rangle |\Phi\rangle = (\alpha|0\rangle + \beta |1\rangle)  (\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle)\)
&lt;/p&gt;

&lt;p&gt;
，展开成
&lt;/p&gt;

\begin{matrix}
\frac{\alpha}{\sqrt{2}}|000\rangle + \frac{\beta}{\sqrt{2}}|100\rangle +
\frac{\alpha}{\sqrt{2}}|011\rangle +
\frac{\beta}{\sqrt{2}}|111\rangle 
\end{matrix}

&lt;p&gt;
t1:  \(|\Psi\rangle\) 和 \(|\Phi_0\rangle\) 经过CNOT门，系统状态变成了
&lt;/p&gt;

\begin{matrix}
\frac{\alpha}{\sqrt{2}}|000\rangle + \frac{\beta}{\sqrt{2}}|110\rangle +
\frac{\alpha}{\sqrt{2}}|011\rangle +
\frac{\beta}{\sqrt{2}}|101\rangle
\end{matrix}

&lt;p&gt;
t2:  \(|\Psi\rangle\) 再经过一个H门，前面我们说过，H门会把 \(|0\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle)\) ，把 \(|1\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle - |1\rangle)\) 。那么把这个替换上面的 \(|\Psi\rangle\) ，系统状态变成了：
&lt;/p&gt;</description>
    </item>
    <item>
      <title>TensorFlow Developer Certificate: My Tips</title>
      <link>/notes/20201005_tensorflow_developer_certificate_my_tips/</link>
      <pubDate>Mon, 05 Oct 2020 00:00:00 +0000</pubDate>
      <guid>/notes/20201005_tensorflow_developer_certificate_my_tips/</guid>
      <description>&lt;p&gt;
I recently got my &lt;a href=&#34;https://www.tensorflow.org/certificate&#34;&gt;TensorFlow Developer Certificate&lt;/a&gt;. Here are some tips I learned from my latest experience with the exam.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Preparation&lt;/b&gt;: Get familiar with Python coding and TensorFlow (of course!). Take some courses if needed. There are plenty of online courses available these days. For example, I found &lt;a href=&#34;https://www.coursera.org/professional-certificates/tensorflow-in-practice&#34;&gt;TensorFlow in Practice&lt;/a&gt; helpful to my preparation. Also, make sure you read the &lt;a href=&#34;https://www.tensorflow.org/extras/cert/TF_Certificate_Candidate_Handbook.pdf&#34;&gt;candidate handbook&lt;/a&gt; before taking the exam.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;PyCharm setup&lt;/b&gt;: You’ll take the exam in PyCharm. So it’ll be a good idea to play around a bit with this IDE. I recommend setting up a practice project according to the instructions in candidate handbook, so that you’ll have an idea what the environment will look like.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>超越时空的联系 – 量子纠缠是怎么回事</title>
      <link>/quantum/20200922_quantum_entanglement/</link>
      <pubDate>Tue, 22 Sep 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20200922_quantum_entanglement/</guid>
      <description>&lt;p&gt;
所谓“遇事不决，量子力学”，“量子纠缠”是量子力学中的一个重要现象，也是科幻故事中经常用到的填坑大杀器。实际上，量子纠缠是真实的物理现象，也是实现量子通信的核心之一。今天我们就来聊聊量子纠缠是怎么回事。
&lt;/p&gt;

&lt;p&gt;
假设我们有2个量子比特A和B，初始状态都是\(|0\rangle\)，然后搭一个简单的电路：
&lt;/p&gt;



&lt;div id=&#34;org84c2c09&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/09/image-8.png&#34; alt=&#34;image-8.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
只需要一个H门和一个CNOT门，就能让A和B之间形成量子纠缠！
&lt;/p&gt;

&lt;p&gt;
不信？我们来分析一下。
&lt;/p&gt;

&lt;p&gt;
t0时刻，两个量子比特都是 \(|0\rangle\) ，整个系统的状态是 \(|00\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
t1时刻，A经过H门后变成了相干态 \(\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle)\) ，而B不变还是 \(|0\rangle\) ，整个系统状态变成 \(\frac{1}{\sqrt{2}}(|00\rangle + |10\rangle)\) 。
&lt;/p&gt;

&lt;p&gt;
t2时刻，经过一个CNOT门。CNOT意思是“Controlled NOT”，就是说当A是1的话，B就取反，否则B不变。因为这里A已经是相干态（ \(|0\rangle\) 和 \(|1\rangle\) 的叠加），那么经过这个CNOT门以后，A如果是 \(|0\rangle\) ，那么B不变还是 \(|0\rangle\) ，而A如果是 \(|1\rangle\) ，那么B就从 \(|0\rangle\) 变成 \(|1\rangle\) 。所以整个系统经过这个CNOT门后，就变成了这样一个状态：
&lt;/p&gt;

&lt;p&gt;
\(\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)\)
&lt;/p&gt;

&lt;p&gt;
看出什么没有？在这个状态下，A和B要么都是 \(|0\rangle\) ，要么都是 \(|1\rangle\) ！也就是说，如果我们测量A得到0，那么我们知道B测量得到肯定也是0，反之如果我们测量A得到1，那么B肯定也是1。通过这个电路，我们把A和B两个量子比特的状态“纠缠”在了一起，这个状态就叫做“ &lt;b&gt;纠缠态&lt;/b&gt; ”。
&lt;/p&gt;

&lt;p&gt;
这时候，如果我们把A和B分开，A和B之间还会处于纠缠态吗？答案是肯定的，只要它们没有退相干，A和B仍然处于会量子纠缠状态。即使我们把B送到银河系的另一端，它们仍然会“纠缠”在一起。甚至，如果我们对其中之一做进一步的量子变换，只要不导致退相干，它们之间仍然会保持纠缠态！
&lt;/p&gt;

&lt;p&gt;
这就是量子纠缠的原理，是不是挺简单的？:-)
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;&lt;b&gt;常见误解：量子纠缠能实现超光速通信？&lt;/b&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
信息的传递不可能超光速，否则会导致因果律的崩溃。表面上看，一端的测量会导致另一端的状态坍缩，这个作用是“瞬时”的，但是实际上这个动作没有在两端之间传递任何信息，因为测量得到0和1完全是随机的。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>用谷歌Cirq模拟双比特Deutsch问题</title>
      <link>/quantum/20200913_cirq_2_bit_deutsch_problem/</link>
      <pubDate>Sun, 13 Sep 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20200913_cirq_2_bit_deutsch_problem/</guid>
      <description>&lt;div id=&#34;outline-container-org573ec2b&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org573ec2b&#34;&gt;谷歌量子计算框架Cirq&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org573ec2b&#34;&gt;
&lt;p&gt;
Cirq是谷歌开发的开源量子计算框架，它让开发者能够方便的用Python搭建量子电路。您可能会问，我没有量子计算机，如何运行我设计的电路呢？别急，Cirq内置了模拟器，您在自己的电脑上就可以模拟您设计的量子电路。如果想在真实的量子计算机上运行，Cirq也提供了相应的接口。
&lt;/p&gt;

&lt;p&gt;
Cirq的源码：&lt;a href=&#34;https://github.com/quantumlib/Cirq&#34;&gt;https://github.com/quantumlib/Cirq&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Cirq的安装，最简单的方法是用pip：
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;python -m pip install --upgrade pip
python -m pip install cirq
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
安装完成，您就可以在电脑上模拟量子计算了！
&lt;/p&gt;

&lt;p&gt;
接下来进入实战，我们要用Cirq来模拟双比特Deutsch问题。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org1972d38&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org1972d38&#34;&gt;回顾一下双比特Deutsch问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org1972d38&#34;&gt;
&lt;p&gt;
回顾一下双比特Deutsch问题：有人给你一个函数f，不告诉你它内部是怎么运作的，只知道它输入2个比特，输出1个比特，并且f有可能是常量函数（无论什么输入，输出总是0或者总是1），也有可能是平衡函数（对所有可能的输入，一半的情况下输出0，另一半情况输出1）。我们只能通过对f的查询，来判断f的性质。在上文我们讨论过，这样的函数f总共有8种可能（2个常量函数，6个平衡函数）。所以，提问的人实际上是从这8个可能的f函数中，随机抽一个给我们来判断。
&lt;/p&gt;


&lt;div id=&#34;org275c147&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2-bit deutsch.svg&#34; alt=&#34;2-bit deutsch.svg&#34; class=&#34;org-svg&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
我们知道量子计算里，每个这样的函数f，都可以包装成相应的可逆变换Uf。所以在量子计算环境下，提问的人实际上是把这8种可能的函数f包装成8个Uf，从里面随机抽一个作为黑盒给我们，让我们判断相应的函数f的性质。
&lt;/p&gt;

&lt;p&gt;
所以要模拟双比特Deutsch问题，我们首先要从提问者的角度，把这8个可能的Uf准备出来。
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;&lt;b&gt;我们从一个简单的例子出发：&lt;/b&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
假如有这样一个平衡函数函数f，它在输入是00或01的情况下输出0，其余情况下输出1，我们把它和相应的Uf输出列在下面的表里：
&lt;/p&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;


&lt;colgroup&gt;
&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-left&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;输入 (x0x1)&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-right&#34;&gt;\[f(x_0x_1)\]&lt;/th&gt;
&lt;th scope=&#34;col&#34; class=&#34;org-left&#34;&gt;\[y\oplus f(x_0x_1)\]&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;00&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;0&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;y&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;01&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;0&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;y&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;10&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;y取反&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;11&lt;/td&gt;
&lt;td class=&#34;org-right&#34;&gt;1&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;y取反&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
从上面这个表中可以看出，Uf的第三个输出端，其状态取决于第一个输入：
&lt;/p&gt;

&lt;p&gt;
如果x0=1，那么在第三个输出端输出，反之如果x0=1，那么就输出。这在量子电路里，就是一个简单的CNOT门：CNOT(x0, y)。所以对于上面这个表里的f函数，包装成相应的Uf内部就是一个简单的CNOT门实现。
&lt;/p&gt;


&lt;div id=&#34;orgbdef2a6&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2-bit deutsch 2.svg&#34; alt=&#34;2-bit deutsch 2.svg&#34; class=&#34;org-svg&#34; width=&#34;400px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
&lt;b&gt;&lt;b&gt;以此类推，对每一个函数f，我们都可以用量子电路来实现相应的Uf。&lt;/b&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
我们把这些Uf放在一个池子里，模拟的时候从里面随机选一个，作为提问者的问题。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgec21bbd&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgec21bbd&#34;&gt;用Cirq模拟双比特Deutsch问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgec21bbd&#34;&gt;
&lt;/div&gt;
&lt;div id=&#34;outline-container-org88730e1&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org88730e1&#34;&gt;首先我们要导入Cirq包：&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org88730e1&#34;&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; cirq
&lt;span style=&#34;font-weight: bold;&#34;&gt;import&lt;/span&gt; random
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org084b69d&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org084b69d&#34;&gt;然后生成这个Uf池子：&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org084b69d&#34;&gt;
&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-python&#34;&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;q0&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;q1&lt;/span&gt;, &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;q2&lt;/span&gt; = cirq.LineQubit.&lt;span style=&#34;font-weight: bold;&#34;&gt;range&lt;/span&gt;(3)
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;constant&lt;/span&gt; = [[], [cirq.X(q2)]]
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;balanced&lt;/span&gt; = [[cirq.CNOT(q0, q2)],
            [cirq.CNOT(q1, q2)],
            [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2)],
            [cirq.CNOT(q0, q2), cirq.X(q2)],
            [cirq.CNOT(q1, q2), cirq.X(q2)],
            [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2), cirq.X(q2)]]

&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;Uf_pool&lt;/span&gt; = [
    [],            &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24120;&amp;#37327;&amp;#20989;&amp;#25968;: f(x0x1) = 0
&lt;/span&gt;    [cirq.X(q2)],  &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24120;&amp;#37327;&amp;#20989;&amp;#25968;: f(x0x1) = 1
&lt;/span&gt;
    [cirq.CNOT(q0, q2)], &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;: f(00/01)=0, f(10/11)=1
&lt;/span&gt;    [cirq.CNOT(q1, q2)], &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;
&lt;/span&gt;    [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2)], &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;
&lt;/span&gt;    [cirq.CNOT(q0, q2), cirq.X(q2)],        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;
&lt;/span&gt;    [cirq.CNOT(q1, q2), cirq.X(q2)],        &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;
&lt;/span&gt;    [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2), cirq.X(q2)] &lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;# &lt;/span&gt;&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;&amp;#24179;&amp;#34913;&amp;#20989;&amp;#25968;
&lt;/span&gt;]
&lt;span style=&#34;font-weight: bold; font-style: italic;&#34;&gt;Uf_attributes&lt;/span&gt; = [
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;constant&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;constant&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
    &lt;span style=&#34;font-style: italic;&#34;&gt;&#34;balanced&#34;&lt;/span&gt;,
]
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org9e5053b&#34; class=&#34;outline-3&#34;&gt;
&lt;h3 id=&#34;org9e5053b&#34;&gt;根据Uf生成量子电路&lt;/h3&gt;
&lt;div class=&#34;outline-text-3&#34; id=&#34;text-org9e5053b&#34;&gt;
&lt;p&gt;
接下来，我们要写一个函数，根据提问者给出的Uf，生成相应的量子电路。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>量子计算编程实战 – 双比特Deutsch问题</title>
      <link>/quantum/20200904_2_bit_deutsch_problem/</link>
      <pubDate>Fri, 04 Sep 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20200904_2_bit_deutsch_problem/</guid>
      <description>&lt;p&gt;
在上一篇 “一文看懂量子计算机的威力 – 从Deutsch问题说起” 中，我们看到了量子计算机只需一次查询就可以判断函数的特性。但是在那个例子中，我们讨论的是一个简单的单比特函数，如果把输入扩展到多比特，量子计算机是否仍然只需要一次查询呢？今天我们就结合谷歌的Cirq框架，看看量子计算的 &lt;b&gt;指数级&lt;/b&gt; 加速。
&lt;/p&gt;

&lt;div id=&#34;outline-container-org001b0f5&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org001b0f5&#34;&gt;双比特Deutsch问题&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org001b0f5&#34;&gt;
&lt;p&gt;
首先和上次一样，假设我们有个函数f，但这次函数f输入2个比特，输出1个比特。这个函数f对我们是黑盒，我们不知道这个函数内部是怎么运作的，只能通过喂给它不同的输入，观察输出。因为输入有2个比特，因此共有4种可能的输入00，01，10，11。
&lt;/p&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;如果f无论什么输入，输出始终是0或者1，我们称它为“常量”函数，记作 \(f_0\) 或者 \(f_1\) ;&lt;/li&gt;
&lt;li&gt;如果f的输出，0和1各占一半，我们称它为“平衡”函数。平衡函数有多少种可能呢？这个问题就相当于说，我们在下面表中的4个输出位置，需要填两个0和两个1，有几种填法？用简单的排列组合可以知道，一共有6种，也就是说一共有6个“平衡”函数。&lt;/li&gt;
&lt;/ul&gt;

&lt;table border=&#34;2&#34; cellspacing=&#34;0&#34; cellpadding=&#34;6&#34; rules=&#34;groups&#34; frame=&#34;hsides&#34;&gt;


&lt;colgroup&gt;
&lt;col  class=&#34;org-right&#34; /&gt;

&lt;col  class=&#34;org-left&#34; /&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;输入&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;输出&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;---&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;---&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;00&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0或者1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;01&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0或者1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;10&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0或者1&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&#34;org-right&#34;&gt;11&lt;/td&gt;
&lt;td class=&#34;org-left&#34;&gt;0或者1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;当然，函数f也有可能既不是常量，也不是平衡的，例如上面的表里面，输出栏里可以填上三个0。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;b&gt;双比特Deutsch问题是问，假如我们已经知道函数f要么是常量，要么是平衡的，如何判断它是常量还是平衡的呢？&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
对于n比特输入，经典计算机需要至少查询一半可能的输入，也就是 \(2^{n-1}+1\) 次（在这个例子里n=2，所以需要查询3次）。显然随着输入比特数的增加，计算复杂性（需要查询的次数）是指数上升的。而 &lt;b&gt;&lt;b&gt;如果用量子计算机，无论输入n有多大，我们始终只需要对函数f进行一次查询！&lt;/b&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
怎么做到呢？套路和上次一样，引入一个辅助输入y，把函数f包装成量子比特版本Uf：
&lt;/p&gt;

\begin{matrix}
U_f |x_0\rangle |x_1\rangle |y\rangle
\to 
|x_0\rangle |x_1\rangle |y \oplus f(x_0x_1)\rangle 
\end{matrix}

&lt;p&gt;
然后在两边加上Hadamard门，电路也和上次的单比特电路差不多：
&lt;/p&gt;


&lt;div id=&#34;org5540850&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/09/image-1.png&#34; alt=&#34;image-1.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
猜猜看在输出端测量，我们会得到啥？
&lt;/p&gt;

&lt;p&gt;
这里先把结论放在这里 &lt;b&gt;：如果输出端得到的是全0，那么函数f一定是常量函数，反之它就是平衡函数。&lt;/b&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org6cda474&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org6cda474&#34;&gt;原理解析&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org6cda474&#34;&gt;
&lt;p&gt;
下面我们做一些推导，可以帮助您理解其中的原理。如果您不感兴趣，可以直接跳到下一篇的实战部分。其实推导并不难，不要被公式吓到 :-)
&lt;/p&gt;

&lt;p&gt;
为便于分析，我在前面的电路图中标出了时间（t0, t1, t2, t3)。
&lt;/p&gt;

&lt;p&gt;
在 &lt;b&gt;时间t0&lt;/b&gt; ，电路的状态是 \(|0\rangle|0\rangle|1\rangle\) 。
&lt;/p&gt;

&lt;p&gt;
在 &lt;b&gt;时间t1&lt;/b&gt; ，前两个量子比特都处于 \(\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)\) 状态，第三个处于 \(\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle)\) 状态。如果我们把前两个量子比特放一起考虑，相当于把他们的状态做一个卷积，也就是：
&lt;/p&gt;

\begin{matrix}
\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) \otimes \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)
=
\frac{1}{2}(|00\rangle + |01\rangle + |10\rangle + |11\rangle)
\end{matrix}

&lt;p&gt;
加上第三个辅助量子比特，整个电路的状态就是
&lt;/p&gt;</description>
    </item>
    <item>
      <title>理解量子计算机的威力：Deutsch问题</title>
      <link>/quantum/deutsch-problem-1-cn/</link>
      <pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/deutsch-problem-1-cn/</guid>
      <description>&lt;div id=&#34;outline-container-org1e3ef9e&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org1e3ef9e&#34;&gt;简介&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org1e3ef9e&#34;&gt;
&lt;p&gt;
量子计算是现在的热门话题之一，很多人可能听说过诸如“量子霸权”之类的名词，那么量子计算为什么会被认为有可能比经典计算机更快呢？这里就从最简单的Deutsch问题入手，解析量子计算的一些基本思路，相信看完后你会对量子计算的潜力有更直观的认识。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-orgfbdada2&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgfbdada2&#34;&gt;一些术语和基础概念&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgfbdada2&#34;&gt;
&lt;p&gt;
首先要先解释一下后面讨论中用到的一些术语和基础知识。
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;量子比特（Qubit）&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
我们知道，一个经典比特（bit）只能表示两种状态（0或者1）。而一个量子比特除了 \(|0\rangle\) 和 \(|1\rangle\) 之外，还可以处于一种叫相干态（superposition）的状态，即“同时”处于 \(|0\rangle\) 和 \(|1\rangle\) 两种状态。这可以用 \(\alpha|0\rangle + \beta|1\rangle\) 来表示（ \(\alpha\) 和 \(\beta\) 都是复数）。一个常见的例子是 \(\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)\) ，测量得到0和1的概率分别是50%。
&lt;/p&gt;

&lt;p&gt;
量子计算电路里常见的几种门（变换）：
&lt;/p&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;I：Identity变换，简单说就是啥也不变，输入啥输出也是啥。&lt;/li&gt;
&lt;li&gt;H: Hadamard门，在这个例子里主要作用是制备相干态，因为H门可以把 \(|0\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)\) ，而把 \(|1\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle)\) 。另外H变换还有个特性，就是HH=I，两个H变换互相抵消。&lt;/li&gt;
&lt;li&gt;Z：输入 \(|x\rangle\) ，输出 \((-1)^x|x\rangle\) ，例如输入 \(|0\rangle\) ，输出 \(|0\rangle\) ，而输入是 \(|1\rangle\) 的话，输出是 \(-|1\rangle\) 。&lt;/li&gt;
&lt;li&gt;X：简单说就是将输入取反，输入 \(|0\rangle\) 的话，输出 \(|1\rangle\) ，而输入 \(|1\rangle\) ，则输出 \(|0\rangle\) 。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
另外，还有两个特性后面会用到：
&lt;/p&gt;

\begin{matrix}
HZH=X \\
HIH=I
\end{matrix}

&lt;p&gt;
好了，需要了解的基础知识就这么些，接下来我们来看量子计算是如何加速Deutsch问题的。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org1692a91&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org1692a91&#34;&gt;问题的提出：如何判断单比特函数的性质？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org1692a91&#34;&gt;
&lt;p&gt;
假设我们有个函数f，输入和输出都是1个比特： \(y=f(x)\) ： 
&lt;/p&gt;

&lt;div id=&#34;org613d2ab&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/deutsch-fx.png&#34; alt=&#34;deutsch-fx.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <title>看懂量子计算机的威力 - 从Deutsch问题说起</title>
      <link>/quantum/20200801_quantum_computing_deustch_problem/</link>
      <pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate>
      <guid>/quantum/20200801_quantum_computing_deustch_problem/</guid>
      <description>&lt;div id=&#34;outline-container-orgc2fd301&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orgc2fd301&#34;&gt;简介&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orgc2fd301&#34;&gt;
&lt;p&gt;
量子计算是现在的热门话题之一，很多人可能听说过诸如“量子霸权”之类的名词，那么量子计算为什么会被认为有可能比经典计算机更快呢？这里就从最简单的Deutsch问题入手，解析量子计算的一些基本思路，相信看完后你会对量子计算的潜力有更直观的认识。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org777f262&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org777f262&#34;&gt;一些术语和基础&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org777f262&#34;&gt;
&lt;p&gt;
首先要先解释一下后面讨论中用到的一些术语和基础知识。
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;量子比特（Qubit）&lt;/b&gt; ：我们知道，一个经典比特（bit）只能表示两种状态（0或者1）。而一个量子比特除了 \(|0\rangle\) 和 \(|1\rangle\) 之外，还可以处于一种叫相干态（superposition）的状态，即“同时”处于 \(|0\rangle\) 和 \(|1\rangle\) 两种状态。这可以用 \(\alpha|0\rangle + \beta|1\rang\) 来表示（ \(\alpha\) 和 \(\beta\) 都是复数）。一个常见的例子是 \(\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)\) ，测量得到0和1的概率分别是50%。
&lt;/p&gt;

&lt;p&gt;
量子计算电路里常见的几种门（变换）：
&lt;/p&gt;

&lt;ul class=&#34;org-ul&#34;&gt;
&lt;li&gt;I：Identity变换，简单说就是啥也不变，输入啥输出也是啥。&lt;/li&gt;
&lt;li&gt;H: Hadamard门，在这个例子里主要作用是制备相干态，因为H门可以把 \(|0\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)\) ，而把 \(|1\rangle\) 变成 \(\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle)\) 。另外H变换还有个特性，就是HH=I，两个H变换互相抵消。&lt;/li&gt;
&lt;li&gt;Z：输入 \(|x\rangle\) ，输出 \((-1)^x|x\rangle\) ，例如输入 \(|0\rangle\) ，输出 \(|0\rangle\) ，而输入是 \(|1\rangle\) 的话，输出是 \(-|1\rangle\) 。&lt;/li&gt;
&lt;li&gt;X：简单说就是将输入取反，输入 \(|0\rangle\) 的话，输出 \(|1\rangle\) ，而输入 \(|1\rangle\) ，则输出 \(|0\rangle\) 。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
另外，还有两个特性后面会用到：
&lt;/p&gt;

&lt;p&gt;
\(HZH=X\) 
\(HIH=I\) 
&lt;/p&gt;

&lt;p&gt;
好了，需要了解的基础知识就这么些，接下来我们来看量子计算是如何加速Deutsch问题的。
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org274a03c&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org274a03c&#34;&gt;问题的提出：如何判断单比特函数的性质？&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org274a03c&#34;&gt;
&lt;p&gt;
假设我们有个函数f，输入和输出都是1个比特： \(y=f(x)\) ：
&lt;/p&gt;


&lt;div id=&#34;org67c8860&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/08/image-300x66.png&#34; alt=&#34;image-300x66.png&#34; width=&#34;320px&#34; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
因为输入和输出都只有1个比特，这个函数无非就是这4种可能：
&lt;/p&gt;

&lt;ol class=&#34;org-ol&#34;&gt;
&lt;li&gt;\(f(x)=0\) ，即不管x是0还是1，f(x)总是0，我们记作 \(f_0\)&lt;/li&gt;
&lt;li&gt;\(f(x)=1\) ，即不管x是0还是1，f(x)总是1，我们记作 \(f_1\)&lt;/li&gt;
&lt;li&gt;\(f(x)=x\) ，即f(x)输出比特和输入比特相同，我们记作 \(f_x\)&lt;/li&gt;
&lt;li&gt;\(f(x) = \bar{x}\) ，即f(x)输出比特与输入比特相反，我们记作 \(f_\bar{x}\)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
前两种可能，我们称为“常量”函数（constant），因为他们的输出是常量；而后两种我们称为“平衡”函数（balanced），因为他们所有可能的输出中间，0和1各占一半。
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Fix A Problem When Applying .tmux.conf</title>
      <link>/notes/20200610_fix_a_problem_when_applying_tmux_conf/</link>
      <pubDate>Wed, 10 Jun 2020 00:00:00 +0000</pubDate>
      <guid>/notes/20200610_fix_a_problem_when_applying_tmux_conf/</guid>
      <description>&lt;p&gt;
For some reason I’m starting to get this error when I apply tmux configuration:
&lt;/p&gt;

&lt;div class=&#34;org-src-container&#34;&gt;
&lt;pre class=&#34;src src-shell&#34;&gt;cut -c3- ~/.tmux.conf | sh -s _apply_configuration returned -2
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
So the problem is that “sh” used to work here, but at some point it doesn’t when a new shell is installed. Furthermore, I guess the reason is that “sh” used to be resolved into “bash” and the syntax was written in bash accordingly. If “sh” gets resolved into some other shell, it might cause syntax error or something.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Fix a Problem in Emacs Python/Jupyter setup</title>
      <link>/notes/20200328_fix_a_problem_in_emacs_python_jupyter_setup/</link>
      <pubDate>Sat, 28 Mar 2020 00:00:00 +0000</pubDate>
      <guid>/notes/20200328_fix_a_problem_in_emacs_python_jupyter_setup/</guid>
      <description>&lt;p&gt;
I’ve configured my Emacs with a nice Python and Jupyter environment. I want to &lt;a href=&#34;../20190827_run_emacs_server_on_macos/&#34;&gt;use it on my Mac laptop&lt;/a&gt;. Recently I noticed this strange issue:
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Problem&lt;/b&gt;: I send my code to Jupyter using C-c C-c, the Jupyter console shows a bunch of strange “Ctrl-G” (“&lt;sup&gt;G&lt;/sup&gt;”) characters. My code was not executed, and the console seems to be stuck there. I have to press Ctrl-C to get back to shell.
&lt;/p&gt;


&lt;div id=&#34;orgffb0079&#34; class=&#34;figure&#34;&gt;
&lt;p&gt;&lt;img src=&#34;./images/2020/03/image-1.png&#34; alt=&#34;image-1.png&#34; width=&#34;480px&#34; /&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Exporting .org files to HTML in batch</title>
      <link>/notes/20200109_exporting_org_files_to_html_in_batch/</link>
      <pubDate>Thu, 09 Jan 2020 00:00:00 +0000</pubDate>
      <guid>/notes/20200109_exporting_org_files_to_html_in_batch/</guid>
      <description>&lt;p&gt;
I use org-mode in Emacs and have multiple .org files to track different information in my life. Sometimes I want to export my .org files as HTML so they can be easily viewed on another device. The org-mode package does provide commands to do this on individual file (such as “org-html-export-to-html”), but doing this on each file manually is tedious. So I decided to write my own elisp function to augment my Emacs environment.
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Solving a magit issue with Emacs 26.3</title>
      <link>/notes/20191218_solving_a_magit_issue_with_emacs_26_3/</link>
      <pubDate>Wed, 18 Dec 2019 00:00:00 +0000</pubDate>
      <guid>/notes/20191218_solving_a_magit_issue_with_emacs_26_3/</guid>
      <description>&lt;p&gt;
Some point after my Emacs was upgraded to 26.3, I started seeing errors whenever I try to open file from git repository (i.e. directories with .git subdirectory). The error was annoying as it prevented me from opening files.
&lt;/p&gt;

&lt;p&gt;
So I dig a little into the error and found out that it was from magit (the package that deals with git stuff). The error was caused by wrong argument number provided to function magit-turn-on-auto-revert-mode-if-desired:
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Run Emacs Server on MacOS</title>
      <link>/notes/20190827_run_emacs_server_on_macos/</link>
      <pubDate>Tue, 27 Aug 2019 00:00:00 +0000</pubDate>
      <guid>/notes/20190827_run_emacs_server_on_macos/</guid>
      <description>&lt;p&gt;
Ok, I’ve been using Emacs for a long time and my Emacs config file is getting bigger and more complex. Emacs is a powerful tool, but it can take longer time to start up with a big config file. However, I want Emacs to be my go-to editor instead of just a heavy-weight IDE. I want to use Emacs when I want to quickly edit something (like “vim &amp;lt;something&amp;gt;” or “nano &amp;lt;something&amp;gt;”). How do I do that with Emacs?
&lt;/p&gt;</description>
    </item>
    <item>
      <title>How to Run GUI Application in Windows Subsystem for Linux (WSL)</title>
      <link>/notes/20190810_how_to_run_gui_application_in_windows_subsystem_for_linux/</link>
      <pubDate>Sat, 10 Aug 2019 00:00:00 +0000</pubDate>
      <guid>/notes/20190810_how_to_run_gui_application_in_windows_subsystem_for_linux/</guid>
      <description>&lt;div id=&#34;outline-container-orga03ddd0&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;orga03ddd0&#34;&gt;Install WSL&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-orga03ddd0&#34;&gt;
&lt;p&gt;
Basically you need to turn on WSL in Program Features. Refer to this document for the steps:
&lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/install-win10&#34;&gt;https://docs.microsoft.com/en-us/windows/wsl/install-win10&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org759c97e&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org759c97e&#34;&gt;Install Linux distribution in WSL&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org759c97e&#34;&gt;
&lt;p&gt;
Refer to same document for steps. I installed Ubuntu but there are other distributions available.
&lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/install-win10&#34;&gt;https://docs.microsoft.com/en-us/windows/wsl/install-win10&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id=&#34;outline-container-org0f536f9&#34; class=&#34;outline-2&#34;&gt;
&lt;h2 id=&#34;org0f536f9&#34;&gt;Install Dependencies&lt;/h2&gt;
&lt;div class=&#34;outline-text-2&#34; id=&#34;text-org0f536f9&#34;&gt;
&lt;p&gt;
GUI applications may have additional dependencies (libraries, etc). An easy way to install proper libraries is by installing some GUI programs, which will automatically resolve and install needed libraries. For example:
&lt;/p&gt;</description>
    </item>
    <item>
      <title>Easy Web Hosting on Alibaba Cloud – Simple Application Server &#43; HTTPS Setup</title>
      <link>/notes/20190729_easy_web_hosting_on_alibaba_cloud_simple_application_server_https_setup/</link>
      <pubDate>Mon, 29 Jul 2019 00:00:00 +0000</pubDate>
      <guid>/notes/20190729_easy_web_hosting_on_alibaba_cloud_simple_application_server_https_setup/</guid>
      <description>&lt;p&gt;
I’ve been thinking about creating a personal website for a while, and finally I put it into action!
&lt;/p&gt;

&lt;p&gt;
There are lots of ways to host a personal website on Alibaba Cloud, just like you will expect from other cloud providers. I picked Simple Application Server which is pretty much a lightweight virtual machine instance with pre-installed configurations (WordPress, Apache, etc). This gives me flexibility of a VM instance, but saves me from the initial setup. A good balance between their Web Hosting and Elastic Computing Service (ECS) in my opinion.
&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
