1
Fork 0
This repository has been archived on 2026-08-15. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2026SEL3-project-Brittle_St.../design/actor-critic/index.html
2026-05-19 21:11:40 +00:00

1300 lines
No EOL
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../../api/training/">
<link rel="next" href="../communication/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.6">
<title>Actor-Critic Architecture - Brittle Star Project</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#actor-critic-architecture" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="Brittle Star Project" class="md-header__button md-logo" aria-label="Brittle Star Project" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Brittle Star Project
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Actor-Critic Architecture
</span>
</div>
</div>
</div>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="Brittle Star Project" class="md-nav__button md-logo" aria-label="Brittle Star Project" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
Brittle Star Project
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../CONTRIBUTING/" class="md-nav__link">
<span class="md-ellipsis">
Contribution Guidelines
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../DEVELOPMENT/" class="md-nav__link">
<span class="md-ellipsis">
Development Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../HPC/" class="md-nav__link">
<span class="md-ellipsis">
HPC Guide
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Api
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Api
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../api/analysis/" class="md-nav__link">
<span class="md-ellipsis">
Analysis &amp; Plotting Tools
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../api/environment/" class="md-nav__link">
<span class="md-ellipsis">
Brittle star environment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../api/evaluation/" class="md-nav__link">
<span class="md-ellipsis">
Checkpoint &amp; Model Evaluation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../api/simulation/" class="md-nav__link">
<span class="md-ellipsis">
Simulation &amp; Evaluation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../api/tracking/" class="md-nav__link">
<span class="md-ellipsis">
Tracking &amp; Monitoring
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../api/training/" class="md-nav__link">
<span class="md-ellipsis">
Training Models
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" checked>
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
<span class="md-ellipsis">
Design
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Design
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Actor-Critic Architecture
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Actor-Critic Architecture
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#implementation-details-network-depth" class="md-nav__link">
<span class="md-ellipsis">
Implementation Details (Network Depth)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../communication/" class="md-nav__link">
<span class="md-ellipsis">
Communication scheme (Message Passing)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../controllers/" class="md-nav__link">
<span class="md-ellipsis">
Levels of modularity and topology
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../input_action_spaces/" class="md-nav__link">
<span class="md-ellipsis">
Input (state) and output (action) spaces
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../learning_algorithm/" class="md-nav__link">
<span class="md-ellipsis">
Reinforcement Learning Algorithm
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../reward_function/" class="md-nav__link">
<span class="md-ellipsis">
Reward function and observation space
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7" >
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0">
<span class="md-ellipsis">
Scripts
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Scripts
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_1" >
<label class="md-nav__link" for="__nav_7_1" id="__nav_7_1_label" tabindex="0">
<span class="md-ellipsis">
Analysis
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7_1">
<span class="md-nav__icon md-icon"></span>
Analysis
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../scripts/analysis/" class="md-nav__link">
<span class="md-ellipsis">
Experiment Analysis Tools
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8" >
<label class="md-nav__link" for="__nav_8" id="__nav_8_label" tabindex="0">
<span class="md-ellipsis">
Src
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_8">
<span class="md-nav__icon md-icon"></span>
Src
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_1" >
<label class="md-nav__link" for="__nav_8_1" id="__nav_8_1_label" tabindex="0">
<span class="md-ellipsis">
Experiment logger
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_8_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_8_1">
<span class="md-nav__icon md-icon"></span>
Experiment logger
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../src/experiment_logger/" class="md-nav__link">
<span class="md-ellipsis">
Experiment Logger
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#implementation-details-network-depth" class="md-nav__link">
<span class="md-ellipsis">
Implementation Details (Network Depth)
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="actor-critic-architecture">Actor-Critic Architecture</h1>
<p>To process observations into actions, our controllers utilize an Actor-Critic architecture. Because we use Proximal
Policy Optimization (PPO), the pipeline fundamentally requires separate networks for the policy (Actor) and the value
estimation (Critic).</p>
<p><strong>Centralized Architecture (Baseline)</strong></p>
<p>This pipeline treats the agent as a single entity and uses standard Proximal Policy Optimization (PPO).</p>
<ul>
<li>Centralized Actor: Composed of two chained MLPs (Sensor <span class="arithmatex">\(\rightarrow\)</span> Motor) passing a hidden state between them. The
centralized sensor receives the concatenated global state vector of all limbs at once and processes it into a hidden
state. The centralized motor receives this hidden state and outputs the joint offsets for all actuators
simultaneously. This is mathematically equivalent to using one large MLP with hidden layers, but splitting makes the
implementation easier by allowing us to reuse the same components for the decentralized modules.</li>
<li>Centralized Critic: Composed of two sequential MLPs (Feature Extractor <span class="arithmatex">\(\rightarrow\)</span> Critic). Because PPO evaluates
the state-value function, this network only receives the concatenated global state vector (no actions). It outputs a
single scalar estimating the expected future reward for the entire agent.</li>
</ul>
<p>Our policy and value networks use separate input networks/feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost.</p>
<pre class="mermaid"><code>graph TD
Obs([Global Observation])
Sens[Sensor]
Act[Motor]
OutAct([Action Distribution&lt;br/&gt;mean, log_std])
Feat[Feature extractor]
Crit[Critic]
OutCrit([Value Estimate&lt;br/&gt;scalar])
Obs --&gt; Sens
Obs --&gt; Feat
Sens --&gt;|"Hidden state"| Act
Feat --&gt;|"Hidden state"| Crit
Act --&gt; OutAct
Crit --&gt; OutCrit</code></pre>
<p><strong>Decentralized Architecture</strong></p>
<p>This pipeline utilizes the "Centralized Training with Decentralized Execution" principle, specifically the NerveNet-MLP
variant.</p>
<ul>
<li>Decentralized Actor, split into three distinct models:</li>
<li>Sensor: A local model at each node. It receives its local state plus the goal vector directly, processing them into
an initial hidden state.</li>
<li>Propagator: Nodes synchronously compute and exchange messages with connected neighbors for <span class="arithmatex">\(N\)</span> steps to update
their hidden states. See <a href="../communication/">communication.md</a> for details.</li>
<li>Motor: A local model uses its final updated hidden state to output the joint offset strictly for its own actuator.</li>
<li>Centralized Critic: Composed of two sequential MLPs (Feature Extractor <span class="arithmatex">\(\rightarrow\)</span> Critic). During training, it
acts globally by taking the concatenated state vectors from all sensors to output a single, global state-value scalar
evaluating the entire agent's pose.</li>
</ul>
<p>To keep the implementation simple, we should use one critic per node in our architecture, but only a single, global
critic for all nodes at once, for the following reasons:</p>
<ol>
<li>Credit Assignment Problem (Ha, 2017): The MuJoCo simulator provides an overall reward based on the brittle star
movement progression, e.g. total distance travelled. Using an isolated critic for each node in the network would not
allow to determine which local action contributed to the global success. A global critic solves this by evaluating
the combined state of the agent at once.</li>
<li>Implementation simplicity: Building a second decentralized message-passing graph for the critic (NerveNet-2) would
require more coding. Using a standard MLP that concatenates all raw input vectors is much easier to program while
mathematically equivalent.</li>
</ol>
<pre class="mermaid"><code>graph TD
Obs([Local Observation])
Sens[Sensor]
Prop[Propagator]
Feat[Feature extractor]
Mot[Motor]
Crit[Critic]
OutMot([Action Distribution&lt;br/&gt;mean, log_std])
OutCrit([Value Estimate&lt;br/&gt;scalar])
Obs --&gt; Sens
Sens --&gt;|"Hidden state"| Prop
Obs --&gt; Feat
Prop --&gt;|"Hidden state"| Mot
Feat --&gt;|"Hidden state"| Crit
Mot --&gt; OutMot
Crit --&gt; OutCrit
Prop -.-&gt;|"message passing"|Prop</code></pre>
<h2 id="implementation-details-network-depth">Implementation Details (Network Depth)</h2>
<p>Inspired by: <a href="https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/">PPO Implementation Details</a></p>
<p>The MLPs used in both pipelines are defined with specific hidden layer configurations to balance learning capability
and computational cost. As of right now, though this might change as we make progress in our experiments, we use:</p>
<ul>
<li>Input Networks (Sensors &amp; Feature Extractors): These networks map the raw state inputs to internal hidden states.
They are configured as standard dense networks with 2 hidden layers of 64 nodes each (<code>[64, 64]</code>) and utilize <code>tanh</code>
activation functions.</li>
<li>Output Networks (Motors, Actors &amp; Critics): The final output models are intentionally kept shallow. The Actor
directly projects the hidden state to a continuous action distribution (<code>mean</code> and <code>log_std</code>) using a single dense
output layer (zero hidden layers) initialized orthogonally. The Critic functions similarly, mapping the hidden
representation to a single scalar value.</li>
</ul>
<p>Note: For the continuous action distributions outputted by the Motor, we explicitly use <code>mean</code> and <code>log_std</code> as advised
by previous research to maintain learning stability.</p>
<p><strong>References</strong></p>
<ul>
<li>Ha, D. (2017, October 29). A Visual Guide to Evolution Strategies. 大トロ ・ Machine Learning. <a href="https://blog.otoro.net/2017/10/29/visual-evolution-strategies/">https://blog.otoro.net/2017/10/29/visual-evolution-strategies/</a></li>
<li>Schulman, John, Filip Wolski, Prafulla Dhariwal, Alec Radford, and Oleg Klimov. Proximal Policy Optimization Algorithms. arXiv:1707.06347. Preprint, arXiv, 28 August 2017. <a href="https://doi.org/10.48550/arXiv.1707.06347">https://doi.org/10.48550/arXiv.1707.06347</a>.</li>
<li>Wang, Tingwu, Renjie Liao, Jimmy Ba, and S. Fidler. NerveNet: Learning Structured Policy with Graph Neural Networks. Conference paper presented at International Conference on Learning Representations. 15 February 2018. <a href="https://www.semanticscholar.org/paper/NerveNet:-Learning-Structured-Policy-with-Graph-Wang-Liao/249408527106d7595d45dd761dd53c83e5a02613">https://www.semanticscholar.org/paper/NerveNet:-Learning-Structured-Policy-with-Graph-Wang-Liao/249408527106d7595d45dd761dd53c83e5a02613</a>.</li>
</ul>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": [], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../../javascripts/mathjax.js"></script>
<script src="https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js"></script>
</body>
</html>