An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Contributions to this software library are always welcome. Please ensure that you post program listings rather than .raw files. They give a reasonable idea of what your program does without having to load them into a DM42 and you can also include comments in your code. Check out the following link for a decoder/encoder: https://technical.swissmicros.com/decoders/dm42/

You can then copy/paste the listing and post it in "code" tags.
User avatar
Patrizia_Favaron
Posts: 12
Joined: Thu Mar 11, 2021 9:44 am
Location: Lomagna

An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Patrizia_Favaron »

1.Background

The Lotka-Volterra equations are used to model the interaction between two populations, one acting as a "prey", and the other as a "predator". Let \(n_1\) then represent the prey population size, and \(n_2\) the predator's. The model assumptions are as follows:
  • In absence of predators, the prey population would grow at a positive rate \(r\) (eventually diverging to \(+\infty\).
  • In absence of preys, the predator population decreases at a finite rate \(-\rho\).
  • When both preys and predators are present, the prey loses biomass at a rate equal to the product of a negative constant, \(-\beta\), multiplied by the predator's population size.
  • Again, if both preys and predators are present, the predator population receives biomass at a positive rate \(+\gamma\), multiplied by the prey's population size.
In general, \(\beta > \gamma\): a biomass transfer efficiency is at work here, in the sense that only a part of the biomass lost by the prey population is gained by the predator; we may assume, for example, that \(\gamma \approx \frac{\beta}{10}\).

With the assumptions stated we may write the model equations:

\(\frac{dn_{1}}{dt} = r n_{1} - \beta n_{2} n_{1} \\ \frac{dn_{2}}{dt} = -\rho n_{2} + \gamma n_{1} n_{2} \\ n_{1}(t_{0}) = N_{1}>0 \\ n_{2}(t_{0}) = N_{2} > 0 \)

subject to

\(n_{i}(t_{i}) = N_{i} > 0\)

The Lotka-Volterra model has a long history. Originally invented by A.G. Lotka (1925) and V. Volterra (1926), independently one on the other, it has been extensively analyzed, and many comparison with real population data have been made, often with good agreement. It also found applications beyond ecology, for instance in economy, as a model of scarcely renewable resources (the "prey") compared to workers' income (the "predator").

And it is so famous that surely I have nothing original to say about it.

What I'm about to tell, is an extension of it aptly named "Generalized Lotka-Volterra Model". Its mathematical form is the same as the original's, with a key difference: the number of populations is not restricted to two, but may be any integer \(n > 1\). Additionally, for convenience, instead of using the population sizes \(n_{i}\) we may use the corresponding population biomasses \(m_{i}\): this rescaling makes reasonings somewhat more intuitive.

With these assumptions we may write the model in compact form as

\(\frac{dm_{i}}{dt} = m_{i} \cdot \left(r_{i} + \sum_{j=1}^{n} a_{ij} m_{j}\right)\)

with \(i=1,2,\ldots,n\). As usual, we assume an initial configuration \(m_{i}(t_{0})=M_{i}\), with \(M_{i} > 0\).

The Generalized Lotka-Volterra model had a great ambition on its days: it was an attempt to predict the time evolution of an entire ecosystem. The research questions it hoped to address was less the individual populations dynamic, as with the original Lotka-Volterra model, and much more to understand something about the (in)stability of trophic networks.

Our exercise is to explore it to the extent allowed by a DM42. You may find this maybe a contrived academic endeavor, and if you think so, today, you are likely right. But, allow me some nostalgy: when on my first voluntary internship at the Ecology Department in the University of Milan, all the computing resources we had to try out the idea was a TI-59 calculator, much more difficult to program and orders of magnitude slower/smaller than a DM42. And yet, I and other intern were able to see "something". I would like to share this experience, modernized, with you: as we'll see, even on small scale versions of it, the GLV model is revealing. Its way...

A second reason the GLV model is interesting has to do with the DM42 itself: the calculator has extensive matrix capabilities built into its RPN implementation. Matrix computations are key to GLV models, of course, but is also the cornerstone of many advanced numerical methods, like solving partial differential equations to mention one. If I'm right, the DM42 matrix capabilities are not one of the most popular subjects in this forum, and so their use may add some general usefulness to the already fascinating exploration of the GLVs.

Before to proceed, it may be useful to rewrite the GLV equations in matrix form:

\(\frac{d}{dt}\mathbf{m} = \mathbf{\mu}(\mathbf{r} + \mathbf{Am})\)

with \(\mathbf{\mu}\) an order-\(n\) matrix having \(\mu_{ij}=0\) whenever \(i \ne j\), and \(\mu_{ii}=m_{i}\) (you may figure it as a matrix equivalent of the populations vector); \(\mathbf{r}\) is the vector of populations growth (or decrease) rates; and finally, \(\mathbf{A}\) is the "community matrix", quantifying the exchanges of biomass among populations. All its elements satisfy a sort of almost-antisymmetry condition:

\(a_{ij}=-\alpha_{ji}a_{ji}\)

with \([\alpha_{ij}]\) the "matrix of transfer efficiencies", characterized by \(1 \ge |\alpha_{ij}| > 0\), \(\alpha_{ij} = 1\) whenever \(a_{ij} < 0\) (the case of a "prey") and \(\alpha_{ij} < 1\) if \(a_{ij} > 0\) (a "predator").

A trophic network can be considered "stable" if for any \(t > t_{0}\) we have \(n_{i} > 0\) for any \(i=1,2,\ldots,n\).

A way to test whether a trophic network with community matrix \(\mathbf{A}\) and growth/decrease vector \(\mathbf{r}\) is stable or not, is to start with positive population sizes and let the simulated ecosystem grow. If at some instant at least one of the population sizes reaches zero or a negative value, then the "ecosystem" is unstable.

Notice how our definition of stability is "in negative": in principle we are not sure of it if we limit ourselves to a finite time interval. This is not a real problem, however, if we imagine (or better, hope) the overall populations dynamics is periodic, as happens with original Lotka-Volterra models.

The problem remains, to how we could reproduce the time evolution of our simulated ecosystem. In mathematical terms, this is equivalent to asking for a solution of the differential equation system constituting our GLV, and checking its positivity. I'll be quite crude on this point, and use the Euler method. More sophisticate solvers of course exist, and their use would in general be recommended considering the GLV equations are non-linear. But this would obscure the code a lot, making it much less informative. And, Euler method is guaranteed to converge to the true solution as the time step tends to zero: not a big deal for the extended precision arithmetic of the DM42.

The discretized form of the problem is then

\(\frac{\mathbf{m}(t+h)-\mathbf{m}(t)}{h} \approx \mathbf{\mu}(t)(\mathbf{r} + \mathbf{Am}(t))\)

or

\(\mathbf{m}(t+h) \approx \mathbf{m}(t) + h \cdot \mathbf{\mu}(t)(\mathbf{r} + \mathbf{Am}(t))\)

2.First implementation

The preceding formula suggests a way to perform one time step, and this is what I will initially do. But first of all, we need a way to change a vector into a same-order diagonal matrix. And even before, we also need a bit of planning.

The HP-42S / DM42 allow to define named variables of various data types, matrices and vectors among them. This is really cute and convenient for our problem. We might for example adhere to the following naming convention:

"N" - Number of populations (input)
"M" - Nx1 vector of population overall biomasses (input on first step, then updated)
"R" - Nx1 vector of population intrinsic growth rates (input)
"DT" - Time step (input)
"A" - NxN community matrix (input)
"MU" - NxN diagonal matrix with MU(i,j) = 0 whenever i is not equal to j, MU(i,i) = M(i) )computed)

