Activity 3: How to Build a Sunflower 🌻¶
In this activity, you will learn how to code the intricate, beautiful packing of florets in a sunflower head, explore the sensitivity of the arrangement of florets to the golden angle, and create animations of a growing sunflower.
Your reading assignment before this activity is “Chapter 4 - Phyllotaxis” from The Algorithmic Beauty of Plants. The inspiration and theory behind the coding you will be doing today comes from there. The formulas, too, also come from the chapter, but ultimately derive from the classic work by Vogel, “A better way to construct the sunflower head”. Both of these works form the basis for today’s activity. Skim over the reading assignment if you have the chance (there is no need to) to get a feel for some of the math we will be using in today’s activity.
References:
P. Prusinkiewicz and A. Lindenmayer. The algorithmic beauty of plants. Springer-Verlag, Berlin, Heidelberg, 1990. ISBN:0-387-97297-8
H. Vogel. A better way to construct the sunflower head. Mathematical Biosciences, 44:179–189, 1979.
The formula for a sunflower¶
From The Algorithmic Beauty of Plants chapter you read, you learned that Vogel had proposed the following formulas to calculate the angle and radius of each floret on a sunflower head:
theta = n * phi
r = sqrt(n)
Where n
is the ordered number of each floret on a sunflower head, theta
is the angle of the floret, phi
is the golden angle, and r
is radius, the floret’s distance from the center of the sunflower. You should think of each sequential floret as having one more “turn” of phi
and having a longer radius than the previous, following a square root function.
Also remember back to trigonometry (nobody’s favorite class, but let’s try to recall) that in a right triangle, cos
and sin
are the adjacent and opposite sides divided by the hypotenuse, respectively. In the context of a radial Cartesian plane centered at (0,0), to retrieve the x
and y
coordinates we use the following equations:
x = r * cos(theta)
y = r * sin(theta)
Finally, we will be working in radians, not degrees. Remember also from trigonometry that to convert degrees into radians, we multiply degrees by \(\displaystyle\frac{\pi}{180}\).
Build a sunflower!¶
Using the equations above, build a sunflower!
But first, since we will be using the golden angle quite a lot, let’s just make that a variable. In the cell below, the golden angle is provided in degrees. Create a new variable called phi
that is the golden angle in radians.
It would be convenient to have \(\pi\). The cell below imports the math
module, which you will often likely need! To retrieve the value of \(\pi\), simply type math.pi
. You are also going to need math
for the math.sqrt()
, math.cos()
, and math.sin()
functions in this lesson.
import math
# From the online encyclopedia of integer sequences:
# https://oeis.org/A096627
golden_angle = 137.5077640500378546463487396283702776206886952699253696312384958261062333851951
# Convert golden_angle to radians here. Call the variable phi
Next, write a for
loop to calculate the x
and y
coordinates for each floret.
Do the following:
Create two lists for your coordinates outside of the loop: a list for the
x
values and a list for they
valuesYour
for
loop should iterate 1000 times, calculating thex
andy
coordinate values for 1000 floretsWithin your loop, for each floret calculate:
Radius
Theta
x coordinate value
y coordinate value
Append
x
andy
coordinate values to their respective lists
# Put your answer here
Next, plot your results using matplotlib!
Pay attention to the size of the points, not too much overlap but not too small, either
Pick an appropriate color
Adjust the alpha appropriately
Consider scaling the x and y axes equal and setting a fixed aspect ratio
Turn the axes off
Always make aesthetically pleasing figures, especially when plotting sunflowers
# You must alway import matplotlib before using it and use the inline code
import matplotlib.pyplot as plt
%matplotlib inline
# Put the code for your plot here
What a difference an angle makes!¶
You learned in the The Algorithmic Beauty of Plants chapter that you read that the packing of florets in a sunflower is extremely sensitive to the exact value of the golden angle. A little bit more or less and the beautifully-spaced arrangement that is readily apparent to the eye is destroyed. This is because of the special mathematical properties of the golden angle.
In this exercise, you will be taking the sunflower florets you just created using your for
loop and creating an animation, where the value of the golden angle deviates from -1 to +1 degrees over 100 steps. Using this animation, you will observe the sensitivity of the packing of florets in a sunflower to the golden angle, and how this sensitivity varies closer to and farther from the center.
Our strategy will be to create a loop within a loop! The logic is this: you have already created a loop that will calculate all the x
& y
coordinates in a sunflower. You put that loop into another loop! The outer loop will iterate over deviations of the golden angle. So, the flow will look like this:
Outer loop creates a new angle value,
Inner loop caclulates all the
x
andy
coordinate values for the new angle value,The inner loop is exited, you are back in the outer loop,
Your previous code to plot a sunflower plots the florets for the current angle,
The outer loop repeats with a new angle value and a new frame of the animation!
Just one more thing. We will be iterating in 100 steps from -1 to +1 degrees of the golden angle. How do you specify a set number of steps between two values? One function to do this is linspace()
, from the numpy
module. linspace()
takes a start value (inclusive), stop value (inclusive), and a number of steps to incrementally iterate over the interval. Use this for your outer loop to minutely adjust the angle value from +- 1 degree of the golden angle!
In the cell below, pseudo-code is provided to help guide you in constructing your first loop of a loop! Remember, you can almost use the previous code (with some modifications) for your loop to calculate x
and y
coordinate values and to plot your sunflower in the loop of loops below.
This exercise is meant for you to spend time thinking about how complex loops work and how to modify and adjust code for different purposes. So spend time thinking through this.
If you’re successful, you will create a beautiful sunflower animation!
Follow the pseudo-code and fill out the missing parts!
# Imports to get the animation to work
from IPython.display import display, clear_output
import time
# Import numpy to use linspace
import numpy as np
# For animation, you need to call a figure
fig = plt.figure(figsize=(10,10))
# Using linspace() to create 100 increments between -1 and +1
for i in np.linspace(-1, 1, 100):
# Here, create a "new_angle" variable that is golden_angle + i
# Remember the new angle must be in radians!
# This is where to insert your previous loop to calculate x and y coords for the new angle
# Remember, theta will be calculated from your new angle! So be sure to change that!
# Remember, properly indent your for loop within the other for loop: indents of indents!
# Remember, keep the x and y coordinate lists outside of this loop (but within the first loop)
# Still calculate x and y values for 1000 florets using your previous loop
# Your previous loop code goes in this space!
# You are now outside of the inner loop and back in the outer loops
# Put your code for your plot of the sunflower here
# This is the code that creates the animation
time.sleep(0.1)
clear_output(wait=True)
display(fig)
fig.clear()
<Figure size 720x720 with 0 Axes>
<Figure size 720x720 with 0 Axes>
But sunflowers grow!¶
Yes, it’s nice to create an animation changing the angle of florets with a fixed radius. But in the real world a sunflower is growing! The florets arise from a meristem (a population of stem cells) at the center of the sunflower. At the periphery of the shoot apical meristem in the center of the sunflower, cells are determined to become a floret. We call the ability of cells, like meristems, to produce different types of tissues “pluripotent”. We say that cells are “determined” or “fated” to become a particular type of tissue as they lose pluripotency. “Differentiated” cells have already become a particular tissue. After a floret has differentiated, it moves away from the center of the sunflower.
In your previous plots, the florets at the periphery were the first to arise, and were “pushed” from the center by all the other florets that subsequently arose. Let’s create an animation where we watch florets arise at the center and move outwards!
You will:
Create pre-populated lists of
thetas
andradii
. Simply use the code you already have to calculate the thetas and radii.Once born, a floret always keeps the same theta. But the radius gets longer and longer.
Our first loop will create lists of theta and radii that increase in length with each iteration. Because of this, we will start with one floret, then two, and so on, and with each additional floret, the radii of the first florets will become longer and longer. We will start with no florets and grow our sunflower, adding more and more. This loop takes one more member of your pre-specified
thetas
andradii
lists with each iteration.There is a problem, though. Each time we add more
thetas
(which are associated with unique florets), the oldest/firstthetas
are at the beginning of the list. The oldestradii
, too, are at the beginning of their list and they will be the shortest radii! The oldest/firstthetas
should have the longest radii, not the shortest!We will use the
.reverse()
function, which reverses the order of elements of a list. What this will do is insure that the oldest/firstthetas
of the thetas list correspond to the longest radii after the radii list is reversed.Think through the reasoning above, it’s complicated. We are adding one more floret with each iteration. But the first floret would always have the shortest radius, unless we reversed the radii list. By reversing the radii list, the first floret will have an increasing radius.
Follow the reasoning above and make sure you understand it. Pseudo-code with comments is provided in the cells below. Using the skeleton, fill out the rest of the code. The code is in two parts:
First, using a
for
loop, create two lists:thetas
andradii
. These lists should contain 750 theta and radius values for florets.Using the pseudo-code provided, create an animation of a growing sunflower.
# In this cell, create two emtpy lists: "thetas" and "radii"
# Then, using a for loop, populate your two lists with theta and radii values for 750 florets
# You can use the code you have already produced
# Put your answer here
# Next, using the pseudo-code below, create an animation of growing florets in a sunflower
# Pay attention to what the two loops are doing and how they function in real time
# Pay attention to indexing of lists
# Pay attention to the use of the .reverse() function
# Understand how this code creates an animation of a growing sunflower, frame-by-frame
# Put your answer below
# Imports to get the animation to work
from IPython.display import display, clear_output
import time
# For animation, you need to call a figure
fig = plt.figure()
for i in range(len(radii)):
xlist = []
ylist = []
# This selects the current set of thetas to use
current_thetas = thetas[0:(i+1)]
# This selects the current set of radii to use
# Everytime the loop runs, this list will be recreated in the "forward" direction
# When created, shortest radii are first, longest radii are last
current_radii = radii[0:(i+1)]
# After creating the list of radii, "reverse" its order
# Reversing the order of the radii means that the
# florets first specified (the first thetas) have the longest radius
# After reversing, longest radii are first, shortest radii are last
current_radii.reverse()
for n in range (i+1):
# Here, for each floret n caclulate r, theta, x, y, and append x & y to their lists
# You are now back in the outer loop
# Put your code to make a plot of a sunflower here
# This is the code that creates the animation
time.sleep(0.001)
clear_output(wait=True)
display(fig)
fig.clear()
# Closes the figure animation once complete
# plt.close()
File "/tmp/ipykernel_29242/2711649133.py", line 54
time.sleep(0.001)
^
IndentationError: expected an indented block