Intersections

Next section of the linear algebra update. I reviewed the end exam of the prerequiste linear algebra course for the MDS and realized I didn't know how to do this (the intersection thing), so very encouraging that this is the next section in the review. (For those following along at home, here's where I'm working from)

Why Intersections?

We use formula of vectors, that only use addition and subtraction, to make flat shapes.

These formulae are part of the universe of 'flat shape equations' (my term):

$$ x \, + \, 2y \; = \; 1 \\ \frac{y}{2} \, - \, 2z \; = \; x $$

These are not (because they either multiply or devide by the variables):

$$ x^2 \, - \, 1 \; = \; y \\ \frac{y}{x} \; = \; 3 $$

Worked example from class:

I have two stocks, A and B. $w_A$ is the porportion of the portfolio invested in A, and $w_B$ is the proportion of the portfolio invested in B, such that:

$$ 0 \; \leqslant \; w_A , \, w_B \; \leqslant \; 1 $$

The portfolio's $\beta$-value (that is, it's correlation with the fluctuation of market movement) is the weighted average of the $\beta$-values of the stocks in the portfolio. (Hey! I have done this kind of math before when studying Corporate Finance, I just don't think we referenced vectors)

The formula works like this (if $\beta_p$ = the beta of the portfolio):

$$ \beta_p \; = \; w_A . \beta_A \, + \, w_B . \beta_B \\ $$

If $\beta_A \; = \; -1$ and $\beta_B \; = \; 2$, then, subbing those values in:

$$ \beta_p \; = \; 2w_B \, - \, w_A $$

If I want the stocks to have limited correlation with market fluctuation, then I would like it to have a correlation of 0. (This would mean that it is orthogonal to the market - no projection).

This means that I now have two equations for the combination of $w_A$ and $w_B$. One for how they comprise the portfolio:

$$ w_A \, + \, w_B \; = \; 1 $$

and, one for how they interact with market fluctuations:

$$ 2w_B \, - \, w_A \; = \; 0 $$

(Oh, this is simultaneous equations again! I get how to do these just as mathematical formula, but am less familiar with how to do it for vectors - which can obviously be used to represent these.)

So, being able to find the intersection point will allow me to maximize the profitability of my portfolio (this is one example of why finding the intersection is helpful)

In this context, variables are considered to be things that we control but may want to change (the weight of the stocks), typically to help us meet certain constraints that represent physical realities or objectives (such as wanting to limit correlation with the fluctuation of the market).

Turning the Math into Lines

All lines have two pieces of information:

The basepoint: $\vec{x}_0$ and, A direction vector: $\vec{v}$

They combine together to give this formula that represents all points on a line:

$$ \vec{x} (t) \; = \; \vec{x}_o \, + \, t\vec{v} $$

If we were to sub in all of the possible values of $t$ we would get a straight line made up of a series of points that is called 'parametrization' because we are expressing the line through the parameter $t$. (Why the heck can't it just be called 'drawing a straight line?!'.....)

Here's a worked example (at the moment I'm not sure if this links to the above example; if it does, I'm not certain how to derive these numbers from the above example):

$$ \vec{x} (t) \; = \; \left\lgroup \matrix{1\cr -2} \right\rgroup \, + \, t \left\lgroup \matrix{3\cr 1} \right\rgroup \\ $$

For $t$ = 0:

$$ \vec{x}(0) \; = \; \left\lgroup \matrix{1\cr -2} \right\rgroup \, + \, 0 \left\lgroup \matrix{3\cr 1} \right\rgroup \; = \; \left\lgroup \matrix{1\cr -2} \right\rgroup $$

What's interesting here is that this is the standard construction for finding the y-intercept in the generally constructed $y \, = \, mx \, + \, c$. This point does not cross the y-axis. So the vector formula has a similar constructions of that in regular math, but doesn't function in the same way.

For $t$ = 1:

$$ \vec{x}(1) \; = \; \left\lgroup \matrix{1\cr -2} \right\rgroup \, + \, 1 \left\lgroup \matrix{3\cr 1} \right\rgroup \; = \; \left\lgroup \matrix{1 \, + \, 3\cr -2 \, + \, 1} \right\rgroup \; = \; \left\lgroup \matrix{4\cr -1} \right\rgroup $$

For $t$ = -1/2:

$$ \vec{x}(\frac{-1}{2}) \; = \; \left\lgroup \matrix{1\cr -2} \right\rgroup \, + \, \frac{-1}{2} \left\lgroup \matrix{3\cr 1} \right\rgroup \; = \; \left\lgroup \matrix{1 \, - \, \frac{3}{2}\cr -2 \, - \, \frac{1}{2}} \right\rgroup \; = \; \left\lgroup \matrix{\frac{-1}{2}\cr \frac{-5}{2}} \right\rgroup $$

There is a relationship between the above formula and that of $y \, = \, mx \, + \, b$ as follows:

basepoint: (0, $b$)

direction vector: (1, $m$)

But this doesn't cover all situations, such as a vertical line. To cover that and others, you use $Ax \, + \, By \, = \, k$ (where A and B are both not 0).

Finding the basepoint

With this formula there are two ways to construct a basepoint, either by assuming B is not zero, or A is not zero.

For B $\neq$ 0, sub 0 for x:

$$ A.0 \, + \, By \; = \; k \quad \therefore \quad y \; = \; \frac{k}{B} \quad \Rightarrow \; \left\lgroup 0, \, \frac{k}{B} \right\rgroup $$

Fro A $\neq$ 0, sub 0 for y:

$$ Ax \, + \, B.0 \; = \; k \quad \therefore \quad x \; = \; \frac{k}{A} \quad \Rightarrow \; \left\lgroup \frac{k}{A}, \, 0 \right\rgroup $$

If the specific formula to be used is: $2x \, + \, 3y \, = \, 6$, then the two base points are:

Sub 0 for x: $(0, \, 2)$

Sub 0 for y: $(3, \, 0)$

Finding the direction vector

The above information also allows us to construct the direction vector.

It is important to understand that the value of $k$ does not impact the direction vector (in similar fashion to the value of the y-intercept not impacting the gradiant of the line). So, the value of the direction vector can be solved using $2x \, + \, 3y \, = \, 0$.

I was tired and lost the solution for this, but the general idea here is that for two dimensions, the direction vector is one that is orthogonal to the vector created by the two base points. That is, a direction vector can typically be constructed as:

$$ \left\lgroup \matrix{B\cr -A} \right\rgroup $$

That is, swapping the coordinates of the original line and negating one. So, in our example:

$$ \left\lgroup \matrix{3\cr -2} \right\rgroup $$

Thus, the use of $Ax \, + \, By \, = \, k$ (as opposed to $y \, = \, mx \, + \, c$) implicitly allows for the construction of the formula for a line using vectors as:

$$ \left\lgroup \matrix{A\cr B} \right\rgroup . \left\lgroup \matrix{x\cr y} \right\rgroup \; = \; k $$

Intersections of Lines in 2D

Parallell Lines

Lines are parallel if their normal vectors are parallel, that is, the vectors differ by a specific factor. Using the normal vector is more applicable because while in two dimensions the diretion vector and the normal vector are equivalent, in higher dimensions, there will not be one single direction vector, so using the normal vector at this point creates more consistency.

If two lines are parallel, they may never intersect, or, they may turn out to be the same line (called coincident lines, intersecting in an infinite amount of points).

Not Parallel Lines

If two lines are not parallel, they will have an intersection at a particular point.

Options for Intersecting Lines

Because of the above two points, the options for intersecting lines are:

  • Unique intersection point
  • No intersection point
  • Infinitely many intersections

How do We Tell if Two Lines are the Same Line?

  1. Pick a point on each of the lines
  2. Draw a line between these points
  3. Find a vector that is orthogonal to this lines
  4. If this vector is orthogonal to the lines selected, they are the same
  5. If it is not orthogonal to the lines, they are not the same

How do we Find the Point of Intersection?

Displaying the formula for the two lines as:

$$ A_1x \, + \, B_1y \; = \; k_1 \\ Cx \, + \, Dy \; = \; k_2 \\ $$

Either A or C can be 0, but they cannot both be 0 at the same time (this would result in the lines both being horizontal, and therefore parallel, but we have already excluded this possibility with the above test).

You then test to see if A = 0. If it does not, then you can do the following rearranging by dividing both sides by A:

$$ x \, + \, \frac{B}{A}y \; = \; \frac{k_1}{A} $$

The next step is to multiply this equation by C:

$$ Cx \, + \frac{BC}{A}y \; = \; \frac{Ck_1}{A} $$

You then subract this from the second equation:

$$ Cx \, + \, Dy \, - \, Cx \, - \, \frac{BC}{A}y \; = \; \frac{-Ck_1}{A} \, + \, k_2 $$

If you cancel out the $Cx$'s and combine the $y$'s, you get:

$$ \left\lgroup D \, - \, \frac{BC}{A} \right\rgroup y \; = \; \frac{-Ck_1}{A} \, + \, k_2 $$

You can then multiply through by $A$ to get:

$$ (AD \, - \, BC)y \; = \; Ck_1 \, + \, Ak_2 $$

To solve for $y$, the equation can be divided by $AD - BC$ to give:

$$ y \; = \; \frac{-Ck_1 \, + \, Ak_2}{AD \, - \, BC} $$

Then, you subtract $\frac{B}{A}$ times this equation from equation 1, to get:

$$ x \; = \; \frac{k_1}{A} \, - \, \frac{B}{A} . \left\lgroup \frac{-Ck_1 \, + \, Ak_2}{AD \, - \, BC} \right\rgroup $$

And then you pull out $\frac{1}{A}$ and then do some internal simplification and get:

$$ x \; = \; \frac{Dk_1 \, - \, Bk_2}{AD \, - \, BC} $$

And finally we get back to some coding!!

Borrowing from previous work:

In [1]:
def scal_mult(v, x):
    '''Multiplies the values in a list as scalar multiplication,
       to 5 decimal places.

    Args:
        list, (float).
    Return:
        list.
    '''
    # Mutliply each element in the list by x
    v = [round(element * x, 5) for element in v]
    return v
In [2]:
def mag_vect(v):
    '''Finds the magnitude of a list as a vector, to 5 decimal places.

    Args:
        list.
    Returns:
        (float).
    '''
    # Find the sum of the squares of all elements and the square root of the sum
    mag = round(sum([element ** 2 for element in v]) ** 0.5, 5)
    return mag
In [36]:
def norm_vect(v):
    '''Normalizes the values in a list as a vector, to 5 decimal places.
       Returns an exception if trying to normalize the zero vector.

    Args:
        list.
    Returns:
        list.
    '''
    try:
        # Find the magnitude of v
        n = mag_vect(v)
    except ZeroDivisionError:
        print('You cannot normalize the zero vector.')
    # Normalize v by using scalar multiplication
    v = scal_mult(v, 1/n)
    # Convert back to float
    v = [round(element, 5) for element in v]
    return v
In [4]:
def dot_prod(v, w):
    '''Returns the inner product of two vectors, to 5 decimal places.

    Args:
        two lists of (float) or (int), of equal length.
    Returns:
        (float).
    '''
    # Create list of combined elements
    prod = list(zip(v, w))
    # Multiply each set of elements together and return their sum
    prod = round(sum([element[0] * element[1] for element in prod]), 5)
    return prod
In [5]:
from math import acos, pi

def ang_vect(v, w, in_radians=True):
    '''Finds the angle between two vectors in either radians (default) or degrees,
       to 5 decimal places.
       Returns an exception if trying to compare to the zero vector.

    Args:
        v, w: two lists of (float) or (int), of equal length.
        in_radians:
            True: return result in radians (default).
            False: return result in degrees.
    Returns:
        (float).
    '''
    # Test for division by zero
    try:
        norm_v = norm_vect(v)
        norm_w = norm_vect(w)
    except ZeroDivisionError:
        print('You cannot use this formula to compare to the zero vector.')
    # Find the angle of dot product of the normalized vectors
    angle = round(acos(round(dot_prod(norm_v, norm_w), 3)), 5)
    if in_radians is False:
        angle = round(angle*180/pi, 5)
    return angle
In [6]:
def para_test_vect(v,w):
    '''Checks the parallelism of two vectors.

    Args:
        two lists of (float) or (int).
    Returns:
        (bool).
    '''
    # Test for zero vector
    if mag_vect(v) == 0 or mag_vect(w) == 0:
        return True
    elif ang_vect(v, w) == 0 or ang_vect(v,w) == round(pi, 5):
        return True
    return False
In [7]:
def orth_test_vect(v,w):
    '''Checks the orthogonality of two vectors.

    Args:
        two lists of (float) or (int).
    Returns:
        (bool).
    '''
    if dot_prod(v, w) == 0:
        return True
    return False

The following systems are given:

$$ (1) \\ \begin{align} 4.046x \, + \, 2.836y &= 1.21 \\ 10.115x \, + \, 7.09y &= 3.025 \end{align} $$

$$ (2) \\ \begin{align} 7.204x \, + \, 3.182y &= 8.68 \\ 8.172x \, + \, 4.114y &= 9.883 \end{align} $$

$$ (3) \\ \begin{align} 1.182x \, + \, 5.562y &= 6.744 \\ 1.773x \, + \, 8.343y &= 9.525 \end{align} $$

And we are required to pass in the normal vectors of the lines and the constant term to the formula. We are required to pass in the normal vectors for the lines and the value of the constant. (Reminder the direction vector for a line is $(B, -A)$.

So initially:

In [8]:
a_1, b_1, k_1, a_2, b_2, k_2 = 4.046, 2.836, 1.21, 10.115, 7.09, 3.025
a_3, b_3, k_3, a_4, b_4, k_4 = 7.204, 3.182, 8.68, 8.172, 4.114, 9.883
a_5, b_5, k_5, a_6, b_6, k_6 = 1.182, 5.562, 6.744, 1.773, 8.343, 9.525

I realized that passing in the values required by the lesson made limited sense and that it was better to use the values for $A$, $B$, and $k$ provided and then conduct the tests (and normalize within the function if needed) rather than separately creately the normal vector first.

The three functions that are required a test if the lines are parallel, a test if the lines are equal, and a return of the point of intersection, with comments if the lines are either parallel or equal.

In [9]:
def para_test_line(A1, B1, K1, A2, B2, K2):
    '''Checks the parallelism of two lines
    
    Args:
        (int) or (float) representing the values of two lines of the format:
        Ax + By = k.
    Returns:
        (bool).
    '''
    v_one, v_two = [B1, A1*-1], [B2, A2*-1]
    return para_test_vect(v_one, v_two)
In [10]:
print(para_test_line(a_1, b_1, k_1, a_2, b_2, k_2))
print(para_test_line(a_3, b_3, k_3, a_4, b_4, k_4))
print(para_test_line(a_5, b_5, k_5, a_6, b_6, k_6))
True
False
True

So the 1st and the 3rd systems are parallel, but we also need to check if they are the same. The way to do this would be to take a base point from each to create a vector and then test the orthogonality. Reminder, for a base point $y \, = \, (0, \, \frac{k}{B})$.

In [11]:
def subt_vect(v, w):
    '''Subtracts the values of w from v as vector subtraction, 
       to 3 decimal places.

    Args:
        two lists of format [(float), (float)].
    Returns:
        list.
    '''
    # Create a list of combined elements
    subt = list(zip(v, w))
    # Compute v - w for each element
    subt = [round(element[0] - element[1], 3) for element in subt]
    return subt
In [12]:
def same_line_test(A1, B1, K1, A2, B2, K2):
    '''Checks whether two lines are the same line.
    
    Args:
        (int) or (float) representing the values of two lines of the format:
        Ax + By = k.
    Returns:
        (bool).
    '''
    if para_test_line is False:
        result = False

    # Create direction vector for line between two basepoints
    bp_vect_1 = [0, K1/B1]
    bp_vect_2 = [0, K2/B2]
    bp_dir_vect = subt_vect(bp_vect_1, bp_vect_2)

    # Test orthogonality of vectors
    if orth_test_vect([B1, -A1], bp_dir_vect) is True:
        result = True
    else:
        result = False
    return result
In [13]:
print(same_line_test(a_1, b_1, k_1, a_2, b_2, k_2))
print(same_line_test(a_3, b_3, k_3, a_4, b_4, k_4))
print(same_line_test(a_5, b_5, k_5, a_6, b_6, k_6))
True
False
False

Ugh! Tested this and originally thought it was rounding errors again. I did a bunch of testing and eventually realized this was due to my faulty conceptualization of the vector between the base points. To fix it I imported another formula subt_vect to get it to run - essentially I was doing my subtraction wrong somehow.

In [47]:
def intersect_line(A1, B1, K1, A2, B2, K2):
    '''Checks whether two lines are parallel, the same line or intersect.
    
    Args:
        (int) or (float) representing the values of two lines of the format:
        Ax + By = k.
    Returns:
        If parallel or same line: (str) indicating status.
        If intersect: list of two (float).
    '''
    result = 0
    if same_line_test(A1, B1, K1, A2, B2, K2) is True:
        result = "These are the same line."
    elif para_test_line(A1, B1, K1, A2, B2, K2) is True:
        result = "These lines are parallel."
    else:
        result = [round((B2*K1 - B1*K2)/(A1*B2 - B1*A2), 3),
                      round((-A2*K1 + A1*K2)/(A1*B2 - B1*A2), 3)]

    return result
In [15]:
print(intersect(a_1, b_1, k_1, a_2, b_2, k_2))
print(intersect(a_3, b_3, k_3, a_4, b_4, k_4))
print(intersect(a_5, b_5, k_5, a_6, b_6, k_6))
These are the same line.
[1.173, 0.073]
These lines are parallel.

Inconsistent Systems

Of course it is possible to look at intersections between lines with more than two lines. Once this happens, sometimes all three lines converge - and these are important systems, but often, they do not - each set of two will intersect with each other but all three won't. This type of situation, where a system of lines does not have a shared common intersection point is called inconsistent.

Planes in 3D

Planes in 3D operated similarly to lines in 2D with the equation for a plane being written as:

$$ Ax \, + \, By \, + \, Cz \; = \; k $$

This can be expressed in vector form as:

$$ \left\lgroup \matrix{A\cr B\cr C} \right\rgroup \, . \, \left\lgroup \matrix{x\cr y\cr z} \right\rgroup \; = \; k $$

If $k$ = 0, then the vector made up of the points $(x, \, y, \, z)$, coming from the origin, is orthogonal to the vector made up of the points $(A, \, B, \, C)$. Therefore, $\vec{ABC}$ is a normal vector to the plane.

In further similarity to lines, changing the value of $k$ shifts the plane but it doesn't change the direction of the plane.

Two planes with parallel normal vectors are considered parallel, they also do not intersect (any point that satisfies the equation for the first will not satisfy the equation for the second).

Checking to see if two planes are actually the same is also similar: a vector connecting one point on each plane will be orthogonal to the normal vector if the two planes are the same.

Oh wow, so after all of the digging around with lines, we're going straight into creating functions to test the equality, parallelism and intersections in planes.

Thank goodness for making all of my original vector formula such that they could take any number of variables!

My plan is to approach things in a similar fashion to what was done with lines - use the values of the equations as the arguments for the function.

In [41]:
a_7, b_7, c_7, k_7 = -0.412, 3.806, 0.728, -3.46
a_8, b_8, c_8, k_8 = 1.03, -9.515, -1.82, 8.65
a_9, b_9, c_9, k_9 = 2.611, 5.528, 0.283, 4.6
a_10, b_10, c_10, k_10 = 7.715, 8.306, 5.342, 3.76
a_11, b_11, c_11, k_11 = -7.926, 8.625, -7.212, -7.952
a_12, b_12, c_12, k_12 = -2.642, 2.875, -2.404, -2.443
In [38]:
def para_test_plane(A1, B1, C1, K1, A2, B2, C2, K2):
    '''Checks the parallelism of two planes
    
    Args:
        (int) or (float) representing the values of two planes of the format:
        Ax + By + Cz = k.
    Returns:
        (bool).
    '''
    v_one, v_two = [A1, B1, C1], [A2, B2, C2]
    return para_test_vect(v_one, v_two)
In [40]:
print(para_test_plane(a_7, b_7, c_7, k_7, a_8, b_8, c_8, k_8))
print(para_test_plane(a_9, b_9, c_9, k_9, a_10, b_10, c_10, k_10))
print(para_test_plane(a_11, b_11, c_11, k_11, a_12, b_12, c_12, k_12))
True
False
True

Testing again had rounding errors, but fixed those by changing the rounding of norm_vect back to 5.

In [45]:
def same_plane_test(A1, B1, C1, K1, A2, B2, C2, K2):
    '''Checks whether two planes are the same plane.
    
    Args:
        (int) or (float) representing the values of two planes of the format:
        Ax + By + Cz = k.
    Returns:
        (bool).
    '''
    if para_test_plane is False:
        result = False

    # Create direction vector for line between two basepoints
    bp_vect_1 = [0, 0, K1/C1]
    bp_vect_2 = [0, 0, K2/C2]
    bp_dir_vect = subt_vect(bp_vect_1, bp_vect_2)

    # Test orthogonality of vectors
    if orth_test_vect([A1, B1, C1], bp_dir_vect) is True:
        result = True
    else:
        result = False
    return result

Hmmm, so the way that they defined their procedure for testing if a line is the same was quite different than mine. And they didn't provide any information on how to select a point on each plane (because they didn't need to do it), which I needed to generate to test orthogonality. I wanted to be able to zero out $x$ and $y$ and just solve for $z$ but I wasn't sure if this was acceptable. A quick check with MathDoctorBob and I was able to confirm that this was indeed acceptable.

If that is the case, for:

$$ \begin{align} Ax \, + \, By \, + \, Cz &= k \\ if \, x,y &= 0 \\ Cz &= k \\ \therefore z &= \frac{k}{C} \end{align} $$

I used that to create my test points.

In [46]:
print(same_plane_test(a_7, b_7, c_7, k_7, a_8, b_8, c_8, k_8))
print(same_plane_test(a_9, b_9, c_9, k_9, a_10, b_10, c_10, k_10))
print(same_plane_test(a_11, b_11, c_11, k_11, a_12, b_12, c_12, k_12))
True
False
False
In [52]:
def intersect_plane(A1, B1, C1, K1, A2, B2, C2, K2):
    '''Checks whether two planes are parallel, the same line or intersect.
    
    Args:
        (int) or (float) representing the values of two planes of the format:
        Ax + By + Cz = k.
    Returns:
        (str) indicating status.
    '''
    result = 0
    if same_plane_test(A1, B1, C1, K1, A2, B2, C2, K2) is True:
        result = "These are the same plane."
    elif para_test_plane(A1, B1, C1, K1, A2, B2, C2, K2) is True:
        result = "These planes are parallel."
    else:
        result = "These planes intersect."

    return result
In [53]:
print(intersect_plane(a_7, b_7, c_7, k_7, a_8, b_8, c_8, k_8))
print(intersect_plane(a_9, b_9, c_9, k_9, a_10, b_10, c_10, k_10))
print(intersect_plane(a_11, b_11, c_11, k_11, a_12, b_12, c_12, k_12))
These are the same plane.
These planes intersect.
These planes are parallel.

Still more to come on intersections of planes, but I guess I'll do an Intersections B post.