Vectors
Vectors as lists, functions and arrows; addition, scaling, dot product and convex combination, on the robot's position.
Covers AQA 7517 4.2.1.4 Abstract data types/data structures · AQA 7517 4.2.8.1 Vectors
Do this lesson in the simulatorPrint it: worksheet (PDF) · answers (PDF) · as a web page
Where is the robot, which way is it facing, and is that marker in front of it or behind? Each of those answers is a vector. A vector is a data structure as well as a piece of maths: an ordered list of numbers that a program can add, scale and multiply. In this lesson BugBot's position and heading become vectors, and the dot product tells it what is ahead.
Three ways to see a vector
As a list of numbers. [2.0, 3.14159, -1.0, 2.718281828] is a vector with four components. Because each component is a real number, it is called a 4-vector over ℝ, and the set of all such vectors is written ℝ⁴. In a program it is a one-dimensional array. BugBot's position, [x, y], is a 2-vector over ℝ, a member of ℝ².
As a function. A vector can be read as a function that maps each index to a value. For the 4-vector above, the domain is the set of indexes {0, 1, 2, 3} and the co-domain is ℝ:
0 ↦ 2.0, 1 ↦ 3.14159, 2 ↦ -1.0, 3 ↦ 2.718281828
A dictionary represents that function directly: {0: 2.0, 1: 3.14159, 2: -1.0, 3: 2.718281828}. This is useful when most components are zero, as in the word-count vectors of the last lesson: store only the ones that are not.
As an arrow. A 2-vector or 3-vector can be drawn as an arrow from the origin to the point it names. The arrow has a magnitude (its length) and a direction. For [x, y] the magnitude is √(x² + y²), by Pythagoras.
import math
v_list = [2.0, 3.14159, -1.0, 2.718281828] # a vector as a list (a 1D array)
v_dict = {0: 2.0, 1: 3.14159, 2: -1.0, 3: 2.718281828} # the same vector as a function: index -> value
print(v_list[1], v_dict[1])
p = [30, 40] # a position as an arrow from the origin
print("magnitude", math.sqrt(p[0] ** 2 + p[1] ** 2))
Adding and scaling
Vector addition adds matching components: [3, 4] + [1, 2] = [4, 6]. As arrows, it puts the second arrow on the end of the first, so adding a vector to a position translates it: moves it without turning it.
Scalar-vector multiplication multiplies every component by one number, the scalar: 2 × [3, 4] = [6, 8]. It scales the arrow: the same direction, twice as long. A negative scalar reverses the direction.
Convex combination
A convex combination of two vectors u and v is
αu + βv, where α ≥ 0, β ≥ 0 and α + β = 1
Every convex combination lies on the straight line segment between the points u and v. With α = 1 you get u, with β = 1 you get v, and with α = β = 0.5 you get the midpoint. So convex combinations let a robot find any point between two places: 25% of the way, halfway, three quarters.
Dot product
The dot product of two vectors of the same size multiplies matching components and adds the results. The answer is a single number, not a vector:
u · v = u₀v₀ + u₁v₁ + ... + uₙ₋₁vₙ₋₁
For u = [3, 4] and v = [1, 2]: u · v = 3 × 1 + 4 × 2 = 11.
Its most useful property connects it to the angle θ between the arrows:
u · v = |u| × |v| × cos θ
- Finding the angle: θ = cos⁻¹((u · v) / (|u| × |v|)). For the example, |u| = 5 and |v| = √5 ≈ 2.236, so cos θ = 11 / 11.18 ≈ 0.984 and θ ≈ 10.3°.
- Perpendicular: if u · v = 0 the vectors are at right angles.
- In front or behind: if u · v > 0 the angle is less than 90°; if it is negative, more than 90°.
- Magnitude: u · u = |u|², so |u| = √(u · u).
- How far along: if
his a unit vector (magnitude 1), thenh · dis how fardreaches in the direction ofh.
BugBot's vectors
position() gives the robot's position vector p. Its heading, θ degrees clockwise from the direction it started in, gives a unit heading vector h = [sin θ, cos θ]: at 0° that is [0, 1], straight ahead, and at 90° it is [1, 0], to the right. For a target at t, the displacement from the robot is d = t - p, and h · d says whether the target is ahead, and how far along the heading it is.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
def dot(a, b):
return a[0] * b[0] + a[1] * b[1]
turn_right(30, angle=60)
theta = math.radians(heading())
h = [math.sin(theta), math.cos(theta)]
p = list(position())
print("heading vector", [round(h[0], 2), round(h[1], 2)])
targets = {"cone": [30, 20], "box": [-20, 10], "wall": [10, -40]}
for name in targets:
t = targets[name]
d = [t[0] - p[0], t[1] - p[1]]
along = dot(h, d)
angle = math.degrees(math.acos(along / math.sqrt(dot(d, d))))
print(name, "is", "ahead" if along > 0 else "behind", "at", round(angle), "degrees off the heading")
d = [targets["cone"][0] - p[0], targets["cone"][1] - p[1]]
forward(50, distance=dot(h, d)) # drive as far along the heading as the cone reaches
print("now at", position())
The cone is almost straight ahead, so driving h · d along the heading brings the robot very close to it. The other two targets have negative dot products: they are behind.
Task: meet on the line
Two beacons are at u = [40, 20] and v = [-20, 60], in cm from the robot's start (x to the right, y forward). Write these three functions yourself, for vectors of any length, without numpy:
add(a, b): returns a new list, the vector sum ofaandb.scale(k, a): returns a new list, the numberktimes the vectora.dot(a, b): returns the dot product ofaandb, a single number.
Then print, in this order:
u.v = <dot product>, which is a whole number.angle = <angle> degrees, the angle betweenuandvrounded to a whole number withround(). You will needmath.acos,math.degreesandmath.sqrt.w = <w>, wherew = 0.25u + 0.75v, built withscaleandadd, printed as a Python list, for examplew = [1.0, 2.0].
Finally drive to w: slide sideways by its x component (left if it is negative) and then forward by its y component.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
u = [40, 20]
v = [-20, 60]
Challenges
- Is
wreally on the segment between the beacons? Check thatw - uis a positive scalar multiple ofv - u. - Find two different whole-number vectors whose dot product with
[3, 4]is zero. What do they have in common? - Rewrite
dotso it takes two vectors stored as dictionaries, where missing indexes mean zero.