The first program is in fact a subroutine, distributing all elements of the "M" vector to "MU":

Code: Select all

01 LBL "RPL"
02 RCL "N"       ; Construct empty matrix "MU", of order "N"
03 ENTER
04 NEWMAT
05 STO "MU"
06 RCL "N"       ; Assign loop initial count register (R00)
07 1E3
08 /
09 1
10 +
11 STO 00
12 LBL 00       ; Loop begin
13 INDEX "M"  ; Get i-th element of vector "M"...
14 RCL 00
15 IP
16 1
17 STOIJ
18 RCLEL
19 STO 01
20 INDEX "MU"  ; ... and store to ii-th position of matrix "MU"
21 RCL 00
22 IP
23 ENTER
24 STOIJ
25 RCL 01
26 STOEL
27 ISG 00         ; Loop end
28 GTO 00
29 CLST           ; Clean-up stack
30 RTN
In the previous code I've used the functions allowing to access and change vector and matrix elements programmatically, employing indices. My overall code looks (and is) not very elegant; I'm quite sure the problem is me, more than the actual matrix indexing mechanism. However (what's really important), the code logics is, I hope, quite readable.

The second, and for now last, program updates the population masses:

Code: Select all

01 LBL "STP"
02 XEQ "RPL"
03 RCL "R"
04 RCL "A"
05 RCL "M"
06 x
07 +
08 RCL "MU"
09 X<>Y
10 x
11 RCL "DT"
12 x
13 RCL "M"
14 +
15 STO "M"
16 RTN
Notice lines 06, 07, 10, 12 and 14: these are all matrix-vector and vector-scalar operations. The HP-42S / DM42 language contains a powerful set of matrix operations (including inversion): the Generalized Lotka Volterra model example just scratches the surface. Their effect is a very compact matrix code.

3.An example

As this work's title implies, this is an "exploration" (with my DM42 playing an instrumental role). It may then be interesting to look at what happens to a set of modeled populations. To stay numerically simple, let's consider N = 3, that is, three populations, let's name them 1, 2 and 3 respectively.

Let's imagine population 1 is autotrophic (a "vegetable"?), with a positive intrinsic growth rate of 1. Population 2, a herbivore, feeds on 1. Its intrinsic growth rate is negative, and equal to -1.5: as with any predator, in condition of starvation the population declines. Population 3, a carnivore, feeds on the herbivore 2, with an intrinsic growth rate of -1.9.

This given, we can say

R = [1, -1.5, -1.9]

To specify the populations coupled dynamics we need to assign the community matrix in a sensible way. As we've seen in the first section, any time a population \(i\)-th feeds on \(j\)-th, then if the population \(j\) loses mass to population \(i\) at rate \(a_{ij} m_{j}\), population \(i\) gains mass at rate \(\alpha_{ji} a_{ij} m_{i}\) (with \(0 < \alpha_{ji} < 1\)) from population \(j\). That is, \(a_{ji} = - \alpha_{ji} a_{ij}\)

According to our assumptions about who feeds on who, we may for example have

\(a_{12} = -0.1\)
\(a_{21} = 0.008\)
\(a_{23} = -0.1\)
\(a_{32} = 0.02\)

Food conversion efficiencies \(\alpha_{ji}\) in the order of 10% are not far from natural values. Because of the different nutrient value of vegetables and animals, the food conversion efficiency of a herbivore is in general lower than a carnivore - and demands quite radical adaptations.

As an initial set of population masses I've selected this:

M = {100, 10, 1}

I'm not sure of the realism of these values. They "should" reflect the tendency of biomass to get smaller as you climb up the food chain ladder.

And last, we need a time step: let me set DT = 0.1

To perform a step, I'll just use the following key-presses:

XEQ "STP"

And this is a short excerpt from the three-population dynamics:

t = 0: M = {100.0000, 10.0000, 1.0000}
t = 1: M = [131.1515, 4.9407, 0.1451]
t = 2: M = [227.7124, 4.2177, 0.0196]
t = 3: M = [349.1706, 9.0822, 0.0027]
t = 4: M = {191.7831, 22.8168, 0.0005}

(Of course, to arrive at t=1 with DT=0.1, we need to press 10 times the sequence XEQ "STP".)

What happened? Apparently, the vegetable 1 and the herbivore 2 are engaged in an oscillating dynamics, not unlike one might find in ordinary Lotka-Volterra models. The top predator 3, only feeding on the relatively scarce herbivore 2, starves to extinction.

Why? In fact, the last formula in section 1 is equivalent to saying we are trying to solve a set of ordinary differential equations using Euler method with a quite long time step, 0.1. Are we witnessing a numerical artifact?

To check whether this could be the case, we may first set DT to an even worse value 1, and see if our dynamics looks different. These are the results:

t = 0: [100, 10, 1]
t = 1: [100, 2, -0.7]

It's even useless to go on: population 3 mass has even become negative. Euler method takes a huge toll using improper steps. But from numerical analysis we know the Euler method converges in the limit of DT tending to zero. So, we could try a smaller time step. In order to avoid pressing too many keys, let's introduce a new variable,

"NDT": Number of time steps

and an "autorepeat" program,

Code: Select all

01 LBL "NSTP"
02 RCL "NDT"
03 1E3
04 /
05 1
06 +
07 STO 02
08 LBL 00
09 XEQ "STP"
10 ISG 02
11 GTO 00
12 RTN
Now, using DT = 0.01 and NDT = 100, we get

t = 0: M = [100.0000, 10.0000, 1.0000]
t = 1: M = {133.0687, 5.1636, 0.1698}
t = 2: M = {228.1301, 4.6382, 0.0274}
t = 3: M = {322.8792, 10.0276, 0.0046}
t = 4: M = {183.4469, 19.3145, 0.0009}
t = 5: M = { 97.4887, 11.9747, 0.0002}
t = 6: M = {112.7694, 5.9192, 3.3168E-5}
t = 7: M = {189.6761, 4.2271, 5.3601E-6}

And now, DT = 0.002, NDT = 500

t = 0: M = [100.0000, 10.0000, 1.0000]
t = 1: M = {133.2286, 5.1831, 0.1730}
t = 2: M = {228.1071, 4.6755, 0.0281}
t = 3: M = {320.5314, 10.0921, 0.0048}

I stop here. But we can see how decreasing the time step we obtain results which, first, are different from the preceding set, and second, results visually converge to something, which we might reasonably assume to be "the" solution.

Incidentally, I've repeated the example with DT = 0.1 and NDT = 10 using a short Fortran program,

Code: Select all

program glv_test

    implicit none

    real, dimension(3,3)    :: a, mu
    real, dimension(3,1)    :: m, r
    real                    :: dt
    integer                 :: i
    integer                 :: iTime

    a = 0.
    a(1,2) = -0.1
    a(2,1) = +0.008
    a(2,3) = -0.1
    a(3,2) = +0.02

    m(1,1) = 100.
    m(2,1) =  10.
    m(3,1) =   1.

    r(1,1) =  1.0
    r(2,1) = -1.5
    r(3,1) = -1.9

    dt = 0.1

    mu = 0.

    print *, "Time, m1, m2, m3"
    print *, 0., m

    do iTime = 1, 1000

        do i = 1, 3
            mu(i,i) = m(i,1)
        end do

        m = m + dt * matmul(mu, r + matmul(a,m))
        where(m < 0.)
            m = 0.
        endwhere

        print *, iTime*dt, m

    end do

end program glv_test
and obtained, if you believe me, the same results. Given I've used single precision on an Intel machine, implementing IEEE 754 binary floating point arithmetics, with a machine epsilon many orders of magnitude coarser than the DM42's, my feeling is that the error here is dominated by Euler's bad behavior with large time steps. We could do better, for example using higher order methods like Runge-Kutta.

4.Nevertheless, numerical archaeology?

The Lotka-Volterra model, in its original simple form, is highly successful and informative of some phenomena occurring in close and extremely small ecosystems: it allows to see oscillatory population dynamics which have been really observed in Nature.

But as we pass to the Generalized Lotka-Volterra models, things tend to go awry very quickly. We have just seen one case: population 3 goes towards a very quick extinction, mainly due to its fast decrease rate in absence of preys. When the herbivores have peaked, Lotka-Volterra-art, the carnivore had already fallen below a critical level, and its population is too small to take advantage of the new food bonanza.

Indeed, assigning sensible community matrices and growth vectors ending in some stable positive configuration is extremely hard. Does this means finding some equilibrium in a generic ecosystem is "difficult"?

In part, yes. It is.

We may notice that the GLV model equations may be used to derive this stationary solution. We just need to assume

\(\frac{d}{dt}\mathbf{m} = \mathbf{0}\)

thus obtaining the equation

\(\mathbf{\mu}(\mathbf{r} + \mathbf{Am}) = \mathbf{0}\)

You may object this is a non-linear equation. But if we assume all diagonal elements of \(\mu\) (that is, all population masses) are positive, then the preceding equation reduces to

\(\mathbf{Am} = -\mathbf{r}\)

Nice, isn't it? It's a linear problem! So we could solve it, then finding

\(\mathbf{m} = -\mathbf{A}^{-1}\mathbf{r}\)

But a quick inspection of our population matrix shows it is not invertible! This leaves the trivial stationary solution

\(\mathbf{\mu} = \mathbf{0}\)

that is,

\(\mathbf{m} = \mathbf{0}\)

In this case, no stationary ("ecological equilibrium") solution exists.

If you write the Lotka-Volterra modell in this same matrix form, you discover an interesting thing: by construction, its community matrix is invertible. With GLV models, this is almost never the case.

Why?

A lot of speculation occurred within the research community. Yet on those far away times no convincing answers were found.

Meanwhile, attempts to construct artificial closed ecosystems, in view of future long-time-span interplanetary missions, have been made, resulting in failures. Building a stable trophic community, and maintaining it, indeed is difficult...

Time passed, and finally I got my Mathematics degree and found a real job (in the field of industrial automation). With some regret I had to switch to other (wonderfully fascinating) problems, and had to left my voluntary internship at Biology/Ecology.

More time passed: when you say, the cases of life. I had entered another company, larger than my first, and still involved in industrial automation. There I got some results, and on a fatal day was summoned by the Head of Personnel. I imagined that had something to do with the people I had asked for my new service of "testing of programmable electronic systems for safety critical applications", but instead was told it would have had much better the mini-group I contributed to found was directed by a man. In the words of the HR head, as a woman (of, more precisely, as me) I was "too motherly and feminine", and could have made customers doubt the Company's authority. :lol: But, I might better have assisted the new boss "staying in the shadow, as any good wife should". I did, for three years - until the people reached full autonomy. Then, resigned and changed company, once again.

This happened in 1996.

And the new company was involved in measurement systems for monitoring the Planetary Boundary Layer.

That is, turbulent fluxes.

It took me some while (say, a couple years :roll: ) to start again, and just learn the new language.

But, surprise! I met the GLV descendants again!

Of course, the GLV line had been completely abandoned. But one of its core ideas, namely the transfer of energy among populations, did somehow survive. Energy, if you think a bit, is a proxy of biomass, and vice-versa. The key difference is, energy fluxes can be expressed locally, and can even be measured, for example using variants of the eddy-covariance method.

Back at home?!

Indeed, knowledge of real ecosystems have vastly improved, and the main reason the GLV cannot work emerged quite many years ago: in GLVs, all interactions are like "me big me eat you small". But this is only a part of the story. Many interactions are parasitic, or symbiotic. There even exist vast regulatory networks, under active investigation in these years, where in the past just forms of mutualism were imagined. For example, the mycelium of mushrooms, often spanning areas of hectares, connect many trees of different species, and transfer nutrients from big, established specimens to cubs, or plants staying in dark spots (the mushroom apparently gains nutrient from all the forest, so is not completely altruistic). These effects can not be described by a community matrix (but, can do if energy fluxes are taken into account ;) ).

