{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conformal invariance\n",
"\n",
"(To be included in the next edition of Sethna, \"Entropy, Order Parameters, and Complexity\")\n",
"\n",
"© 2017, James P. Sethna, all rights reserved."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The Ising model on a square lattice has an emergent rotation invariance as well as an emergent scale invariance. The complex patterns of up and down spins look the same on long length scales also when rotated by an angle. Indeed, making use of the symmetries under changes of length scale, position, and angle (plus one spatially nonuniform transformation), systems at their critical points have a conformal symmetry group.\n",
"\n",
"In two dimensions, the conformal symmetry group becomes huge. Roughly\n",
"speaking, any complex analytic function $f(z) = u(x+ i y) + i v(x+i y)$\n",
"takes a snapshot of an Ising model $M(x,y)$ and warps it into a new\n",
"magnetization pattern at $(u,v)$ that 'looks the same'. (Here $u$, $v$, $x$, and $y$ are all real.)\n",
"\n",
"You may remember\n",
"that most ordinary functions (like $z^2$, $\\sqrt{z}$,\n",
"$\\sin(z)$, $\\log(z)$, and $\\exp(i z)$) are analytic, and all of them\n",
"yield cool transformations of the Ising model -- weird and warped when\n",
"magnified until you see the pixels, but recognizably Ising-like on long\n",
"scales.\n",
"\n",
"(a) What analytic function shrinks a region uniformly by a factor $b$,\n",
"holding $z=0$ fixed? What analytic function translates the lattice by\n",
"a vector $r_0 = (u_0,v_0)$? What analytic function rotates a region by\n",
"an angle $\\theta$? Expanding $f(z+\\delta) = f(z) + \\delta \\partial f / \\partial z$,\n",
"show that an analytic function $f$ to linear order in $\\delta$ is a rotation\n",
"and dilation about $z$ followed by a translation. What complex number gives\n",
"the net translation?\n",
"\n",
"In the renormalization group, we first coarse grain the system\n",
"(shrinking by a factor $b$) and then rescale the magnetization\n",
"(by some power $b^{y_M}$)\n",
"in order to return to statistically the same critical state:\n",
"$\\widehat M = b^{y_M} M$.\n",
"This rescaling turns the larger\n",
"pixels more gray; a mostly up-spin\n",
"'white' region with tiny pixels is mimicked by a large single pixel with the\n",
"statistically averaged gray color.\n",
"\n",
"We can discover the correct\n",
"power for $M$ by examining the rescaling of the correlation function\n",
"$C(r) = \\langle M(x) M(x+r) \\rangle$.\n",
"In the Ising model at its critical point the correlation function\n",
"$C(r) \\sim r^{-(2-d+\\eta)}$. In dimension $d=2$, $\\eta=1/4$. We expect\n",
"that the correlation function for the conformally transformed magnetization\n",
"will be the same as the original correlation function.\n",
"\n",
"(b) If we coarse-grain by a uniform factor $b$, what power of $b$ must we multiply $M$ by to make\n",
"$C(r) = \\langle \\widehat{M}(f(z)) \\widehat{M}(f(z)+r) \\rangle$?\n",
"\n",
"When our conformal transformation takes a pixel at $M(z)$ to a warped pixel\n",
"of area $A$ at $f(z)$, it rescales the magnetization by\n",
" $\\widehat{M}(f(z)) = |A|^{-1/16} M(z)$.\n",
"The pixel area for a locally uniform compression by $b$ changes by\n",
"$|d{f}/d{z}|^2 = 1/b^2$. You may use this to check your answer to part~(b)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we proceed to some computations. Load up the packages."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%pylab\n",
"from scipy import *\n",
"from scipy.misc import imread\n",
"from matplotlib.collections import PolyCollection"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now load a snapshot of the Ising model, replacing the directory shown with your local directory and the filename with your image file. Transform the image into an array"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Load image\n",
"Simage = imread(\"Snapshots/Tc.png\", flatten=True)\n",
"# Image is stored as gray scale (0,255). Ising uses down=0->-1, up=255->1. \n",
"S = 2*Simage/255 - 1\n",
"figure()\n",
"imshow(S, interpolation='nearest', cmap='gray',origin='lower')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"'Transform' selects an L x L region from S, generates a list of spins and square polygons covering the unit square in the complex plane, transforms the squares into quadrilaterals under the map f(z), rescales the magnetization S according to the quadrilateral area by $A^{-1/16}$, converts the quadrilaterals back into real coordinates, and returns the warped polygons and rescaled magnetization."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def Transform(S,f,L=None,**fkwargs):\n",
" \"\"\"Returns polygons and rescaled spins, given original spin lattice S, distorted \n",
" according to function f(z)\"\"\"\n",
"\n",
" # L is length of corner of lattice to be analyzed\n",
" # Use small L to debug your code and also to explore by zooming, \n",
" # before settling on final plots\n",
" if L is None:\n",
"# L = len(S)\n",
" L = 512\n",
" # Unpack L x L lower-right-hand corner of 2D spin array into 1D\n",
" spins = S[:L,-L:].flatten()\n",
"\n",
" # Map lattice to complex plane (0,1] x (0,1]j.\n",
" latticeCenters = (1./L)*array([(m+0.5)+1.j*(n+0.5) for n in range(L) for m in range(L)])\n",
"\n",
" # One pixel in the original complex lattice\n",
" pixelExtent = (1./L)*array([-0.5-0.5j,-0.5+0.5j,0.5+0.5j,0.5-0.5j])\n",
"\n",
" # All the squares\n",
" undeformedSquares = array([center+pixelExtent for center in latticeCenters])\n",
" \n",
" # Distort quadrilaterals under f(z)\n",
" # fkwargs passes optional constants like \"b=0.25\" to your function f\n",
" dQ = f(undeformedSquares,**fkwargs)\n",
" \n",
" # Find areas of quadrilaterals using the 'shoelace formula' for the area of a polygon.\n",
" areas = -0.5*sum(dQ*np.roll(dQ.conj(),1,axis=1),axis=1).imag\n",
" \n",
" # Rescale spins by corresponding areas\n",
" rescaledSpins = areas**(-1/16.)*spins\n",
" \n",
" # Drop spins whose quadrilaterals go to infinity, or which have negative areas \n",
" # (which have a point in the interior at infinity, and should be 'filled' on the outside)\n",
" dQ = dQ[isfinite(areas)&(areas>0.)] \n",
" rescaledSpins = rescaledSpins[isfinite(areas)&(areas>0.)]\n",
"\n",
" # Return to real coordinates\n",
" quads = transpose(array([dQ.real,dQ.imag]),axes=(1,2,0))\n",
" \n",
" return quads, rescaledSpins"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"GraphQuads plots the resulting magnetization pattern, given the function $f(z)$ and the length. Note that $\\log(z)$, for example, is singular at the grid point (0,0); GraphQuads and Transform will send RuntimeWarning messages about such events. (We drop those infinite-area polygons before we graph.)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def GraphQuads(S,f,L=None,**fkwargs):\n",
" quads, rescaledSpins = Transform(S,f,L,**fkwargs)\n",
" fig, ax = plt.subplots()\n",
" coll = PolyCollection(quads, array=rescaledSpins, cmap='gray', \\\n",
" edgecolors='none',antialiased=False)\n",
" ax.add_collection(coll)\n",
" ax.axis('scaled')\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For example, try $f(z) = \\log(z)$ with a small $L$, to explore. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"GraphQuads(S,log,64)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Define your own function, like\n",
"```\n",
"def fexp(z): return exp(2.j * pi * z)\n",
"```\n",
"or \n",
"```\n",
"def fTwoProteins(z):\n",
" u=-0.02\n",
" w = exp(2*pi*z)\n",
" return (w+u)/(u*w+1)\n",
"```\n",
"Zoom in to a square region. Find an interesting region; one with squares that vary in orientation, and in length by at least a factor of two. (The region x=(-0.5,0.),y=(-0,0.5) for log above is a bit too small.) \n",
"\n",
"Run now for large $L$, and save the whole plot and the zoomed plot to print and hand in. \n",
"\n",
"(c) Discuss your zoomed plot critically - does it appear that the Ising correlations and visual structures have been faithfully retained by your analytic transformation?\n",
"\n",
"(d) Load an Ising model equilibrated at T=100, and at T=3, and a coarsened Ising model quenched to low temperatures with a coarsening length of 4-5 pixels. Distort and zoom with each. Are the correlations and visual structures retained away from the critical point? (T100.png, T3.png, and Tcoarsening.png are available.)\n",
"\n",
"(e) Invent a non-analytic function, and use it to distort your Ising model. (The author tried two methods: inventing functions $u(x,y)$ and $v(x,y)$, and using the real part of $f(z)$ and the imaginary part of $g(z)$.) Find an example that makes for an interesting picture. As above, examine the latter critically - does it appear that the Ising correlations and visual structures have been faithfully retained by your analytic transformation? Describe the distortions you see. (Are the pixels still approximately square?)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
}