Jekyll2017-06-21T11:55:54-04:00https://bitworking.org/BitWorkingJoe Gregorio - REST, Web, Python, Go, APIs, Dad, Husband, Maker, or any linear combination of such. Googler.
Compute and Moore’s Law2017-06-21T00:00:00-04:002017-06-21T00:00:00-04:00https://bitworking.org/news/2017/06/compute-and-moores-law<p>This article from Technology Review,
<a href="https://www.technologyreview.com/s/607917/how-ai-can-keep-accelerating-after-moores-law/">How AI Can Keep Accelerating After Moore’s Law</a>
is a good follow-on from a previous article
<a href="https://www.technologyreview.com/s/601441/moores-law-is-dead-now-what/">Moore’s Law Is Dead. Now What?</a>.</p>
<p>From the second article:</p>
<blockquote>
<p>Engineers have kept GPUs getting more powerful because they can be more
specialized to the particular math they need to perform for graphics or
machine learning, he says.</p>
</blockquote>
<p>In order to continue to squeeze more performance out of the same number of
transistors and/or watts, we are going to need to get closer to the metal, and
the metal is going to have to become more and more specialized, or at the very
least, the metal has to stop looking like monolithic CPUs with a small number
of cores.</p>
<p>GPUs by themselves, even if you only have OpenGL, are attractive because
of the enormous amount of specialized computational power available. This power is what
originally attracted people to General Purpose GPU
<a href="https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units">GPGPU</a>, which started as
people transforming scientific computations into a graphical form to get them
to run on a GPU. That work in turn drove the creation and adoption of general
compute APIs like CUDA and then OpenCL, which expose the underlying compute
units and specialized memory access features in a GPU.</p>
<p>Compute APIs are much closer to the metal, exposing the underlying power of
the GPU without the intervening machinery and bugs of the OpenGL abstraction.
The API surface of these compute APIs are much smaller, which should mean
simpler and less buggy drivers. In addition the compute APIs are focused in
part on scientific applications, so the results from using compute APIs should
be much more repeatable. At the very least using compute APIs we are <a href="https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/log.html">in
control of what performance/accuracy tradeoff to make</a>. Compute APIs are also
becoming more widely available, with every next generation API (Vulkan, DX12,
and Metal) supporting a compute component.</p>
<p>One of the more surprising things I learned recently was exactly how sloppy
OpenGL could be. For example, from the <a href="https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/log.html">documentation for the OpenCL log
function</a>,
you can choose between the native hardware accelerated version
of log, or use a log function that will return accurate results.</p>
<blockquote>
<p>native_log computes natural logarithm over an implementation-defined range. <strong>The maximum error is implementation-defined</strong>.</p>
</blockquote>
<p>I think it’s time for me to find an OpenCL library for <a href="https://golang.org">Go</a> and
start exploring <a href="https://cloud.google.com/gpu/">GPU’s on Google Compute Engine</a>.</p>This article from Technology Review, How AI Can Keep Accelerating After Moore’s Law is a good follow-on from a previous article Moore’s Law Is Dead. Now What?.North Carolina Urbanization and Rural Flight2017-06-18T00:00:00-04:002017-06-18T00:00:00-04:00https://bitworking.org/news/2017/06/north-carolina-urbanization<p>
The rural areas of North Carolina are emptying out.
</p>
<style>
.tooltip {
position: absolute;
width: 200px;
height: 28px;
pointer-events: none;
}
text {
font-size: 15px;
}
</style>
<div id=plot></div>
<p style="text-align: center; font-size: 90%;">
<a href="http://www.indexmundi.com/facts/united-states/quick-facts/north-carolina/population-growth#table">
North Carolina County Population vs Count Population Growth Rate (2010 -
2014).</a><br>
Click on the graph to toggle between population density and county
population.
</p>
<p>
North Carolina is not immune to <a
href="https://en.wikipedia.org/wiki/Rural_flight">Rural Flight</a>, nor is
it a special victim, as the phenomenon is happening world wide.
Countrysides around the world are emptying out.
</p>
<iframe style="margin: 1em 2em;" width="560" height="315"
src="https://www.youtube.com/embed/B67LTsGENPQ" frameborder="0"
allowfullscreen></iframe>
<p>
It's important to keep these broader patterns in mind, first so you don't
go <a
href="http://www.roanoke-chowannewsherald.com/2015/12/08/woodland-rejects-solar-farm/">
blaming random local events that look like correlation</a>:
</p>
<blockquote>
<p>
Mary Hobbs has been living in Woodland for 50 years and said she has
watched it slowly becoming a ghost town with no job opportunities for
young people.
</p>
<p>
She said her home is surrounded by solar farms and is no longer worth its
value because of those facilities.
</p>
</blockquote>
<p>And from the same article:</p>
<blockquote>
<p>
Bobby Mann said he watched communities dry up when I-95 came along and
warned that would happen to Woodland because of the solar farms.
</p>
<p>
“You’re killing your town,” he said. “All the young people are going to
move out.”
</p>
</blockquote>
<p>
But also so that when trying to <a
href="http://www.ncruralcenter.org/about-us/news/756-ruralurban-coalitions-highlighted-during-election-season">formulate
policy</a> we temper trying to stop rural flight, which is probably
impossible, with programs to help with relocation and the humane winding down of rural towns,
because the <a href="http://www.radiolab.org/story/seneca-nebraska/">alternatives are
pretty grim</a>. And yes, let me state explicitly what I'm implying
above, which is that people might have to move. Just like companies are not
owed a business model, people aren't owed the job of their choice brought
to their doorstop. But what should also be acknowledged is that moving
for the poor is incredibly difficult and that any solutions proposed should
address that gap.
</p>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
(function () {
var margin = {top: 20, right: 20, bottom: 60, left: 60},
width = 640 - margin.left - margin.right,
height = 480 - margin.top - margin.bottom;
var xlabels = ["Density (people/mi^2)", "Population per County (100K)"];
var x = d3.scalePow().exponent(1/10).range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var svg = d3.select("#plot").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var tooltip = d3.select("#plot").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var line = svg.append('line')
.attr('id', 'fit')
.attr('stroke-width', '2')
.attr('stroke', 'grey');
var data = [
// Name, % change, population, size
["Alamance",3,155792, 435],
["Alexander",0.5,37392, 263],
["Alleghany",-2.5,10879, 236],
["Anson",-4.4,25765, 537],
["Ashe",-0.6,27126, 427],
["Avery",-0.1,17773, 247],
["Beaufort",-0.4,47585, 959],
["Bertie",-5.6,20106, 741],
["Bladen",-1.5,34657, 887],
["Brunswick",10.6,118836, 860],
["Buncombe",5.1,250539, 660],
["Burke",-1.6,89486, 515],
["Cabarrus",7.8,192103, 365],
["Caldwell",-1.9,81484, 474],
["Camden",3.5,10331, 306],
["Carteret",3.5,68811, 1341],
["Caswell",-2.7,23082, 428],
["Catawba",0.1,154534, 414],
["Chatham",8.2,68698, 709],
["Cherokee",-1.1,27141, 497],
["Chowan",-1.5,14572, 233],
["Clay",-0.1,10581, 221],
["Cleveland",-1,97076,469 ],
["Columbus",-2,56953, 954],
["Craven",1,104510, 774],
["Cumberland",2.2,326328, 658],
["Currituck",6.1,24976, 526],
["Dare",3.5,35104, 1562],
["Davidson",0.7,164072, 567],
["Davie",0.5,41434, 267],
["Duplin",2.4,59882, 819],
["Durham",9.1,294460, 298],
["Edgecombe",-2.9,54933, 507],
["Forsyth",4.2,365298, 413],
["Franklin",3.7,62860, 495],
["Gaston",2.4,211127, 364],
["Gates",-5.1,11567, 346],
["Graham",-2.4,8644, 302],
["Granville",1.7,58500, 537],
["Greene",-1.3,21093, 266],
["Guilford",4.9,512119, 658],
["Halifax",-3.1,52970, 731],
["Harnett",10.5,126666, 601],
["Haywood",0.7,59471, 555],
["Henderson",4.1,111149, 375],
["Hertford",-1.4,24308, 360],
["Hoke",9.9,51611, 392],
["Hyde",-2.3,5676, 1424],
["Iredell",4.5,166675, 597],
["Jackson",1.8,40981, 494],
["Johnston",7.4,181423, 796],
["Jones",-0.8,10076, 473],
["Lee",3.1,59662, 259],
["Lenoir",-1.7,58485, 402],
["Lincoln",2,79829, 307],
["Macon",-0.1,33875, 519],
["Madison",1.8,21157, 452],
["Martin",-4.3,23454, 461],
["McDowell",-0.1,44965, 446],
["Mecklenburg",10.1,1012539, 546],
["Mitchell",-1.7,15311, 222],
["Montgomery",-1.4,27395, 502],
["Moore",5.5,93077, 706],
["Nash",-1.5,94357, 543],
["New Hanover",6.7,216298, 328],
["Northampton",-7.4,20463, 551],
["Onslow",5.5,187589, 909],
["Orange",5,140420, 401],
["Pamlico",-1.5,12948, 566],
["Pasquotank",-2.1,39787, 289],
["Pender",7.8,56250, 933],
["Perquimans",0.1,13466, 329],
["Person",-0.8,39132, 404],
["Pitt",4.3,175354, 655],
["Polk",-0.7,20357, 239],
["Randolph",0.7,142778, 790],
["Richmond",-1.9,45733, 480],
["Robeson",0.4,134760, 951],
["Rockingham",-2.1,91696, 572],
["Rowan",0.1,138630, 524],
["Rutherford",-1.8,66600, 566],
["Sampson",1,64050, 947],
["Scotland",-1.6,35576, 321],
["Stanly",0,60600, 404],
["Stokes",-2.1,46419, 456],
["Surry",-1,72968, 538],
["Swain",2.1,14274, 541],
["Transylvania",-0.1,33045, 381],
["Tyrrell",-6.6,4115, 600],
["Union",8.6,218568, 640],
["Vance",-1.8,44614, 270],
["Wake",10.8,998691, 857],
["Warren",-3.5,20231, 444],
["Washington",-4.9,12570, 424],
["Watauga",2.9,52560, 313],
["Wayne",1.5,124456, 557],
["Wilkes",-0.7,68838, 760],
["Wilson",0.2,81401, 374],
["Yadkin",-1.6,37792, 337],
["yancey",-1.1,17614, 313]
]
var mode = 0;
function calcDerivedColumn() {
if (mode ==0) {
data.forEach(function(d) {
d[4] = d[2]/d[3];
});
} else {
data.forEach(function(d) {
d[4] = d[2]/100000;
});
}
}
calcDerivedColumn();
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('dx', '-20em')
.attr('dy', '-2em')
.text('County Population Change (%)');
svg.append('text')
.attr('transform', `translate(${width/2},${height + 2*margin.bottom/3})`)
.attr('dx', '-8em')
.attr('id', 'xlabel')
.text(xlabels[mode]);
svg.append('text')
.attr('id', 'r')
.attr('dx', '25em')
.attr('dy', '20em')
.text('r=');
x.domain(d3.extent(data, function(d) { return d[4]; }));
y.domain(d3.extent(data, function(d) { return d[1]; }));
function plotLine(t) {
var xSeries=[];
var ySeries=[];
data.forEach(function(d) {
xSeries.push(Math.log10(d[4]));
ySeries.push(d[1]);
});
var ls = leastSquares(xSeries, ySeries);
xSeries.sort(function(a, b) {
return a - b;
});
var x1= xSeries[0];
var y1= ls.m * x1 + ls.b;
var x2= xSeries[xSeries.length-1];
var y2= ls.m * x2 + ls.b;
x1 = Math.pow(10, x1);
x2 = Math.pow(10, x2);
svg.selectAll('#fit').transition(t)
.attr('x1', x(x1))
.attr('y1', y(y1))
.attr('x2', x(x2))
.attr('y2', y(y2));
svg.selectAll('#r')
.text('r=' + ls.r.toPrecision(2));
}
plotLine(d3.transition());
// Enter the scatterplot.
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d[4]); })
.attr("cy", function(d) { return y(d[1]); })
.attr("fill", function(d) { return d[1] < 0 ? "#ff3333" : "#009999"; })
.attr("title", function(d) { return d[0]; })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d[0])
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
d3.selectAll("svg")
.on("click", function(d) {
toggle();
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("id", "xaxis")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
function toggle() {
mode = (mode+1)%2;
var t = d3.transition()
.duration(750);
svg.selectAll('#xlabel')
.text(xlabels[mode]);
calcDerivedColumn();
x.domain(d3.extent(data, function(d) { return d[4]; }));
y.domain(d3.extent(data, function(d) { return d[1]; }));
plotLine(t);
svg.selectAll("circle")
.data(data, function(d) { return d; })
.transition(t)
.attr("cx", function(d) { return x(d[4]); })
d3.selectAll("#xaxis")
.call(d3.axisBottom(x));
}
function leastSquares(xSeries, ySeries) {
var reduceSumFunc = function(prev, cur) { return prev + cur; };
var xBar = xSeries.reduce(reduceSumFunc) * 1.0 / xSeries.length;
var yBar = ySeries.reduce(reduceSumFunc) * 1.0 / ySeries.length;
var ssXX = xSeries.map(function(d) { return (d-xBar)*(d-xBar); })
.reduce(reduceSumFunc);
var ssYY = ySeries.map(function(d) { return (d-yBar)*(d-yBar); })
.reduce(reduceSumFunc);
var ssXY = xSeries.map(function(d, i) { return (d - xBar) * (ySeries[i] - yBar); })
.reduce(reduceSumFunc);
var slope = ssXY / ssXX;
var intercept = yBar - (xBar * slope);
var r = ssXY / (Math.sqrt(ssXX) * Math.sqrt(ssYY));
return {m: slope, b: intercept, r: r};
}
})();
</script>The rural areas of North Carolina are emptying out. .tooltip { position: absolute; width: 200px; height: 28px; pointer-events: none; }Noisy Frogs2017-06-15T00:00:00-04:002017-06-15T00:00:00-04:00https://bitworking.org/news/2017/06/noisy-frogs<p>Why yes, the frogs in the pond across the road are rather loud.</p>
<p><img src="/images/noisy_frogs_small.png" alt="Graph showing a peak noise level of 72 dB." /></p>
<p>BTW,
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.forscience.whistlepunk&hl=en">Google Science Journal</a>
is awesome.</p>Why yes, the frogs in the pond across the road are rather loud.Moving to Jekyll2017-06-15T00:00:00-04:002017-06-15T00:00:00-04:00https://bitworking.org/news/2017/06/moving-to-jekyll<p>I am in the process of moving this blog over to
<a href="https://jekyllrb.com/">Jekyll</a>, while at the same time admitting that I’m
never going to finish writing my own blogging software, and that I really
should concentrate on the writing, and not the underlying software.</p>
<p>This is also an admission that for a while there may be general brokenness
such as broken links and some horribly formatted posts.</p>
<p>Also, I will be turning on comments, which is a longer post in itself, but
let’s just say that after years of watching abuse and generally uncivil
behavior on all the social networks I’ve decided to revert to old technology
from a simpler time, where I get to have absolute control over what appears
next to my writing. Call me old fashioned.</p>
<p><em>Update</em>: And comments are now enabled, the <a href="https://github.com/jekyll/minima">Jekyll Minima
theme</a> and <a href="https://disqus.com/">Disqus</a>
made that way too easy.</p>I am in the process of moving this blog over to Jekyll, while at the same time admitting that I’m never going to finish writing my own blogging software, and that I really should concentrate on the writing, and not the underlying software.Tech Driven Deflation2017-04-19T00:00:00-04:002017-04-19T00:00:00-04:00https://bitworking.org/news/2017/04/tech-driven-deflation<p>
The article <a href="https://singularityhub.com/2017/04/14/will-tech-driven-deflation-export-japans-economic-woes-to-the-world/">Will Tech-Driven Deflation Export Japan’s Economic Woes to the World?</a>
reminded me of this video, "Evolution of the Desk":
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/uGI00HV7Cfw?rel=0" frameborder="0" allowfullscreen=""></iframe>
<p>
The origin for the above video is <a href="http://bestreviews.com/electronics#evolution-of-the-desk">http://bestreviews.com/electronics#evolution-of-the-desk</a>.
</p>
<p>
Each time an object gets digitized that's one less object to
manufacture, one less object to ship, one less object consuming
raw materials. It's actually more surprising that this hasn't had
an even larger impact on the U.S. economy.
</p>
<p>
<b>Update:</b> A <a href="https://www.brookings.edu/wp-content/uploads/2016/08/varian.pdf">great
slide deck from Hal Varian</a> covers the same ground with a slightly different take, pointing
out that much of this progress not only doesn't increase GDP, it actually
reduces it, which is a problem of using GDP as a metric.
</p>The article Will Tech-Driven Deflation Export Japan’s Economic Woes to the World? reminded me of this video, "Evolution of the Desk": The origin for the above video is http://bestreviews.com/electronics#evolution-of-the-desk.Prometheus vs InfluxDB2017-03-18T00:00:00-04:002017-03-18T00:00:00-04:00https://bitworking.org/news/2017/03/prometheus<p>
We just finished migrating all of our monitoring from <a href="https://www.influxdata.com/">InfluxDB</a> to <a href="https://prometheus.io/">Prometheus</a> and I thought I'd write up
our reasons for the change. Please note that these are my own personal
observations and relate to a specific project, these issue may not apply
to you and you should evaluate each product for your own uses.
</p>
<p>
<b>Update:</b> To clarify, the versions of InfluxDB and Prometheus that I
am talking about are InfluxDB 1.1.1 and Prometheus 1.5.2.
</p>
<h2>Push vs Pull</h2>
<dl>
<dt>InfluxDB</dt>
<dd>
InfluxDB is a push based system, i.e. your running application needs
to actively push data into the monitoring system.
</dd>
<dt>Prometheus</dt>
<dd>
Prometheus is a pull based system, the Prometheus server fetches the
metrics values from the running application periodically.
</dd>
</dl>
<p>
With centralized control of how polling is done with Prometheus I can
switch from polling every minute to every 10 seconds just by adjusting the
configuration of the Prometheus server. With InfluxDB I would have to
redeploy every application with a change to how often they should push
metrics. In addition the Prometheus pull method allows Prometheus to
create and offer a synthetic "UP" metric that monitors whether an
application is up and running.
For short lived applications Prometheus has a <a href="https://github.com/prometheus/pushgateway">push gateway</a>.
</p>
<h2>Datastore</h2>
<dl>
<dt>InfluxDB</dt>
<dd>
InfluxDB has a monolithic database for both metric values and indices.
</dd>
<dt>Prometheus</dt>
<dd>
Prometheus uses LevelDB for indices, but each metric is stored in its
own file.
</dd>
</dl>
<p>
Both use key/value datastores, but how they use them is very different
and it affects the performance of the products. InfluxDB was
slower and took up substantially more disk space than Prometheus for the
same exact set of metics. Just starting up InfluxDB and sending a small
number of metrics to it caused the datastore to grow to 1GB, and then
grow rapidly from there to 100's of GB for our full set of metrics,
while Prometheus has yet to crack 10GB with all of our metrics.
And let's not even go into the number of times InfluxDB lost all of our
data, either from a crash or from a failed attempt to upgrade the
version of InfluxDB that we were running.
</p>
<p>
<b>Update:</b> I was also reminded there's another datastore related issue
with startup time, while Prometheus starts in a matter of seconds,
InfluxDB would regularly take 5 minutes to restart while it either
validated or rebuilt its indices and would not collect data during the
entire process.
</p>
<h2>CPU</h2>
<p>
Probably closely related to the datastore efficiency, InfluxDB was coming
close to maxing out the server it was running on, while Prometheus running
on an identical instance peaks at maybe 0.2 load.
</p>
<h2>Query Language</h2>
<dl>
<dt>InfluxDB</dt>
<dd>
InfluxDB uses a variant of SQL.
</dd>
<dt>Prometheus</dt>
<dd>
Uses a substantially simpler and more direct querying model.
</dd>
</dl>
<p>
What would you rather type?
</p>
<pre><code>SELECT * FROM "cpu_load_short" WHERE "value" > 0.9</code></pre>
<p>or</p>
<pre><code>cpu_load_short > 0.9</code></pre>
<h2>Configuration</h2>
<dl>
<dt>InfluxDB</dt>
<dd>
Configuration is done through a mix of config files and SQL
commands sent to the server.
</dd>
<dt>Prometheus</dt>
<dd>
Text files.
</dd>
</dl>
<p>
Prometheus config is simply YAML files, and the entire config is done via
files. With InfluxDB you have to worry that some of the config, for
example, creating the named database that metrics are to be stored in,
actually gets done. Additionally Prometheus just picks more reasonable
defaults, for example, it defaults to only storing data for 15 days, while
InfluxDB defaults to storing all data forever, and if you don't want to
store all data forever you need to construct an SQL command to send to the
server to control how data is retained.
</p>We just finished migrating all of our monitoring from InfluxDB to Prometheus and I thought I'd write up our reasons for the change. Please note that these are my own personal observations and relate to a specific project, these issue may not apply to you and you should evaluate each product for your own uses. Update: To clarify, the versions of InfluxDB and Prometheus that I am talking about are InfluxDB 1.1.1 and Prometheus 1.5.2.Geometric Algebra applied to Physics2017-01-01T00:00:00-05:002017-01-01T00:00:00-05:00https://bitworking.org/news/ga/physics<style type="text/css" media="screen">
img {
vertical-align: baseline;
}
th {
background: #fff;
}
</style>
<script src="/js/ga2d.js" type="text/javascript" charset="utf-8"></script>
<script src="/js/draw_ga2d.js" type="text/javascript" charset="utf-8"></script>
<p>
Geometric Algebra can be applied to Physics, and many of the introductions
to GA online cover this, but they immediately jump to electromagnetic
fields or quantum mechanics, which is unfortunate since GA can also
greatly simplify 2D kinematics. One such example is uniform circular
motion.
</p>
<p class=aside>
You should be familiar with all the concepts presented in <a
href="https://bitworking.org/news/ga/2d">An Introduction to Geometric
Algebra over R^2</a> before proceeding.
</p>
<p>
If we have a vector <b>p</b> that moves at a constant rate of ω
rad/s and has a starting position <b>p<sub>0</sub></b>, then we can
describe the vector <b>p</b> very easily:
</p>
<dl>
<dd>$$\boldsymbol{p} = \boldsymbol{p_0} e^{\omega t \boldsymbol{I}}$$</dd>
</dl>
<button id=rotor_toggle>Start/Stop</button>
<canvas id=rotor width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('rotor_toggle');
var running = false;
var omega = 0.5;
var start = Date.now();
var p0 = ga2d.vec(0, -1);
var f = new draw_ga2d.Frame(document.getElementById('rotor'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var p = ga2d.mul(p0, rotor);
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.vec(p, "p");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Let's figure out what the derivative of a Rotor looks like, by
first recalling its definition:
</p>
<dl>
<dd>$$ e^{\theta \boldsymbol{I}} := \cos(\theta) + \sin(\theta)\boldsymbol{I}$$</dd>
</dl>
<p>
We take the derivative with respect to θ:
</p>
<dl>
<dd>
$$
\begin{align*}
\frac{d}{d \theta} e^{\theta \boldsymbol{I}} &= \frac{d}{d \theta} (\cos(\theta) + \sin(\theta)\boldsymbol{I}) \\
&= -\sin(\theta) + \cos(\theta)\boldsymbol{I} \\
\end{align*}
$$
</dd>
</dl>
<p>
At this point observe that <em>cos</em> and <em>sin</em> just changed
places, along with a sign change, but we know of another operation that does
the same thing, which is multiplication by <b>I</b>, so we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\frac{d}{d \theta} e^{\theta \boldsymbol{I}} &= \frac{d}{d \theta} (\cos(\theta) + \sin(\theta)\boldsymbol{I}) \\
&= -\sin(\theta) + \cos(\theta)\boldsymbol{I} \\
&= \boldsymbol{I} (\cos(\theta) + \sin(\theta)\boldsymbol{I}) \\
&= \boldsymbol{I} e^{\theta \boldsymbol{I}} \\
\end{align*}
$$
</dd>
</dl>
<p>
Not only does the derivative have a nice neat expression, we can read off
from the formula what is happening, which is that the derivative is a vector
that is rotated 90 degrees from the original vector. Also note that
normally the geometric product ins't commutative, but in this case both
parts are rotors, so the order doesn't matter.
</p>
<p>
We can go through the same process to show what happens if θ has
a constant multiplier <em>k</em>:
</p>
<dl>
<dd>
$$
\begin{align*}
\frac{d}{d \theta} e^{k \theta \boldsymbol{I}} &= \frac{d}{d \theta} (\cos(k \theta) + \sin(k \theta)\boldsymbol{I}) \\
&= k \boldsymbol{I} e^{k \theta \boldsymbol{I}} \\
\end{align*}
$$
</dd>
</dl>
<p>
With our new derivative in hand we can now find the velocity vector
for our position vector <b>p</b>, since velocity is just the derivative
of position with respect to time.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{v} &= \frac{d}{dt} \boldsymbol{p} \\
&= \frac{d}{dt} \boldsymbol{p_0} e^{\omega t \boldsymbol{I}} \\
&= \boldsymbol{p_0} \omega \boldsymbol{I} e^{\omega t \boldsymbol{I}} \\
&= \omega \boldsymbol{p_0} \boldsymbol{I} e^{\omega t \boldsymbol{I}} \\
\end{align*}
$$
</dd>
</dl>
<p>
Again, because we using Geometric Algebra, we can read off
what is going on geometrically from the formula, that is,
the derivative is a vector orthogonal to the position vector
that is scaled by ω.
</p>
<button id=velocity_toggle>Start/Stop</button>
<canvas id=velocity width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('velocity_toggle');
var running = false;
var omega = 0.5;
var omegaScalar = ga2d.scalar(omega);
var I = [0, 0, 0, 1];
var start = Date.now();
var p0 = ga2d.vec(0, -1);
var f = new draw_ga2d.Frame(document.getElementById('velocity'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var p = ga2d.mul(p0, rotor);
var v = ga2d.mul(omegaScalar, ga2d.mul(p0, ga2d.mul(I, rotor)));
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.vec(p, "p");
f.vecFrom(v, p, "v");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Note that we've drawn the vector as starting from the position,
but that's not required.
</p>
<button id=sep_velocity_toggle>Start/Stop</button>
<canvas id=sep_velocity width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('sep_velocity_toggle');
var running = false;
var omega = 0.5;
var omegaScalar = ga2d.scalar(omega);
var I = [0, 0, 0, 1];
var start = Date.now();
var p0 = ga2d.vec(0, -1);
var f = new draw_ga2d.Frame(document.getElementById('sep_velocity'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var p = ga2d.mul(p0, rotor);
var v = ga2d.mul(omegaScalar, ga2d.mul(p0, ga2d.mul(I, rotor)));
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.vec(p, "p");
f.vec(v, "v");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
We get the acceleration vector in the same manner, by taking
the derivative of the velocity vector with respect to time.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= \frac{d}{dt} \boldsymbol{v} \\
&= \frac{d}{dt} \omega \boldsymbol{p_0} \boldsymbol{I} e^{\omega t \boldsymbol{I}} \\
&= \omega \boldsymbol{p_0} \boldsymbol{I} \omega \boldsymbol{I} e^{\omega t \boldsymbol{I}} \\
&= \omega^2 \boldsymbol{p_0} \boldsymbol{I} \boldsymbol{I} e^{\omega t \boldsymbol{I}} \\
&= - \omega^2 \boldsymbol{p_0} e^{\omega t \boldsymbol{I}}
\end{align*}
$$
</dd>
</dl>
<p>
And again we can just read off from the formula what is going on
geometrically, which is that we end up with a vector that is rotated
180 degrees from the position vector, and scaled by ω<sup>2</sup>.
</p>
<button id=accel_toggle>Start/Stop</button>
<canvas id=accel width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('accel_toggle');
var running = false;
var omega = 0.5;
var omegaScalar = ga2d.scalar(omega);
var I = [0, 0, 0, 1];
var start = Date.now();
var p0 = ga2d.vec(0, -1);
var f = new draw_ga2d.Frame(document.getElementById('accel'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var p = ga2d.mul(p0, rotor);
var v = ga2d.mul(omegaScalar, ga2d.mul(p0, ga2d.mul(I, rotor)));
var a = ga2d.mul(omegaScalar, ga2d.mul(v, I));
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.vec(p, "p");
f.vec(v, "v");
f.vec(a, "a");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
We can place the acceleration and velocity vectors as starting
from the positition vector, and that looks like:
</p>
<button id=accel_from_toggle>Start/Stop</button>
<canvas id=accel_from width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('accel_from_toggle');
var running = false;
var omega = 0.5;
var omegaScalar = ga2d.scalar(omega);
var I = [0, 0, 0, 1];
var start = Date.now();
var p0 = ga2d.vec(0, -1);
var f = new draw_ga2d.Frame(document.getElementById('accel_from'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var p = ga2d.mul(p0, rotor);
var v = ga2d.mul(omegaScalar, ga2d.mul(p0, ga2d.mul(I, rotor)));
var a = ga2d.mul(omegaScalar, ga2d.mul(v, I));
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.vecFrom(v, p, "v");
f.vecFrom(a, p, "a");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Note how simple this was to derive and that the geometric interpretation
could be read off of the resulting formulas. We didn't need to leave the
2D plane, that is, all of these calculations took place in 𝔾<sup>2</sup>.
The more classical derivations for uniform circular motion rely on the
cross-product which takes you out of ℝ<sup>2</sup> into ℝ<sup>3</sup> and
which doesn't work in higher level dimensions.
</p>img { vertical-align: baseline; }An Introduction to Geometric Algebra over R^22016-12-21T00:00:00-05:002016-12-21T00:00:00-05:00https://bitworking.org/news/ga/2d<style type="text/css" media="screen">
img {
vertical-align: baseline;
}
th {
background: #fff;
}
body {counter-reset: h3}
h3 {counter-reset: h4}
h4 {counter-reset: h5}
h5 {counter-reset: h6}
h3:before {counter-increment: h3; content: counter(h3) ". "}
h4:before {counter-increment: h4; content: counter(h3) "." counter(h4) ". "}
h5:before {counter-increment: h5; content: counter(h3) "." counter(h4) "." counter(h5) ". "}
h6:before {counter-increment: h6; content: counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "}
</style>
<script src="/js/ga2d.js" type="text/javascript" charset="utf-8"></script>
<script src="/js/draw_ga2d.js" type="text/javascript" charset="utf-8"></script>
<div class=floatnav>
<ul>
<li><a href="#linear_algebra">Linear Algebra</a></li>
<li><a href="#geometric_algebra">Geometric Algebra</a></li>
<li><a href="#applications">Applications</a></li>
<ul>
<li><a href="#multiplying_vectors">Multiplying Vectors</a></li>
<li><a href="#rotors">Rotors</a></li>
<li><a href="#double_angle">Double Angle Formula</a></li>
<li><a href="#complex">Complex Numbers</a></li>
<li><a href="#B">Characterizing B</a></li>
<li><a href="#ratios">Ratios</a></li>
<li><a href="#conjugates">Conjugates</a></li>
</ul>
</ul>
</div>
<p>
Geometric Algebra is fascinating, and I believe solves a large number of
problems that arise from a more traditional approach to vectors, but
I've been very disappointed with the quality of books and explanations
I've found, most of them zooming off into abstract realms too quickly,
or spending an inordinate amount of time building up a generalized theory
before finally getting to something useful.
</p>
<p>
Below is an explanation of Geometric Algebra that will start with a simple
two dimensional vector space, i.e. ℝ<sup>2</sup>. This will be a concise
introduction to 𝔾<sup>2</sup>, the Geometric Algebra over ℝ<sup>2</sup>,
and then quickly pivot to applications in 𝔾<sup>2</sup>. This introduction
will not cover the fascinating history of GA, <a href="</a://en.wikipedia.org/wiki/Clifford_algebra">Clifford Algebras</a>, or
<a href="https://en.wikipedia.org/wiki/Hermann_Grassmann">Hermann Grassman</a>.
</p>
<p>
I'll presume a familiarity with <a
href="https://en.wikipedia.org/wiki/Linear_algebra">Linear Algebra</a>,
and then we'll introduce the geometric product on
that and we'll have the Geometric Alegebra
over two dimensions: 𝔾<sup>2</sup>.
</p>
<p>
</p>
<h3 id=linear_algebra>Linear Algebra</h3>
<blockquote>
Linear algebra is the branch of mathematics concerning vector spaces and
linear mappings between such spaces. It includes the study of lines,
planes, and subspaces, but is also concerned with properties common to all
vector spaces. -<a
href="https://en.wikipedia.org/wiki/Linear_algebra">Wikipedia</a>
</blockquote>
<p>
You should be familiar with the following axioms and definitions from
Linear Algebra:
</p>
<table>
<tr><td>$$(\boldsymbol{a} + \boldsymbol{b}) + \boldsymbol{c} = \boldsymbol{a} + (\boldsymbol{b} + \boldsymbol{c})$$</td> <td>Associative</td> <td>(1)</td></tr>
<tr><td>$$\boldsymbol{a} + \boldsymbol{b} = \boldsymbol{b} + \boldsymbol{a} $$</td> <td>Commutative</td> <td>(2)</td></tr>
<tr><td>$$\boldsymbol{0} + \boldsymbol{b} = \boldsymbol{b} $$</td> <td>Identity</td> <td>(3)</td></tr>
<tr><td>$$\boldsymbol{-a} + \boldsymbol{a} = \boldsymbol{0}$$</td> <td>Inverse</td> <td>(4)</td></tr>
<tr><td>$$c(\boldsymbol{a} + \boldsymbol{b}) = c\boldsymbol{a} + c\boldsymbol{b} $$</td> <td>Scalar Distributive</td> <td>(5)</td></tr>
<tr><td>$$1 \boldsymbol{b} = \boldsymbol{b} $$</td> <td>Multiplicative Identity</td> <td>(6)</td></tr>
<tr><td>$$\boldsymbol{a} \cdot \boldsymbol{b} = ||\boldsymbol{a}|| ||\boldsymbol{b}|| \cos \theta$$</td> <td>Dot/Inner Product</td> <td>(7)</td></tr>
<tr><td>$$\boldsymbol{a} \cdot \boldsymbol{b} = \sum_{i}\boldsymbol{a_i}\boldsymbol{b_i} $$</td> <td>Dot/Inner Product (Alternate)</td> <td>(8)</td></tr>
</table>
<p>In particular, for ℝ<sup>2</sup> we have an orthonormal basis:</p>
<dl>
<dd>$$ \boldsymbol{e_{1}} := (1,0) $$</dd>
<dd>$$ \boldsymbol{e_{2}} := (0,1) $$</dd>
</dl>
<p>where:</p>
<dl>
<dd>$$ \boldsymbol{e_{1}} \perp \boldsymbol{e_{2}} $$</dd>
</dl>
<canvas id=basis width=150 height=150></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var e1 = ga2d.vec(1, 0);
var e2 = ga2d.vec(0, 1);
var f = new draw_ga2d.Frame(document.getElementById('basis'));
f.axes();
f.vec(e1, "e1");
f.vec(e2, "e2");
f.expandTo([0, 2, 0, 0]);
f.draw();
})();
</script>
<p>
We know how to do vector addition and scalar multiplication of vectors,
and that any vector can be represented as a linear combination of
basis elements.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= -1 \boldsymbol{e_{1}} + 2 \boldsymbol{e_{2}} \\
\boldsymbol{b} &= 2 \boldsymbol{e_{1}} + 3 \boldsymbol{e_{2}} \\
\boldsymbol{a} + \boldsymbol{b} &= 1 \boldsymbol{e_1} + 5 \boldsymbol{e_2}
\end{align*}
$$
</dd>
</dl>
<canvas id=add width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var a = ga2d.vec(-1, 2);
var b = ga2d.vec(2, 3);
var c = ga2d.add(a, b);
var f = new draw_ga2d.Frame(document.getElementById('add'));
f.axes();
f.vec(a, "a");
f.vec(b, "b");
f.vec(c, "a+b");
f.draw();
})();
</script>
<p>
Things to remember about the dot product, or inner product, is that it is 0 for orthogonal
vectors:
</p>
<dl>
<dd>$$ \boldsymbol{e_{1}} \perp \boldsymbol{e_{2}} \implies \boldsymbol{e_1} \cdot \boldsymbol{e_2} = 0$$</dd>
</dl>
<p>
And that a vector dot with itself gives the square of the norm of the
vector, since $$\cos 0 = 1$$:
</p>
<dl>
<dd>$$ \boldsymbol{a} \cdot \boldsymbol{a} = {||\boldsymbol{a}||}^2$$</dd>
</dl>
<p>
One important thing to notice about Linear Algebra is how often you have
to step outside of ℝ<sup>2</sup> to get work done. That is, operations
frequently have to take place outside ℝ<sup>2</sup> or those operations
give you results outside of ℝ<sup>2</sup>. For example, the dot product of
two vector returns a scalar, which is not a member of ℝ<sup>2</sup>.
</p>
<dl>
<dd>$$ \boldsymbol{a} \cdot \boldsymbol{b} := ||\boldsymbol{a}|| ||\boldsymbol{b}|| \cos(\theta) $$</dd>
</dl>
<p>
Similarly, to rotate vectors you have to create matrices, which don't
exist in ℝ<sup>2</sup>, and apply them to vectors through matrix
multiplication.
</p>
<p>
One final example is the cross-product, which takes two vectors and
operates on them to produce a vector that is orthogonal to the original
two vectors, but if you are in ℝ<sup>2</sup> it doesn't exist, you have to
then view that cross-product vector as existing in ℝ<sup>3</sup>, which
the original ℝ<sup>2</sup> is embedded in.
</p>
<p>
All of this stands in stark contrast to 𝔾<sup>2</sup>, where these
operations take place in 𝔾<sup>2</sup>, in fact, many of the constructs we
use in Linear Algebra, such as rotations, exist as elements of
𝔾<sup>2</sup>, and applying those operations is just a matter of taking the
geometric product of those objects. Not only is 𝔾<sup>2</sup> closed under
many of these operations, but the operations exist as elements in
𝔾<sup>2</sup>.
</p>
<h3 id=geometric_algebra>Geometric Algebra</h3>
<p>
The Geometric Algebra of 𝔾<sup>2</sup> builds upon ℝ<sup>2</sup>,
extending it by adding multiplication, i.e. a geometric product.
Before we get to the geometric product we need to first quickly learn
about the exterior product.
</p>
<h4 id=exterior_product>Exterior Product</h4>
<p>
The exterior product operates on two vectors and is written as:
</p>
<dl>
<dd>$$\boldsymbol{a} \wedge \boldsymbol{b}$$</dd>
</dl>
<canvas id=wedge width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var a = ga2d.vec(Math.sqrt(2)/2, Math.sqrt(2)/2);
var b = ga2d.vec(Math.sqrt(2)/2, -Math.sqrt(2)/2);
var ab = ga2d.add(a, b);
var a_neg = ga2d.mul(ga2d.scalar(-1), a);
var b_neg = ga2d.mul(ga2d.scalar(-1), b);
var f = new draw_ga2d.Frame(document.getElementById('wedge'));
f.vec(a, "a");
f.vecFrom(b, a, "b");
f.region([
[0,0,0,0],
a,
ab,
b,
], "", "rgba(100, 0, 255, 0.2)");
f.draw();
})();
</script>
<p>
The exterior product represents the oriented area defined by the two
vectors, or more precisely is represents an oriented area in the plane
defined by those vectors, also known as a bivector. There are two
important aspects of this, the first is that the exact shape doesn't
matter. For example, the bivectors represented below are equal
because they have the same orientation (counter-clockwise) and the same
area (3).
</p>
<dl>
<dd>$$ (1, 0) \wedge (0, 3) = (3, 0) \wedge (0, 1)$$</dd>
</dl>
<canvas id=wedge2 width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var a = ga2d.vec(1, 0);
var b = ga2d.vec(0, 3);
var c = ga2d.vec(3, 0);
var d = ga2d.vec(0, 1);
var ab = ga2d.add(a, b);
var cd = ga2d.add(c, d);
var f = new draw_ga2d.Frame(document.getElementById('wedge2'));
f.vec(a, "(1, 0)");
f.vecFrom(b, a, "(0, 3)");
f.vec(c, "(3, 0)");
f.vecFrom(d, c, "(0, 1)");
f.region([
[0,0,0,0],
a,
ab,
b,
], "", "rgba(100, 0, 255, 0.2)");
f.region([
[0,0,0,0],
c,
cd,
d,
], "", "rgba(255, 0, 100, 0.2)");
f.expandTo([0, 4, 0, 0]);
f.draw();
})();
</script>
<p>
The second important factor is that the exterior product is
anticommutative, that is, if you reverse the order of the vectors
involved then the sign of the exterior product changes.
</p>
<dl>
<dd>$$\boldsymbol{a} \wedge \boldsymbol{b} = - \boldsymbol{b} \wedge \boldsymbol{a}$$</dd>
</dl>
<p>
Using two of the vectors above, note that the order that they are used in
the exterior product will make the bivectors either clockwise or counter-clockwise.
</p>
<canvas id=wedge_reverse width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var a = ga2d.vec(1, 0);
var b = ga2d.vec(0, 3);
var ab = ga2d.add(a, b);
var f = new draw_ga2d.Frame(document.getElementById('wedge_reverse'));
f.vec(a, "(1, 0)");
f.vecFrom(b, a, "(0, 3)");
f.region([
[0,0,0,0],
a,
ab,
b,
], "", "rgba(100, 0, 255, 0.2)");
f.draw();
})();
</script>
<canvas id=wedge_reverse_2 width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var c = ga2d.vec(0, 3);
var d = ga2d.vec(1, 0);
var cd = ga2d.add(c, d);
var f = new draw_ga2d.Frame(document.getElementById('wedge_reverse_2'));
f.vec(c, "(0, 3)");
f.vecFrom(d, c, "(1, 0)");
f.region([
[0,0,0,0],
c,
cd,
d,
], "", "rgba(255, 0, 100, 0.2)");
f.draw();
})();
</script>
<p>
The properties of the exterior product are:
</p>
<table>
<tr><td>$$(\boldsymbol{a} \wedge \boldsymbol{b}) \wedge \boldsymbol{c} = \boldsymbol{a} \wedge (\boldsymbol{b} \wedge \boldsymbol{c})$$</td> <td>Associative</td> <td>(1)</td></tr>
<tr><td>$$c(\boldsymbol{a} \wedge \boldsymbol{b}) = c\boldsymbol{a} \wedge \boldsymbol{b} = \boldsymbol{a} \wedge c\boldsymbol{b}$$</td> <td>Scalar Associativity</td> <td>(2)</td></tr>
<tr><td>$$\boldsymbol{a} \wedge (\boldsymbol{b} + \boldsymbol{c}) = \boldsymbol{a} \wedge \boldsymbol{b} + \boldsymbol{a} \wedge \boldsymbol{c}$$</td> <td>Left Distributive</td> <td>(3)</td></tr>
<tr><td>$$(\boldsymbol{a} + \boldsymbol{b}) \wedge \boldsymbol{c} = \boldsymbol{a} \wedge \boldsymbol{c} + \boldsymbol{b} \wedge \boldsymbol{c}$$</td> <td>Right Distributive</td> <td>(4)</td></tr>
<tr><td>$$\boldsymbol{a} \wedge \boldsymbol{b} = -\boldsymbol{b} \wedge \boldsymbol{a}$$</td> <td>Anti-symmetric</td> <td>(5)</td> </tr>
<tr><td>$$\boldsymbol{a} \parallel \boldsymbol{b} \Rightarrow \boldsymbol{a} \wedge \boldsymbol{b} = 0$$</td> <td>Zero for Parallel Vectors.</td> <td>(6)</td> </tr>
</table>
<p>
In what is going to become a recurring theme, let's look at what
this means in terms of basis vectors. Since any vector can be written
as a linear combination of basis vectors we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}} \\
\boldsymbol{b} &= b_1 \boldsymbol{e_{1}} + b_2 \boldsymbol{e_{2}}
\end{align*}
$$
</dd>
</dl>
<p>
If we take their exterior product we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} \wedge \boldsymbol {b} &= (a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}}) \wedge (b_1 \boldsymbol{e_{1}} + b_2 \boldsymbol{e_{2}}) \\
&= a_1 b_1 \boldsymbol{e_{1}} \wedge \boldsymbol{e_{1}}
+ a_1 b_2 \boldsymbol{e_{1}} \wedge \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \wedge \boldsymbol{e_{1}}
+ a_2 b_2 \boldsymbol{e_{2}} \wedge \boldsymbol{e_{2}} & \text{via 1} \\
&= 0 + a_1 b_2 \boldsymbol{e_{1}} \wedge \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \wedge \boldsymbol{e_{1}} + 0 & \text{via 6} \\
&= a_1 b_2 \boldsymbol{e_{1}} \wedge \boldsymbol{e_{2}}
- a_2 b_1 \boldsymbol{e_{1}} \wedge \boldsymbol{e_{2}} & \text{via 5} \\
&= ( a_1 b_2 - a_2 b_1 )\boldsymbol{e_{1}} \wedge \boldsymbol{e_{2}} & \text{via 2}
\end{align*}
$$
</dd>
</dl>
<p>
So the exterior product of any two vectors can expressed as just a scalar
mulitple of <b>e<sub>1</sub>^e<sub>2</sub></b>
</p>
<h4 id=geometric_product>Geometric Product</h4>
<p>
Now that we know about the exterior product, we can define the geometric
product, which is just the sum of the inner product and the exterior product:
</p>
<dl>
<dd>$$\boldsymbol{a} \boldsymbol{b} := \boldsymbol{a} \cdot \boldsymbol{b} +\boldsymbol{a} \wedge \boldsymbol{b}$$</dd>
</dl>
<p>
Using just the above definition you can show that the geometric product
has the following properties:
</p>
<table>
<tr><td>$$(\boldsymbol{a} \boldsymbol{b}) \boldsymbol{c} = \boldsymbol{a} (\boldsymbol{b} \boldsymbol{c})$$</td> <td>Associative</td> <td>(1)</td></tr>
<tr><td>$$c(\boldsymbol{a} \boldsymbol{b}) = c\boldsymbol{a} \boldsymbol{b} = \boldsymbol{a} c \boldsymbol{b}$$</td> <td>Scalar Associativity</td> <td>(2)</td></tr>
<tr><td>$$\boldsymbol{a} (\boldsymbol{b} + \boldsymbol{c}) = \boldsymbol{a} \boldsymbol{b} + \boldsymbol{a} \boldsymbol{c}$$</td> <td>Left Distributive</td> <td>(3)</td></tr>
<tr><td>$$(\boldsymbol{a} + \boldsymbol{b}) \boldsymbol{c} = \boldsymbol{a} \boldsymbol{c} + \boldsymbol{b} \boldsymbol{c}$$</td> <td>Right Distributive</td> <td>(4)</td></tr>
<tr><td>$$\boldsymbol{a} \parallel \boldsymbol{a} \Rightarrow \boldsymbol{a} \boldsymbol{a} = \boldsymbol{a} \cdot \boldsymbol{a} = ||\boldsymbol{a}|| $$</td> <td>Norm</td> <td>(5)</td> </tr>
<tr><td>$$\boldsymbol{a} \boldsymbol{b} \neq \boldsymbol{b} \boldsymbol{a} $$</td> <td>Non-Commutative, except in some cases.</td> <td>(6)</td> </tr>
<tr><td>$$\boldsymbol{a} \neq 0 \Rightarrow \boldsymbol{a} (\frac{1}{||\boldsymbol{a}||^2} \boldsymbol{a}) = 1$$</td> <td>Vector Inverses</td> <td>(7)</td> </tr>
<tr><td>$$\boldsymbol{a} \perp \boldsymbol{b} \Rightarrow \boldsymbol{a} \boldsymbol{b} = \boldsymbol{a} \wedge \boldsymbol{b}$$</td> <td>Orthogonal vector multiplication.</td> <td>(8)</td> </tr>
</table>
<p>
With the geometric product as defined above, and vector addition, our
Geometric Algebra 𝔾<sup>2</sup> forms a <a
href="https://en.wikipedia.org/wiki/Associative_algebra">unital
associative algebra</a> with an orthonormal basis:
</p>
<dl>
<dd>
$$
1, \boldsymbol{e_1}, \boldsymbol{e_2}, \boldsymbol{e_{1} e_{2}}
$$
</dd>
</dl>
<p>
We can work out a multiplication table for the basis elements, with
the observation that if two elements are orthogonal then their dot product
is zero, so that implies that the geometric product reduces to the
exterior product between orthogonal vectors, which is anti-symmetric.
So that implies for each of our basis vectors:
</p>
<dl>
<dd>
$$
\boldsymbol{e_1} \boldsymbol{e_2} = \boldsymbol{e_1} \wedge \boldsymbol{e_2}
$$
</dd>
</dl>
<p>
And that implies, by the anti-symmetry of the exterior product:
</p>
<dl>
<dd>
$$
\boldsymbol{e_1} \boldsymbol{e_2} = - \boldsymbol{e_2} \boldsymbol{e_1}
$$
</dd>
</dl>
<p>
And the geometric product of any basis element with itself, because
they are parallel means the exterior product is zero, so:
</p>
<dl>
<dd>
$$
\boldsymbol{e_1} \boldsymbol{e_1} = \boldsymbol{e_1} \cdot \boldsymbol{e_1} = ||\boldsymbol{e_1}||^2 = 1
$$
</dd>
</dl>
<p>
Note that we'll end up writing a lot of equations with basis vectors
multiplied together, so it's useful to have a shorthand, i.e.
<b>e<sub>12</sub></b> will be used as a short-hand for
<b>e<sub>1</sub> e<sub>2</sub></b>.
</p>
<p>
We can now complete a multiplication table for the geometric
product of all the basis elements:
</p>
<table border="1" cellspacing="5" cellpadding="5">
<tr><th></th> <th>\(1\)</th> <th>\(e_1\)</th> <th>\(e_2\)</th> <th>\(e_{12}\)</th></tr>
<tr><th>\(1\)</th> <td>\(1\)</td> <th>\(e_1\)</th> <th>\(e_2\)</th> <th>\(e_{12}\)</th></tr>
<tr><th>\(e_1\)</th> <td>\(e_1\)</td> <td>\(1\)</td> <td>\(e_{12}\)</td> <td>\(e_2\)</td></tr>
<tr><th>\(e_2\)</th> <td>\(e_2\)</td> <td>\(-e_{12}\)</td> <td>\(1\)</td> <td>\(-e_1\)</td></tr>
<tr><th>\(e_{12}\)</th> <td>\(e_{12}\)</td> <td>\(-e_2\)</td> <td>\(e_1\)</td> <td>\(-1\)</td></tr>
</table>
<p>
Now that we know what elements of 𝔾<sup>2</sup> look like and how to
manipulate them, it's now time to put them to work.
</p>
<h3 id=applications>Applying Geometric Algebra</h3>
<h4 id=multiplying_vectors>Multiplying Vectors</h4>
<p>
Let's start by multiplying two vectors:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}} \\
\boldsymbol{b} &= b_1 \boldsymbol{e_{1}} + b_2 \boldsymbol{e_{2}}
\end{align*}
$$
</dd>
</dl>
<p>
Under the geometric product we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} \boldsymbol {b} &= (a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}}) (b_1 \boldsymbol{e_{1}} + b_2 \boldsymbol{e_{2}}) \\
&= a_1 b_1 \boldsymbol{e_{1}} \boldsymbol{e_{1}}
+ a_1 b_2 \boldsymbol{e_{1}} \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \boldsymbol{e_{1}}
+ a_2 b_2 \boldsymbol{e_{2}} \boldsymbol{e_{2}} \\
&= a_1 b_1 + a_1 b_2 \boldsymbol{e_{1}} \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \boldsymbol{e_{1}} + a_2 b_2 \\
&= a_1 b_1 + a_2 b_2 + a_1 b_2 \boldsymbol{e_{12}}
- a_2 b_1 \boldsymbol{e_{12}} \\
&= (a_1 b_1 + a_2 b_2) + (a_1 b_2 - a_2 b_1) \boldsymbol{e_{12}}
\end{align*}
$$
</dd>
</dl>
<p>
We can see that from the product of two vectors we get a scalar and a bivector.
</p>
<p>
What if we take a scalar and a bivector and multiply it by a vector?
Note that below we are using a capital letter for our scalar plus bivector.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}} \\
\boldsymbol{B} &= B_1 + B_2 \boldsymbol{e_{12}}
\end{align*}
$$
</dd>
</dl>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} \boldsymbol {B} &= (a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}}) (B_1 + B_2 \boldsymbol{e_{12}}) \\
&= a_1 B_1 \boldsymbol{e_{1}}
+ a_1 B_2 \boldsymbol{e_{1}} \boldsymbol{e_{12}}
+ a_2 B_1 \boldsymbol{e_{2}}
+ a_2 B_2 \boldsymbol{e_{2}} \boldsymbol{e_{12}} \\
&= a_1 B_1 \boldsymbol{e_{1}}
+ a_2 B_1 \boldsymbol{e_{2}}
+ a_1 B_2 \boldsymbol{e_{1}} \boldsymbol{e_{12}}
+ a_2 B_2 \boldsymbol{e_{2}} \boldsymbol{e_{12}} \\
&= a_1 B_1 \boldsymbol{e_{1}}
+ a_2 B_1 \boldsymbol{e_{2}}
+ a_1 B_2 \boldsymbol{e_{2}}
- a_2 B_2 \boldsymbol{e_{1}} \\
&= ( a_1 B_1 - a_2 B_2 )\boldsymbol{e_{1}}
+ ( a_2 B_1 + a_1 B_2 ) \boldsymbol{e_{2}} \\
\end{align*}
$$
</dd>
</dl>
<p>
That product gives us back a vector, so <b>B</b> is an element of 𝔾<sup>2</sup>
that operates on vectors through the geometric product to give us another
vector.
</p>
<h4 id=rotors>Rotors</h4>
<p>
A special case of <b>B</b> is called a Rotor. This
Rotor is an element of 𝔾<sup>2</sup> that is just a restatement of <a
href="https://en.wikipedia.org/wiki/Euler's_formula">Euler's formula</a>
in 𝔾<sup>2</sup>.
</p>
<p>
First, for reasons that will become clearer later, we will begin to
abbreviate <b>e<sub>12</sub></b> as <b>I</b>. Our Rotor is then defined
as:
</p>
<dl>
<dd>$$ e^{\theta \boldsymbol{I}} := \cos(\theta) + \sin(\theta)\boldsymbol{I}$$</dd>
</dl>
<p>
If you multiply any vector by this Rotor on the right it will rotate
that vector θ degrees in the direction from <b>e<sub>1</sub></b> to
<b>e<sub>2</sub></b>. If you multiply that same vector on the left by
this Rotor it will be rotated θ degrees in the opposite direction.
</p>
<p>
For example, here is a dynamic illustration of the Rotor in action,
In this case, we are multiplying <b>e<sub>1</sub></b> by
e<sup>ωt<b>I</b></sup>, where t is time, and ω is
the rate, in radians per second, that the vector undergoes rotation.
In this example we set ω = 1, so the vector should complete
a full circle every 2π seconds.
</p>
<dl>
<dd>$$\boldsymbol{v} = \boldsymbol{e_1} e^{t \boldsymbol{I}}$$</dd>
</dl>
<button id=rotor_toggle>Start/Stop</button>
<canvas id=rotor width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('rotor_toggle');
var running = false;
var omega = 1;
var start = Date.now();
var e1 = ga2d.vec(1, 0);
var f = new draw_ga2d.Frame(document.getElementById('rotor'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var dest = ga2d.mul(e1, rotor);
f.clear();
f.expandTo([0, 1, 1, 0]);
f.expandTo([0, -1, -1, 0]);
f.axes();
f.vec(dest, "v");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Caveat: Rotors only work like this in ℝ<sup>2</sup>, in ℝ<sup>3</sup> and
above the formulation changes, so be aware of that.
</p>
<p>
Using geometric algebra makes it easy to read off this formula
and determine what is going to happen, i.e. the <b>e<sub>1</sub></b>
vector is going to be operated on via geometric product and the result
will be another vector that is rotated <em>ω t</em> radians in
a counter-clockwise direction.
</p>
<p>
Since our Rotator is a member of 𝔾<sup>2</sup> it can be combined with
other operations. For example, we could start with a vector <em>p</em> at
an initial position and then perturb it by adding it to another vector
that is multiplied by our Rotor. In this case we set ω = 2.
</p>
<dl>
<dd>$$\boldsymbol{v} = \boldsymbol{p} + 0.5 \boldsymbol{e_1} e^{2 t \boldsymbol{I}}$$</dd>
</dl>
<button id=sum_rotor_toggle>Start/Stop</button>
<canvas id=sum_rotor width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('sum_rotor_toggle');
var running = false;
var omega = 2;
var start = Date.now();
var p = ga2d.vec(1, 1);
var e1 = ga2d.vec(0.5, 0);
var f = new draw_ga2d.Frame(document.getElementById('sum_rotor'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var dest = ga2d.add(p, ga2d.mul(e1, rotor));
f.clear();
f.expandTo([0, 1.5, 1.5, 0]);
f.axes();
f.vec(dest, "v");
f.vec(p, "p");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
We can take that one step further and rotate the whole thing around
the origin, where we set ω<sub>1</sub> = 2.9 and ω<sub>2</sub> =
1.
</p>
<dl>
<dd>$$\boldsymbol{v} = (\boldsymbol{p} + 0.5 \boldsymbol{e_1} e^{\omega_1 t \boldsymbol{I}})e^{\omega_2 t \boldsymbol{I}}$$</dd>
</dl>
<button id=sum_rotor2_toggle>Start/Stop</button>
<canvas id=sum_rotor2 width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('sum_rotor2_toggle');
var running = false;
var omega1 = 2.9;
var omega2 = 1;
var start = Date.now();
var p = ga2d.vec(1, 1);
var e1 = ga2d.vec(0.5, 0);
var f = new draw_ga2d.Frame(document.getElementById('sum_rotor2'));
var step = function() {
var rotor1 = ga2d.e(omega1 * (Date.now() - start)/1000);
var rotor2 = ga2d.e(omega2 * (Date.now() - start)/1000);
var dest = ga2d.mul(ga2d.add(p, ga2d.mul(e1, rotor1)), rotor2);
f.clear();
f.expandTo([0, 2, 2, 0]);
f.expandTo([0, -2, -2, 0]);
f.axes();
f.vec(dest, "v");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
That might be easier to follow if instead of drawing the vector
we draw the trail of points where the vector has been.
</p>
<button id=trail_rotor2_toggle>Start/Stop</button>
<canvas id=trail_rotor2 width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('trail_rotor2_toggle');
var running = false;
var omega1 = 2.9;
var omega2 = 1;
var start = Date.now();
var p = ga2d.vec(1, 1);
var e1 = ga2d.vec(0.5, 0);
var f = new draw_ga2d.Frame(document.getElementById('trail_rotor2'));
var lastPoint = [];
var step = function() {
var rotor1 = ga2d.e(omega1 * (Date.now() - start)/1000);
var rotor2 = ga2d.e(omega2 * (Date.now() - start)/1000);
var dest = ga2d.mul(ga2d.add(p, ga2d.mul(e1, rotor1)), rotor2);
if (lastPoint === []) {
lastPoint = dest;
return
}
f.reset();
f.segment(lastPoint, dest);
f.draw();
lastPoint = dest;
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
lastPoint = [];
f.clear();
f.expandTo([0, 2, 2, 0]);
f.expandTo([0, -2, -2, 0]);
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<h4 id=double_angle>Double Angle Formula</h4>
<p>
Some of the power of Geometric Algebra comes from being able to go back
and forth between looking at a problem geometrically and looking at it
algrebraically. For example, it is easy to reason that rotating a vector
θ degrees twice is the same as rotating that same vector 2 θ
degrees. We can write that out as an algebraic expression:
</p>
<dl>
<dd>
$$
e^{2 \theta \boldsymbol{I}} = e^{\theta \boldsymbol{I}} e^{\theta \boldsymbol{I}}
$$
</dd>
</dl>
<p>
If we expand both sides of the equations above using the definition of
<em>e</em> we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\cos 2 \theta + \sin 2 \theta \boldsymbol{I} &= (\cos \theta + \sin \theta \boldsymbol{I}) (\cos \theta + \sin \theta \boldsymbol{I}) \\
&= \cos^2 \theta + 2 \cos \theta \sin \theta \boldsymbol{I} + \sin^2 \theta \boldsymbol{I}^2 \\
&= \cos^2 \theta + 2 \cos \theta \sin \theta \boldsymbol{I} - \sin^2 \theta \\
&= \cos^2 \theta - \sin^2 \theta + 2 \cos \theta \sin \theta \boldsymbol{I}
\end{align*}
$$
</dd>
</dl>
<p>
Comparing the coefficients on the left hand side of the equation to that on
the right hand side we find we have derived the
<a href="http://mathworld.wolfram.com/Double-AngleFormulas.html">Double Angle Formulas</a>:
</p>
<dl>
<dd>
$$
\cos 2 \theta = \cos^2 \theta - \sin^2 \theta
$$
</dd>
<dd>
$$
\sin 2 \theta = 2 \cos \theta \sin \theta
$$
</dd>
</dl>
<p>
You could start with the same geometric reasoning about any two angles,
α and β, and use the same derivation to get the general <a
href="https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Angle_sum_and_difference_identities">Angle
sum identities</a>. The power here is the ability to move back and
forth between algebraic and geometric reasoning quickly and easily.
</p>
<h4 id=complex>Complex Numbers</h4>
<p>
From our definition of our Rotator, if we set ω to 90 degrees then
since <em>cos</em> becomes 0 we are left with only <b>I</b>, which is a
90 degree Rotator. But if we apply a 90 degree Rotator twice we should get
a 180 degree Rotator:
</p>
<dl>
<dd>$$
\begin{align*}
\boldsymbol{I} \boldsymbol{I} &= \boldsymbol{e_{12}} \boldsymbol{e_{12}} \\
&= \boldsymbol{e_1} \boldsymbol{e_2} \boldsymbol{e_1} \boldsymbol{e_2} \\
&= - \boldsymbol{e_1} \boldsymbol{e_1} \boldsymbol{e_2} \boldsymbol{e_2} \\
&= - 1 \boldsymbol{e_2} \boldsymbol{e_2} \\
&= - 1 \\
\end{align*}
$$</dd>
</dl>
<p>
And -1 is exactly what we would expect, since that's what you multiply
a vector by to rotate it 180 degrees. But what we also have is a quantity
in 𝔾<sup>2</sup> that when squared is equal to -1. This should remind you
of <em>i</em> in the complex numbers ℂ, but without the need to take the
square root of a negative number, or invoke anything imaginary. In
fact the subset of all linear combinations of <b>{1, I}</b> is closed
under the geometric product and is isomorphic to ℂ.
</p>
<h4 id=B>Characterizing B</h4>
<p>Now that we have learned about Rotors, let's apply that knowledge
to characterize elements of the form:
</p>
<dl>
<dd>
$$ \boldsymbol{B} = B_1 + B_2 \boldsymbol{e_{12}} $$
</dd>
</dl>
<p>
First, let's look at the relationship between any two non-zero vectors.
</p>
<canvas id=two width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var a = ga2d.vec(-1, 2);
var b = ga2d.vec(2, 3);
var f = new draw_ga2d.Frame(document.getElementById('two'));
f.axes();
f.vec(a, "a");
f.vec(b, "b");
f.draw();
})();
</script>
<p>
We can reason out geometrically that given <b>b</b> we can get <b>a</b>
from it by first scaling <b>b</b> to have a norm of 1, then rotating it to
have the same direction as <b>a</b>, and then finally scaling that unit
vector to have the same length as <b>a</b>. Now write that out
algrebraically, where θ is the angle between the two vectors.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} &= ||\boldsymbol{a}|| e^{\theta \boldsymbol{I}} \frac{1}{||\boldsymbol{b}||} \boldsymbol{b} \\
&= \frac{||\boldsymbol{a}||}{||\boldsymbol{b}||} e^{\theta \boldsymbol{I}} \boldsymbol{b}
\end{align*}
$$
</dd>
</dl>
<p>
If we look at any product of two non-zero vectors, <b>ab</b>, we know we
get an operator that, under the geometric product, takes vectors and
returns new vectors. If we substitute our derivation of how to get <b>a</b>
from <b>b</b>, then we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{ab} &= \frac{||\boldsymbol{a}||}{||\boldsymbol{b}||} e^{\theta \boldsymbol{I}} \boldsymbol{b} \boldsymbol{b} \\
&= \frac{||\boldsymbol{a}||}{||\boldsymbol{b}||} e^{\theta \boldsymbol{I}} ||\boldsymbol{b}||^2 \\
&= ||\boldsymbol{a}|| ||\boldsymbol{b}|| e^{\theta \boldsymbol{I}}
\end{align*}
$$
</dd>
</dl>
<p>
So every such operator <b>ab</b> is actually just a rotation and a
dilation. We can see this in action if we have the operator <b>ab</b> and
apply it to vector <b>c</b> to get vector <b>d</b>. The animation will
perturb vector <b>b</b> to show how that affects vector <b>d</b>.
</p>
<p>
</p>
<dl>
<dd>
$$
\boldsymbol{d} = \boldsymbol{ab}\boldsymbol{c}
$$
</dd>
</dl>
<button id=ab_toggle>Start/Stop</button>
<canvas id=ab width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('ab_toggle');
var running = false;
var omega = 2;
var start = Date.now();
var a = ga2d.vec(0.8, 0);
var b = ga2d.vec(1, 0.2);
var c = ga2d.vec(0, 0.5);
var p = ga2d.vec(0.2, 0);
var f = new draw_ga2d.Frame(document.getElementById('ab'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var bp = ga2d.add(b, ga2d.mul(rotor, p))
var op = ga2d.mul(bp, a);
var d = ga2d.mul(op, c)
f.clear();
f.axes();
f.vec(a, "a");
f.vec(bp, "b");
f.vec(c, "c");
f.vec(d, "d");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Our generalized form for the geometric product of two vectors is:
</p>
<dl>
<dd>
$$ \boldsymbol{B} = B_1 + B_2 \boldsymbol{e_{12}} $$
</dd>
</dl>
<p>
We can use what we've learned so far to break that apart into
its scalar and Rotor components:
</p>
<dl>
<dd>
$$
\boldsymbol{B} = k e^{\theta \boldsymbol{I}}
$$
</dd>
</dl>
<p>
Start by applying <b>B</b> to a unit basis element, which we know
has a norm of 1, which gives us a new vector <b>v</b>.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{v} &= \boldsymbol{B}\boldsymbol{e_1} \\
&= k e^{\theta \boldsymbol{I}} \boldsymbol{e_1}
\end{align*}
$$
</dd>
</dl>
<p>
We can see from the last equation that <b>v</b> has a norm of k,
and now that we know k, we can divide <b>B</b> by k to get our Rotor.
</p>
<dl>
<dd>
$$
\begin{align*}
k &= ||\boldsymbol{B}\boldsymbol{e_1}|| = \sqrt{B_{1}^{2} + B_{2}^{2}} \\
e^{\theta \boldsymbol{I}} &= \frac{1}{k} \boldsymbol{B}
\end{align*}
$$
</dd>
</dl>
<h4 id=ratios>Ratios</h4>
<p>
While applying the operator <b>ab</b> above did show some of the behavior,
it may be useful to start over, this time building our operator from a
ratio, i.e. if we have two vectors <b>a</b> and <b>b</b>, and given a
third vector <b>c</b>, we'd like to calculate the vector <b>d</b> so that
they have the same ratio, i.e.
</p>
<dl>
<dd>
$$ \boldsymbol{d} / \boldsymbol{c} = \boldsymbol{b} / \boldsymbol{a} $$
</dd>
</dl>
<p>
The geometric product isn't commutative, so we have to choose a side
to do the division on, so we will write this as:
</p>
<dl>
<dd>
$$
\boldsymbol{d}\boldsymbol{c^{-1}} = \boldsymbol{b}\boldsymbol{a^{-1}}
$$
</dd>
</dl>
<p>
But that's just a simple algrebraic equation we can solve
by multiplying both sides by <b>c</b>.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{d}\boldsymbol{c^{-1}} &= \boldsymbol{b}\boldsymbol{a^{-1}} \\
\boldsymbol{d}\boldsymbol{c^{-1}}\boldsymbol{c} &= \boldsymbol{b}\boldsymbol{a^{-1}}\boldsymbol{c} \\
\boldsymbol{d} &= \boldsymbol{b}\boldsymbol{a^{-1}}\boldsymbol{c}
\end{align*}
$$
</dd>
</dl>
<p>
The operator <b>ba<sup>-1</sup></b> should preserve the angle between
<b>a</b> and <b>b</b>, and also dilate <b>d</b> proportionally to the
norms of <b>a</b> and <b>b</b>. The following animation shows
that relationship, also perturbing <b>b</b> to show the affect on
<b>d</b>.
</p>
<button id=ratio_toggle>Start/Stop</button>
<canvas id=ratio width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('ratio_toggle');
var running = false;
var omega = 2;
var start = Date.now();
var a = ga2d.vec(0.8, 0);
var b = ga2d.vec(1, 0.2);
var c = ga2d.vec(0, 0.5);
var p = ga2d.vec(0.2, 0);
var f = new draw_ga2d.Frame(document.getElementById('ratio'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var bp = ga2d.add(b, ga2d.mul(rotor, p))
var op = ga2d.mul(bp, ga2d.inv(a));
var d = ga2d.mul(op, c)
f.clear();
f.axes();
f.vec(a, "a");
f.vec(bp, "b");
f.vec(c, "c");
f.vec(d, "d");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<h4 id=conjugates>Conjugates and Inverses</h4>
<p>
Let's see what the difference between <b>ab</b> and <b>ba</b> is.
First let's multiply out in terms of basis vectors:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} \boldsymbol {b} &= (a_1 \boldsymbol{e_{1}} + a_2 \boldsymbol{e_{2}}) (b_1 \boldsymbol{e_{1}} + b_2 \boldsymbol{e_{2}}) \\
&= a_1 b_1 \boldsymbol{e_{1}} \boldsymbol{e_{1}}
+ a_1 b_2 \boldsymbol{e_{1}} \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \boldsymbol{e_{1}}
+ a_2 b_2 \boldsymbol{e_{2}} \boldsymbol{e_{2}} \\
&= a_1 b_1 + a_1 b_2 \boldsymbol{e_{1}} \boldsymbol{e_{2}}
+ a_2 b_1 \boldsymbol{e_{2}} \boldsymbol{e_{1}} + a_2 b_2 \\
&= a_1 b_1 + a_2 b_2 + a_1 b_2 \boldsymbol{e_{12}}
- a_2 b_1 \boldsymbol{e_{12}} \\
&= (a_1 b_1 + a_2 b_2) + (a_1 b_2 - a_2 b_1) \boldsymbol{I}
\end{align*}
$$
</dd>
</dl>
<p>
If we swap <b>a</b> and <b>b</b> we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{b} \boldsymbol {a} &= (b_1 a_1 + b_2 a_2) + (b_1 a_2 - b_2 a_1) \boldsymbol{I} \\
&= (a_1 b_1 + a_2 b_2) + (b_1 a_2 - b_2 a_1) \boldsymbol{I} \\
&= (a_1 b_1 + a_2 b_2) + (a_2 b_1 - a_1 b_2) \boldsymbol{I} \\
&= (a_1 b_1 + a_2 b_2) - (a_1 b_2 - a_2 b_1) \boldsymbol{I} \\
\end{align*}
$$
</dd>
</dl>
<p>In that last step we just factor out a -1 from the coefficient of
<b>I</b>. If we substitute:
</p>
<dl>
<dd>
$$
\begin{align*}
B_1 &= a_1 b_1 + a_2 b_2 \\
B_2 &= a_1 b_2 - a_2 b_1
\end{align*}
$$
</dd>
</dl>
<p>
Then we get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{a} \boldsymbol {b} &= B_1 + B_2 \boldsymbol{I} \\
\boldsymbol{b} \boldsymbol {a} &= B_1 - B_2 \boldsymbol{I}
\end{align*}
$$
</dd>
</dl>
<p>
So if we reverse the order of the geometric product of our vectors
we end up with the equivalent of the <a href="https://en.wikipedia.org/wiki/Complex_conjugate">complex conjugate</a>.
</p>
<p>
We will note the reverse of the product of two vectors with the dagger.
While this maps to the conjugate in 𝔾<sup>2</sup>, reversing a product
of multiple vectors will be more important and powerful in 𝔾<sup>3</sup>.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{B} &= \boldsymbol{a} \boldsymbol{b} &= B_1 + B_2 \boldsymbol{I} \\
\boldsymbol{B}^{\dagger} &= \boldsymbol{b} \boldsymbol{a} &= B_1 - B_2 \boldsymbol{I}
\end{align*}
$$
</dd>
</dl>
<p>
If we multiply them together we find:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{B} \boldsymbol{B}^{\dagger} &= \boldsymbol{abba} \\
&= \boldsymbol{a} ||\boldsymbol{b}||^2 \boldsymbol{a} \\
&= ||\boldsymbol{b}||^2 \boldsymbol{aa} \\
&= ||\boldsymbol{b}||^2 ||\boldsymbol{a}||^2 \\
&= ||\boldsymbol{a}||^2 ||\boldsymbol{b}||^2
\end{align*}
$$
</dd>
</dl>
<p>
Their product just ends up being a scalar, so if divide by that
scalar value we should get:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{B} \frac{\boldsymbol{B}^{\dagger}}{||\boldsymbol{a}||^2 ||\boldsymbol{b}||^2} &= \frac{||\boldsymbol{a}||^2 ||\boldsymbol{b}||^2}{ ||\boldsymbol{a}||^2 ||\boldsymbol{b}||^2} \\
&= 1
\end{align*}
$$
</dd>
</dl>
<p>
Which means we've found the multiplicative inverse of <b>B</b>.
</p>
<dl>
<dd>
$$
\boldsymbol{B}^{-1} = \frac{\boldsymbol{B}^{\dagger}}{\boldsymbol{B}\boldsymbol{B^{\dagger}}
</dd>
</dl>
<p>
Normally geometric products aren't commutative, but in this case
we can see that we get the same result when we reverse the order
of B and B dagger:
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{B}^{\dagger}\boldsymbol{B} &= \boldsymbol{baab} \\
&= \boldsymbol{b} ||\boldsymbol{a}||^2 \boldsymbol{b} \\
&= ||\boldsymbol{a}||^2 \boldsymbol{bb} \\
&= ||\boldsymbol{a}||^2 ||\boldsymbol{b}||^2
\end{align*}
$$
</dd>
</dl>
<p>
So our inverse will work whether applied on the left or on the right.
</p>
<p>
Let's see how that inverse operates by applying it to
our previous ratio example. This time we'll not only apply
the <b>ba<sup>-1</sup></b> operator, but also apply it's inverse
to <b>c</b> to see how it compares.
</p>
<dl>
<dd>
$$
\begin{align*}
\boldsymbol{d} &= \boldsymbol{b}\boldsymbol{a^{-1}}\boldsymbol{c} \\
\boldsymbol{d'} &= (\boldsymbol{b}\boldsymbol{a^{-1}})^{-1}\boldsymbol{c}
\end{align*}
$$
</dd>
</dl>
<button id=inv_ratio_toggle>Start/Stop</button>
<canvas id=inv_ratio width=300 height=300></canvas>
<script type="text/javascript" charset="utf-8">
(function () {
var button = document.getElementById('inv_ratio_toggle');
var running = false;
var omega = 2;
var start = Date.now();
var a = ga2d.vec(0.8, 0);
var b = ga2d.vec(1, 0.2);
var c = ga2d.vec(0, 0.5);
var p = ga2d.vec(0.2, 0);
var f = new draw_ga2d.Frame(document.getElementById('inv_ratio'));
var step = function() {
var rotor = ga2d.e(omega * (Date.now() - start)/1000);
var bp = ga2d.add(b, ga2d.mul(rotor, p))
var op = ga2d.mul(bp, ga2d.inv(a));
var d = ga2d.mul(op, c)
var norm = ga2d.mul(op, ga2d.conj(op))[0];
var opinv = ga2d.mul(ga2d.scalar(1.0/norm), ga2d.conj(op));
var dinv = ga2d.mul(opinv, c)
f.clear();
f.axes();
f.vec(a, "a");
f.vec(bp, "b");
f.vec(c, "c");
f.vec(d, "d");
f.vec(dinv, "d'");
f.draw();
if (running) {
window.requestAnimationFrame(step);
}
};
button.addEventListener('click', function() {
running = !running;
if (running) {
start = Date.now();
window.requestAnimationFrame(step);
}
});
step();
})();
</script>
<p>
Note that starting from conjugates isn't the only way to construct such an inverse, we could,
for example, note that because each non-zero vector has a multiplicative
inverse, we can come to the same conclusion:
</p>
<dl>
<dd>
$$
\begin{align*}
1 &= \boldsymbol{a} (\frac{\boldsymbol{a}}{||\boldsymbol{a}||^2}) \\
&= \boldsymbol{a} 1 (\frac{\boldsymbol{a}}{||\boldsymbol{a}||^2}) \\
&= \boldsymbol{a} (\boldsymbol{b} (\frac{\boldsymbol{b}}{||\boldsymbol{b}||^2}) (\frac{\boldsymbol{a}}{||\boldsymbol{a}||^2}) \\
&= \boldsymbol{ab} (\frac{\boldsymbol{ba}}{||\boldsymbol{a}||^2||\boldsymbol{b}||^2})
\end{align*}
$$
</dd>
</dl>
<h3 id=further_reading>Further Reading</h3>
<p>
There are other introductions to GA around the web, some of the ones I've
found helpful are:
</p>
<ul>
<li><a href="http://www.jaapsuter.com/geometric-algebra/">Geometric Algebra Primer</a></li>
<li><a href="http://scholarworks.sjsu.edu/cgi/viewcontent.cgi?article=7943&context=etd_theses">Geometric Algebra: An Introduction with Applications in Euclidean and Conformal Geometry</a></li>
</ul>img { vertical-align: baseline; }Pat McCrory in the context of elite overproduction.2016-12-05T00:00:00-05:002016-12-05T00:00:00-05:00https://bitworking.org/news/2016/12/mccrory_in_context<p>
As we head into the fourth week of Pat McCrory's failure to conceeed in
the NC gubernatorial race, it's important to look at McCrory's infantile
behavior in a larger context. While it would be tempting to try to
psychoanalyze his continued intransigence as yet another man-child temper
tantrum, there are larger forces at work, of which McCrory is just one
sad symptom.
</p>
<p>
The root of the problem stems from the ever widening wealth gap and
subsequent <a href="https://www.bloomberg.com/view/articles/2013-11-20/blame-rich-overeducated-elites-as-our-society-frays">elite
overproduction</a>. As more and more millionaires and billionaires are
minted they seek to convert their newfound wealth into political power.
But the levers of power are limited, there's a finite number of House and
Senate seats, there are only 50 governors, and only one President. No
new power levers are being created, yet there are more and more people
scrambling for them. More and more millionaires think they should be on
the city council or run for their state legislature, while more
billionaires think they too should be President. In a simple example of
the law of supply and demand, you can see the price of running for office
rising steeply over the last 40 years, <a href="http://www.bbc.com/news/the-reporters-35365848">with the cost of
running for President exceeding $2 billion in 2012</a>.
</p>
<p>
But what happens when the demand outstrips the supply so that no manner
of money can buy you that cherished lever of power? When there are
multiple millionaires, each backed by a group of billionaires, all vying
for power? What do you pay then? You pay in social norms. Common decency. The
destruction of these are the price you pay. Pat McCrory, in a desperate
bid to retain his power, is willing to violate every norm of U.S.
democracy and attempt to destroy all faith in the election process, the
same process that put him in power four years ago. If Pat McCrory can't be
governor, well then, he might as well burn down the entire edifice so no
one else can be either.
</p>
<p>
You can also see this playing out on the national stage, with Donald Trump
willing to violate every norm, digging deep into the veins of xenophobia,
racism, and bigotry to propel himself into office. Under normal
circumstances no politician would openly court the worse side of the human
tribal instinct, the horrors from the last rise of fascism leading up to
WWII have been too close and too fresh in memory. But time has passed, the
last survivors of WWII are dying out, the reality of tens of millions of
people dying in wars, revolutions, and pogroms are just dry history
lessons now, not to be considered in the raw and ugly scramble for power.
</p>
<p>
So don't blame Pat McCrory, as infantile and destructive as his behavior
has become, he is just a symptom of a much larger breakdown in political
norms, and these are just a couple steps along a longer arc of societal
disintegration. We're already seeing the normal U.S. two-party system
fragment into five distinct parties; the neo-liberal wing of the
Democratic party as exemplified by Hillary Clinton, the populist wing of
Bernie Sanders followers, the traditional big business GOP, the Tea Party
republicans, and finally the Trumpers. And this isn't the end of the
disruption, merely the beginning. Layer on global warming, the continued
disruption of technology, and the world wide migration of people from
rural areas into cities and we have all the ingredients for massive
upheaval. Will we descend into our own Cultural Revolution, shatter the
country in another Civil War, or will the similar rise of fascism across
Europe lead to another world engulfing spasm of death and desctruction?
<a href="http://www.salon.com/2016/10/01/breaking-point-america-approaching-a-period-of-disintegration-argues-anthropologist-peter-turchin/">The
patterns are all there, the roots of the problem can be clearly mapped
out, and while there's no guaranteed way to avoid the coming
disintegration, maybe we should at least try</a>.
</p>As we head into the fourth week of Pat McCrory's failure to conceeed in the NC gubernatorial race, it's important to look at McCrory's infantile behavior in a larger context. While it would be tempting to try to psychoanalyze his continued intransigence as yet another man-child temper tantrum, there are larger forces at work, of which McCrory is just one sad symptom. The root of the problem stems from the ever widening wealth gap and subsequent elite overproduction. As more and more millionaires and billionaires are minted they seek to convert their newfound wealth into political power. But the levers of power are limited, there's a finite number of House and Senate seats, there are only 50 governors, and only one President. No new power levers are being created, yet there are more and more people scrambling for them. More and more millionaires think they should be on the city council or run for their state legislature, while more billionaires think they too should be President. In a simple example of the law of supply and demand, you can see the price of running for office rising steeply over the last 40 years, with the cost of running for President exceeding $2 billion in 2012. But what happens when the demand outstrips the supply so that no manner of money can buy you that cherished lever of power? When there are multiple millionaires, each backed by a group of billionaires, all vying for power? What do you pay then? You pay in social norms. Common decency. The destruction of these are the price you pay. Pat McCrory, in a desperate bid to retain his power, is willing to violate every norm of U.S. democracy and attempt to destroy all faith in the election process, the same process that put him in power four years ago. If Pat McCrory can't be governor, well then, he might as well burn down the entire edifice so no one else can be either. You can also see this playing out on the national stage, with Donald Trump willing to violate every norm, digging deep into the veins of xenophobia, racism, and bigotry to propel himself into office. Under normal circumstances no politician would openly court the worse side of the human tribal instinct, the horrors from the last rise of fascism leading up to WWII have been too close and too fresh in memory. But time has passed, the last survivors of WWII are dying out, the reality of tens of millions of people dying in wars, revolutions, and pogroms are just dry history lessons now, not to be considered in the raw and ugly scramble for power. So don't blame Pat McCrory, as infantile and destructive as his behavior has become, he is just a symptom of a much larger breakdown in political norms, and these are just a couple steps along a longer arc of societal disintegration. We're already seeing the normal U.S. two-party system fragment into five distinct parties; the neo-liberal wing of the Democratic party as exemplified by Hillary Clinton, the populist wing of Bernie Sanders followers, the traditional big business GOP, the Tea Party republicans, and finally the Trumpers. And this isn't the end of the disruption, merely the beginning. Layer on global warming, the continued disruption of technology, and the world wide migration of people from rural areas into cities and we have all the ingredients for massive upheaval. Will we descend into our own Cultural Revolution, shatter the country in another Civil War, or will the similar rise of fascism across Europe lead to another world engulfing spasm of death and desctruction? The patterns are all there, the roots of the problem can be clearly mapped out, and while there's no guaranteed way to avoid the coming disintegration, maybe we should at least try.Surely we’ve seen this before. Not.2016-11-19T00:00:00-05:002016-11-19T00:00:00-05:00https://bitworking.org/news/2016/11/seen_this_before<p>You might think that as the U.S. moves from an industrial and
manufacturing based economy to a knowledge based economy that we surely
have weathered similar tranistions. For example, as we moved from an
agricultural economy to a manufacturing based one. While we did indeed
weather the same changes, the vital difference is the timescale over which
those changes took place.
</p>
<p>As you can see from this data on
<a href="https://ourworldindata.org/agricultural-employment/">agricultural
employment</a>, we did experience a loss of 6 million agricultural jobs
over a 50 year period from 1910 to 1960. In contrast, from <a href="https://www.brookings.edu/blog/the-avenue/2016/03/15/voter-anger-explained-in-one-chart/">Voter
anger explained—in one chart</a>, you can see that the U.S. economy
also lost 6 million manufacturing jobs, but this time in just 10 years, from 2000 to 2010.
</p>You might think that as the U.S. moves from an industrial and manufacturing based economy to a knowledge based economy that we surely have weathered similar tranistions. For example, as we moved from an agricultural economy to a manufacturing based one. While we did indeed weather the same changes, the vital difference is the timescale over which those changes took place. As you can see from this data on agricultural employment, we did experience a loss of 6 million agricultural jobs over a 50 year period from 1910 to 1960. In contrast, from Voter anger explained—in one chart, you can see that the U.S. economy also lost 6 million manufacturing jobs, but this time in just 10 years, from 2000 to 2010.