123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- <!doctype html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
- <meta name="generator" content="pdoc 0.5.2" />
- <title>run_simulated_annealing API documentation</title>
- <meta name="description" content="Simulated annealing algorithm used to match the simulation of the
- Antennae to the observations by comparing binarized images." />
- <link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'>
- <link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'>
- <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet">
- <style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}#index .two-column{column-count:2}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.name small{font-weight:normal}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase;cursor:pointer}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
- <style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
- <style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
- </head>
- <body>
- <main>
- <article id="content">
- <header>
- <h1 class="title"><code>run_simulated_annealing</code> module</h1>
- </header>
- <section id="section-intro">
- <p>Simulated annealing algorithm used to match the simulation of the
- Antennae to the observations by comparing binarized images.</p>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">"""Simulated annealing algorithm used to match the simulation of the
- Antennae to the observations by comparing binarized images."""
- import numpy as np
- import scipy
- import pickle
- from scipy import ndimage
- from fast_histogram import histogram2d
- from scipy.signal import convolve2d
- from numba import jit
- from matplotlib.image import imread
- import datetime
- from simulation import Simulation, Galaxy
- T = .25 # Initial tempreature
- STEPS = 0
- DECAY = .998 # Exponential decay factor
- def simToBitmap(sim, theta, phi, view, scale, x, y, galaxy):
- """Obtain a bitmap of one galaxy as viewed from a given direction.
- The binning has been chosen so that the scale and the offset (x,y)
- are expected to be approximately 1 and (0, 0) respectively.
- Parameters:
- sim (Simulation): Simulation to project.
- theta (float): polar angle of viewing direction.
- phi (float): azimuthal angle of viewing direction.
- view (float): rotation angle along viewing direction.
- scale (float): scaling factor.
- x (float): x offset
- y (float): y offset
- galaxy (int): galaxy to plot. Either 0, or 1. They are assumed
- to have the same number of particles.
- """
- # Obtain components in new x',y' plane
- r_vec = (sim.project(theta, phi, view) - [[x+.12,y+1.3]]) * scale
- # Select a single galaxy. We match them separately in the algorithm.
- if galaxy==0: r_vec = r_vec[2:len(r_vec)//2-1] #omit central masses
- if galaxy==1: r_vec = r_vec[len(r_vec)//2-1:]
- # Use a fast histogram, much faster than numpy ()
- H = histogram2d(r_vec[:,0], r_vec[:,1],
- range=[[-5,5], [-5,5]], bins=(50, 50))
- im = np.zeros((50, 50))
- H = convolve2d(H, np.ones((2,2)), mode='same') # Smooth the image
- im[H>=1] = 1 # Binary the image
-
- return im
- @jit(nopython=True) # Numba annotation
- def bitmapScoreAlgo(im1, im2):
- """Computes the f1 score betwen two binarized images
- Parameters
- im1 (arr): nxm ground truth image
- im2 (arr): nxm candidate image
-
- Returns
- f1 score
- """
- TP = np.sum((im1==1.0) & (im2==1.0))
- TN = np.sum((im1==0.0) & (im2==0.0))
- FP = np.sum((im1==0.0) & (im2==1.0))
- FN = np.sum((im1==1.0) & (im2==0.0))
- if TP==0: return 0
- precision = TP/(TP+FP)
- recall = TP/(TP+FN)
- return 2*precision*recall / (precision + recall)
- # The matching algorithm attempts to improve the match by shifting the
- # image by one pixel in each direction. If none improve the score the
- # f1-score of said local maximum is retuned. To make this highly efficient
- # (as this is run millions of time) we explicitely write functions to
- # shift an image by 1 pixel in each direction, as these can then be compiled
- # using Numba (jit annotation) to low-level code.
- # Performance is crucial here and must sadly be prioritized over conciseness
- @jit(nopython=True)
- def shiftBottom(im, im2):
- """Shifts an image by one pixel downwards.
-
- Parameters:
- im (arr): the nxm image to shift by one pixel.
- im2 (arr): an nxm array where the new image will be placed.
- Returns:
- A reference to im2
- """
- im2[1:] = im[:-1]
- im2[0] = 0
- return im2
- @jit(nopython=True)
- def shiftTop(im, im2):
- """Shifts an image by one pixel upwards."""
- im2[:-1] = im[1:]
- im2[-1] = 0
- return im2
- @jit(nopython=True)
- def shiftLeft(im, im2):
- """Shifts an image by one pixel to the left."""
- im2[:,1:] = im[:,:-1]
- im2[:,0] = 0
- return im2
- @jit(nopython=True)
- def shiftRight(im, im2):
- """Shifts an image by one pixel to the right."""
- im2[:,:-1] = im[:,1:]
- im2[:,-1] = 0
- return im2
- @jit
- def bitmapScore(im1, im2, _prev=None, _bestscore=None, _zeros=None):
- """Computes the bitmap score between two images. This is the f1-score
- but we allow the algorithm to attempt to improve the score by
- shifting the images. The algorithm is implemented recursively.
- Parameters:
- im1 (array): The ground truth nxm image.
- im2 (array): The candidate nxm imgae.
- _prev: Used internally for recursion.
- _bestscore: Used internally for recursion.
- _zeros: Used internally for recursion.
-
- Returns:
- f1-score for the two images.
- """
- # When the function is called externally, initialize an array of zeros
- # and compute the score for no shifting. The zeros array is used for
- # performance to only create a new array once.
- if _bestscore is None:
- _bestscore = bitmapScoreAlgo(im1, im2)
- _zeros = np.zeros_like(im2)
- # Attempt to improve the score by shifting the image in a direction
- # Keeping track of _prev allows to not 'go back' and undo and attempt
- # to undo a shift needlessly.
- if _prev is not 0: # try left
- shifted = shiftLeft(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=1, _bestscore=score, _zeros=_zeros)
- if _prev is not 1: # try right
- shifted = shiftRight(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=0, _bestscore=score, _zeros=_zeros)
- if _prev is not 2: # try top
- shifted = shiftTop(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=3, _bestscore=score, _zeros=_zeros)
- if _prev is not 3: # try bottom
- shifted = shiftBottom(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=2, _bestscore=score, _zeros=_zeros)
- # Return _bestscore if shifting did not improve (local maximum).
- return _bestscore
- def attemptSimulation(theta1, phi1, theta2, phi2, rmin=1, e=.5, R=2,
- disk1=.75, disk2=.65, mu=1, plot=False, steps=2000):
- """Runs a simulation with the given parameters and compares it
- to observations of the antennae to return a score out of 2.
- Parameters:
- theta1 (float): polar angle for the spin of the first galaxy.
- phi1 (float): azimuthal angle for the spin of the first galaxy.
- theta2 (float): polar angle for the spin of the second galaxy.
- phi2 (float): azimuthal angle for the spin of the second galaxy.
- rmin (float): closest distance of approach of orbit.
- e (float): eccentricity of the orbit.
- R (float): initial separation
- disk1 (float): radius of the uniform disk of the first galaxy
- disk2 (float): radius of the uniform disk of the first galaxy
- mu (float): ratio of masses of the two galaxies
- plot (float): if true the simulation will be saved to data/annealing/
- steps (float): number of times the f1 score will be computed, along
- random viewing directions per 100 timesteps.
- Returns:
- f1-score: score obtained by the simulation.
- """
- # Initialize the simulation
- sim = Simulation(dt=1E-3, soft=0.1, verbose=False, CONFIG=None, method='bruteForceNumbaOptimized')
- galaxy1 = Galaxy(orientation = (theta1, phi1), centralMass=2/(1+mu),
- sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},
- disk={'model':'uniform', 'particles':2000, 'l':disk1, 'totalMass':0},
- halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})
- galaxy2 = Galaxy(orientation = (theta2, phi2), centralMass=2*mu/(1+mu),
- sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},
- disk={'model':'uniform', 'particles':2000, 'l':disk2, 'totalMass':0},
- halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})
- sim.setOrbit(galaxy1, galaxy2, e=e, R0=R, rmin=rmin) # define the orbit
- # Run the simulation manually
- # Initialize the parameters consistently with the velocities
- sim.rprev_vec = sim.r_vec - sim.v_vec * sim.dt
- # Keep track of the best score
- bestScore = 0
- # and its corresponding binarized image and parameters
- bestImage, bestParams = 0, 0
- hasReachedPericenter = False
- # Run until tmax = 25
- for i in range(25001):
- sim.step()
- if i%100==0: # Every $\Delta t = 0.1$
- # Check if we are close to pericenter
- centers = sim.r_vec[sim.type[:,0] == 'center']
- if np.linalg.norm(centers[0] - centers[1]) < 1.3*rmin:
- hasReachedPericenter = True
- # Do not evaluate the f1-score until pericenter.
- if not hasReachedPericenter: continue
- # Check multiple (steps) viewing directions at random
- localBestScore = 0
- localBestImage, localBestParams = 0, 0
- for j in range(steps):
- # The viewing directions are isotropically distributed
- theta = np.arccos(np.random.uniform(-1, 1))
- phi = np.random.uniform(0, 2*np.pi)
- # Rotation along line of sight
- view = np.random.uniform(0, 2*np.pi)
- scale = np.random.uniform(0.6, 1.0)
- x = np.random.uniform(-1.0, 1.0) # Offsets
- y = np.random.uniform(-1.0, 1.0)
- # Get images for each galaxy and compute their score separately
- im1 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=0)
- im2 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=1)
- score = bitmapScore(GT1, im1) + bitmapScore(GT2, im2)
- if score > localBestScore:
- localBestScore = score
- localBestImage = [im1,im2]
- localBestParams = [i, theta, phi, view, scale, x, y]
- if bestScore < localBestScore:
- bestScore = localBestScore
- bestImage = localBestImage
- bestParams = localBestParams
- if plot:
- sim.save('annealing', type='2D')
-
- print('Best score for this attempt', bestScore)
- print('using viewing parameters', bestParams)
-
- return bestScore
- ################################################################################
- ################################################################################
- # Generate a (50, 50) bitmap for each galaxy
- # They are stored globally in GT1 and GT2 (Ground Truth)
- im = imread('literature/figs/g1c.tif')
- im = np.mean(im, axis=-1)
- im = scipy.misc.imresize(im, (50,50))
- GT1 = np.zeros((50, 50))
- GT1[im > 50] = 1
- im = imread('literature/figs/g2c.tif')
- im = np.mean(im, axis=-1)
- im = scipy.misc.imresize(im, (50,50))
- GT2 = np.zeros((50, 50))
- # Define the limits and relate scale of the variations in each parameter
- # In the same order as attemptSimulation
- # phi1, theta1, phi2, theta2, rmin (fixed), e, R (fixed), disk1, disk2
- LIMITS = np.array([[np.pi, 2 * np.pi], [-np.pi, np.pi],
- [0, np.pi], [-np.pi, np.pi],
- [1,1], [.5,1.0], [2.2,2.2], [.5,.8], [.5,.8]])
- VARIATIONS = np.array([.08, .15, .08, .15, 0, .01, 0, .01, .01])
- # Choose a random starting point and evaluate it
- bestparams = [np.random.uniform(*l) for l in LIMITS]
- log = []
- bestscore = attemptSimulation(*bestparams, steps=500)
- print('Starting with score', bestscore, 'with parameters', bestparams)
- log.append([bestscore, bestparams, True])
- for i in range(STEPS):
- T = T * DECAY #exponential decay
- # Perturb the parameters
- params = bestparams + np.random.normal(scale=VARIATIONS) * 2 * T / 0.04
- # Allow the angles from $-\pi$ to $\pi$ to wrap around
- for j in [1,3]:
- params[j] = np.mod(params[j] - LIMITS[j][0], LIMITS[j][1] - LIMITS[j][0])
- params[j] += LIMITS[j][0]
- # Clip parameters outside their allowed range
- params = np.clip(params, LIMITS[:, 0], LIMITS[:, 1])
- # Evaluate the score for this attempt, use more steps as i progresses
- # so as to reduce the noise in the evaluation of the score
- score = attemptSimulation(*params, steps=500 + i)
- # Perform simulated annealing with a typical exponential rule
- if score > bestscore or np.exp(-(bestscore-score)/T) > np.random.rand():
- # Flip to this new point
- print('NEW BEST ____', i, T, score, params)
- bestscore = score
- bestparams = params
- log.append([score, params, True])
- else: # Remain in the old point
- log.append([score, params, False])
- # Save the progress for future plotting
- pickle.dump(log, open('data/logs/logb.pickle', "wb" ) ) </code></pre>
- </details>
- </section>
- <section>
- </section>
- <section>
- </section>
- <section>
- <h2 class="section-title" id="header-functions">Functions</h2>
- <dl>
- <dt id="run_simulated_annealing.attemptSimulation"><code class="name flex">
- <span>def <span class="ident">attemptSimulation</span></span>(<span>theta1, phi1, theta2, phi2, rmin=1, e=0.5, R=2, disk1=0.75, disk2=0.65, mu=1, plot=False, steps=2000)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Runs a simulation with the given parameters and compares it
- to observations of the antennae to return a score out of 2.</p>
- <h2 id="parameters">Parameters</h2>
- <dl>
- <dt><strong><code>theta1</code></strong> : <code>float</code></dt>
- <dd>polar angle for the spin of the first galaxy.</dd>
- <dt><strong><code>phi1</code></strong> : <code>float</code></dt>
- <dd>azimuthal angle for the spin of the first galaxy.</dd>
- <dt><strong><code>theta2</code></strong> : <code>float</code></dt>
- <dd>polar angle for the spin of the second galaxy.</dd>
- <dt><strong><code>phi2</code></strong> : <code>float</code></dt>
- <dd>azimuthal angle for the spin of the second galaxy.</dd>
- <dt><strong><code>rmin</code></strong> : <code>float</code></dt>
- <dd>closest distance of approach of orbit.</dd>
- <dt><strong><code>e</code></strong> : <code>float</code></dt>
- <dd>eccentricity of the orbit.</dd>
- <dt><strong><code>R</code></strong> : <code>float</code></dt>
- <dd>initial separation</dd>
- <dt><strong><code>disk1</code></strong> : <code>float</code></dt>
- <dd>radius of the uniform disk of the first galaxy</dd>
- <dt><strong><code>disk2</code></strong> : <code>float</code></dt>
- <dd>radius of the uniform disk of the first galaxy</dd>
- <dt><strong><code>mu</code></strong> : <code>float</code></dt>
- <dd>ratio of masses of the two galaxies</dd>
- <dt><strong><code>plot</code></strong> : <code>float</code></dt>
- <dd>if true the simulation will be saved to data/annealing/</dd>
- <dt><strong><code>steps</code></strong> : <code>float</code></dt>
- <dd>number of times the f1 score will be computed, along
- random viewing directions per 100 timesteps.</dd>
- </dl>
- <h2 id="returns">Returns</h2>
- <p>f1-score: score obtained by the simulation.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">def attemptSimulation(theta1, phi1, theta2, phi2, rmin=1, e=.5, R=2,
- disk1=.75, disk2=.65, mu=1, plot=False, steps=2000):
- """Runs a simulation with the given parameters and compares it
- to observations of the antennae to return a score out of 2.
- Parameters:
- theta1 (float): polar angle for the spin of the first galaxy.
- phi1 (float): azimuthal angle for the spin of the first galaxy.
- theta2 (float): polar angle for the spin of the second galaxy.
- phi2 (float): azimuthal angle for the spin of the second galaxy.
- rmin (float): closest distance of approach of orbit.
- e (float): eccentricity of the orbit.
- R (float): initial separation
- disk1 (float): radius of the uniform disk of the first galaxy
- disk2 (float): radius of the uniform disk of the first galaxy
- mu (float): ratio of masses of the two galaxies
- plot (float): if true the simulation will be saved to data/annealing/
- steps (float): number of times the f1 score will be computed, along
- random viewing directions per 100 timesteps.
- Returns:
- f1-score: score obtained by the simulation.
- """
- # Initialize the simulation
- sim = Simulation(dt=1E-3, soft=0.1, verbose=False, CONFIG=None, method='bruteForceNumbaOptimized')
- galaxy1 = Galaxy(orientation = (theta1, phi1), centralMass=2/(1+mu),
- sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},
- disk={'model':'uniform', 'particles':2000, 'l':disk1, 'totalMass':0},
- halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})
- galaxy2 = Galaxy(orientation = (theta2, phi2), centralMass=2*mu/(1+mu),
- sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},
- disk={'model':'uniform', 'particles':2000, 'l':disk2, 'totalMass':0},
- halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})
- sim.setOrbit(galaxy1, galaxy2, e=e, R0=R, rmin=rmin) # define the orbit
- # Run the simulation manually
- # Initialize the parameters consistently with the velocities
- sim.rprev_vec = sim.r_vec - sim.v_vec * sim.dt
- # Keep track of the best score
- bestScore = 0
- # and its corresponding binarized image and parameters
- bestImage, bestParams = 0, 0
- hasReachedPericenter = False
- # Run until tmax = 25
- for i in range(25001):
- sim.step()
- if i%100==0: # Every $\Delta t = 0.1$
- # Check if we are close to pericenter
- centers = sim.r_vec[sim.type[:,0] == 'center']
- if np.linalg.norm(centers[0] - centers[1]) < 1.3*rmin:
- hasReachedPericenter = True
- # Do not evaluate the f1-score until pericenter.
- if not hasReachedPericenter: continue
- # Check multiple (steps) viewing directions at random
- localBestScore = 0
- localBestImage, localBestParams = 0, 0
- for j in range(steps):
- # The viewing directions are isotropically distributed
- theta = np.arccos(np.random.uniform(-1, 1))
- phi = np.random.uniform(0, 2*np.pi)
- # Rotation along line of sight
- view = np.random.uniform(0, 2*np.pi)
- scale = np.random.uniform(0.6, 1.0)
- x = np.random.uniform(-1.0, 1.0) # Offsets
- y = np.random.uniform(-1.0, 1.0)
- # Get images for each galaxy and compute their score separately
- im1 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=0)
- im2 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=1)
- score = bitmapScore(GT1, im1) + bitmapScore(GT2, im2)
- if score > localBestScore:
- localBestScore = score
- localBestImage = [im1,im2]
- localBestParams = [i, theta, phi, view, scale, x, y]
- if bestScore < localBestScore:
- bestScore = localBestScore
- bestImage = localBestImage
- bestParams = localBestParams
- if plot:
- sim.save('annealing', type='2D')
-
- print('Best score for this attempt', bestScore)
- print('using viewing parameters', bestParams)
-
- return bestScore</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.bitmapScore"><code class="name flex">
- <span>def <span class="ident">bitmapScore</span></span>(<span>im1, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Computes the bitmap score between two images. This is the f1-score
- but we allow the algorithm to attempt to improve the score by
- shifting the images. The algorithm is implemented recursively.</p>
- <h2 id="parameters">Parameters</h2>
- <dl>
- <dt><strong><code>im1</code></strong> : <code>array</code></dt>
- <dd>The ground truth nxm image.</dd>
- <dt><strong><code>im2</code></strong> : <code>array</code></dt>
- <dd>The candidate nxm imgae.</dd>
- <dt><strong><code>_prev</code></strong></dt>
- <dd>Used internally for recursion.</dd>
- <dt><strong><code>_bestscore</code></strong></dt>
- <dd>Used internally for recursion.</dd>
- <dt><strong><code>_zeros</code></strong></dt>
- <dd>Used internally for recursion.</dd>
- </dl>
- <h2 id="returns">Returns</h2>
- <p>f1-score for the two images.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit
- def bitmapScore(im1, im2, _prev=None, _bestscore=None, _zeros=None):
- """Computes the bitmap score between two images. This is the f1-score
- but we allow the algorithm to attempt to improve the score by
- shifting the images. The algorithm is implemented recursively.
- Parameters:
- im1 (array): The ground truth nxm image.
- im2 (array): The candidate nxm imgae.
- _prev: Used internally for recursion.
- _bestscore: Used internally for recursion.
- _zeros: Used internally for recursion.
-
- Returns:
- f1-score for the two images.
- """
- # When the function is called externally, initialize an array of zeros
- # and compute the score for no shifting. The zeros array is used for
- # performance to only create a new array once.
- if _bestscore is None:
- _bestscore = bitmapScoreAlgo(im1, im2)
- _zeros = np.zeros_like(im2)
- # Attempt to improve the score by shifting the image in a direction
- # Keeping track of _prev allows to not 'go back' and undo and attempt
- # to undo a shift needlessly.
- if _prev is not 0: # try left
- shifted = shiftLeft(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=1, _bestscore=score, _zeros=_zeros)
- if _prev is not 1: # try right
- shifted = shiftRight(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=0, _bestscore=score, _zeros=_zeros)
- if _prev is not 2: # try top
- shifted = shiftTop(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=3, _bestscore=score, _zeros=_zeros)
- if _prev is not 3: # try bottom
- shifted = shiftBottom(im2, _zeros)
- score = bitmapScoreAlgo(im1, shifted)
- if score > _bestscore: return bitmapScore(im1, shifted,
- _prev=2, _bestscore=score, _zeros=_zeros)
- # Return _bestscore if shifting did not improve (local maximum).
- return _bestscore</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.bitmapScoreAlgo"><code class="name flex">
- <span>def <span class="ident">bitmapScoreAlgo</span></span>(<span>im1, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Computes the f1 score betwen two binarized images</p>
- <dl>
- <dt><strong><code>Parameters</code></strong></dt>
- <dd>im1 (arr): nxm ground truth image
- im2 (arr): nxm candidate image</dd>
- <dt><strong><code>Returns</code></strong></dt>
- <dd>f1 score</dd>
- </dl></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit(nopython=True) # Numba annotation
- def bitmapScoreAlgo(im1, im2):
- """Computes the f1 score betwen two binarized images
- Parameters
- im1 (arr): nxm ground truth image
- im2 (arr): nxm candidate image
-
- Returns
- f1 score
- """
- TP = np.sum((im1==1.0) & (im2==1.0))
- TN = np.sum((im1==0.0) & (im2==0.0))
- FP = np.sum((im1==0.0) & (im2==1.0))
- FN = np.sum((im1==1.0) & (im2==0.0))
- if TP==0: return 0
- precision = TP/(TP+FP)
- recall = TP/(TP+FN)
- return 2*precision*recall / (precision + recall)</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.shiftBottom"><code class="name flex">
- <span>def <span class="ident">shiftBottom</span></span>(<span>im, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Shifts an image by one pixel downwards.</p>
- <h2 id="parameters">Parameters</h2>
- <dl>
- <dt><strong><code>im</code></strong> : <code>arr</code></dt>
- <dd>the nxm image to shift by one pixel.</dd>
- <dt><strong><code>im2</code></strong> : <code>arr</code></dt>
- <dd>an nxm array where the new image will be placed.</dd>
- </dl>
- <h2 id="returns">Returns</h2>
- <p>A reference to im2</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit(nopython=True)
- def shiftBottom(im, im2):
- """Shifts an image by one pixel downwards.
-
- Parameters:
- im (arr): the nxm image to shift by one pixel.
- im2 (arr): an nxm array where the new image will be placed.
- Returns:
- A reference to im2
- """
- im2[1:] = im[:-1]
- im2[0] = 0
- return im2</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.shiftLeft"><code class="name flex">
- <span>def <span class="ident">shiftLeft</span></span>(<span>im, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Shifts an image by one pixel to the left.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit(nopython=True)
- def shiftLeft(im, im2):
- """Shifts an image by one pixel to the left."""
- im2[:,1:] = im[:,:-1]
- im2[:,0] = 0
- return im2</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.shiftRight"><code class="name flex">
- <span>def <span class="ident">shiftRight</span></span>(<span>im, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Shifts an image by one pixel to the right.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit(nopython=True)
- def shiftRight(im, im2):
- """Shifts an image by one pixel to the right."""
- im2[:,:-1] = im[:,1:]
- im2[:,-1] = 0
- return im2</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.shiftTop"><code class="name flex">
- <span>def <span class="ident">shiftTop</span></span>(<span>im, im2)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Shifts an image by one pixel upwards.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">@jit(nopython=True)
- def shiftTop(im, im2):
- """Shifts an image by one pixel upwards."""
- im2[:-1] = im[1:]
- im2[-1] = 0
- return im2</code></pre>
- </details>
- </dd>
- <dt id="run_simulated_annealing.simToBitmap"><code class="name flex">
- <span>def <span class="ident">simToBitmap</span></span>(<span>sim, theta, phi, view, scale, x, y, galaxy)</span>
- </code></dt>
- <dd>
- <section class="desc"><p>Obtain a bitmap of one galaxy as viewed from a given direction.
- The binning has been chosen so that the scale and the offset (x,y)
- are expected to be approximately 1 and (0, 0) respectively.</p>
- <p>Parameters:
- sim (Simulation): Simulation to project.
- theta (float): polar angle of viewing direction.
- phi (float): azimuthal angle of viewing direction.
- view (float): rotation angle along viewing direction.
- scale (float): scaling factor.
- x (float): x offset
- y (float): y offset
- galaxy (int): galaxy to plot. Either 0, or 1. They are assumed
- <br>
- to have the same number of particles.</p></section>
- <details class="source">
- <summary>Source code</summary>
- <pre><code class="python">def simToBitmap(sim, theta, phi, view, scale, x, y, galaxy):
- """Obtain a bitmap of one galaxy as viewed from a given direction.
- The binning has been chosen so that the scale and the offset (x,y)
- are expected to be approximately 1 and (0, 0) respectively.
- Parameters:
- sim (Simulation): Simulation to project.
- theta (float): polar angle of viewing direction.
- phi (float): azimuthal angle of viewing direction.
- view (float): rotation angle along viewing direction.
- scale (float): scaling factor.
- x (float): x offset
- y (float): y offset
- galaxy (int): galaxy to plot. Either 0, or 1. They are assumed
- to have the same number of particles.
- """
- # Obtain components in new x',y' plane
- r_vec = (sim.project(theta, phi, view) - [[x+.12,y+1.3]]) * scale
- # Select a single galaxy. We match them separately in the algorithm.
- if galaxy==0: r_vec = r_vec[2:len(r_vec)//2-1] #omit central masses
- if galaxy==1: r_vec = r_vec[len(r_vec)//2-1:]
- # Use a fast histogram, much faster than numpy ()
- H = histogram2d(r_vec[:,0], r_vec[:,1],
- range=[[-5,5], [-5,5]], bins=(50, 50))
- im = np.zeros((50, 50))
- H = convolve2d(H, np.ones((2,2)), mode='same') # Smooth the image
- im[H>=1] = 1 # Binary the image
-
- return im</code></pre>
- </details>
- </dd>
- </dl>
- </section>
- <section>
- </section>
- </article>
- <nav id="sidebar">
- <h1>Index</h1>
- <div class="toc">
- <ul></ul>
- </div>
- <ul id="index">
- <li><h3><a href="#header-functions">Functions</a></h3>
- <ul class="two-column">
- <li><code><a title="run_simulated_annealing.attemptSimulation" href="#run_simulated_annealing.attemptSimulation">attemptSimulation</a></code></li>
- <li><code><a title="run_simulated_annealing.bitmapScore" href="#run_simulated_annealing.bitmapScore">bitmapScore</a></code></li>
- <li><code><a title="run_simulated_annealing.bitmapScoreAlgo" href="#run_simulated_annealing.bitmapScoreAlgo">bitmapScoreAlgo</a></code></li>
- <li><code><a title="run_simulated_annealing.shiftBottom" href="#run_simulated_annealing.shiftBottom">shiftBottom</a></code></li>
- <li><code><a title="run_simulated_annealing.shiftLeft" href="#run_simulated_annealing.shiftLeft">shiftLeft</a></code></li>
- <li><code><a title="run_simulated_annealing.shiftRight" href="#run_simulated_annealing.shiftRight">shiftRight</a></code></li>
- <li><code><a title="run_simulated_annealing.shiftTop" href="#run_simulated_annealing.shiftTop">shiftTop</a></code></li>
- <li><code><a title="run_simulated_annealing.simToBitmap" href="#run_simulated_annealing.simToBitmap">simToBitmap</a></code></li>
- </ul>
- </li>
- </ul>
- </nav>
- </main>
- <footer id="footer">
- <p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.5.2</a>.</p>
- </footer>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
- <script>hljs.initHighlightingOnLoad()</script>
- </body>
- </html>
|