#set document(title: "3.3 Linear systems of ODEs", author: "Jiří Lebl") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 3.3#h(0.6em)Linear systems of ODEs First let us talk about matrix or vector valued functions. Such a function is just a matrix whose entries depend on some variable. If #math.equation(block: false, alt: "t")[$t$] is the independent variable, we write a #emph[vector valued function] #math.equation(block: false, alt: "x → open parenthesis t close parenthesis")[$arrow(x) ( t )$] as #math.equation(block: true, alt: "x → open parenthesis t close parenthesis equals [ x sub 1 open parenthesis t close parenthesis; x sub 2 open parenthesis t close parenthesis; ⋮; x sub n open parenthesis t close parenthesis ]")[$arrow(x) ( t ) = [ x_(1) ( t ) \ x_(2) ( t ) \ ⋮ \ x_(n) ( t ) ]$] Similarly a matrix valued function #math.equation(block: false, alt: "A open parenthesis t close parenthesis")[$A ( t )$] is #math.equation(block: true, alt: "A open parenthesis t close parenthesis equals [ a sub 11 open parenthesis t close parenthesis, a sub 12 open parenthesis t close parenthesis, ⋯, a sub 1 n open parenthesis t close parenthesis; a sub 21 open parenthesis t close parenthesis, a sub 22 open parenthesis t close parenthesis, ⋯, a sub 2 n open parenthesis t close parenthesis; ⋮, ⋮, ⋱, ⋮; a sub n 1 open parenthesis t close parenthesis, a sub n 2 open parenthesis t close parenthesis, ⋯, a sub n n open parenthesis t close parenthesis ]")[$A ( t ) = [ a_(11) ( t ) & a_(12) ( t ) & ⋯ & a_(1 n) ( t ) \ a_(21) ( t ) & a_(22) ( t ) & ⋯ & a_(2 n) ( t ) \ ⋮ & ⋮ & ⋱ & ⋮ \ a_(n 1) ( t ) & a_(n 2) ( t ) & ⋯ & a_(n n) ( t ) ]$] We can talk about the derivative #math.equation(block: false, alt: "A prime open parenthesis t close parenthesis")[$A^(′) ( t )$] or #math.equation(block: false, alt: "the fraction d A over d t")[$frac(d A, d t)$]. This is just the matrix valued function whose #math.equation(block: false, alt: "i j to the power t h")[$i j^(t h)$] entry is #math.equation(block: false, alt: "a i j ′ open parenthesis t close parenthesis")[$a_(i j)^(′) ( t )$]. Rules of differentiation of matrix valued functions are similar to rules for normal functions. Let #math.equation(block: false, alt: "A open parenthesis t close parenthesis")[$A ( t )$] and #math.equation(block: false, alt: "B open parenthesis t close parenthesis")[$B ( t )$] be matrix valued functions. Let #math.equation(block: false, alt: "c")[$c$] be a scalar and let #math.equation(block: false, alt: "C")[$C$] be a constant matrix. Then #math.equation(block: true, alt: "open parenthesis A open parenthesis t close parenthesis plus B open parenthesis t close parenthesis close parenthesis prime equals A prime open parenthesis t close parenthesis plus B prime open parenthesis t close parenthesis; open parenthesis A open parenthesis t close parenthesis B open parenthesis t close parenthesis close parenthesis prime equals A prime open parenthesis t close parenthesis B open parenthesis t close parenthesis plus A open parenthesis t close parenthesis B prime open parenthesis t close parenthesis; open parenthesis c A open parenthesis t close parenthesis close parenthesis prime equals c A prime open parenthesis t close parenthesis; open parenthesis C A open parenthesis t close parenthesis close parenthesis prime equals C A prime open parenthesis t close parenthesis;")[$attach(( A ( t ) + B ( t ) ), t: ′) = A^(′) ( t ) + B^(′) ( t ) \ ( A ( t ) B ( t ) )^(′) = A^(′) ( t ) B ( t ) + A ( t ) B^(′) ( t ) \ ( c A ( t ) )^(′) = c A^(′) ( t ) \ ( C A ( t ) )^(′) = C A^(′) ( t ) \ ( A ( t ) C )^(′) = A^(′) ( t ) C$] Note the order of the multiplication in the last two expressions. A #emph[first order linear system of ODEs] is a system that can be written as the vector equation #math.equation(block: true, alt: "x → open parenthesis t close parenthesis equals P open parenthesis t close parenthesis x → open parenthesis t close parenthesis plus f → open parenthesis t close parenthesis")[$arrow(x) ( t ) = P ( t ) arrow(x) ( t ) + arrow(f) ( t )$] where #math.equation(block: false, alt: "P open parenthesis t close parenthesis")[$P ( t )$] is a matrix valued function, and #math.equation(block: false, alt: "x → open parenthesis t close parenthesis")[$arrow(x) ( t )$] and #math.equation(block: false, alt: "f → open parenthesis t close parenthesis")[$arrow(f) ( t )$] are vector valued functions. We will often suppress the dependence on #math.equation(block: false, alt: "t")[$t$] and only write #math.equation(block: false, alt: "x → equals P x → plus f →")[$arrow(x) = P arrow(x) + arrow(f)$]. A solution of the system is a vector valued function #math.equation(block: false, alt: "x →")[$arrow(x)$] satisfying the vector equation. For example, the equations #math.equation(block: true, alt: "x 1 ′ equals 2 t x sub 1 plus e to the power t x sub 2 plus t squared; x 2 ′ equals the fraction x sub 1 over t minus x sub 2 plus e to the power t")[$x_(1)^(′) = 2 t x_(1) + e^(t) x_(2) + t^(2) \ x_(2)^(′) = frac(x_(1), t) − x_(2) + e^(t)$] can be written as #math.equation(block: true, alt: "x prime → equals [ 2 t, e to the power t; the fraction 1 over t, minus 1 ] x prime → plus [ t squared; e to the power t ]")[$arrow(x^(′)) = [ 2 t & e^(t) \ frac(1, t) & − 1 ] arrow(x^(′)) + [ t^(2) \ e^(t) ]$] We will mostly concentrate on equations that are not just linear, but are in fact #emph[constant coefficient] equations. That is, the matrix #math.equation(block: false, alt: "P")[$P$] will be constant; it will not depend on #math.equation(block: false, alt: "t")[$t$]. When #math.equation(block: false, alt: "f → equals 0 →")[$arrow(f) = arrow(0)$] (the zero vector), then we say the system is #emph[homogeneous]. For homogeneous linear systems we have the principle of superposition, just like for single homogeneous equations. #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #strong[Superposition] Let #math.equation(block: false, alt: "x prime → equals P x prime →")[$arrow(x^(′)) = P arrow(x^(′))$] be a linear homogeneous system of ODEs. Suppose that #math.equation(block: false, alt: "x → sub 1 , and so on , x → sub n")[$arrow(x)_(1) , … , arrow(x)_(n)$] are #math.equation(block: false, alt: "n")[$n$] solutions of the equation, then #math.equation(block: true, alt: "x → equals c sub 1 x → sub 1 plus c sub 2 x → sub 2 plus and so on plus c sub n x → sub n")[$arrow(x) = c_(1) arrow(x)_(1) + c_(2) arrow(x)_(2) + … + c_(n) arrow(x)_(n)$] is also a solution. Furthermore, if this is a system of #math.equation(block: false, alt: "n")[$n$] equations #math.equation(block: false, alt: "open parenthesis P i s n times n close parenthesis")[$( P " " upright(i) upright(s) " " upright(n) × upright(n) )$], and #math.equation(block: false, alt: "x → sub 1 , and so on , x → sub n")[$arrow(x)_(1) , … , arrow(x)_(n)$] are linearly independent, then every solution can be written as (3.3.1). ] Linear independence for vector valued functions is the same idea as for normal functions. The vector valued functions #math.equation(block: false, alt: "x → sub 1 , x → sub 2 , and so on , x → sub n")[$arrow(x)_(1) , arrow(x)_(2) , … , arrow(x)_(n)$] are linearly independent when #math.equation(block: true, alt: "c sub 1 x → sub 1 plus c sub 2 x → sub 2 plus and so on plus c sub n x → sub n equals 0 →")[$c_(1) arrow(x)_(1) + c_(2) arrow(x)_(2) + … + c_(n) arrow(x)_(n) = arrow(0)$] has only the solution #math.equation(block: false, alt: "c sub 1 equals c sub 2 equals and so on equals c sub n equals 0")[$c_(1) = c_(2) = … = c_(n) = 0$], where the equation must hold for all #math.equation(block: false, alt: "t")[$t$]. #examplebox("Example 1")[][ #math.equation(block: false, alt: "x → sub 1 equals [ t squared; t ] , x → sub 2 equals [ 0; 1 plus t ] , x → sub 3 equals [ minus t squared; 1 ]")[$arrow(x)_(1) = [ t^(2) \ t ] , arrow(x)_(2) = [ 0 \ 1 + t ] , arrow(x)_(3) = [ − t^(2) \ 1 ]$] are linearly depdendent because #math.equation(block: false, alt: "x → sub 1 plus x → sub 3 equals x → sub 2")[$arrow(x)_(1) + arrow(x)_(3) = arrow(x)_(2)$], and this holds for all #math.equation(block: false, alt: "t")[$t$]. So #math.equation(block: false, alt: "c sub 1 equals 1 , c sub 2 equals minus 1")[$c_(1) = 1 , c_(2) = − 1$] and #math.equation(block: false, alt: "c sub 3 equals 1")[$c_(3) = 1$] above will work. On the other hand if we change the example just slightly #math.equation(block: false, alt: "x → sub 1 equals [ t squared; t ] , x → sub 2 equals [ 0; t ] , x → sub 3 equals [ minus t squared; 1 ]")[$arrow(x)_(1) = [ t^(2) \ t ] , arrow(x)_(2) = [ 0 \ t ] , arrow(x)_(3) = [ − t^(2) \ 1 ]$], then the functions are linearly independent. First write #math.equation(block: false, alt: "c sub 1 x → sub 1 plus c sub 2 x → sub 2 plus c sub 3 x → sub 3 equals 0 →")[$c_(1) arrow(x)_(1) + c_(2) arrow(x)_(2) + c_(3) arrow(x)_(3) = arrow(0)$] and note that it has to hold for all #math.equation(block: false, alt: "t")[$t$]. We get that #math.equation(block: true, alt: "c sub 1 x → sub 1 plus c sub 2 x → sub 2 plus c sub 3 x → sub 3 equals [ c sub 1 t squared minus c sub 3 t cubed; c sub 1 t plus c sub 2 t plus c sub 3 ] equals [ 0; 0 ]")[$c_(1) arrow(x)_(1) + c_(2) arrow(x)_(2) + c_(3) arrow(x)_(3) = [ c_(1) t^(2) − c_(3) t^(3) \ c_(1) t + c_(2) t + c_(3) ] = [ 0 \ 0 ]$] In other words #math.equation(block: false, alt: "c sub 1 t squared minus c sub 3 t cubed equals 0")[$c_(1) t^(2) − c_(3) t^(3) = 0$] and #math.equation(block: false, alt: "c sub 1 t plus c sub 2 t plus c sub 3 equals 0")[$c_(1) t + c_(2) t + c_(3) = 0$]. If we set #math.equation(block: false, alt: "t equals 0")[$t = 0$], then the second equation becomes #math.equation(block: false, alt: "c sub 3 equals 0")[$c_(3) = 0$]. However, the first equation becomes #math.equation(block: false, alt: "c sub 1 t squared equals 0")[$c_(1) t^(2) = 0$] for all #math.equation(block: false, alt: "t")[$t$] and so #math.equation(block: false, alt: "c sub 1 equals 0")[$c_(1) = 0$]. Thus the second equation is just #math.equation(block: false, alt: "c sub 2 t equals 0")[$c_(2) t = 0$], which means #math.equation(block: false, alt: "c sub 2 equals 0")[$c_(2) = 0$]. So #math.equation(block: false, alt: "c sub 1 equals c sub 2 equals c sub 3 equals 0")[$c_(1) = c_(2) = c_(3) = 0$] is the only solution and #math.equation(block: false, alt: "x → sub 1 , x → sub 2")[$arrow(x)_(1) , arrow(x)_(2)$] and #math.equation(block: false, alt: "x → sub 3")[$arrow(x)_(3)$] are linearly independent. ] The linear combination #math.equation(block: false, alt: "c sub 1 x → sub 1 plus c sub 2 x → sub 2 plus and so on plus c sub n x → sub n")[$c_(1) arrow(x)_(1) + c_(2) arrow(x)_(2) + … + c_(n) arrow(x)_(n)$] could always be written as #math.equation(block: true, alt: "X open parenthesis t close parenthesis c →")[$X ( t ) arrow(c)$] where #math.equation(block: false, alt: "X open parenthesis t close parenthesis")[$X ( t )$] is the matrix with columns #math.equation(block: false, alt: "x → sub 1 , and so on , x → sub n")[$arrow(x)_(1) , … , arrow(x)_(n)$], and #math.equation(block: false, alt: "c →")[$arrow(c)$] is the column vector with entries #math.equation(block: false, alt: "c sub 1 , and so on , c sub n")[$c_(1) , … , c_(n)$]. The matrix valued function #math.equation(block: false, alt: "X open parenthesis t close parenthesis")[$X ( t )$] is called the#emph[fundamental matrix], or the #emph[fundamental matrix solution]. To solve nonhomogeneous first order linear systems, we use the same technique as we applied to solve single linear nonhomogeneous equations. #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ Let #math.equation(block: false, alt: "x → prime equals P x → plus f →")[$arrow(x)^(′) = P arrow(x) + arrow(f)$] be a linear system of ODEs. Suppose #math.equation(block: false, alt: "x → sub p")[$arrow(x)_(p)$] is one particular solution. Then every solution can be written as #math.equation(block: true, alt: "x → equals x → sub c plus x → sub p")[$arrow(x) = arrow(x)_(c) + arrow(x)_(p)$] where #math.equation(block: false, alt: "x → sub c")[$arrow(x)_(c)$] is a solution to the associated homogeneous equation #math.equation(block: false, alt: "open parenthesis x → equals P x → close parenthesis")[$( arrow(x) = P arrow(x) )$]. ] So the procedure will be the same as for single equations. We find a particular solution to the nonhomogeneous equation, then we find the general solution to the associated homogeneous equation, and finally we add the two together. Alright, suppose you have found the general solution #math.equation(block: false, alt: "x → prime equals P x → plus f →")[$arrow(x)^(′) = P arrow(x) + arrow(f)$]. Now you are given an initial condition of the form #math.equation(block: true, alt: "x → t sub 0 equals b →")[$arrow(x) t_(0) = arrow(b)$] for some constant vector #math.equation(block: false, alt: "b →")[$arrow(b)$]. Suppose that #math.equation(block: false, alt: "X open parenthesis t close parenthesis")[$X ( t )$] is the fundamental matrix solution of the associated homogeneous equation (i.e. columns of #math.equation(block: false, alt: "X open parenthesis t close parenthesis")[$X ( t )$] are solutions). The general solution can be written as #math.equation(block: true, alt: "x → open parenthesis t close parenthesis equals X open parenthesis t close parenthesis c → plus x → sub p open parenthesis t close parenthesis")[$arrow(x) ( t ) = X ( t ) arrow(c) + arrow(x)_(p) ( t )$] We are seeking a vector #math.equation(block: false, alt: "c →")[$arrow(c)$] such that #math.equation(block: true, alt: "b → equals x → open parenthesis t sub 0 close parenthesis equals X open parenthesis t sub 0 close parenthesis c → plus x → sub p open parenthesis t sub 0 close parenthesis")[$arrow(b) = arrow(x) ( t_(0) ) = X ( t_(0) ) arrow(c) + arrow(x)_(p) ( t_(0) )$] In other words, we are solving for #math.equation(block: false, alt: "c →")[$arrow(c)$] the nonhomogeneous system of linear equations #math.equation(block: true, alt: "X open parenthesis t sub 0 close parenthesis c → equals b → minus x → sub p open parenthesis t sub 0 close parenthesis")[$X ( t_(0) ) arrow(c) = arrow(b) − arrow(x)_(p) ( t_(0) )$] #examplebox("Example 2")[][ In #link("https://math.libretexts.org/Bookshelves/Differential_Equations/Differential_Equations_for_Engineers_(Lebl)/3%3A_Systems_of_ODEs/3.1%3A_Introduction_to_Systems_of_ODEs")[Section 3.1] we solved the system #math.equation(block: true, alt: "x 1 ′ equals x sub 1; x 2 ′ equals x sub 1 minus x sub 2")[$x_(1)^(′) = x_(1) \ x_(2)^(′) = x_(1) − x_(2)$] with initial conditions #math.equation(block: false, alt: "x sub 1 open parenthesis 0 close parenthesis equals 1 , x sub 2 open parenthesis 0 close parenthesis equals 2")[$x_(1) ( 0 ) = 1 , x_(2) ( 0 ) = 2$]. #solutionbox[ This is a homogeneous system, so #math.equation(block: false, alt: "f → open parenthesis t close parenthesis equals 0 →")[$arrow(f) ( t ) = arrow(0)$]. We write the system and the initial conditions as #math.equation(block: true, alt: "x → prime equals [ 1, 0; 1, minus 1 ] x → , x → open parenthesis 0 close parenthesis equals [ 1; 2 ]")[$arrow(x)^(′) = [ 1 & 0 \ 1 & − 1 ] arrow(x) , #h(1em) arrow(x) ( 0 ) = [ 1 \ 2 ]$] We found the general solution was #math.equation(block: false, alt: "x sub 1 equals C sub 1 e to the power t")[$x_(1) = C_(1) e^(t)$] and #math.equation(block: false, alt: "x sub 2 equals the fraction c sub 1 over 2 e to the power t plus c sub 2 e to the power minus t")[$x_(2) = frac(c_(1), 2) e^(t) + c_(2) e^(− t)$]. Letting #math.equation(block: false, alt: "C sub 1 equals 1")[$C_(1) = 1$] and #math.equation(block: false, alt: "C sub 2 equals 0")[$C_(2) = 0$], we obtain the solution #math.equation(block: false, alt: "[ e to the power t; the fraction 1 over 2 e to the power t ]")[$[ e^(t) \ frac(1, 2) e^(t) ]$]. Letting #math.equation(block: false, alt: "C sub 1 equals 0")[$C_(1) = 0$] and #math.equation(block: false, alt: "C sub 2 equals 1")[$C_(2) = 1$], we obtain #math.equation(block: false, alt: "[ 0; e to the power minus t ]")[$[ 0 \ e^(− t) ]$]. These two solutions are linearly independent, as can be seen by setting #math.equation(block: false, alt: "t equals 0")[$t = 0$], and noting that the resulting constant vectors are linearly independent. In matrix notation, the fundamental matrix solution is, therefore, #math.equation(block: true, alt: "X open parenthesis t close parenthesis equals [ e to the power t, 0; the fraction 1 over 2 e to the power t, e to the power minus t ]")[$X ( t ) = [ e^(t) & 0 \ frac(1, 2) e^(t) & e^(− t) ]$] Hence to solve the initial problem we solve the equation #math.equation(block: true, alt: "X open parenthesis 0 close parenthesis open parenthesis → c close parenthesis equals b →")[$X ( 0 ) arrow( c ) = arrow(b)$] or in other words, #math.equation(block: true, alt: "[ 1, 0; the fraction 1 over 2, 1 ] c → equals [ 1; 2 ]")[$[ 1 & 0 \ frac(1, 2) & 1 ] arrow(c) = [ 1 \ 2 ]$] #math.equation(block: true, alt: "x → open parenthesis t close parenthesis equals X open parenthesis t close parenthesis c → equals [ e to the power t, 0; the fraction 1 over 2 e to the power t, e to the power minus t ] [ 1; the fraction 3 over 2 ] equals [ e to the power t; the fraction 1 over 2 e to the power t plus the fraction 3 over 2 e to the power minus t ]")[$arrow(x) ( t ) = X ( t ) arrow(c) = [ e^(t) & 0 \ frac(1, 2) e^(t) & e^(− t) ] [ 1 \ frac(3, 2) ] = [ e^(t) \ frac(1, 2) e^(t) + frac(3, 2) e^(− t) ]$] This agrees with our previous solution from #link("https://math.libretexts.org/Bookshelves/Differential_Equations/Differential_Equations_for_Engineers_(Lebl)/3%3A_Systems_of_ODEs/3.1%3A_Introduction_to_Systems_of_ODEs")[Section 3.1]. ] ]