This is not to say GLVs are useless. On the contrary: I'm using them, in my university lessons, as an example of mathematical model which is promising, simple to implement (to the point a DM42 suffices to explore many of them, if patience holds), but, regrettably, wrong.

Errors, in modeling, are very often more interesting than successes. A model is an approximation of reality, useful to someone's (stated or implied) needs. It should take the reality by and large, focus attention on some narrow and reproducible part of it, and allow to make useful predictions sensibly. But sometimes the simplification is too harsh. In some other cases the simplification was right, but the agenda hidden behind the model crafters was not (or, plainly not acceptable). In some other cases, the simplification seems good, but yields surprises (as GLVs did).

As Isaac Asimov once told, however, if is just this "Well, that's funny" moments which produce the breakthroughs. It isn't the resounding "Eureka!", as a true mathematician would say.

In the case of GLVs, their failure to represent reality triggered a paradigm change. Quite massive, I'd say. And fostered the birth of many new entire research fields.

So, can we say they're so bad? :D


Sorry so sloppy, as usual. But if I made you curious, I'd be glad. I'm here by the way: if you have any questions (and I imagine my lack of clarity will foster many), I'm here.

Patrizia
User avatar
Walter
Posts: 3070
Joined: Tue May 02, 2017 11:13 am
Location: On a mission close to DRS, Germany

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Walter »

