{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Graphs and Networks in Linear Algebra\n", "\n", "This notebook is based on section 10.1 of Strang's *Linear Algebra* textbook.\n", "\n", "One interesting source of large matrices in linear algebra is a [graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics), a collection of *nodes* (vertices) and *edges* (arrows from one vertex to another). Graphs are used in many applications to represent *relationships* and *connectivity*, such as:\n", "\n", "* For computer networks, nodes could represent web pages, and edges could represent links.\n", "* For circuits, edges could represent wires (or resistors) and nodes junctions.\n", "* For transportation, nodes could represent cities and edges roads.\n", "* In bioinformatics, graphs can represent gene regulatory networks.\n", "* In sociology, nodes could represent people and edges relationships.\n", "* ... and many, many other applications ...\n", "\n", "In this notebook, we explain how a graph can be represented by a *matrix*, and how linear algebra can tell us properties of the graph and can help us do computations on graph-based problems. There is a particularly beautiful connection to Kirchhoff's laws of circuit theory." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Packages for this notebook\n", "\n", "To run the code in this notebook, you'll need to install a few Julia packages used below. To do so, uncomment the following line and run it:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Pkg.add.([\"Graphs\", \"MetaGraphs\", \"GraphPlot\", \"NamedColors\", \"RowEchelon\", Interact\", \"SymPy\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "...and then run this cell to import the packages:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.webio.node+json": { "children": [], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": {}, "type": "node" }, "text/html": [ "
\n", "

The WebIO Jupyter extension was not detected. See the\n", "\n", " WebIO Jupyter integration documentation\n", "\n", "for more information.\n", "

\n" ], "text/plain": [ "WebIO._IJuliaInit()" ] }, "metadata": { "application/vnd.webio.node+json": { "kernelId": "8a9fe2d3-11ba-493f-9228-1ded0ad827a7" } }, "output_type": "display_data" } ], "source": [ "using Interact, RowEchelon, Graphs, MetaGraphs, GraphPlot, NamedColors, LinearAlgebra\n", "import SymPy\n", "using SymPy: Sym" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Julia Graph-visualization code\n", "\n", "There are several Julia packages for manipulating graphs, e.g. [LightGraphs](https://github.com/JuliaGraphs/LightGraphs.jl), along with several packages for visualizing graphs, e.g. [GraphViz](https://github.com/Keno/GraphViz.jl). LightGraphs is oriented towards fast and sophisticated graph computations, however, and here I just want to do some simple and pretty visualizations with simple algorithms based on those in Strang's 18.06 textbook.\n", "\n", "So, here I define a simple `MyGraph` wrapper around LightGraphs directed graphs, with metadata attached via the MetaGraphs package, for basic plotting via the GraphPlots package." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "labels (generic function with 1 method)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "struct MyGraph\n", " g::MetaDiGraph\n", "end\n", "Base.copy(mg::MyGraph) = MyGraph(copy(mg.g))\n", "function MyGraph(edges::Pair{<:Integer,<:Integer}...)\n", " g = SimpleDiGraphFromIterator(Edge(e) for e in edges)\n", " MyGraph(MetaDiGraph(g))\n", "end\n", "\n", "using Random\n", "function deterministic_spring_layout(g::AbstractGraph; seed::Integer=0, kws...)\n", " rng = MersenneTwister(seed)\n", " spring_layout(g, 2 .* rand(rng, nv(g)) .- 1.0, 2 .* rand(rng,nv(g)) .- 1.0; kws...)\n", "end\n", "function Base.show(io::IO, m::MIME\"image/svg+xml\", mg::MyGraph)\n", " show(io, m, \n", " gplot(mg.g, layout=deterministic_spring_layout,\n", " nodelabel=map(v -> get(MetaGraphs.props(mg.g, v), :label, v), vertices(mg.g)),\n", " nodefillc=map(v -> get(MetaGraphs.props(mg.g, v), :color, \"gray\"), vertices(mg.g)),\n", " edgelabel=map(ie -> get(MetaGraphs.props(mg.g, ie[2]), :label, ie[1]), enumerate(edges(mg.g))),\n", " edgestrokec=map(e -> get(MetaGraphs.props(mg.g, e), :color, \"lightgray\"), edges(mg.g)),\n", " ))\n", "end\n", "\n", "function nodecolors!(g::MyGraph, nodes::AbstractVector{<:Integer}, color::String=\"red\")\n", " for n in nodes\n", " set_prop!(g.g, n, :color, color)\n", " end\n", " g\n", "end\n", "nodecolors(g, nodes, color) = nodecolors!(copy(g), nodes, color)\n", "edgearr(g, e) = e\n", "edgearr(g, e::AbstractVector{<:Integer}) = collect(edges(g))[e]\n", "edgearr(g, e::AbstractVector{<:Pair}) = Edge.(e)\n", "function edgecolors!(g::MyGraph, edges::AbstractVector, color::String=\"red\")\n", " for e in edgearr(g.g, edges)\n", " set_prop!(g.g, e, :color, color)\n", " end\n", " g\n", "end\n", "edgecolors(g::MyGraph, edges::AbstractVector, color::String=\"red\") = edgecolors!(copy(g), edges, color)\n", "\n", "# A little code so that we can label graph nodes/edges with SymPy expressions.\n", "# convert strings like \"v_2 - v_0\" from SymPy to nicer Unicode strings like \"v₂ - v₀\"\n", "subchar(d::Integer) = Char(UInt32('₀')+d)\n", "subchar(c::Char) = subchar(UInt32(c)-UInt32('0'))\n", "subchar(s::String) = replace(s, r\"_[0-9]\" => s -> subchar(s[2]))\n", "labelstring(s::SymPy.Sym) = subchar(repr(\"text/plain\", s))\n", "labelstring(x) = x\n", "\n", "function labels!(g::MyGraph; edges=nothing, nodes=nothing)\n", " if edges !== nothing\n", " for (e,E) in zip(MetaGraphs.edges(g.g), edges)\n", " set_prop!(g.g, e, :label, labelstring(E))\n", " end\n", " end\n", " if nodes !== nothing\n", " for (n,N) in zip(vertices(g.g), nodes)\n", " set_prop!(g.g, n, :label, labelstring(N))\n", " end\n", " end\n", " g\n", "end\n", "labels(g::MyGraph; kws...) = labels!(copy(g); kws...)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "randgraph (generic function with 1 method)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# generate a random graph with a given average #edges per node\n", "function randgraph(numnodes::Integer, edgespernode::Real)\n", " p = edgespernode/numnodes # probability of each edge\n", " e = Vector{Pair{Int,Int}}()\n", " for i = 1:numnodes, j = 1:numnodes\n", " if i != j && rand() < p\n", " push!(e, i=>j)\n", " end\n", " end\n", " return MyGraph(e...)\n", "end" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "incidence (generic function with 1 method)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# returns the incidence matrix for g\n", "function incidence(g::MyGraph)\n", " A = zeros(Int, ne(g.g), nv(g.g))\n", " for (i,e) in enumerate(edges(g.g))\n", " A[i,e.src] = -1\n", " A[i,e.dst] = +1\n", " end\n", " return A\n", "end" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "leftnullspace (generic function with 1 method)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Find the loops in g by the simplest \"textbook\" manner:\n", "# get a basis for the left nullspace incidence matrix.\n", "# We do this via the rref form, rather than nullspace(A'), because\n", "# we want a \"nice\" basis of ±1 and 0 entries.\n", "function leftnullspace(g::MyGraph)\n", " A = incidence(g)\n", " R = rref(Matrix(A'))\n", " m, n = size(R)\n", " pivots = Int[]\n", " for i = 1:m\n", " j = findfirst(!iszero, R[i,:])\n", " j !== nothing && push!(pivots, j)\n", " end\n", " r = length(pivots) # rank\n", " free = Int[j for j=1:n if j ∉ pivots]\n", " N = zeros(Int, n, n-r)\n", " k = 0\n", " for (k,j) in enumerate(free)\n", " N[pivots, k] = -R[1:r, j]\n", " N[j, k] = 1\n", " end\n", " return N\n", "end" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tree (generic function with 1 method)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# color the edges of a spanning tree of g, by the textbook\n", "# method of finding the pivot rows of the incidence matrix\n", "function pivotrows(g::MyGraph)\n", " A = incidence(g)\n", " R = rref(Matrix(A'))\n", " m, n = size(R)\n", " pivots = Int[]\n", " for i = 1:m\n", " j = findfirst(!iszero, R[i,:])\n", " j !== nothing && push!(pivots, j)\n", " end\n", " return pivots\n", "end\n", "colortree(g::MyGraph, color::String=\"red\") = edgecolors(g, pivotrows(g), color)\n", "tree(g::MyGraph) = MyGraph(MetaDiGraph(SimpleDiGraphFromIterator(edgearr(g.g, pivotrows(g)))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Graphs and incidence matrices\n", "\n", "Let's start by looking at an example graph with 6 nodes 8 edges. Computers are pretty good at drawing graphs for us:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g = MyGraph(1=>4, 4=>5, 5=>6, 6=>3, 3=>2, 2=>1, 2=>6, 4=>6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A key way to represent a graph in linear algebra is the [incidence matrix](https://en.wikipedia.org/wiki/Incidence_matrix). As defined in Strang's textbook, this is a matrix where the **rows correspond to edges** and the **columns correspond to nodes**. (Some authors use the transpose of this instead.)\n", "\n", "In particular, in the row for each edge going **from node N to node M**, there is a **-1 in column N** and a **+1 in column N**.\n", "\n", "For example, the incidence matrix of the graph above is:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8×6 Matrix{Int64}:\n", " -1 0 0 1 0 0\n", " 1 -1 0 0 0 0\n", " 0 -1 0 0 0 1\n", " 0 1 -1 0 0 0\n", " 0 0 0 -1 1 0\n", " 0 0 0 -1 0 1\n", " 0 0 0 0 -1 1\n", " 0 0 1 0 0 -1" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = incidence(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is an interesting structure if you think about *loops* in the graph. For example, in the graph above there is a loop among nodes 6, 3, 2, via edges 4,3,8. Let's look at the rows of $A$ corresponding to those edges:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×6 Matrix{Int64}:\n", " 0 1 -1 0 0 0\n", " 0 -1 0 0 0 1\n", " 0 0 1 0 0 -1" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[[4,3,8],:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we **add these rows** we get **zero**:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1×6 adjoint(::Vector{Int64}) with eltype Int64:\n", " 0 0 0 0 0 0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[4,:]' + A[3,:]' + A[8,:]'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In general, it is easy to see that **any loop in the graph** corresponds to **dependent rows**: if we sum the rows going around the loop (with a minus sign for arrows in the wrong direction), we get zero.\n", "\n", "The reason is simple: we get a -1 in a column when we *leave* a node, and a +1 in the column when we *enter* a node. When we go around the loop, we leave and enter each node, so the sum is zero.\n", "\n", "But dependent rows correspond to **elements of the left nullspace**:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1×6 Matrix{Int64}:\n", " 0 0 0 0 0 0" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[0 0 1 1 0 0 0 1] * A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That means that the number of \"independent\" (primitive) loops in a graph is related to the **rank** of the incidence matrix, and the **independent rows of A have no loops**.\n", "\n", "Let's look at the row-reduced echelon (rref) form of $A^T$:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×8 Matrix{Int64}:\n", " 1 0 0 0 0 -1 -1 0\n", " 0 1 0 0 0 -1 -1 0\n", " 0 0 1 0 0 1 1 -1\n", " 0 0 0 1 0 0 0 -1\n", " 0 0 0 0 1 0 -1 0\n", " 0 0 0 0 0 0 0 0" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Matrix{Int}(rref(Matrix(A')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that the rank of $A$ is 5:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rank(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This means that there are **five loop-free (independent) edges**, and there are **three** (8 - 5) primitive loops. Using the rref form of $A^T$, we can read off a basis for the left nullspace from the free columns (6,7,8):" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8×3 Matrix{Int64}:\n", " 1 1 0\n", " 1 1 0\n", " -1 -1 1\n", " 0 0 1\n", " 0 1 0\n", " 1 0 0\n", " 0 1 0\n", " 0 0 1" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N = leftnullspace(g)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×3 Matrix{Int64}:\n", " 0 0 0\n", " 0 0 0\n", " 0 0 0\n", " 0 0 0\n", " 0 0 0\n", " 0 0 0" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A' * N" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's visualize these loops by plotting the edges in a different color (red) one by one,\n", "with help from the Interact package to give us an interactive widget:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "animloops (generic function with 1 method)" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colorloop(g::MyGraph, n::Vector) = edgecolors!(edgecolors(g, findall(n .> 0), \"red\"), findall(n .< 0), \"blue\")\n", "function animloops(g::MyGraph)\n", " L = leftnullspace(g)\n", " @manipulate for loop in 1:size(L,2)\n", " colorloop(g, L[:,loop])\n", " end\n", "end" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "application/vnd.webio.node+json": { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "loop" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 3, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "11758169215002176570", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"1\",\"2\",\"3\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"11758169215002176570\",\"id\":\"12445682383640240541\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"11758169215002176570\",\"id\":\"13173288597411769607\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"11758169215002176570\",\"id\":\"12445682383640240541\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"11758169215002176570\",\"id\":\"13173288597411769607\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "12445682383640240541", "sync": false, "value": 0 }, "index": { "id": "13173288597411769607", "sync": true, "value": 2 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "id": "3988630758424797120", "name": "obs-node" }, "nodeType": "ObservableNode", "props": {}, "type": "node" } ], "instanceArgs": { "handlers": {}, "id": "7944926038166744988", "imports": { "data": [], "type": "async_block" }, "mount_callbacks": [], "observables": { "obs-node": { "id": "3988630758424797120", "sync": false, "value": { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "setInnerHtml": "\n\n\n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n 1\n \n \n \n \n 2\n \n \n \n \n 3\n \n \n \n \n 4\n \n \n \n \n 5\n \n \n \n \n 6\n \n \n \n \n 7\n \n \n \n \n 8\n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n 1\n \n \n \n \n 2\n \n \n \n \n 3\n \n \n \n \n 4\n \n \n \n \n 5\n \n \n \n \n 6\n \n \n\n\n" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": {}, "type": "node" }, "text/html": [ "\n", " \n", "\n" ], "text/plain": [ "Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"loop\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 3, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable(2), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000283c9bb00, Task (runnable) @0x0000000283c9bb00), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"1\\\",\\\"2\\\",\\\"3\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"11758169215002176570\\\",\\\"id\\\":\\\"12445682383640240541\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"11758169215002176570\\\",\\\"id\\\":\\\"13173288597411769607\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"11758169215002176570\\\",\\\"id\\\":\\\"12445682383640240541\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"11758169215002176570\\\",\\\"id\\\":\\\"13173288597411769607\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Observable(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")))], Dict{Symbol, Any}())" ] }, "execution_count": 18, "metadata": { "application/vnd.webio.node+json": { "kernelId": "8a9fe2d3-11ba-493f-9228-1ded0ad827a7" } }, "output_type": "execute_result" } ], "source": [ "animloops(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These three loops are **not the only loops** in the graph, but the **other loops can be made from combinations of these loops**. (Similarly, the columns of $N$ are not the *whole* left nullspace, they are just a **basis** for the n nullspace.\n", "\n", "For example, the loop between nodes 1-4-5-6-3-2 can be made by starting with 1-4-5-6-2 and \"adding\" the 6-3-2 loop.\n", "\n", "In this sense, a basis for the left nullspace of $A$ is a \"basis\" for the other loops in the graph: we say that they are \"primitive\" loops." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colorloop(g, N[:,2] + N[:,3]) # add two loops to make another loop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is fun to do the same thing for bigger graphs, chosen at random:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "application/vnd.webio.node+json": { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "loop" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 20, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "1320740273246710239", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"20\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"1320740273246710239\",\"id\":\"14295287463229216227\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"1320740273246710239\",\"id\":\"18381593539664038702\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"1320740273246710239\",\"id\":\"14295287463229216227\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"1320740273246710239\",\"id\":\"18381593539664038702\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "14295287463229216227", "sync": false, "value": 0 }, "index": { "id": "18381593539664038702", "sync": true, "value": 10 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "id": "9170528261463681105", "name": "obs-node" }, "nodeType": "ObservableNode", "props": {}, "type": "node" } ], "instanceArgs": { "handlers": {}, "id": "8189222639433794150", "imports": { "data": [], "type": "async_block" }, "mount_callbacks": [], "observables": { "obs-node": { "id": "9170528261463681105", "sync": false, "value": { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "setInnerHtml": "\n\n\n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n 1\n \n \n \n \n 2\n \n \n \n \n 3\n \n \n \n \n 4\n \n \n \n \n 5\n \n \n \n \n 6\n \n \n \n \n 7\n \n \n \n \n 8\n \n \n \n \n 9\n \n \n \n \n 10\n \n \n \n \n 11\n \n \n \n \n 12\n \n \n \n \n 13\n \n \n \n \n 14\n \n \n \n \n 15\n \n \n \n \n 16\n \n \n \n \n 17\n \n \n \n \n 18\n \n \n \n \n 19\n \n \n \n \n 20\n \n \n \n \n 21\n \n \n \n \n 22\n \n \n \n \n 23\n \n \n \n \n 24\n \n \n \n \n 25\n \n \n \n \n 26\n \n \n \n \n 27\n \n \n \n \n 28\n \n \n \n \n 29\n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n 1\n \n \n \n \n 2\n \n \n \n \n 3\n \n \n \n \n 4\n \n \n \n \n 5\n \n \n \n \n 6\n \n \n \n \n 7\n \n \n \n \n 8\n \n \n \n \n 9\n \n \n \n \n 10\n \n \n\n\n" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": {}, "type": "node" }, "text/html": [ "\n", " \n", "\n" ], "text/plain": [ "Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"loop\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 20, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable(10), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x00000002837fef80, Task (runnable) @0x00000002837fef80), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"1\\\",\\\"2\\\",\\\"3\\\",\\\"4\\\",\\\"5\\\",\\\"6\\\",\\\"7\\\",\\\"8\\\",\\\"9\\\",\\\"10\\\",\\\"11\\\",\\\"12\\\",\\\"13\\\",\\\"14\\\",\\\"15\\\",\\\"16\\\",\\\"17\\\",\\\"18\\\",\\\"19\\\",\\\"20\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"1320740273246710239\\\",\\\"id\\\":\\\"14295287463229216227\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"1320740273246710239\\\",\\\"id\\\":\\\"18381593539664038702\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"1320740273246710239\\\",\\\"id\\\":\\\"14295287463229216227\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"1320740273246710239\\\",\\\"id\\\":\\\"18381593539664038702\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Observable(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[MyGraph({10, 29} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")))], Dict{Symbol, Any}())" ] }, "execution_count": 20, "metadata": { "application/vnd.webio.node+json": { "kernelId": "8a9fe2d3-11ba-493f-9228-1ded0ad827a7" } }, "output_type": "execute_result" } ], "source": [ "gbig = randgraph(10, 3)\n", "animloops(gbig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversely, the *independent* rows of $A$ (corresponding to the **pivot columns** of the rref form of $A^T$) form a **maximal set of edges with no loops**. A graph with no loops is called a [tree](https://en.wikipedia.org/wiki/Tree_(graph_theory)), and this particular tree is called a [spanning tree](https://en.wikipedia.org/wiki/Spanning_tree) because it touches all of (\"spans\") the nodes (assuming the graph is connected).\n", "\n", "Let's color the spanning tree (loop-free edges) of our example graph red:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colortree(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also discard all of the edges that are *not* in the spanning tree, and we are left with a more boring graph of *just* the spanning tree:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 5} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can do the same thing for our bigger random example:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", " \n", " \n", " 10\n", " \n", " \n", " \n", " \n", " 11\n", " \n", " \n", " \n", " \n", " 12\n", " \n", " \n", " \n", " \n", " 13\n", " \n", " \n", " \n", " \n", " 14\n", " \n", " \n", " \n", " \n", " 15\n", " \n", " \n", " \n", " \n", " 16\n", " \n", " \n", " \n", " \n", " 17\n", " \n", " \n", " \n", " \n", " 18\n", " \n", " \n", " \n", " \n", " 19\n", " \n", " \n", " \n", " \n", " 20\n", " \n", " \n", " \n", " \n", " 21\n", " \n", " \n", " \n", " \n", " 22\n", " \n", " \n", " \n", " \n", " 23\n", " \n", " \n", " \n", " \n", " 24\n", " \n", " \n", " \n", " \n", " 25\n", " \n", " \n", " \n", " \n", " 26\n", " \n", " \n", " \n", " \n", " 27\n", " \n", " \n", " \n", " \n", " 28\n", " \n", " \n", " \n", " \n", " 29\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", " \n", " \n", " 10\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({10, 29} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "colortree(gbig)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", " \n", " \n", " 10\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({10, 9} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree(gbig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can make trees from even larger graphs, for fun:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", " \n", " \n", " 10\n", " \n", " \n", " \n", " \n", " 11\n", " \n", " \n", " \n", " \n", " 12\n", " \n", " \n", " \n", " \n", " 13\n", " \n", " \n", " \n", " \n", " 14\n", " \n", " \n", " \n", " \n", " 15\n", " \n", " \n", " \n", " \n", " 16\n", " \n", " \n", " \n", " \n", " 17\n", " \n", " \n", " \n", " \n", " 18\n", " \n", " \n", " \n", " \n", " 19\n", " \n", " \n", " \n", " \n", " 20\n", " \n", " \n", " \n", " \n", " 21\n", " \n", " \n", " \n", " \n", " 22\n", " \n", " \n", " \n", " \n", " 23\n", " \n", " \n", " \n", " \n", " 24\n", " \n", " \n", " \n", " \n", " 25\n", " \n", " \n", " \n", " \n", " 26\n", " \n", " \n", " \n", " \n", " 27\n", " \n", " \n", " \n", " \n", " 28\n", " \n", " \n", " \n", " \n", " 29\n", " \n", " \n", " \n", " \n", " 30\n", " \n", " \n", " \n", " \n", " 31\n", " \n", " \n", " \n", " \n", " 32\n", " \n", " \n", " \n", " \n", " 33\n", " \n", " \n", " \n", " \n", " 34\n", " \n", " \n", " \n", " \n", " 35\n", " \n", " \n", " \n", " \n", " 36\n", " \n", " \n", " \n", " \n", " 37\n", " \n", " \n", " \n", " \n", " 38\n", " \n", " \n", " \n", " \n", " 39\n", " \n", " \n", " \n", " \n", " 40\n", " \n", " \n", " \n", " \n", " 41\n", " \n", " \n", " \n", " \n", " 42\n", " \n", " \n", " \n", " \n", " 43\n", " \n", " \n", " \n", " \n", " 44\n", " \n", " \n", " \n", " \n", " 45\n", " \n", " \n", " \n", " \n", " 46\n", " \n", " \n", " \n", " \n", " 47\n", " \n", " \n", " \n", " \n", " 48\n", " \n", " \n", " \n", " \n", " 49\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " 1\n", " \n", " \n", " \n", " \n", " 2\n", " \n", " \n", " \n", " \n", " 3\n", " \n", " \n", " \n", " \n", " 4\n", " \n", " \n", " \n", " \n", " 5\n", " \n", " \n", " \n", " \n", " 6\n", " \n", " \n", " \n", " \n", " 7\n", " \n", " \n", " \n", " \n", " 8\n", " \n", " \n", " \n", " \n", " 9\n", " \n", " \n", " \n", " \n", " 10\n", " \n", " \n", " \n", " \n", " 11\n", " \n", " \n", " \n", " \n", " 12\n", " \n", " \n", " \n", " \n", " 13\n", " \n", " \n", " \n", " \n", " 14\n", " \n", " \n", " \n", " \n", " 15\n", " \n", " \n", " \n", " \n", " 16\n", " \n", " \n", " \n", " \n", " 17\n", " \n", " \n", " \n", " \n", " 18\n", " \n", " \n", " \n", " \n", " 19\n", " \n", " \n", " \n", " \n", " 20\n", " \n", " \n", " \n", " \n", " 21\n", " \n", " \n", " \n", " \n", " 22\n", " \n", " \n", " \n", " \n", " 23\n", " \n", " \n", " \n", " \n", " 24\n", " \n", " \n", " \n", " \n", " 25\n", " \n", " \n", " \n", " \n", " 26\n", " \n", " \n", " \n", " \n", " 27\n", " \n", " \n", " \n", " \n", " 28\n", " \n", " \n", " \n", " \n", " 29\n", " \n", " \n", " \n", " \n", " 30\n", " \n", " \n", " \n", " \n", " 31\n", " \n", " \n", " \n", " \n", " 32\n", " \n", " \n", " \n", " \n", " 33\n", " \n", " \n", " \n", " \n", " 34\n", " \n", " \n", " \n", " \n", " 35\n", " \n", " \n", " \n", " \n", " 36\n", " \n", " \n", " \n", " \n", " 37\n", " \n", " \n", " \n", " \n", " 38\n", " \n", " \n", " \n", " \n", " 39\n", " \n", " \n", " \n", " \n", " 40\n", " \n", " \n", " \n", " \n", " 41\n", " \n", " \n", " \n", " \n", " 42\n", " \n", " \n", " \n", " \n", " 43\n", " \n", " \n", " \n", " \n", " 44\n", " \n", " \n", " \n", " \n", " 45\n", " \n", " \n", " \n", " \n", " 46\n", " \n", " \n", " \n", " \n", " 47\n", " \n", " \n", " \n", " \n", " 48\n", " \n", " \n", " \n", " \n", " 49\n", " \n", " \n", " \n", " \n", " 50\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({50, 49} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree(randgraph(50, 8))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Graphs and Kirchhoff's circuit laws\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An elegant application of the incidence matrix and its subspaces arises if we think of the graph as representing an **electrical circuit**:\n", "\n", "* Each edge represents a wire/resistor, with an unknown current $i$. The *direction* of the edge indicates the *sign convention* ($i>0$ indicates current flowing in the direction of the arrow).\n", "* Each node represents a junction, with an unknown voltage $v$.\n", "\n", "Let's visualize this by re-labeling our graph from above. We'll use the [SymPy](https://github.com/JuliaPy/SymPy.jl) package to allow us to do *symbolic* (not numeric) calculations with the incidence matrix." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " i₁\n", " \n", " \n", " \n", " \n", " i₂\n", " \n", " \n", " \n", " \n", " i₃\n", " \n", " \n", " \n", " \n", " i₄\n", " \n", " \n", " \n", " \n", " i₅\n", " \n", " \n", " \n", " \n", " i₆\n", " \n", " \n", " \n", " \n", " i₇\n", " \n", " \n", " \n", " \n", " i₈\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " v₁\n", " \n", " \n", " \n", " \n", " v₂\n", " \n", " \n", " \n", " \n", " v₃\n", " \n", " \n", " \n", " \n", " v₄\n", " \n", " \n", " \n", " \n", " v₅\n", " \n", " \n", " \n", " \n", " v₆\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels(g, edges=[Sym(\"i_$i\") for i = 1:size(A,1)], nodes=[Sym(\"v_$i\") for i = 1:size(A,2)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Kirchhoff's voltage law (KVL)\n", "\n", "Let's start doing some linear algebra. What happens if we *multiply* our incidence matrix $A$ by a vector of voltages, one per node?" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}v_{1}\\\\v_{2}\\\\v_{3}\\\\v_{4}\\\\v_{5}\\\\v_{6}\\end{array} \\right]$\n" ], "text/plain": [ "6-element Vector{Sym}:\n", " v₁\n", " v₂\n", " v₃\n", " v₄\n", " v₅\n", " v₆" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v = [Sym(\"v_$i\") for i = 1:size(A,2)]" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8×6 Matrix{Int64}:\n", " -1 0 0 1 0 0\n", " 1 -1 0 0 0 0\n", " 0 -1 0 0 0 1\n", " 0 1 -1 0 0 0\n", " 0 0 0 -1 1 0\n", " 0 0 0 -1 0 1\n", " 0 0 0 0 -1 1\n", " 0 0 1 0 0 -1" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}- v_{1} + v_{4}\\\\v_{1} - v_{2}\\\\- v_{2} + v_{6}\\\\v_{2} - v_{3}\\\\- v_{4} + v_{5}\\\\- v_{4} + v_{6}\\\\- v_{5} + v_{6}\\\\v_{3} - v_{6}\\end{array} \\right]$\n" ], "text/plain": [ "8-element Vector{Sym}:\n", " -v₁ + v₄\n", " v₁ - v₂\n", " -v₂ + v₆\n", " v₂ - v₃\n", " -v₄ + v₅\n", " -v₄ + v₆\n", " -v₅ + v₆\n", " v₃ - v₆" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What we get are the **voltage difference** (and in particular, the **voltage rise**) across each edge. It is easier to see this if we use the elements of $Av$ to directly label the edges of our graph:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " -v₁ + v₄\n", " \n", " \n", " \n", " \n", " v₁ - v₂\n", " \n", " \n", " \n", " \n", " -v₂ + v₆\n", " \n", " \n", " \n", " \n", " v₂ - v₃\n", " \n", " \n", " \n", " \n", " -v₄ + v₅\n", " \n", " \n", " \n", " \n", " -v₄ + v₆\n", " \n", " \n", " \n", " \n", " -v₅ + v₆\n", " \n", " \n", " \n", " \n", " v₃ - v₆\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " v₁\n", " \n", " \n", " \n", " \n", " v₂\n", " \n", " \n", " \n", " \n", " v₃\n", " \n", " \n", " \n", " \n", " v₄\n", " \n", " \n", " \n", " \n", " v₅\n", " \n", " \n", " \n", " \n", " v₆\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels(g, edges=A*v, nodes=v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's ask the inverse question: **what voltage differences $d=Av$** can possibly arise? i.e. what $d$ are in $C(A)$? \n", "\n", "Remember, $A$ is **not full rank**: its rank is 5, but there are 8 rows (8 edges). So, $C(A)$ is 5-dimensional (\"missing\" three dimensions). Equivalently $C(A)$ is **orthogonal to the left nullspace**, which has three rows. What does this mean?\n", "\n", "Let's visualize the differences d:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " d₁\n", " \n", " \n", " \n", " \n", " d₂\n", " \n", " \n", " \n", " \n", " d₃\n", " \n", " \n", " \n", " \n", " d₄\n", " \n", " \n", " \n", " \n", " d₅\n", " \n", " \n", " \n", " \n", " d₆\n", " \n", " \n", " \n", " \n", " d₇\n", " \n", " \n", " \n", " \n", " d₈\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " v₁\n", " \n", " \n", " \n", " \n", " v₂\n", " \n", " \n", " \n", " \n", " v₃\n", " \n", " \n", " \n", " \n", " v₄\n", " \n", " \n", " \n", " \n", " v₅\n", " \n", " \n", " \n", " \n", " v₆\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = [Sym(\"d_$j\") for j = 1:size(A,1)]\n", "labels(g, edges=d, nodes=v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If $N$ is a basis for the left nullspace, we must have $N^T d = 0$, or:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}d_{1} + d_{2} - d_{3} + d_{6}\\\\d_{1} + d_{2} - d_{3} + d_{5} + d_{7}\\\\d_{3} + d_{4} + d_{8}\\end{array} \\right]$\n" ], "text/plain": [ "3-element Vector{Sym}:\n", " d₁ + d₂ - d₃ + d₆\n", " d₁ + d₂ - d₃ + d₅ + d₇\n", " d₃ + d₄ + d₈" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N' * d" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But what is this? Remember, each element of the left nullspace corresponded to a **loop in the graph**. Saying $N^T d = 0$, or $d \\perp N(A^T)$, is equivalent to saying that the **sum of the voltage rises around each loop = 0**.\n", "\n", "But this is precisely [Kirchhoff's voltage law](https://en.wikipedia.org/wiki/Kirchhoff's_circuit_laws) from circuit theory!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Kirchhoff's current law (KCL)\n", "\n", "To actually solve circuit problems, we need three additional ingredients:\n", "\n", "* The voltage difference $d$ must be divided by a resistance $R$ to get the *current* $i$ through that edge: $i = -d/R = -Yd$ (where $Y=1/R$ is the \"admittance\"), by [Ohm's law](https://en.wikipedia.org/wiki/Ohm's_law). Note that we need a minus sign to get the current in the direction of the arrow, since $d$ was the the voltage *rise* across the edge.\n", "\n", "* The sum of the currents $i$ entering each node must be zero, by Kirchhoff's current law (KCL).\n", "\n", "* To get a nontrivial solution, we need some kind of *source*: a battery or current source, to start currents flowing.\n", "\n", "How do we represent each one of these steps by linear-algebra operations?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ohm's law\n", "\n", "To represent Ohm's law, we need to multiply the voltage differences $d=Av$ by a *diagonal matrix* of admittances:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{rrrrrrrr}Y_{1}&0&0&0&0&0&0&0\\\\0&Y_{2}&0&0&0&0&0&0\\\\0&0&Y_{3}&0&0&0&0&0\\\\0&0&0&Y_{4}&0&0&0&0\\\\0&0&0&0&Y_{5}&0&0&0\\\\0&0&0&0&0&Y_{6}&0&0\\\\0&0&0&0&0&0&Y_{7}&0\\\\0&0&0&0&0&0&0&Y_{8}\\end{array}\\right]$\n" ], "text/plain": [ "8×8 Matrix{Sym}:\n", " Y₁ 0 0 0 0 0 0 0\n", " 0 Y₂ 0 0 0 0 0 0\n", " 0 0 Y₃ 0 0 0 0 0\n", " 0 0 0 Y₄ 0 0 0 0\n", " 0 0 0 0 Y₅ 0 0 0\n", " 0 0 0 0 0 Y₆ 0 0\n", " 0 0 0 0 0 0 Y₇ 0\n", " 0 0 0 0 0 0 0 Y₈" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y = diagm(0=>[Sym(\"Y_$i\") for i = 1:size(A,1)])" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}Y_{1} d_{1}\\\\Y_{2} d_{2}\\\\Y_{3} d_{3}\\\\Y_{4} d_{4}\\\\Y_{5} d_{5}\\\\Y_{6} d_{6}\\\\Y_{7} d_{7}\\\\Y_{8} d_{8}\\end{array} \\right]$\n" ], "text/plain": [ "8-element Vector{Sym}:\n", " Y₁⋅d₁\n", " Y₂⋅d₂\n", " Y₃⋅d₃\n", " Y₄⋅d₄\n", " Y₅⋅d₅\n", " Y₆⋅d₆\n", " Y₇⋅d₇\n", " Y₈⋅d₈" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y*d" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}Y_{1} \\left(- v_{1} + v_{4}\\right)\\\\Y_{2} \\left(v_{1} - v_{2}\\right)\\\\Y_{3} \\left(- v_{2} + v_{6}\\right)\\\\Y_{4} \\left(v_{2} - v_{3}\\right)\\\\Y_{5} \\left(- v_{4} + v_{5}\\right)\\\\Y_{6} \\left(- v_{4} + v_{6}\\right)\\\\Y_{7} \\left(- v_{5} + v_{6}\\right)\\\\Y_{8} \\left(v_{3} - v_{6}\\right)\\end{array} \\right]$\n" ], "text/plain": [ "8-element Vector{Sym}:\n", " Y₁⋅(-v₁ + v₄)\n", " Y₂⋅(v₁ - v₂)\n", " Y₃⋅(-v₂ + v₆)\n", " Y₄⋅(v₂ - v₃)\n", " Y₅⋅(-v₄ + v₅)\n", " Y₆⋅(-v₄ + v₆)\n", " Y₇⋅(-v₅ + v₆)\n", " Y₈⋅(v₃ - v₆)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y*A*v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Net current into each node\n", "\n", "Given the currents $i$, a little thought shows that the net current flowing into each node is precisely $A^T i$:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{r}- i_{1} + i_{2}\\\\- i_{2} - i_{3} + i_{4}\\\\- i_{4} + i_{8}\\\\i_{1} - i_{5} - i_{6}\\\\i_{5} - i_{7}\\\\i_{3} + i_{6} + i_{7} - i_{8}\\end{array} \\right]$\n" ], "text/plain": [ "6-element Vector{Sym}:\n", " -i₁ + i₂\n", " -i₂ - i₃ + i₄\n", " -i₄ + i₈\n", " i₁ - i₅ - i₆\n", " i₅ - i₇\n", " i₃ + i₆ + i₇ - i₈" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i = [Sym(\"i_$j\") for j=1:size(A,1)]\n", "A'*i" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " i₁\n", " \n", " \n", " \n", " \n", " i₂\n", " \n", " \n", " \n", " \n", " i₃\n", " \n", " \n", " \n", " \n", " i₄\n", " \n", " \n", " \n", " \n", " i₅\n", " \n", " \n", " \n", " \n", " i₆\n", " \n", " \n", " \n", " \n", " i₇\n", " \n", " \n", " \n", " \n", " i₈\n", " \n", " \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", " \n", " \n", " -i₁ + i₂\n", " \n", " \n", " \n", " \n", " -i₂ - i₃ + i₄\n", " \n", " \n", " \n", " \n", " -i₄ + i₈\n", " \n", " \n", " \n", " \n", " i₁ - i₅ - i₆\n", " \n", " \n", " \n", " \n", " i₅ - i₇\n", " \n", " \n", " \n", " \n", " i₃ + i₆ + i₇ - i₈\n", " \n", " \n", "\n", "\n" ], "text/plain": [ "MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels(g, edges=i, nodes=A'*i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Why is this? The reason is that each row $A^T$ corresponds to a node, and has $\\pm 1$ for each edge going into or out of the node, exactly the right sign to sum the net currents flowing in:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×8 adjoint(::Matrix{Int64}) with eltype Int64:\n", " -1 1 0 0 0 0 0 0\n", " 0 -1 -1 1 0 0 0 0\n", " 0 0 0 -1 0 0 0 1\n", " 1 0 0 0 -1 -1 0 0\n", " 0 0 0 0 1 0 -1 0\n", " 0 0 1 0 0 1 1 -1" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Putting it together, given voltages $v$, the net current flowing **out of** each node is\n", "\n", "$$\n", "A^T Y A v\n", "$$\n", "\n", "The matrix $A^T Y A$ is a very special and important kind of matrix. It is obviously **symmetric**, and later on in the course we will see that any matrix of this form is necessarily **positive semidefinite** (all pivots are ≥ 0). Many important matrices in science, engineering, statistics, and other fields take on this special form.\n", "\n", "If we multiply $A^T Y A$ together, not all of its specialness is apparent. It is often better to leave it in \"factored\" form:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\left[ \\begin{array}{rrrrrr}Y_{1} + Y_{2}&- Y_{2}&0&- Y_{1}&0&0\\\\- Y_{2}&Y_{2} + Y_{3} + Y_{4}&- Y_{4}&0&0&- Y_{3}\\\\0&- Y_{4}&Y_{4} + Y_{8}&0&0&- Y_{8}\\\\- Y_{1}&0&0&Y_{1} + Y_{5} + Y_{6}&- Y_{5}&- Y_{6}\\\\0&0&0&- Y_{5}&Y_{5} + Y_{7}&- Y_{7}\\\\0&- Y_{3}&- Y_{8}&- Y_{6}&- Y_{7}&Y_{3} + Y_{6} + Y_{7} + Y_{8}\\end{array}\\right]$\n" ], "text/plain": [ "6×6 Matrix{Sym}:\n", " Y₁ + Y₂ -Y₂ 0 … 0 0\n", " -Y₂ Y₂ + Y₃ + Y₄ -Y₄ 0 -Y₃\n", " 0 -Y₄ Y₄ + Y₈ 0 -Y₈\n", " -Y₁ 0 0 -Y₅ -Y₆\n", " 0 0 0 Y₅ + Y₇ -Y₇\n", " 0 -Y₃ -Y₈ … -Y₇ Y₃ + Y₆ + Y₇ + Y₈" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A' * Y * A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Null space\n", "\n", "If we just say that the net current flowing out of each node is zero, we get the equation:\n", "$$\n", "A^T Y A v = 0\n", "$$\n", "or $v \\in N(A^T Y A) = N(A)$.\n", "\n", "It is an amazing and important fact that $N(A^T Y A) = N(A)$!! (You saw a version of this in homework.) Why is this? Clearly, if $Ax = 0$ then $A^T Y Ax=0$. But what about the converse? Here is a trick: if $A^T Y Ax =0$, then $x^T A^T Y A x=0 = (Ax)^T Y (Ax)$. Let $y=Ax$. It is easy to see that $y^T Y y = \\sum_i Y_i y_i^2 = 0$ only if $y=0$, since all of the admittances $Y_i$ are positive. (We will later say that $Y$ is a \"positive-definite matrix\".) This means that $A^T Y Ax =0$ implies that $y=Ax=0$, which implies that $x \\in N(A)$.\n", "\n", "What is $N(A)$? The rank of $A$ is 5, so $N(A)$ must be **1-dimensional**. A basis for it is:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6×1 Matrix{Float64}:\n", " 0.40824829046386296\n", " 0.4082482904638631\n", " 0.4082482904638628\n", " 0.40824829046386335\n", " 0.4082482904638631\n", " 0.40824829046386313" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nullspace(A)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "But this is, of course, just the space of vectors where **all voltages are equal**. In hindsight, this should be obvious: if all the voltages are equal, then their difference are zero, and the currents are zero, and KCL is satisfied." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Current sources\n", "\n", "Of course, it is much more interesting to think about circuits when the currents are nonzero!\n", "\n", "To do this, we must consider a **source term** in the equations, and in particular we could try to solve\n", "\n", "$$\n", "A^T Y A v = s\n", "$$\n", "\n", "for some $s\\ne 0$. What does $s$ represent? It is precisely an **external source of current** flowing **out of each node**.\n", "\n", "For this to have a solution, however, we must have $s \\in C(A^T Y A) = N((A^T Y A)^T)^\\perp = N(A^T Y A)^\\perp = N(A)^\\perp$ (since $A^T Y A$ is symmetric, the left and right nullspaces are equal). We know a basis for $N(A)$ from above, so this boils down to:\n", "\n", "$$\n", "\\begin{pmatrix} 1 & 1 & 1 & 1 & 1 & 1 \\end{pmatrix} s = 0 = \\sum_{i=1}^6 s_i\n", "$$\n", "\n", "That is, to have a solution, **all current that flows in must flow out**, so that the net current flowing into the circuit is zero. This makes a lot of physical sense!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just for fun, let's solve this circuit problem when the current is flowing **into node 2** and **out through node 1**, with slider controls for the 8 admittances, and label the edges with the currents." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "twodigits (generic function with 1 method)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "twodigits(x) = round(x, digits=2)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "application/vnd.webio.node+json": { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₁" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "5432103940800811028", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"5432103940800811028\",\"id\":\"523688131832745273\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"5432103940800811028\",\"id\":\"1932163917185879796\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"5432103940800811028\",\"id\":\"523688131832745273\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"5432103940800811028\",\"id\":\"1932163917185879796\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "523688131832745273", "sync": false, "value": 0 }, "index": { "id": "1932163917185879796", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₂" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "1718417666948007598", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"1718417666948007598\",\"id\":\"8725565259840982185\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"1718417666948007598\",\"id\":\"3511672556481969786\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"1718417666948007598\",\"id\":\"8725565259840982185\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"1718417666948007598\",\"id\":\"3511672556481969786\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "8725565259840982185", "sync": false, "value": 0 }, "index": { "id": "3511672556481969786", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₃" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "15970815832708533116", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"15970815832708533116\",\"id\":\"2420044375029248402\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"15970815832708533116\",\"id\":\"554882307497398093\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"15970815832708533116\",\"id\":\"2420044375029248402\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"15970815832708533116\",\"id\":\"554882307497398093\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "2420044375029248402", "sync": false, "value": 0 }, "index": { "id": "554882307497398093", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₄" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "3780806694388362671", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"3780806694388362671\",\"id\":\"16800950861448885590\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"3780806694388362671\",\"id\":\"636691891107015737\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"3780806694388362671\",\"id\":\"16800950861448885590\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"3780806694388362671\",\"id\":\"636691891107015737\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "16800950861448885590", "sync": false, "value": 0 }, "index": { "id": "636691891107015737", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₅" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "6128907041811942585", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"6128907041811942585\",\"id\":\"15585748669024378077\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"6128907041811942585\",\"id\":\"16496876739494999155\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"6128907041811942585\",\"id\":\"15585748669024378077\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"6128907041811942585\",\"id\":\"16496876739494999155\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "15585748669024378077", "sync": false, "value": 0 }, "index": { "id": "16496876739494999155", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₆" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "7909771970869587988", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"7909771970869587988\",\"id\":\"16774039972888896363\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"7909771970869587988\",\"id\":\"9956793018930867036\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"7909771970869587988\",\"id\":\"16774039972888896363\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"7909771970869587988\",\"id\":\"9956793018930867036\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "16774039972888896363", "sync": false, "value": 0 }, "index": { "id": "9956793018930867036", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₇" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "8740962986163213682", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"8740962986163213682\",\"id\":\"10158758216819727422\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"8740962986163213682\",\"id\":\"16039901869771518637\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"8740962986163213682\",\"id\":\"10158758216819727422\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"8740962986163213682\",\"id\":\"16039901869771518637\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "10158758216819727422", "sync": false, "value": 0 }, "index": { "id": "16039901869771518637", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [ { "children": [ { "children": [ { "children": [ "Y₈" ], "instanceArgs": { "namespace": "html", "tag": "label" }, "nodeType": "DOM", "props": { "className": "interact ", "style": { "padding": "5px 10px 0px 10px" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-left" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "input" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}", "orient": "horizontal", "type": "range" }, "className": "slider slider is-fullwidth", "max": 100, "min": 1, "step": 1, "style": {} }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-center" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "p" }, "nodeType": "DOM", "props": { "attributes": { "data-bind": "text: formatted_val" } }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row-right" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } ], "instanceArgs": { "handlers": { "changes": [ "(function (val){return (val!=this.model[\"changes\"]()) ? (this.valueFromJulia[\"changes\"]=true, this.model[\"changes\"](val)) : undefined})" ], "index": [ "(function (val){return (val!=this.model[\"index\"]()) ? (this.valueFromJulia[\"index\"]=true, this.model[\"index\"](val)) : undefined})" ] }, "id": "4003882290061871992", "imports": { "data": [ { "name": "knockout", "type": "js", "url": "/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js" }, { "name": "knockout_punches", "type": "js", "url": "/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js" }, { "name": null, "type": "js", "url": "/assetserver/dc651e248070c73d6a4eaa2766193d4a09a13f05-all.js" }, { "name": null, "type": "css", "url": "/assetserver/0318b018d0ff756043bf1ee25f56ab5c74ef7889-style.css" }, { "name": null, "type": "css", "url": "/assetserver/f798281fed8908611ae453136bda5c1a2e6bffd7-bulma_confined.min.css" } ], "type": "async_block" }, "mount_callbacks": [ "function () {\n var handler = (function (ko, koPunches) {\n ko.punches.enableAll();\n ko.bindingHandlers.numericValue = {\n init: function(element, valueAccessor, allBindings, data, context) {\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\n stringified.subscribe(function(value) {\n var val = parseFloat(value);\n if (!isNaN(val)) {\n valueAccessor()(val);\n }\n });\n valueAccessor().subscribe(function(value) {\n var str = JSON.stringify(value);\n if ((str == \"0\") && ([\"-0\", \"-0.\"].indexOf(stringified()) >= 0))\n return;\n if ([\"null\", \"\"].indexOf(str) >= 0)\n return;\n stringified(str);\n });\n ko.applyBindingsToNode(\n element,\n {\n value: stringified,\n valueUpdate: allBindings.get('valueUpdate'),\n },\n context,\n );\n }\n };\n var json_data = {\"formatted_vals\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"1.0\",\"1.1\",\"1.2\",\"1.3\",\"1.4\",\"1.5\",\"1.6\",\"1.7\",\"1.8\",\"1.9\",\"2.0\",\"2.1\",\"2.2\",\"2.3\",\"2.4\",\"2.5\",\"2.6\",\"2.7\",\"2.8\",\"2.9\",\"3.0\",\"3.1\",\"3.2\",\"3.3\",\"3.4\",\"3.5\",\"3.6\",\"3.7\",\"3.8\",\"3.9\",\"4.0\",\"4.1\",\"4.2\",\"4.3\",\"4.4\",\"4.5\",\"4.6\",\"4.7\",\"4.8\",\"4.9\",\"5.0\",\"5.1\",\"5.2\",\"5.3\",\"5.4\",\"5.5\",\"5.6\",\"5.7\",\"5.8\",\"5.9\",\"6.0\",\"6.1\",\"6.2\",\"6.3\",\"6.4\",\"6.5\",\"6.6\",\"6.7\",\"6.8\",\"6.9\",\"7.0\",\"7.1\",\"7.2\",\"7.3\",\"7.4\",\"7.5\",\"7.6\",\"7.7\",\"7.8\",\"7.9\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\",\"8.5\",\"8.6\",\"8.7\",\"8.8\",\"8.9\",\"9.0\",\"9.1\",\"9.2\",\"9.3\",\"9.4\",\"9.5\",\"9.6\",\"9.7\",\"9.8\",\"9.9\",\"10.0\"],\"changes\":WebIO.getval({\"name\":\"changes\",\"scope\":\"4003882290061871992\",\"id\":\"11487652428459938951\",\"type\":\"observable\"}),\"index\":WebIO.getval({\"name\":\"index\",\"scope\":\"4003882290061871992\",\"id\":\"10134177006576795128\",\"type\":\"observable\"})};\n var self = this;\n function AppViewModel() {\n for (var key in json_data) {\n var el = json_data[key];\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n }\n \n [this[\"formatted_val\"]=ko.computed( function(){\n return this.formatted_vals()[parseInt(this.index())-(1)];\n }\n,this)]\n [this[\"changes\"].subscribe((function (val){!(this.valueFromJulia[\"changes\"]) ? (WebIO.setval({\"name\":\"changes\",\"scope\":\"4003882290061871992\",\"id\":\"11487652428459938951\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"changes\"]=false}),self),this[\"index\"].subscribe((function (val){!(this.valueFromJulia[\"index\"]) ? (WebIO.setval({\"name\":\"index\",\"scope\":\"4003882290061871992\",\"id\":\"10134177006576795128\",\"type\":\"observable\"},val)) : undefined; return this.valueFromJulia[\"index\"]=false}),self)]\n \n }\n self.model = new AppViewModel();\n self.valueFromJulia = {};\n for (var key in json_data) {\n self.valueFromJulia[key] = false;\n }\n ko.applyBindings(self.model, self.dom);\n}\n);\n (WebIO.importBlock({\"data\":[{\"name\":\"knockout\",\"type\":\"js\",\"url\":\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\"},{\"name\":\"knockout_punches\",\"type\":\"js\",\"url\":\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\"}],\"type\":\"async_block\"})).then((imports) => handler.apply(this, imports));\n}\n" ], "observables": { "changes": { "id": "11487652428459938951", "sync": false, "value": 0 }, "index": { "id": "10134177006576795128", "sync": true, "value": 50 } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "field interact-widget" }, "type": "node" }, { "children": [ { "children": [], "instanceArgs": { "id": "5333283363638451252", "name": "obs-node" }, "nodeType": "ObservableNode", "props": {}, "type": "node" } ], "instanceArgs": { "handlers": {}, "id": "941155555585390175", "imports": { "data": [], "type": "async_block" }, "mount_callbacks": [], "observables": { "obs-node": { "id": "5333283363638451252", "sync": false, "value": { "children": [ { "children": [], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "setInnerHtml": "\n\n\n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n i₁ = -0.3\n \n \n \n \n i₂ = 0.7\n \n \n \n \n i₃ = 0.2\n \n \n \n \n i₄ = -0.1\n \n \n \n \n i₅ = -0.1\n \n \n \n \n i₆ = -0.2\n \n \n \n \n i₇ = -0.1\n \n \n \n \n i₈ = -0.1\n \n \n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n \n \n 1\n \n \n \n \n 2\n \n \n \n \n 3\n \n \n \n \n 4\n \n \n \n \n 5\n \n \n \n \n 6\n \n \n\n\n" }, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": { "className": "interact-flex-row interact-widget" }, "type": "node" } } }, "systemjs_options": null }, "nodeType": "Scope", "props": {}, "type": "node" } ], "instanceArgs": { "namespace": "html", "tag": "div" }, "nodeType": "DOM", "props": {}, "type": "node" }, "text/html": [ "\n", " \n", "\n" ], "text/plain": [ "Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₁\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000283a659f0, Task (runnable) @0x0000000283a659f0), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"5432103940800811028\\\",\\\"id\\\":\\\"523688131832745273\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"5432103940800811028\\\",\\\"id\\\":\\\"1932163917185879796\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"5432103940800811028\\\",\\\"id\\\":\\\"523688131832745273\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"5432103940800811028\\\",\\\"id\\\":\\\"1932163917185879796\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₂\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000287ef1e40, Task (runnable) @0x0000000287ef1e40), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"1718417666948007598\\\",\\\"id\\\":\\\"8725565259840982185\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"1718417666948007598\\\",\\\"id\\\":\\\"3511672556481969786\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"1718417666948007598\\\",\\\"id\\\":\\\"8725565259840982185\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"1718417666948007598\\\",\\\"id\\\":\\\"3511672556481969786\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₃\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000287f38e70, Task (runnable) @0x0000000287f38e70), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"15970815832708533116\\\",\\\"id\\\":\\\"2420044375029248402\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"15970815832708533116\\\",\\\"id\\\":\\\"554882307497398093\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"15970815832708533116\\\",\\\"id\\\":\\\"2420044375029248402\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"15970815832708533116\\\",\\\"id\\\":\\\"554882307497398093\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₄\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x000000028a171b60, Task (runnable) @0x000000028a171b60), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"3780806694388362671\\\",\\\"id\\\":\\\"16800950861448885590\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"3780806694388362671\\\",\\\"id\\\":\\\"636691891107015737\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"3780806694388362671\\\",\\\"id\\\":\\\"16800950861448885590\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"3780806694388362671\\\",\\\"id\\\":\\\"636691891107015737\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₅\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x000000028a557990, Task (runnable) @0x000000028a557990), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"6128907041811942585\\\",\\\"id\\\":\\\"15585748669024378077\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"6128907041811942585\\\",\\\"id\\\":\\\"16496876739494999155\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"6128907041811942585\\\",\\\"id\\\":\\\"15585748669024378077\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"6128907041811942585\\\",\\\"id\\\":\\\"16496876739494999155\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₆\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x000000028b07ab30, Task (runnable) @0x000000028b07ab30), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"7909771970869587988\\\",\\\"id\\\":\\\"16774039972888896363\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"7909771970869587988\\\",\\\"id\\\":\\\"9956793018930867036\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"7909771970869587988\\\",\\\"id\\\":\\\"16774039972888896363\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"7909771970869587988\\\",\\\"id\\\":\\\"9956793018930867036\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₇\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000287ee8d00, Task (runnable) @0x0000000287ee8d00), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"8740962986163213682\\\",\\\"id\\\":\\\"10158758216819727422\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"8740962986163213682\\\",\\\"id\\\":\\\"16039901869771518637\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"8740962986163213682\\\",\\\"id\\\":\\\"10158758216819727422\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"8740962986163213682\\\",\\\"id\\\":\\\"16039901869771518637\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Scope(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"Y₈\"], Dict{Symbol, Any}(:className => \"interact \", :style => Dict{Any, Any}(:padding => \"5px 10px 0px 10px\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-left\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol, Any}(:max => 100, :min => 1, :attributes => Dict{Any, Any}(:type => \"range\", Symbol(\"data-bind\") => \"numericValue: index, valueUpdate: 'input', event: {change: function (){this.changes(this.changes()+1)}}\", \"orient\" => \"horizontal\"), :step => 1, :className => \"slider slider is-fullwidth\", :style => Dict{Any, Any}()))], Dict{Symbol, Any}(:className => \"interact-flex-row-center\")), Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol, Any}(:attributes => Dict(\"data-bind\" => \"text: formatted_val\")))], Dict{Symbol, Any}(:className => \"interact-flex-row-right\"))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")), Dict{String, Tuple{AbstractObservable, Union{Nothing, Bool}}}(\"changes\" => (Observable(0), nothing), \"index\" => (Observable{Any}(50), nothing)), Set{String}(), nothing, Asset[Asset(\"js\", \"knockout\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout.js\"), Asset(\"js\", \"knockout_punches\", \"/Users/stevenj/.julia/packages/Knockout/HReiN/src/../assets/knockout_punches.js\"), Asset(\"js\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/all.js\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/InteractBase/Qhvxg/src/../assets/style.css\"), Asset(\"css\", nothing, \"/Users/stevenj/.julia/packages/Interact/PENUy/src/../assets/bulma_confined.min.css\")], Dict{Any, Any}(\"changes\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")], \"index\" => Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"index\\\"]()) ? (this.valueFromJulia[\\\"index\\\"]=true, this.model[\\\"index\\\"](val)) : undefined})\")]), WebIO.ConnectionPool(Channel{Any}(32), Set{AbstractConnection}(), Condition(Base.InvasiveLinkedList{Task}(Task (runnable) @0x0000000287ede290, Task (runnable) @0x0000000287ede290), Base.AlwaysLockedST(1))), WebIO.JSString[WebIO.JSString(\"function () {\\n var handler = (function (ko, koPunches) {\\n ko.punches.enableAll();\\n ko.bindingHandlers.numericValue = {\\n init: function(element, valueAccessor, allBindings, data, context) {\\n var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n stringified.subscribe(function(value) {\\n var val = parseFloat(value);\\n if (!isNaN(val)) {\\n valueAccessor()(val);\\n }\\n });\\n valueAccessor().subscribe(function(value) {\\n var str = JSON.stringify(value);\\n if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n return;\\n if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n return;\\n stringified(str);\\n });\\n ko.applyBindingsToNode(\\n element,\\n {\\n value: stringified,\\n valueUpdate: allBindings.get('valueUpdate'),\\n },\\n context,\\n );\\n }\\n };\\n var json_data = {\\\"formatted_vals\\\":[\\\"0.1\\\",\\\"0.2\\\",\\\"0.3\\\",\\\"0.4\\\",\\\"0.5\\\",\\\"0.6\\\",\\\"0.7\\\",\\\"0.8\\\",\\\"0.9\\\",\\\"1.0\\\",\\\"1.1\\\",\\\"1.2\\\",\\\"1.3\\\",\\\"1.4\\\",\\\"1.5\\\",\\\"1.6\\\",\\\"1.7\\\",\\\"1.8\\\",\\\"1.9\\\",\\\"2.0\\\",\\\"2.1\\\",\\\"2.2\\\",\\\"2.3\\\",\\\"2.4\\\",\\\"2.5\\\",\\\"2.6\\\",\\\"2.7\\\",\\\"2.8\\\",\\\"2.9\\\",\\\"3.0\\\",\\\"3.1\\\",\\\"3.2\\\",\\\"3.3\\\",\\\"3.4\\\",\\\"3.5\\\",\\\"3.6\\\",\\\"3.7\\\",\\\"3.8\\\",\\\"3.9\\\",\\\"4.0\\\",\\\"4.1\\\",\\\"4.2\\\",\\\"4.3\\\",\\\"4.4\\\",\\\"4.5\\\",\\\"4.6\\\",\\\"4.7\\\",\\\"4.8\\\",\\\"4.9\\\",\\\"5.0\\\",\\\"5.1\\\",\\\"5.2\\\",\\\"5.3\\\",\\\"5.4\\\",\\\"5.5\\\",\\\"5.6\\\",\\\"5.7\\\",\\\"5.8\\\",\\\"5.9\\\",\\\"6.0\\\",\\\"6.1\\\",\\\"6.2\\\",\\\"6.3\\\",\\\"6.4\\\",\\\"6.5\\\",\\\"6.6\\\",\\\"6.7\\\",\\\"6.8\\\",\\\"6.9\\\",\\\"7.0\\\",\\\"7.1\\\",\\\"7.2\\\",\\\"7.3\\\",\\\"7.4\\\",\\\"7.5\\\",\\\"7.6\\\",\\\"7.7\\\",\\\"7.8\\\",\\\"7.9\\\",\\\"8.0\\\",\\\"8.1\\\",\\\"8.2\\\",\\\"8.3\\\",\\\"8.4\\\",\\\"8.5\\\",\\\"8.6\\\",\\\"8.7\\\",\\\"8.8\\\",\\\"8.9\\\",\\\"9.0\\\",\\\"9.1\\\",\\\"9.2\\\",\\\"9.3\\\",\\\"9.4\\\",\\\"9.5\\\",\\\"9.6\\\",\\\"9.7\\\",\\\"9.8\\\",\\\"9.9\\\",\\\"10.0\\\"],\\\"changes\\\":WebIO.getval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"4003882290061871992\\\",\\\"id\\\":\\\"11487652428459938951\\\",\\\"type\\\":\\\"observable\\\"}),\\\"index\\\":WebIO.getval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"4003882290061871992\\\",\\\"id\\\":\\\"10134177006576795128\\\",\\\"type\\\":\\\"observable\\\"})};\\n var self = this;\\n function AppViewModel() {\\n for (var key in json_data) {\\n var el = json_data[key];\\n this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n }\\n \\n [this[\\\"formatted_val\\\"]=ko.computed( function(){\\n return this.formatted_vals()[parseInt(this.index())-(1)];\\n }\\n,this)]\\n [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"4003882290061871992\\\",\\\"id\\\":\\\"11487652428459938951\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"index\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"index\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"index\\\",\\\"scope\\\":\\\"4003882290061871992\\\",\\\"id\\\":\\\"10134177006576795128\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"index\\\"]=false}),self)]\\n \\n }\\n self.model = new AppViewModel();\\n self.valueFromJulia = {};\\n for (var key in json_data) {\\n self.valueFromJulia[key] = false;\\n }\\n ko.applyBindings(self.model, self.dom);\\n}\\n);\\n (WebIO.importBlock({\\\"data\\\":[{\\\"name\\\":\\\"knockout\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/2df50fa7f121fedbb109a97bba894e56aef8c852-knockout.js\\\"},{\\\"name\\\":\\\"knockout_punches\\\",\\\"type\\\":\\\"js\\\",\\\"url\\\":\\\"/assetserver/bb2c111220e03062c2973eb306d97c8fc96857d6-knockout_punches.js\\\"}],\\\"type\\\":\\\"async_block\\\"})).then((imports) => handler.apply(this, imports));\\n}\\n\")])], Dict{Symbol, Any}(:className => \"field interact-widget\")), Observable(Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[MyGraph({6, 8} directed Int64 metagraph with Float64 weights defined by :weight (default weight 1.0))], Dict{Symbol, Any}(:className => \"interact-flex-row interact-widget\")))], Dict{Symbol, Any}())" ] }, "execution_count": 42, "metadata": { "application/vnd.webio.node+json": { "kernelId": "8a9fe2d3-11ba-493f-9228-1ded0ad827a7" } }, "output_type": "execute_result" } ], "source": [ "@manipulate for Y₁=0.1:0.1:10,\n", " Y₂=0.1:0.1:10,\n", " Y₃=0.1:0.1:10,\n", " Y₄=0.1:0.1:10,\n", " Y₅=0.1:0.1:10,\n", " Y₆=0.1:0.1:10,\n", " Y₇=0.1:0.1:10,\n", " Y₈=0.1:0.1:10\n", " s = [1,-1,0,0,0,0]\n", " Y = diagm(0=>[Y₁,Y₂,Y₃,Y₄,Y₅,Y₆,Y₇,Y₈])\n", " nodecolors!(labels(g, edges=[subchar(\"i_$j = $i\") for (j,i) in enumerate(twodigits.(Y*A*(pinv(A'*Y*A) * s)))]),\n", " [1,2])\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that if we make the admittance $Y_2$ really large compared to all of the other admittances, then nearly all of the current should flow just over that one edge. Conversely, if we make $Y_2$ really *small*, it is almost like \"cutting\" that wire: almost all of the current should flow through the *other* edges.\n", "\n", "Hooray, math (and physics) works!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sparsity\n", "\n", "The case of matrices arising from graphs illustrates another point that I've made many times: **really large matrices are often sparse (mostly 0)** in practice.\n", "\n", "For example, imagine a circuit with a million nodes. For the most part, there will only be *wires between nearby nodes*. Or imagine a graph where the nodes are websites and the edges are links: there are billions of sites, but *each site only links to a few other sites* (a few hundred at most, usually). In such cases, the **incidence matrix is mostly zero**, and similarly for $A^T Y A$ etcetetera.\n", "\n", "This is hugely important, because solving $Ax=b$ and most other matrix equations scale as $\\sim n^3$ for $n \\times n$ matrices. $1000 \\times 1000$ matrices are easy (< 1 second), but $n=10^6$ would require supercomputers, and $n=10^9$ would be impossibly hard. What saves us is that there are **much faster algorithms for sparse matrices**. We won't learn much about such algorithms in 18.06, but the key point is to know that they exist.\n", "\n", "If you encounter a large sparse matrix problem in the future, go read about sparse matrix algorithms!" ] } ], "metadata": { "@webio": { "lastCommId": "124bc382b0be4e3f8fe62e6f2805cf54", "lastKernelId": "8a9fe2d3-11ba-493f-9228-1ded0ad827a7" }, "anaconda-cloud": {}, "kernelspec": { "display_name": "Julia 1.8.3", "language": "julia", "name": "julia-1.8" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.8.3" } }, "nbformat": 4, "nbformat_minor": 1 }