I have recently been working in Matlab, so there are questions. Look at the code, please. If amountOfOptions = 2, I should get OptionPrice = [x x] . Vector Size OptionPrice(1x2). It seems he receives the output it produces but it gives the last result of the assignment.
function [startStockPrice, strike, barrier, riskFreeRate, timeToExpiry, volatility, CallOrPut, UpOrDown, OptionPrice, time] = OutBarrierOption(amountOfOptions)
%%%%%%%%%% Option parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
startStockPrice = 70 + 40 * rand(1,amountOfOptions); % Stock price starts at $100.
strike = 120 + 30 * rand(1,amountOfOptions); % Strike price for the option ($).
barrier = 300 + 300 * rand(1,amountOfOptions); % Barrier price for the option ($).
riskFreeRate = 0.05 + 0.1 * rand(1,amountOfOptions); % 0.5 annual percent.
timeToExpiry = fix(1 + 3 * rand(1, amountOfOptions)); % Lifetime of the option in years. (Time to maturity)
volatility = 0.35 + 0.3 * rand(1,amountOfOptions); % 20% annual volatility. % UpOrDown - 'U' or 'D'
M = 1e4; % Number of Monte-Carlo trials
N = 100; % Number of observations
OptionPrice = 0;
tic;
for k = 1:amountOfOptions
dt(k)=timeToExpiry(k)/N;
for i=1:M
S(1) = startStockPrice(k)*exp((riskFreeRate(k)-(volatility(k)*volatility(k)/2)*dt(k)) + volatility(k)*sqrt(dt(k))*randn);
final_vals=[S(1)];
for j=1:N-1;
S(j + 1) = S(j)* exp((riskFreeRate(k) - 0.5* volatility(k)*volatility(k))*dt(k) + volatility(k)* sqrt(dt(k))* randn);
final_vals=S(1:j+1);
end
end
if max(final_vals) <= barrier(k)
option_values=max(final_vals - strike(k),0); % Evaluate the Call option options
present_vals = exp(-riskFreeRate(k)*timeToExpiry(k))*option_values; % Discount under r-n assumption
OptionPrice = mean(present_vals); % Take the average
else
% Hit the barrier, so the option is withdrawn.
present_vals = 0;
OptionPrice = mean(present_vals);
end
end
time = toc;
end
Related
Using Matlab I am trying to construct a neural network that can classify handwritten digits that are 30x30 pixels. I use backpropagation to find the correct weights and biases. The network starts with 900 inputs, then has 2 hidden layers with 16 neurons and it ends with 10 outputs. Each output neuron has a value between 0 and 1 that represents the belief that the input should be classified as a certain digit. The problem is that after training, the output becomes almost indifferent to the input and it goes towards a uniform belief of 0.1 for each output.
My approach is to take each image with 30x30 pixels and reshape it to be vectors of 900x1 (note that 'Images_vector' is already in the vector format when it is loaded). The weights and biases are initiated with random values between 0 and 1. I am using stochastic gradiƫnt descent to update the weights and biases with 10 randomly selected samples per batch. The equations are as described by Nielsen.
The script is as follows.
%% Inputs
numberofbatches = 1000;
batchsize = 10;
alpha = 1;
cutoff = 8000;
layers = [900 16 16 10];
%% Initialization
rng(0);
load('Images_vector')
Images_vector = reshape(Images_vector', 1, 10000);
labels = [ones(1,1000) 2*ones(1,1000) 3*ones(1,1000) 4*ones(1,1000) 5*ones(1,1000) 6*ones(1,1000) 7*ones(1,1000) 8*ones(1,1000) 9*ones(1,1000) 10*ones(1,1000)];
newOrder = randperm(10000);
Images_vector = Images_vector(newOrder);
labels = labels(newOrder);
images_training = Images_vector(1:cutoff);
images_testing = Images_vector(cutoff + 1:10000);
w = cell(1,length(layers) - 1);
b = cell(1,length(layers));
dCdw = cell(1,length(layers) - 1);
dCdb = cell(1,length(layers));
for i = 1:length(layers) - 1
w{i} = rand(layers(i+1),layers(i));
b{i+1} = rand(layers(i+1),1);
end
%% Learning process
batches = randi([1 cutoff - batchsize],1,numberofbatches);
cost = zeros(numberofbatches,1);
c = 1;
for batch = batches
for i = 1:length(layers) - 1
dCdw{i} = zeros(layers(i+1),layers(i));
dCdb{i+1} = zeros(layers(i+1),1);
end
for n = batch:batch+batchsize
y = zeros(10,1);
disp(labels(n))
y(labels(n)) = 1;
% Network
a{1} = images_training{n};
z{2} = w{1} * a{1} + b{2};
a{2} = sigmoid(0, z{2});
z{3} = w{2} * a{2} + b{3};
a{3} = sigmoid(0, z{3});
z{4} = w{3} * a{3} + b{4};
a{4} = sigmoid(0, z{4});
% Cost
cost(c) = sum((a{4} - y).^2) / 2;
% Gradient
d{4} = (a{4} - y) .* sigmoid(1, z{4});
d{3} = (w{3}' * d{4}) .* sigmoid(1, z{3});
d{2} = (w{2}' * d{3}) .* sigmoid(1, z{2});
dCdb{4} = dCdb{4} + d{4} / 10;
dCdb{3} = dCdb{3} + d{3} / 10;
dCdb{2} = dCdb{2} + d{2} / 10;
dCdw{3} = dCdw{3} + (a{3} * d{4}')' / 10;
dCdw{2} = dCdw{2} + (a{2} * d{3}')' / 10;
dCdw{1} = dCdw{1} + (a{1} * d{2}')' / 10;
c = c + 1;
end
% Adjustment
b{4} = b{4} - dCdb{4} * alpha;
b{3} = b{3} - dCdb{3} * alpha;
b{2} = b{2} - dCdb{2} * alpha;
w{3} = w{3} - dCdw{3} * alpha;
w{2} = w{2} - dCdw{2} * alpha;
w{1} = w{1} - dCdw{1} * alpha;
end
figure
plot(cost)
ylabel 'Cost'
xlabel 'Batches trained on'
With the sigmoid function being the following.
function y = sigmoid(derivative, x)
if derivative == 0
y = 1 ./ (1 + exp(-x));
else
y = sigmoid(0, x) .* (1 - sigmoid(0, x));
end
end
Other than this I have also tried to have 1 of each digit in each batch, but this gave the same result. Also I have tried varying the batch size, the number of batches and alpha, but with no success.
Does anyone know what I am doing wrong?
Correct me if I'm wrong: You have 10000 samples in you're data, which you divide into 1000 batches of 10 samples. Your training process consists of running over these 10000 samples once.
This might be too little, normally your training process consists of several epochs (one epoch = iterating over every sample once). You can try going over your batches multiple times.
Also for 900 inputs your network seems small. Try it with more neurons in the second layer. Hope it helps!
This is my code to make an integration in the rectangular method in the matlab
f=#(x) (x^(1/2))
a = 1
b = 10
% step size
h = 0.25
n = 0 % the counter
xn= a + (n * h)
%%
%Rectangle Method:
s=0
for i =0:n-1
s = s + f(xn)
end
Rectangle = h * s
the answer should be around 20, but i'm getting 29.5
what's the problem?
Two errors:
1) xn is not updated.
2) number of points n is set to zero.
There are other minor issues which I did not fix, e.g. right and left boundary points should both contribute to the sum with weight 1/2.
Quick fix:
f=#(x) (x^(1/2));
a = 1;
b = 10 ;
% step size
h = 0.25;
n = (b-a)/h; % the counter
%%
%Rectangle Method:
s=0;
for i =0:n-1
xn= a + (i * h);
s = s + f(xn);
end
Rectangle = h * s;
I'm a little bit of a FFT amateur (not trained in physics!) so I'm hoping someone around here has the expertise to give me a hint as to how I should go about doing this next step.
So I'm trying to generate the power spectra of time-space pattern via MATLAB from a visual stimulus as shown below. This is basically a plot of the movement trajectory of 10 dots (sine wave) within a time frame of 2 seconds with the distance labelled in degrees. (200x160 matrix - 10ms per frame on the y-axis and 0.1 degrees per frame on the x-axis).
I have done fft2, fftshift and a log transform on this stimulus and the resulting output is this.
First off, I am a little confused as to what this transformed image exactly represent? Is the centre displaying the high or low frequency data of the stimulus? And what do the x and y-axis now represents in this transformed plot?
I am actually hoping to convert the transformed image such that the y axis reflects temporal frequency between -30 to 30Hz and the x axis, spatial frequency between -30deg/cycle to 30deg/cycle. Perhaps someone could give me an idea of how I should go about doing this? (ie. is there a MATLAB function that is able to handle this sort of conversion?)
A sample of the codes to reproduce the plots are:-
function STotal = playINTOdotty (varargin)
deg_speed = 15.35; %dva/s
nr_of_dots = 10;
motion_type = 'const';
%Number of iterations
runs = 1;
stim_x = 160; %1 frame = 0.1d
stim_t = 200; %1 frame = 10ms
sin_cycle_dur = 80; %80;
max_speed = deg_speed/5.15; %This is very, very abstract. Basically plot out stim image and you'll see 5.15 is the best value.
sd = (sin_cycle_dur/2)/6;
mu = (sin_cycle_dur/2)/2;
sineTOTAL = 0;
counter = 1;
if nargin > 0
nr_of_dots = varargin{1};
end
if nargin > 1
deg_speed = varargin{2};
end
if nargin > 2
motion_type = varargin{3};
end
thisFTTOTAL = zeros(stim_t,stim_x);
stimTOTAL = zeros(stim_t,stim_x);
% initialize stim
stim = zeros(stim_t, stim_x) + .5;
%% define random dots for simulation/generation of position (before scaling to mean speed)
start_dot_pos = round(rand(1,nr_of_dots) .* stim_x);
dot_pos = zeros(stim_t, nr_of_dots);
dot_pos(1,:) = start_dot_pos;
%dot_pos(1,:) = 0;
dot_pos_sim = zeros(stim_t, nr_of_dots);
dot_pos_sim(1,:) = start_dot_pos;
%dot_pos_sim(1,:) = 0;
%% define random dots for neutral condition. dot_pos1 is for Sine and dot_pos2 for Constant
start_dot_pos1 = round(rand(1,nr_of_dots/2) .* stim_x);
dot_pos1 = zeros(stim_t, nr_of_dots/2);
dot_pos1(1,:) = start_dot_pos1;
dot_pos_sim1 = zeros(stim_t, nr_of_dots/2);
dot_pos_sim1(1,:) = start_dot_pos1;
start_dot_pos2 = round(rand(1,nr_of_dots/2) .* stim_x);
dot_pos2 = zeros(stim_t, nr_of_dots/2);
dot_pos2(1,:) = start_dot_pos2;
dot_pos_sim2 = zeros(stim_t, nr_of_dots/2);
dot_pos_sim2(1,:) = start_dot_pos2;
%% Mean of Constant speed
CTotal = max_speed*sin_cycle_dur;
Cmean = max_speed/2;
for q = 1:runs
%% Calculate position list to allow calculation of Gmean and Smean for scaling
for t = 2:stim_t
switch motion_type
case 'sine'
sine_speed = max_speed .* sin((t-1) / sin_cycle_dur *2*pi); %Sine formula
sineTOTAL = sineTOTAL + abs(sine_speed); %Add all sine generated values from Sine formula to get an overall total for mean calculation
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + max_speed .* sin((t-1) / sin_cycle_dur *2*pi); %Sine simulated matrix (before scaling)
case 'gaussian'
x = linspace((mu-4*sd),(mu+4*sd),sin_cycle_dur/2); %Gaussian formula part 1
y = 1/(2*pi*sd)*exp(-(x-mu).^2/(2*sd^2)); %Gaussian formula part 2
scalefactor = max_speed / (1/(2*pi*sd));
y = y*scalefactor;
y1 = y;
y2 = -y;
yTOTAL = [y,y2,y,y2,y,y2,y,y2,y,y2]; %y and y2 forms a full gaussian cycle. Two cycles here (80+80 frames) + 1 (Because stim_t is 161)
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + yTOTAL(:,t); %Gaussian simulated matrix (before scaling)
case 'const'
if t > 10 && t <= 30 %This is hard coding at its best. Need to change this some time. Basically definding dot positions based on the specified stim_t range.
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 50 && t <= 70
con_speed = -max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 90 && t <= 110
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 130 && t <= 150
con_speed = -max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 170 && t <= 190
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
else
con_speed = 0;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
end
case 'neutral' %Fusion of Sine + Const codes (similar to above) to generate neutral.
sine_speed = max_speed .* sin((t-1) / sin_cycle_dur *2*pi);
sineTOTAL = sineTOTAL + abs(sine_speed);
dot_pos_sim1(t,:) = dot_pos_sim1(t-1,:) + max_speed .* sin((t-1) / sin_cycle_dur *2*pi);
if t > 10 && t <= 30
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 50 && t <= 70
con_speed = -max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 90 && t <= 110
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 130 && t <= 150
con_speed = -max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 170 && t <= 190
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
else
con_speed = 0;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
end
end
end
yT = 0; %counter to sum up all of gaussian's speed to form a total from all frames
%% Calculate means
for y = 1:stim_t
switch motion_type
case 'sine'
Smean = sineTOTAL/stim_t;
case 'gaussian'
yT = sum(y1) + sum(abs(y2)) * 5; %5 cycles of y,y2
Gmean = yT/stim_t;
case 'neutral'
Smean = sineTOTAL/stim_t;
end
end
%% Scale positions to Cmean
for t = 1:stim_t
switch motion_type
case 'sine'
dot_pos(t,:) = dot_pos_sim(t,:) .* (Cmean/Smean);
case 'gaussian'
dot_pos(t,:) = dot_pos_sim(t,:) .* (Cmean/Gmean);
case 'const'
dot_pos(t,:) = dot_pos_sim(t,:);
case 'neutral'
dot_pos1(t,:) = dot_pos_sim1(t,:) .* (Cmean/Smean); %For Sine
dot_pos2(t,:) = dot_pos_sim2(t,:); %For Constant
end
end
%rounding
dot_pos = round(dot_pos);
dot_pos1 = round(dot_pos1);
dot_pos2 = round(dot_pos2);
%wrapping
dot_pos = mod(dot_pos,stim_x)+1;
dot_pos1 = mod(dot_pos1,stim_x)+1;
dot_pos2 = mod(dot_pos2,stim_x)+1;
%Dots given a value of 1 to the 0.5 stim matrix
for t = 1:stim_t
switch motion_type
case 'sine'
stim(t,dot_pos(t,:)) = 1;
case 'gaussian'
stim(t,dot_pos(t,:)) = 1;
case 'const'
stim(t,dot_pos(t,:)) = 1;
case 'neutral'
stim(t,dot_pos1(t,:)) = 1;
stim(t,dot_pos2(t,:)) = 1;
end
end
F = fft2(stim);
S = abs(F);
Fc = (fftshift(F));
S2 = abs(Fc); %If without log transform within iteration
%S2 = log(1+abs(Fc)); %Log transform within iteration
thisFTTOTAL = thisFTTOTAL + S2;
end
thisFTTOTAL = thisFTTOTAL/runs;
S2 = log(1+abs(thisFTTOTAL)); %If without log transform within iteration
%S2 = thisFTTOTAL; %If log transform within iteration
figure (1)
colormap('gray');
x=linspace(0,16,5);
y=linspace(0,2,10);
imagesc(x,y,stim);
xlabel('degrees');
ylabel('seconds');
xlim([0 16])
figure (2)
colormap('gray');
imagesc(S2);
**EDIT : Trying to recreate something along the lines of the following, where I only want the power-spectra plots within the range of -30 to 30 cycle/degree and -30 to 30Hz:-
Just to have an idea on how the fft works on a 2D space,you can have a look here and,more useful, here.
In other words, if you do an 2D fft of an image like this (please note that a row it is just a sin function, really easy to implement in matlab):
corresponds to:
Now, if you build a similar image but with a different period you will obtain a similar result but the point in the 2D fft will be closer. For example:
where the fft will be:
The orientation of the sinusoid correlates with the orientation of the peaks in the Fourier image relative to the central DC point. In this case a tilted sinusoidal pattern creates a tilted pair of peaks in the Fourier image:
You can try to combine the different image and observe the different pattern in the 2Dfft:
I strongly recommend you to have a look on the related link at the beginnig of the answer.
I have to simulate a simple queue in Matlab using Lindley's equation:
W_{n+1}^Q = max(0, W_n^Q + S_n - X_{n+1}
I think I have done so, with the following code, but I am trying to run it several times and cannot save the information correct. The variables I want to save at the end of running the simulation are only saving the information from the last attempt (here for m=3).. while I clearly would like to see this for all runs (m=1,2,3).
for m=1:3
l = 1.1; % try this value for lambda
N = 10000; % let 1000 people arrive
X = exprnd(l,[1,N]); % make 1000 exponential interarrivals
S = 2*rand(1,N); % uniform on [0,2]
w = zeros(1,N);
sum1 = zeros(1,m);
avg1 = zeros(1,m);
max1 = zeros(1,m);
for i=1:N
if i==1 % first customer doesn't have to wait
w(i) = 0;
else % following customers follow lindley's equation
w(i) = max(w(i-1) + S(i-1) - X(i), 0); % n-th customer's waiting time
count(i) = w(i) > 15; % count number of times greater than 15
end
end
max1(m) = max(w);
sum1(m) = sum(count); % sum number of times greater than 15
avg1(m) = sum1(m)/N; % divide by 1000 to get probability delay is greater than 15
end
You are initializing sum1, avg1 and max1 inside the for loop so in every iteration, these variables are set to zero (i.e. by initialization). This is the reason you loose your previous iteration value. To avoid this, initialize sum1, avg1 and max1 before you for loop. Refer below code for reference. HTH
sum1 = zeros(1,m);
avg1 = zeros(1,m);
max1 = zeros(1,m);
for m=1:3
l = 1.1; % try this value for lambda
N = 10000; % let 1000 people arrive
X = exprnd(l,[1,N]); % make 1000 exponential interarrivals
S = 2*rand(1,N); % uniform on [0,2]
w = zeros(1,N);
for i=1:N
if i==1 % first customer doesn't have to wait
w(i) = 0;
else % following customers follow lindley's equation
w(i) = max(w(i-1) + S(i-1) - X(i), 0); % n-th customer's waiting time
count(i) = w(i) > 15; % count number of times greater than 15
end
end
max1(m) = max(w);
sum1(m) = sum(count); % sum number of times greater than 15
avg1(m) = sum1(m)/N; % divide by 1000 to get probability delay is greater than 15
end
i tried to port this python implementation of a continuous RBM to Matlab:
http://imonad.com/rbm/restricted-boltzmann-machine/
I generated 2-dimensional trainingdata in the shape of a (noisy) circle and trained the rbm with 2 visible an 8 hidden layers. To test the implementation i fed uniformly distributed randomdata to the RBM and plotted the reconstructed data (Same procedure as used in the link above).
Now the confusing part: With trainingdata in the range of (0,1)x(0,1) i get very satisfying results, however with trainingdata in range (-0.5,-0.5)x(-0.5,-0.5) or (-1,0)x(-1,0) the RBM reconstructs only data in the very right top of the circle. I dont understand what causes this, is it just a bug in my implementation i dont see?
Some plots, the blue dots are the training data, the red dots are the reconstructions.
Here is my implementation of the RBM:
Training:
maxepoch = 300;
ksteps = 10;
sigma = 0.2; % cd standard deviation
learnW = 0.5; % learning rate W
learnA = 0.5; % learning rate A
nVis = 2; % number of visible units
nHid = 8; % number of hidden units
nDat = size(dat, 1);% number of training data points
cost = 0.00001; % cost
moment = 0.9; % momentum
W = randn(nVis+1, nHid+1) / 10; % weights
dW = randn(nVis+1, nHid+1) / 1000; % change of weights
sVis = zeros(1, nVis+1); % state of visible neurons
sVis(1, end) = 1.0; % bias
sVis0 = zeros(1, nVis+1); % initial state of visible neurons
sVis0(1, end) = 1.0; % bias
sHid = zeros(1, nHid+1); % state of hidden neurons
sHid(1, end) = 1.0; % bias
aVis = 0.1*ones(1, nVis+1);% A visible
aHid = ones(1, nHid+1); % A hidden
err = zeros(1, maxepoch);
e = zeros(1, maxepoch);
for epoch = 1:maxepoch
wPos = zeros(nVis+1, nHid+1);
wNeg = zeros(nVis+1, nHid+1);
aPos = zeros(1, nHid+1);
aNeg = zeros(1, nHid+1);
for point = 1:nDat
sVis(1:nVis) = dat(point, :);
sVis0(1:nVis) = sVis(1:nVis); % initial sVis
% positive phase
activHid;
wPos = wPos + sVis' * sHid;
aPos = aPos + sHid .* sHid;
% negative phase
activVis;
activHid;
for k = 1:ksteps
activVis;
activHid;
end
tmp = sVis' * sHid;
wNeg = wNeg + tmp;
aNeg = aNeg + sHid .* sHid;
delta = sVis0(1:nVis) - sVis(1:nVis);
err(epoch) = err(epoch) + sum(delta .* delta);
e(epoch) = e(epoch) - sum(sum(W' * tmp));
end
dW = dW*moment + learnW * ((wPos - wNeg) / numel(dat)) - cost * W;
W = W + dW;
aHid = aHid + learnA * (aPos - aNeg) / (numel(dat) * (aHid .* aHid));
% error
err(epoch) = err(epoch) / (nVis * numel(dat));
e(epoch) = e(epoch) / numel(dat);
disp(['epoch: ' num2str(epoch) ' err: ' num2str(err(epoch)) ...
' ksteps: ' num2str(ksteps)]);
end
save(['rbm_' filename '.mat'], 'W', 'err', 'aVis', 'aHid');
activHid.m:
sHid = (sVis * W) + randn(1, nHid+1);
sHid = sigFun(aHid .* sHid, datRange);
sHid(end) = 1.; % bias
activVis.m:
sVis = (W * sHid')' + randn(1, nVis+1);
sVis = sigFun(aVis .* sVis, datRange);
sVis(end) = 1.; % bias
sigFun.m:
function [sig] = sigFun(X, datRange)
a = ones(size(X)) * datRange(1);
b = ones(size(X)) * (datRange(2) - datRange(1));
c = ones(size(X)) + exp(-X);
sig = a + (b ./ c);
end
Reconstruction:
nSamples = 2000;
ksteps = 10;
nVis = 2;
nHid = 8;
sVis = zeros(1, nVis+1); % state of visible neurons
sVis(1, end) = 1.0; % bias
sHid = zeros(1, nHid+1); % state of hidden neurons
sHid(1, end) = 1.0; % bias
input = rand(nSamples, 2);
output = zeros(nSamples, 2);
for sample = 1:nSamples
sVis(1:nVis) = input(sample, :);
for k = 1:ksteps
activHid;
activVis;
end
output(sample, :) = sVis(1:nVis);
end
RBM's were originally designed to work only with binary data. But also work with data between 0 and 1. Its part of the algorithm. Further reading
As input is in the range of [0 1] for both x and y, this is why they stay in that ares. Changing the input to input = (rand(nSamples, 2)*2) -1; results in input sampled from a range of [-1 1] and therefore the red dots will be more spread out around the circle.