Impressive. Are you publishing your seminar papers her?
WP43 SN00000, 34S, and 31S for obvious reasons; HP-35, 45, ..., 35S, 15CE, DM16L S/N# 00093, DM42β SN:00041
User avatar
salvomic
Posts: 186
Joined: Sat Dec 30, 2017 10:09 am
Location: Ragusa, Sicily
Contact:

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by salvomic »

not "sloppy" at all, but very impressive and interesting. Thank you!

Salvo
∫aL√0mic (IT9CLU) :: DM42 (SN: 00881), DM41X (SN 00523), DM16, HP Prime, 50g, 41CX, 42s, 71b, 15C, 12C, 35s, WP34s -- Free42
User avatar
Patrizia_Favaron
Posts: 12
Joined: Thu Mar 11, 2021 9:44 am
Location: Lomagna

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Patrizia_Favaron »

Dear Walter,

oh, no, not publishing them here. But, preparing! May is coming...

Patrizia
User avatar
Patrizia_Favaron
Posts: 12
Joined: Thu Mar 11, 2021 9:44 am
Location: Lomagna

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Patrizia_Favaron »

dear Salvomic,

too generous! I know I'm sloppy! Maybe, around 1983, when I first met the GLVs, then even had some appropriate language about! :)

Patrizia
User avatar
Walter
Posts: 3070
Joined: Tue May 02, 2017 11:13 am
Location: On a mission close to DRS, Germany

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Walter »

