I recently conducted a customized ansible training to some awesome engineers. As part of our fun with Ansible, we created an ansible script to compute the value of . We did this by using the following formula:
The idea is to subdivide the interval into rectangles. The width of this rectangle multiplied by its height given by will give the area of the rectangle. Summing all the rectangles will give the value .
If the number of nodes is given by size
, then the interval will be divided into size
intervals. Each node is assigned an interval between .
Ansible has an array variable called play_hosts
that keeps track of the nodes that participate in the playbook run. The position of the node in this array will allow the node to determine which interval it will sum rectangles. So if a node’s position is given by position
, then that node will sum rectangles from
up to
The secret ingredient here is to be able to get the results of each node’s computation and allow the bastion host to sum the partial results. To do this, we define a variable called pi_var
that uses jinja2 templates. The code below achieves by appending each node’s value of slice_of_pi
to the variable o
.
pi_var: |
{%- set o=[] %}
{%- for i in play_hosts %}
{%- if o.append(hostvars[i].slice_of_pi) %}
{%- endif %}
{%- endfor %}
{{ o }}
Each node will then compute according to the interval it will operate on:
echo ""|awk '{
pi=0;
size= {{ play_hosts|length }};
N=int({{ num_intervals }}/size)*size;
width=1/N;
x=1/size*{{ position }};
for(i=0;i<N/size;i++){
x=x+width;
pi=pi+1/(1+x*x)
};
print 4*pi/N
}'
Finally, here is the ansible playbook:
---
- hosts: all
vars:
num_intervals: 1000
pi_var: |
{%- set o=[] %}
{%- for i in play_hosts %}
{%- if o.append(hostvars[i].slice_of_pi) %}
{%- endif %}
{%- endfor %}
{{ o }}
gather_facts: no
tasks:
- set_fact:
position: "{{ play_hosts.index(inventory_hostname) }}"
- name: Each node computes partial value according to the range assigned to it
shell: |
echo ""|awk '{
pi=0;
size= {{ play_hosts|length }};
N=int({{ num_intervals }}/size)*size;
width=1/N;
x=1/size*{{ position }};
for(i=0;i<N/size;i++){
x=x+width;
pi=pi+1/(1+x*x)
};
print 4*pi/N
}'
register: pi_reg
- set_fact:
slice_of_pi: "{{ pi_reg.stdout }}"
- name: Print partial sums from all nodes
debug:
var: pi_var
run_once: true
delegate_to: 127.0.0.1
- name: Sum to get the value of Pi
set_fact:
pi: '{{ pi_var|map("float")|sum }}'
- name: Print value of Pi
debug:
var: pi
run_once: true
delegate_to: 127.0.0.1
To see this, we know that if , then
Differentiating both sides, we get
Since , we can imagine a right triangle with the following sides:
Using this triangle,
Therefore,
Integrating between 0 and 1 we get:
and the result follows.
Written on May 26th, 2019 by Bobby Corpus