Cara Patrizia,

What's in May? A seminar? A conference?
WP43 SN00000, 34S, and 31S for obvious reasons; HP-35, 45, ..., 35S, 15CE, DM16L S/N# 00093, DM42β SN:00041
User avatar
Patrizia_Favaron
Posts: 12
Joined: Thu Mar 11, 2021 9:44 am
Location: Lomagna

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Patrizia_Favaron »

It's a planned seminar, at University of Insubria. The subject will be mathematical models, and GLVs were so nice... I guess my cubs-of-engineer will appreciate: using Julia, they may write the code in three or four lines themselves.

And besides: isn't the errors (in models, movies, ...) so amusing to find? I've used often. Even the most shy students participate.
User avatar
OlidaBel
Posts: 58
Joined: Thu Mar 11, 2021 8:52 am
Location: Belgium

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by OlidaBel »

Thank you very much Patrizia for such an interesting article, very well written and clear !
The scientific details and others questions about this biologic/ecologic modeling are welcome, aside the programmation (which is indeed, sometimes ;) , only a tool).
How do you insert mathematic formulas with greek letters ?
---
Olivier
48GX, Prime G2, 50G, DM15L, DM42, 28S, HP 15c CE
User avatar
Patrizia_Favaron
Posts: 12
Joined: Thu Mar 11, 2021 9:44 am
Location: Lomagna

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by Patrizia_Favaron »

Thank you very much! <3

For inserting Greek letters and mathematical symbols, you may just employ the "latex" tag - to insert one, you may use the 'latex' button immediately above the text insertion field (the one in which I'm writing now).

Inside the 'latex' tag you may then provide a subset of LaTeX commands (quite large, if I'm right). For example, the Greek letter \(\alpha\) can be specified as "\alpha", or a more complex symbol like \(\frac{1}{n}\sum_{i=1}^{n}x_{i}\) by the command "\frac{1}{n}\sum_{i=1}^{n}x_{i}".

LaTeX, in this days, may seem quite a bit archaeology, but is very very convenient to insert maths in texts. And, you may count on numberless tutorials. :)

My advice is that you check the visual results using "Preview", just bottom to the text insertion field.
User avatar
OlidaBel
Posts: 58
Joined: Thu Mar 11, 2021 8:52 am
Location: Belgium

Re: An Exploration of Generalized Lotka-Volterra Equations: an Early Attempt to Model Ecosystems Function

Post by OlidaBel »

LateX, ...of course. I couldn't have time to learn it, and later it was too late, I was on the business (mostly non scientific).
I'm always atonished by its layout beauty.
---
Olivier
48GX, Prime G2, 50G, DM15L, DM42, 28S, HP 15c CE
Post Reply