Korean farmers call for longer grace period

April 8, 2024

green field and trees under cloudy sky
Photo by Q. Hưng Phạm on Pexels.com

South Korean farmers faced with their traditional system of animal agriculture being phased out by 2027 are calling for a longer grace period, in addition to direct financial compensation, as reported by ABC News on January 9.

One farmer was quoted as saying:

..they’re infringing upon freedom of occupational option. We can’t just sit idly.

A former farming association secretary attending a rally was quoted by the ABC as saying:

…industry workers are in their 60s and 70s, which means they are seeking retirement, not new occupations.

Some farmers quoted by ABC News admitted that:

…their businesses will naturally disappear when older people, their main customers, die. 

These are South Korean farmers, such as Ju Yeongbong, a former secretary general of a farmers’ association, and Son Won Hak, a farmer and leader of a farmers’ association.

“We do recognise that far more people do not eat dog meat compared to those who do. We do know the market is decreasing… but still, it’s our right to run a business,” said Joo Yeong-bong, an experienced dog farmer and the president of the Korean Association of Edible Dog.

Joo said: “…but still, it’s our right to run a business…”

Is it really a right to run such a business, though?

South Korea’s parliament has endorsed landmark legislation outlawing the country’s dwindling dog meat industry, as public calls for the ban have grown sharply amid animal rights campaigns and worries about the country’s international image.

Some angry dog farmers said they plan to file a constitutional appeal and launch rallies in protest, suggesting that heated debate would continue.

ABC News, January 9 2024

The ABC News article goes on to say that:

Dog meat consumption, a centuries-old practice on the Korean Peninsula, is neither explicitly banned nor legalised in South Korea.

Recent surveys show more people want its ban and a majority of South Koreans don’t eat dog meat any longer.

But the surveys also indicated one in every three South Koreans still oppose the ban even though they don’t eat dog meat.

On Tuesday, the National Assembly passed the bill by a 208-0 vote.

President Yoon Suk Yeol’s government supports the ban, so the subsequent steps to make it law are considered formality.

The bill would make the slaughtering, breeding, trade and sale of dog meat for human consumption illegal from 2027, and punish such acts with two to three years in prison.

But it doesn’t stipulate penalties for eating dog meat.

So, while this is obviusly positive news for anyone who thinks dogs should not be consumed by humans, can we now expect a growing black market in dog meat to emerge in South Korea, just as there is a Moon Bear bile black market in Vietnam, now that the bear bile industry is being dismantled there, in some cases willingly, by former bear bile farmers?

In 2019, the Humane Society International was quoted as estimating that “…nearly 30 million dogs are killed just across Asia every year for human consumption”. A dog is slaughtered every second, somewhere in Asia, e.g. Vietnam, South Korea, and the Philippines. (SBS News)

Many people in Western countries are likely to be horrified to learn that dogs are consumed for food in South Korea and elsewhere, in addition to being used to create leather goods in some places, such as China, as shown in this undercover investigation.

But can you just imagine what would happen if the Australian government not only decided to phase out the caged hen system in 2036, and live sheep export in a few years’ time (fulfilling an election promise), but in addition (changing only one word from a quote above):

…to make the slaughtering, breeding, trade and sale of pig meat for human consumption illegal from 2027, and punish such acts with two to three years in prison.

Just imagine the public outcry!

As it is, the egg industry wants an additional decade, and given the mood so far, some members of the sheep industry will quite likely start looking like those farmers who have been protesting in South Korea soon enough. This is despite the live export by sea of sheep, cows, goats, and deer having been phased out by our neighbour, New Zealand since April 2023. NZ can still export by air and so will Australian sheep farmers, so it’s still not an end to the trade as a whole!

It’s possible to have sympathy for such opposing voices from the point of view that no-one wants to see anyone’s livelihood threatened or for peoples’ mental health to suffer. But sometimes, change is necessary for there to be progress toward a better world.

It was an argument with an old friend about the annual Chinese Yulin meat festival that accelerated my thinking about the difference between what we in Australia vs other countries, think of as livestock.

But the uncomfortable, fundamental question is simply this…

Is there an important difference between dogs, cows, pigs, sheep, goats, or deer?

They’re all mammals, like us. They care for their young, show fear, feel pain, and each in their own way, express emotion.

That some may be considered smarter than others is immaterial.

That they are sentient, feeling creatures is not immaterial.

I suspect that the majority of people in Australia, if asked: “would you eat a dog?”, would look at you as if you were insane and disgusting, while at the same time considering eating pigs, sheep or cows to be normal.

That we consider some animals to be friends and others to be food or fur is at least in part, an accident of the circumstances of our birth, the culture we have been raised in.

As Peter Singer declares in his book, Animal Liberation:

I am no more outraged by the slaughter of horses or dogs for meat than I am by the slaughter of pigs for this purpose.

In a very mundane sense, stripped of any notions of reverence, culture is the accumulated beliefs, practices and norms of people, doing things over a long enough period of time that they can say to themselves and to others: “this is what we do and it’s what we’ve always done”.

The same is also often true for the religion people are born into.

But are cultural practices and beliefs always moral and worthy of respect? Sometimes, saying things like “we are upholding our cultural practices by doing XYZ”, when laid bare, is not much different to saying “we are doing XYZ, because that’s the way we’ve always done things around here”.

See the Five Monkeys Experiment for a nice illustration of this.

Are all our laws moral?

Slavery was once legal, widespread and socially acceptable.

Homosexuality was once illegal and in some places, it still is.

The fact that the dog meat trade will soon be illegal in South Korea has no bearing on whether it is now or ever was moral.

Do we really need laws or gods to tell us what is right and wrong?

Can it be that something is moral in one place but not in another, or does it seem more likely that it is just considered illegal or culturally acceptable in one place and time but not another?

If you subscribe to rules-based ethics, what you consider to be right and wrong depends upon the rule, e.g.

  • Have dominion over all the creatures of the earth, vs
  • prevent unnecessary suffering and death.

If you take your moral guidance from holy books, you are doing what you think gods are telling you to do, which may or may not be the same as what’s right.

Consequentialist ethics tend to focus more on the concrete than the abstract. Suffering and whether or not to consider others as a means to our ends, are the key variables in the moral calculus here.

I discovered when we suffer, we suffer as equals, and in their capacity to suffer, a dog, is a pig, is a bear, is a boy.

Philip Wollen

Waiting for T Corona Borealis

April 7, 2024

T Corona Borealis — T CrB for short — is one of ten known recurrent novae. At a distance of around 3000 light years, it was first discovered as a nova eruption in 1866 by John Birmingham. Outbursts occur approximately once every 80 years. It may have been observed in 1217 and in 1787 as well.

T CrB is expected to explode again this year reaching naked eye visibility, around magnitude 2 or 3. The last outburst was in 1946. As always when we talk about astronomical objects, these events happened long ago, 3000 years ago in this case, with the evidence only expected to reach us this year due to the speed of light.

This is likely to be the brightest nova of a generation, certainly of the known recurrent novae. It will quickly rise from a visual band magnitude of 9 or 10 within a day or two to become visible to the naked for a few days, remaining a binocular object for a week or thereabouts, then returning to its pre-eruption magnitude within a month or so.

Exactly when it will brighten is uncertain and is discussed by nova expert Brad Schaefer and others in this AAVSO article, but the prediction is 2024.4 +/- 0.3, so May or June, but it could be earlier or later.

There was a pre-eruption dip in the light curve, 1.1 years before the 1946 outburst. A similar dip happened in March 2023 as shown in the last two years of T CrB observations in V and B bands, more prominent in the B band.

T CrB is located low in the north-eastern sky from Adelaide starting in the late evening. This Stellarium screenshot shows the circumstance for Apr 8 at midnight when T CrB is around 15 degrees above the horizon. Waiting an hour or two will help make observing easier with T CrB culminating at around 29 degrees above the horizon, but the region is viewable from around 11:30pm with a clear NE horizon.

This unprocessed, untracked image was taken with my DSLR (Canon 1100D, 100mm lens, f2.0, ISO 100, 10 secs) on Apr 7 at 2am, so it’s a little further rotated anti-clockwise than the Stellarium view above. The red arrow points to where T CrB will become visible and the green arrow points to alpha CrB (Alphecca). This shows the bright stars of the constellation of Corona Borealis.

Here is an AAVSO finder chart, with stars only down to magnitude 5. You’ll need to rotate it slightly clockwise to match the views above.

The comparison star marked 22 is the magnitude 2.2 star Alpheccca (alpha CrB). Izar and Arcturus (epsilon and alpha Bootis) do not appear in this finder chart, and are at upper left of the constellation Corona Borealis in this orientation. T CrB may approach Alpheccca in brightness.

I’ll be looking out for T CrB whenever I can stay up late enough or get up early enough until the outburst happens, using just the unaided eye in the first instance. Once visible in outburst, I’ll make estimates with 7×50 binoculars and time-permitting, DSLR images for subsequent photometry, submitting both to the AAVSO International Database.

Messages will also be posted on the AAVSO nova forum when the T CrB outburst happens.

On the N Days Of Christmas, part 6 (TL;DR)

April 6, 2024

This post summarises the results of earlier posts.

For each method, the following are given:

  • A worked example for n=12 days, the only one that matters in practice for The Twelve Days of Christmas as opposed to the general problem of The N Days of Christmas I’m interested in.
  • The mathematical notation generalising the method, sometimes shown together with the worked example.
  • A function in the Julia programming language showing:
    • how to compute the N Days of Christmas using the method;
    • the time taken for n=100,000 and n=1000,000,000 on my M2 Mac OS X laptop, i.e. the time taken for the function calls daysofxmas(100000) and daysofxmas(1000000000) to complete.

Where multiple functions were presented for a method in earlier posts, the most efficient and representative (closest to the mathematical notation) is given.

Subtle considerations regarding function parameter and variable types that allow larger values of n to yield correct results are omitted from the code here; assume very large (128 bit) integer types.

Headings link back to the post in which the method was first introduced.

Cumulative Row Addition

Worked example for 12 days

  • Day 1: 1
  • Day 2: 1 + 2 = 3
  • Day 3: 1 + 2 + 3 = 6
  • Day 4: 1 + 2 + 3 + 4 = 10
  • Day 5: 1 + 2 + 3 + 4 + 5 = 15
  • Day 6: 1 + 2 + 3 + 4 + 5 + 6 = 21
  • Day 7: 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28
  • Day 8: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36
  • Day 9: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45
  • Day 10: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55
  • Day 11: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 = 66
  • Day 12: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 = 78

Adding all the results in bold gives: 1 + 3 + 6 + 10 + 15 + 21 + 28 + 36 + 45 + 55 + 66 + 78 = 364.

Mathematical notation

Julia function

function daysofxmas(n)
    total = 0
    for day in 1:n
        daysum = sum(1:day)
        total += daysum
    end
    total
end

Run-time for n=100,000: 0.0005 seconds

Run-time for n=1000,000,000: 8.5 seconds

Notes

  • The mathematical notation is given with application to 12 days. 12 can be replaced by n for generality.
  • The run-time for this method can be significantly higher. In particular, if two nested loops are used instead of a single outer loop and the efficient sum() call shown here, the time taken can be closer to 20 seconds instead of a fraction of a second. See the Row Addition post for more details.

Column Addition, i.e. Dot Product

Worked example for 12 days

  • 12 lots of gift 1 = 12
  • 11 lots of gift 2 = 22
  • 10 lots of gift 3 = 30
  • 9 lots of gift 4 = 36
  • 8 lots of gift 5 = 40
  • 7 lots of gift 6 = 42
  • 6 lots of gift 7 = 42
  • 5 lots of gift 8 = 40
  • 4 lots of gift 9 = 36
  • 3 lots of gift 10 = 30
  • 2 lots of gift 11 = 22
  • 1 lot of gift 12 = 12

Mathematical notation

Julia function

function daysofxmas(n)
    days = [1:n;]
    sum(days .* reverse(days))
end

Run-time for n=100,000: 0.0007 seconds

Run-time for n=1000,000,000: 33.7 seconds

Notes

  • The mathematical notation is given with application to 12 days.
  • There’s a lot more going on in the function code than meets the eye. Reversing a large list (of 100,000 elements in this case) takes time, multiplication is time consuming, and so on. There are faster ways to do this, but few quite so concise.
  • The simplification shown in the worked example of doubling half the sequence of additions is not used in the function here, because as noted in the Dot Product post, it does not help the run-time and complicates the code.

Triangular Numbers

Worked example for 12 days and mathematical notation

Julia function

function triangular(n)
    div(n*(n+1), 2)
end

function daysofxmas(n)
    total = 0
    for n in 1:n
        total += triangular(n)
    end
    total
end

Run-time for n=100,000: 0.0003 seconds

Run-time for n=1000,000,000: 6.8 seconds

Notes

  • See the Triangular Numbers post for the background of how we arrive at triangular numbers. Spoiler alert, it involves triangles. 🙂 A clue is that half of the width times the height of a rectangle is the area of a triangle, which if you peer closely at the triangular function above, you’ll see.

Polynomial Model

Worked example for 12 days and mathematical notation

Julia function

function daysofxmas(n)
    div(n^3, 6) + div(n^2, 2) + div(n, 3)
end

Run-time for n=100,000: 0.00001 seconds

Run-time for n=1000,000,000: 0.00001 seconds

Notes

  • See Polynomial Model re: how the polynomial expression is arrived at.
  • This is the fastest of all the approaches, for any value of n.

Summary

Taking the results for n=100,000 and n=1,000,000,000 into account, we have the following ranking:

RankingMethod
1Polynomial Model
2Triangular Numbers
3Row Addition
4Dot Product
Ranking of methods and their functions

As n gets larger, the run-time difference between the methods due to these time complexity variations becomes more obvious, as can be seen from the results.

The polynomial model method computes the N Days of Christmas in constant time vs linear or quadratic time for the other methods.

It satisfies my initial goal of a simple expression with the lowest computational time complexity and shortest run-time, for any value of n. It is more than a million times faster than its nearest competitor for n = 1 billion.

If I was looking for the fastest possible function implementations, I’d use a language like C, but Julia is better than some other scientific computing languages in this regard.

“Simplest” or “most compact” may be in the eye of the beholder when it comes to mathematical notation (they’re all fairly compact) but shortest function run-time is harder to argue with.

Rally against 2024 duck shooting season

March 16, 2024

On the morning of Sunday, March 16 2024, I attended a rally to protest against the continuation of duck hunting in South Australia following on from the failed duck hunting review by the SA Labor government.

The new season began on the day of the rally and finishes at the end of June, sunrise to sunset. A maximum of 10 ducks per day per shooter can be killed. Tens of thousands of ducks and other birds will be killed. More will die slowly of their injuries.

I don’t really enjoy going to rallies or protests and there are many things I’d rather be doing. But this is the kind of protest I feel compelled to attend.

Short speeches were given by long-time animal welfare advocate Tammy Franks (SA Greens), the new Animal Justice Party candidate for Dunstan (by-election in a week) Frankie Bray, and Steven Langley from Birds SA.

Members of the public stopped to listen and watch for a minute during the event.

This was followed by a short silence in consideration of the birds being killed or wounded at that very moment, and a walk from the steps of parliament house to Victoria Square along the footpath with simple signs and “ban duck hunting” vocalisations.

It was an orderly, non-disruptive display by a bunch of people (hard to know how many: between 50 and 100 I’d say) who just want to see an end to unnecessary carnage. The message to government was simple: ban duck hunting.

It was all over in an hour. Will anything change? Probably. Eventually. Will the public be better informed? Perhaps.

On the website of the Sporting Shooters’ Association of Australia (SSAA) in the article “A beginner’s guide to duck hunting“, there is a section titled Ethics, containing statements such as (my emphasis):

Ethics is conducting yourself in a sportsman-like manner and treating other hunters with respect.

Don’t squabble over ducks. Sometimes, especially when shooting on big swamps, more than one hunter will fire at a duck. There is nothing more childish than two grown men fighting over a dead duck. The simple solution is to walk away and go back to hunting.

The only time a duck should be shot on the water is when a wounded duck has to be humanely dispatched. The shot should only be taken when it is safe to do so.

SSAA

“Treating other hunters with respect”.

But not ducks or other birds?

“Nothing more childish than two grown men fighting over a dead duck”?

How about killing a duck in the first place?

“When a wounded duck has to be humanely dispatched”.

Dispatched? At least be honest and use the word “killed”.

Humanely? There is no humane way to kill an animal that didn’t want to die.

At the end of the article we have this:

Duck hunting is a challenging pastime and once you have experienced it, it will get in your blood and you will never tire of it. The beautiful smell of the wetlands, the sound of the whistling wings of passing ducks – to a duck hunter, that is the elixir of life.

SSAA

This makes it sound like a romantic adventure, but omits one tiny thing: the killing and maiming part.

Reminiscent of “I love the smell of napalm in the morning” from the movie Apocalypse Now.

These sorts of speciesist statements are a reminder of how far there is to go.

It’s all about the hunters apparently, reportedly less than 1% of the SA population. Everyone else (including people and birds) be damned!

On the N Days Of Christmas, part 5

March 10, 2024

Sometimes, you need to visualise data to understand it.

Here is a plot of the number of days vs the total number of gifts, ending with our favourite, traditional data point: 12 days vs 364 total gifts.

Drawing a straight line through these data points isn’t possible, so the relationship between days and total gifts is not linear. There’s clear upward curvature.

What we need to do is create a model of the data.

A first reasonable thought is that the data may represent exponential growth, something we’ve all become accustomed to in the age of COVID. That turns out not to be the case here, as a little reflection will show; there is not a pattern of doubling or tripling, except for the first few days.

There are numerous models we could use, some more complex than others, but to cut a long story short, a polynomial model turns out to fit the data best. Perfectly, in fact. In particular, the model is a so-called polynomial of degree 3.

But I’m getting slightly ahead of myself.

Spreadsheets like Excel, Numbers, and LibreOffice allow a plot and model to be created, much like the ones above, for which I used the R programming language. Here’s what this looks like in Excel:

There are a few things to notice.

The first is the equation at top right of the plot: the 3rd degree polynomial, 3rd because the largest exponent to which the variable x (day number here) is raised is 3.

The second thing to notice is R2 = 1. This R-squared value is a measure of the “goodness of fit” of the model to the data.

A polynomial of degree 2 is not a good fit, and adding more terms, e.g. to give a 4th degree polynomial, doesn’t improve the fit.

The third thing to notice is the column in the table at left titled Total (model). This is the output of applying the polynomial model to numbers in the Day column. It’s almost the same, but not quite the same as the Total column, which is the actual total number of gifts we’ve been computing through different means all along.

The R-squared goodness of fit measure is very close to 1 (perfect fit) but not exactly. It appears that it’s being rounded up. This happens no matter what spreadsheet software you use. Not only that, but the actual model equation itself differs across software used. LibreOffice gave the smallest error of the spreadsheets I tried because the model had constant multipliers in each term with more decimal places than Excel or Numbers.

So, what’s going on? The answer is that in a computer, real numbers are most often represented using some variant of so-called floating-point format. The dominant standard for floating-point numbers is IEEE-754, created in 1985. It’s very successful for general purpose computation, but errors of various sorts can creep in such that the result is an approximation. The topic of the representation of real numbers for computation would take us down another rabbit hole.

The question is: can we take this approximate model and make it exact? The answer, as Bob the Builder might say is: yes we can!

The coefficient (constant multiplier) 0.1667 in the first term is just an approximation for the rational number one sixth, a rational number being an exact number represented as the division of two integers (whole numbers), i.e. a fraction.

The coefficient 0.5 in the second term is of course just one half, another rational number.

The coefficient 0.3333 in the 3rd term is just an approximation for the rational number one third.

The 4th term 2E-11 is engineering notation for 2-11 which is 0.00000000002, a very small number, added to the result, again, to adjust for the fact that the model coefficients are approximations of exact rational numbers.

Given this, the model simply becomes:

I’ve used n instead of x here, since this is the variable name we have used all along to denote number of days.

This turns out to give answers that are in perfect agreement with the other methods we have been using to compute the total number of gifts for a given number of days.

As the statistician Paul Box is reported to have said:

All models are wrong, but some are useful.

Sometimes they’re actually not wrong at all, as in this case. The initial polynomial model was “wrong but useful” but the simplified form is exactly right.

Even though the model was created using data for only the first 12 days, it works for larger numbers just as our other methods do, making it truly predictive, something that not all models can claim to be. Admittedly, as models go, this is a simple one.

Here is the working for f(12):

which in words is: one sixth of 12 cubed, plus one half of 12 squared, plus one third of 12, which evaluates to 288 + 72 + 4 = 364.

This polynomial of degree 3 corresponds to one sixth of a 12x12x12 cube (volume 1728) + one half of a 12×12 square (area 144) + one third of a line 12 units long.

This function, f(n), is the most compact, general expression for computing the total gifts for the Nth day of Christmas I’ve found so far.

It also turns out to be the fastest to run on a computer, since no matter how large n becomes, the function executes in constant time, O(1), meaning that computation time is not sensitive to the size of n.

The best we have been able to do before now is linear time, O(n). The last two posts have discussed computational complexity in the context of this problem, so refer to those for more detail.

The actual run-time (at least on my M2 Mac) is around one millionth of a second, for any value of n.

As in the last two posts, I wrote code in the Julia programming language to implement this function. Unlike many programming languages, Julia supports rational numbers as a first class data type. So, one way to write f(n) in Julia as daysofxmas(n) is:

function daysofxmas(n)
    1//6*n^3 + 1//2*n^2 + 1//3*n
end

where 1//6 is the rational number (fraction) one sixth, for example, and n^3 is n3.

Here’s an example in which the function is applied to 12:

julia> daysofxmas(12)
364//1

The result is literally the fraction 364 over 1, simply because we were very explicit about using rational numbers as coefficients. While that’s an exact answer, we know that the answer really is just a positive whole number. We can convert the result to a suitable integer data type:

function daysofxmas(n)
    Int128(1//6*n^3 + 1//2*n^2 + 1//3*n)
end

A sample run of the modified function gives the simple integer result we’re after:

julia> daysofxmas(12)
364

A reminder from previous posts that the intention is to generalise the gift total calculation to any positive number of days n, i.e. not just n = 12 days, just because we can, not because we have to! 🙂

The final twist is that integers in programming languages have limits, so that performing operations like exponentiation and multiplication on very large integers as we do here can lead to integer “overflow”, resulting in answers that seem to make no sense or give errors:

julia> daysofxmas(1000000000)
ERROR: InexactError: Int128(-1965449412722243072//3)
...

This can be solved by converting the data type of n itself (the default integer type in Julia is Int64, i.e. a 64 bit integer) to a larger integer type so that intermediate operations within the expression don’t overflow, at least where the value of n becomes too large. This also achieves the rational to integer type conversion added in the previous version of the function. The modified function is as follows:

# 9 
function daysofxmas(n)
    if n > 100000000
        n = Int128(n)
    end

    1//6*n^3 + 1//2*n^2 + 1//3*n
end

giving the correct, yet ridiculous, total number of gifts for n = 1,000,000,000 (one billion) of 166,666,667,166,666,667,000,000,000 or approximately 167 million billion billion:

julia> daysofxmas(1000000000)
166666667166666667000000000

While writing this post, I realised that implementations of the daysofxmas function in the previous 2 posts fail to give the correct answer beyond values of n of around 100,000,000. For n = 1,000,000,000, instead of an error like the one above, the total number of gifts given is 2.4 billion billion by these functions instead of 167 million billion billion. The reason, integer overflow, is related to the error encountered above in which the use of rational numbers led to an error instead of an incorrect result due to the vagaries of the way arithmetic operations work in programming languages and the hardware they target.

The next part will summarise the different methods, results, and Julia function run-times, taking into account the correction necessary to give the correct result, even for large values of n.

Beyond this, there appears to be a link between triangular numbers and the polynomial model presented here. This will be explored in the final post.

Nova in Scorpius: update

February 17, 2024

I took wide field DSLR images of PNV J17261813-3809354, now Nova Sco 2024 or V1723 Sco, on February 11, 12, 15, and 17 at around 5:30am Adelaide time (ACDT) with subsequent calibration and photometry yielding observations submitted to the AAVSO International Database (AID).

The nova is marked on this image (click to expand):

Trailing is becoming apparent on this 10 second exposure, visual band image (calibrated and median stacked from a subset of images), as is the deliberate defocussing to spread the light over multiple elements of the DSLR’s Bayer array.

Here is the light curve as of February 17:

After around 9 days, there are only 116 visual band observations from observers around the world, 49 from DSLRs, 27 from some other imager, and 40 from visual observing (binoculars, telescope).

The nova seems to have peaked at around magnitude 6.8 or 6.9 and as of the time of writing (February 17) is dimmer than magnitude 8 and is steadily declining.

My 4 visual band DSLR observations are shown in purple, with the one under the cross hairs at magnitude 8.1 in close agreement re: time and magnitude with a visual observation made by Andrew Pearce (the discoverer). I have also submitted blue and red band observations, not shown above.

My imaging gear is fairly minimal, as shown below:

Canon 1100D DSLR, 100mm f2 lens on 25+ year old Manfrotto tripod
(USB connection to my dad’s old Mac)
Custom built light box and DSLR to obtain flat frames for calibration
(Mac in dark at right)

You can’t choose the time or sky conditions. Here are hand-held iPhone 13 images of the some of the pre-dawn skies, before and after observation on Feb 15 and Feb 11:

Nova in Scorpius

February 11, 2024

The Central Bureau for Astronomical Telegrams reports that Andrew Pearce in Western Australia (Nedlands) discovered a transient in Scorpius on 2024-02-08, at a visual magnitude of 7.8, and 7.4 on 2024-02-09.

A CCD observation by Andrew a little over half a day later gave a visual (Johnson V) magnitude of 7.2.

It was also independently discovered by Y. Sakurai in Japan (Mito), estimated at magnitude 7.1 on 2024-02-09, and found on images by R. H. McNaught, New South Wales (Coonabarabran).

The transient is designated PNV J17261813-3809354 and it’s position on the sky corresponds to Gaia DR3 5974053153713533184, a 19.4 visual magnitude star.

From nothing observed in the field on 2024-02-07 (Andrew’s DSLR), it rose by more than 10 magnitudes within a day.

The Astronomer’s Telegram has classified the object as a nova near visible peak, however the behaviour of these objects can sometimes be surprising.

The following Stellarium fields show the transient’s approximate position (click to enlarge) at about 3:30am Australian Eastern Daylight Time.

A 10 degree field finder chart from AAVSO is shown below:

The chart will need to be rotated by 90 degrees anti-clockwise to match the sky shown.

I observed the field with 10×50 binoculars this morning from around 5am to 6am Adelaide time (ACDT).

There was a significant amount of cloud around the area that made observing the nova difficult. I proceeded to take DSLR images before sunrise and there is some apparently useful data there. I will do the photometry later today and post an update.

Some updates are also starting to flow into AAVSO and Variable Stars South forums.

On the N Days Of Christmas, part 4

January 19, 2024

I listened to an ABC Radio National Ockham’s Razor podcast episode called “The magic of storytelling… in maths?” awhile ago and the speaker reminded me about triangular numbers. I realised there was a connection between this and the current problem that leads to a more compact way to represent the first method presented in part 1.

Recall also from previous posts the intention to generalise the gift total calculation to any positive number of days n, i.e. not just n = 12 days, just because we can, not because we have to! 🙂

Imagine a triangle of height 12, the interior of which is represented by dots, and so consists of 12 rows of dots. The first row has 1 dot, the second row as 2 dots, the third row has 3 dots, the 12th row has 12 dots, and in general, the nth row has n dots.

The first so-called triangular number is 1 with each subsequent number being that row’s number of dots plus the number of dots in all the previous rows.

So for example, the second triangular number is 2+1 = 3. The 7th triangular number is 7+6+5+4+3+2+1 = 28, as shown above.

This simple function in the Julia programming language yields the nth triangular number:

function triangular(n)
    num = 0
    for i in 1:n
       num += i
    end
    num
end

with the following run yielding the first 12 triangular numbers:

julia> [triangular(n) for n in 1:12]
12-element Vector{Int64}:
  1
  3
  6
 10
 15
 21
 28
 36
 45
 55
 66
 78

or even more simply:

julia> [sum(1:n) for n in 1:12]

In case you’re wondering, Int64 is just the type (64 bit integer) of the elements of the resulting list or sequence (vector) of triangular numbers.

Adding these numbers gives 364, the number of gifts in the 12 days of Christmas since the triangular numbers are just the green column of the table from part 1.

Now imagine the dots converted to green squares.

If we place another triangle, inverted on top of the green triangle with the same 12 x 12 dimension (in red, as shown), we now have a 12 x 13 rectangle, or in general, n x (n+1), or simply n(n+1).

Since the area of a triangle is half that of a rectangle, the area of the green (or red) triangle is only half of n(n+1).

Here is the mathematical notation for:

  • the resulting function
  • applying the function to 12 to get the number of gifts on day 12 (calculation detail shown), and
  • the resulting summation of function applications for the days 1 to 12:

Since each component of the triangle has unit size (1×1 square, or in other words, a dot), it turns out that the triangle area is equivalent to the sum of all these units, which is the same as the number of gifts for day n.

Here is some Julia code that gives the number of gifts for the 12 days of Christmas using the triangular function:

function triangular(n)
    n*(n+1)/2
end

julia> sum([triangular(n) for n in 1:12])
364

We can wrap this up in the function daysofxmas, which I’ll label #8, since the last function in part 3 was #7:

# 8
function daysofxmas(n)
    sum([triangular(n) for n in 1:n])
end

In the language of computational complexity and Big-O notation, the second triangular function has time complexity O(1) — or, order 1 — whereas the first implementation of triangular has time complexity O(n).

What O(1) means for n(n+1)/2 is that computing the nth triangular number takes so-called constant time: as fast the a machine can carry out the addition, multiplication, and addition operations. As n gets larger, the time to compute remains the same.

What O(n) means for the first triangular function in this post is that computing the nth triangular number has so-called linear time complexity, or time proportional to the number of days, n. So, as n gets larger, so does the time taken to complete the calculation.

For an n of 12, the time taken in the calculation for both implementations of the function triangular is very small, around a millionth of a second. For n = 1 billion, the first function (using a loop) takes about 4 seconds, whereas the second still just takes around a millionth of a second.

While the area-based triangular function is a big improvement over the brute force approach to find the number of gifts on the nth day (by replacing each row addition), it must still be coupled with a summation step to give the total number of gifts for the 12 days of Christmas, as shown in the example run above (function #8), using sum. This requires O(n) time to complete.

For n = 1 billion, that’s around 6.8 seconds to compute the total of gifts. Not bad, but we can do better, as we saw in the last post where we went down numerous implementation rabbit holes for the daysofxmas function.

Note: Before wrapping up (gift pun unintended) below, it’s worth going back to read part 3 later if you haven’t yet, to peek down those rabbit holes, but it’s quite a bit longer than this post and may be best viewed on a screen larger than a smartphone’s (or printed). While it doesn’t introduce additional mathematical approaches it’s likely to be interesting to anyone wanting to understand more about the computational approach, the tradeoffs of different algorithms, why I chose to use the Julia programming language here, optimisation, and the likelihood that your intuition regarding how to make things run faster is very likely to lead you astray. It also spells out further the relationship between the brute force approaches and the mathematics (e.g. see reference to dot product in table below).

So, let’s summarise the performance of all the functions from part 3 and part 4 now, this time with n = 100,000 — a paltry 100 thousand days of Christmas, yielding a mere 166,671,666,700,000 — about 167 million million gifts.

FunctionTime (seconds)
#1: single loop with sum function0.0005
#2: two loops24
#3: two sum functions0.0004
#4: dot product0.0007
#5: dot product refinement0.0007
#6: parallelised version of #213
#7: remembering all previous day totals 0.0005
#8: summation over triangular numbers0.0003
Run time of daysofxmas(100000) for each of the functions presented in parts 3 and 4

Apart from the obvious outliers, #2 and #6, there’s not much difference for n=100,000. As n becomes larger, #2 and #6 become impractical and the difference between the others becomes more obvious. See the next post for more.

What I said in part 1 is that I want the most compact function (the simplest, with the least number of terms) possible that computes the gift total for any n. But because I also want this to be done in the shortest time possible, the function daysofxmas itself must have constant time, not linear time complexity. That means not doing any kind of summation at all!

Is there a more compact function that does not require the use of a summation step? Answering that question is the focus of part 5.

On the N Days of Christmas, part 3

January 14, 2024

man in grey sweater holding yellow sticky note
Photo by hitesh choudhary on Pexels.com

Before moving on to the next approach to the problem, this post presents code — functions — in the programming language Julia that automate the row and column methods of the first two posts.

In addition, these functions generalise to any positive number of days n. This will come in handy in the event that we are ever invaded by an alien species or artificial general intelligence is achieved, and our alien or robot overlords command that there should be say, 100 days, instead of 12 days of Christmas. 🙂

When I first wrote this post, I included examples in the R and Python languages in addition to Julia, an emerging language that’s gaining popularity, especially in scientific computing which has been dominated for decades by Fortran, MATLAB, R, Python and a handful of other languages.

In the end I decided that presenting examples in just one language was best since it avoids arbitrary differences between languages and, frankly, some languages have counter-intuitive constructs, all of which serves only to distract from the main point of the post.

For the first method in part 1, summing all the gifts received per day and adding all the the resulting values (the green column of the table in part 2), looks like this in mathematical notation:

  • The variable i below the right sigma (summation) symbol takes on values from 1 to day, all of which are added, leading to the day totals in the green column of part 2’s table (shown below again).
  • The left summation corresponds to the addition of each day total number in the green column to arrive at the final gift total in purple.

The table from part 2 is shown here for reference:

Here is a Julia function, daysofxmas, that is one possible implementation of this notation, taking a parameter n and returning the total gifts received in n days. Invocations of the function where n is 12 and 100 are shown. The line with “# 1” is a comment. I’ll use this to number distinct functions for reference.

  • The for loop corresponds to the left-most summation in the mathematical notation,
  • sum(1:day) corresponds to the right-most summation in which each whole number from 1 to day is added and assigned to the variable daysum, and
  • each day’s sum is accumulated in the variable total which is returned from the function.
# 1
function daysofxmas(n)
    total = 0
    for day in 1:n
        daysum = sum(1:day)
        total += daysum
    end
    total
end

julia> daysofxmas(12)
364

julia> daysofxmas(100)
171700

171,700 is obviously a lot of gifts and we wouldn’t want to have to add all those gifts per day by hand! 364 is bad enough.

Adding a formatted print call to the function shows day number, sum of gifts per day, and cumulative total, after which the final total is printed, as before. The weird-looking formatting strings (e.g. %2i) tell Julia how many spaces a number should occupy (2 or 3 here) in the output and what type of numbers to expect (integers, i.e. whole numbers).

using Printf: @printf

function daysofxmas(n)
    total = 0
    for day in 1:n
        daysum = sum(1:day)
        total += daysum
        @printf("%2i: %2i => %3i\n",
                day, daysum, total)
    end
    total
end

julia> daysofxmas(12)
 1:  1 =>   1
 2:  3 =>   4
 3:  6 =>  10
 4: 10 =>  20
 5: 15 =>  35
 6: 21 =>  56
 7: 28 =>  84
 8: 36 => 120
 9: 45 => 165
10: 55 => 220
11: 66 => 286
12: 78 => 364
364

Two for loops could have been used instead, which has the advantage of matching the notation’s summation structure more explicitly, but requires a bit more code and is not as fast as the first implementation above (which we’ll return to below).

# 2
function daysofxmas(n)
    total = 0
    for day in 1:n
        daysum = 0
        for i in 1:day
            daysum += i
        end
        total += daysum
    end
    total
end

Instead, a more compact form of this function uses a language feature known as the list comprehension. Here, the inner sum yields a list of per-day gift totals, which the outer sum adds up (again, the green column of the table in part 2).

# 3
function daysofxmas(n)
    sum([sum(1:day) for day in 1:n])
end

This is faster than function #2 and about the same as #1. It is also a close match to the mathematical sigma notation above, represented as Julia code.

In addition, it is more declarative, allowing us to focus more on expressing what we want to do rather than how. Another advantage is that it allows a programming language like Julia to have better control over what to optimise for best performance.

Building up the expression returned from line 3 in stages with n = 12, makes it easier to see what’s going on, if you are interested in the detail:

julia> [day for day in 1:12]
12-element Vector{Int64}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12

julia> [sum(1:day) for day in 1:12]
12-element Vector{Int64}:
  1
  3
  6
 10
 15
 21
 28
 36
 45
 55
 66
 78

julia> sum([sum(1:day) for day in 1:12])
364

Now recall the second method presented in part 2: the result of summing the blue cells in the table, where each corresponding value in the sequences 1, 2, …, 12 and 12, …, 2, 1 is multiplied, with the products then being added:

In mathematics, this is known as the dot product:

Again, 12 can be generalised to n:

The following Julia code defines an implementation of the function daysofxmas that carries out this particular dot product.

Again, invocations of the function where n is 12 and 100 are shown.

#4
function daysofxmas(n)
    days = [1:n;]
    sum(days .* reverse(days))
end

julia> daysofxmas(12)
364

julia> daysofxmas(100)
171700

[1:n;] means: generate a sequence, a vector, of all the whole numbers from 1 to n.

Use of the reverse function here can be replaced by [n:-1:1;] which says: give me a vector of all the whole numbers from n to 1, where -1 means “step -1”, i.e. start from n and count down by 1, stopping at 1. The end result is the same.

All of the functions presented here yield their result in a tiny fraction of a second, even for a silly 1000 or 10,000 days of Christmas (166,716,670,000 or more than 166 billion gifts), both of which would require the length of the year to change as well of course!

Even for a truly stupid 100,000 days of Christmas, the time to compute the result (166,671,666,700,000 or more than 166 trillion gifts) is less than a thousandth of a second or better, faster than either Python or R on my Mac (M2 processor, 32 GB RAM).

That is, except for the function definition with the two nested loops (#2), which takes 3 seconds, more than a thousand times slower than all the other function definitions shown. There are good reasons for this that have to do with efficient use of processor resources. I’ll return to this point.

Function #3 could also be modified to implement the shortened form of the calculation, twice the first half of the series, as discussed in part 2: 2 x (12 + 22 + 30 + 36 + 40 + 42), but would need to take into account whether n is odd or even, since a sequence with an odd number of values cannot be split in half such that both halves have the same number of values and the left half is the reverse of the right half. Think of the sequences 1, 2, 3, 2, 1 and 1, 2, 3, 3, 2, 1. The second satisfies this constraint but the first doesn’t.

I was going to avoid that complication, since the code runs fast anyway compared with the effort of manual calculation. But, I couldn’t help myself. 🙂

One implementation of this modification to function #3 in Julia is shown next.

There’s no need to pay too much attention to this code unless you want to. The commentary that remains in this post is the main thing to take note of.

# 5
function daysofxmas(n)
    days = [1:n;]
    products = days .* reverse(days)
    numproducts = length(products)
    halflength = div(numproducts, 2)
    total = 2 * sum(products[1:halflength])
    if isodd(numproducts)
        total += products[halflength+1]
    end
    total
end

This implementation is more complex and this greater code complexity and effort may be misplaced.

Just because one mathematical expression is shorter to write down than another, doesn’t mean that implementing it as a function will make it faster or require less code. For one thing, I’ve had to add more code to take account of the odd-length-check requirement. Most of the code after line 4 is new.

As the famous Computer Scientist, Donald Knuth said:

source: http://realmwalkofsoul.weebly.com/developer-diary/development-update-2-optimization-the-root-of-all-evil

Well, maybe not all evil, but it’s generally a bad idea to make guesses about whether a different approach will be faster or which parts of your code need to run faster, since your intuition or common sense will very likely fail you. That’s what the discipline of computational complexity and profiling tools are for.

No, not social profiling. Code profiling.

It turns out that when you use a profiling tool on this function, most of the total runtime is spent in the dot product expression: days .* reverse(days). So, working with half of the dot product doesn’t help since it has already been computed.

Any implementation that improves the runtime of #4 or #5 would have to compute the dot product in a different way (e.g. just half of it) or compute it in parallel. Both are possible and I will briefly explore the second of these here, not with the dot multiply implementations of the function (#4 or #5), but with function #2.

Why? Interestingly, it turns out that the slowest definition of the function (#2) we’ve seen so far, the one with two loops and simple addition, can greatly benefit from a parallel implementation.

But to really stress the daysofxmas function, let’s make n = 100,000. A billion days of Christmas! This results in 166,666,667,166,666,667,000,000,000 gifts. That’s 167 million billion billion.

So, how long does this take to run for each function implemented so far? Most

FunctionTime (seconds)
#10.0005
#224
#30.0004
#40.0007
#50.0007
Run time of daysofxmas(100000) for each of the 4 functions presented so far

If function #2 is modified in the following apparently minimal way…

# 6
function daysofxmas(n)
    total = 0
    @simd for day in 1:n
        daysum = 0
        @simd for i in 1:day
            daysum += i
        end
        total += daysum
    end
    total
end

…the run-time for a billion days of Christmas becomes 13 seconds!

And, how has the code been changed? A Julia directive (actually a so-called macro) @simd has been added before each for loop. SIMD is an acronym for Single Instruction Multiple Data.

What this means here is that the work of the iterations (for loops) is split up, such that chunks of each vector are operated on by the same machine instructions simultaneously rather than sequentially, i.e. one after another.

This is one example of parallel execution. Note that just randomly adding such a directive to any old for loop won’t necessarily improve the run-time because of code dependencies within the loop. It may in fact make it worse. Such is the case if @simd is added before the for loop in function #1, which roughly doubles the runtime.

Exploring what actually happens when such a directive is added to a language like Julia as well as the nuances of parallel computing is, well, a whole other story.

Is there another implementation that is simpler and competes with #1 or #6 for speed? Yes there is. It turns out to be a minor variation on function #1.

# 7
function daysofxmas(n)
    total = 0
    daytotal = 0
    for day in 1:n
       daytotal += day
       total += daytotal
    end
    total
end

Here we use a cumulative day total, remembering all the day additions previously done, only adding the current day number to the day total before updating the overall total. This takes the same amount of time to run for 100,000 days of Christmas as function #1: 0.0005 seconds.

What this last example underscores is that the algorithm matters as dictated by the discipline of computational complexity. Function #7 has time complexity of O(n) which is so-called Big-O notation meaning that there is a simple linear relationship between the number of days of Christmas, n, and the time it takes to compute the number of gifts. This is in contrast with function #2 that has a time complexity of O(n2) such that the time taken to compute the gifts for n days is the square of n, which for large n can get big very quickly, e.g. for n = 10,000, n2 is 100,000,000 instead of just n.

As an aside, #4 and #5 use more computer memory than all the other functions, because of the large vectors they hold in memory.

But enough of this!

Back to the mathematics (and a bit of code) in part 4, but still very much continuing down the path to generalisation and brevity.

On the N Days Of Christmas, part 2

December 31, 2023

The yellow row of the table above highlights an example of one of the days: the 21 gifts given on the sixth day: 6 geese a-laying, 5 golden rings, 4 calling birds, 3 french hens, 2 turtle doves, and a partridge in a pear tree.

The right-most column (green) is the sum of each day’s gift total using the approach described in part 1.

The bottom right-most cell (purple) shows the addition of the numbers in the green cells to give the total number of gifts for the 12 days of Christmas.

The final row (blue) shows that there are:

  • 12 lots of gift 1 = 12
  • 11 lots of gift 2 = 22
  • 10 lots of gift 3 = 30
  • 9 lots of gift 4 = 36
  • 8 lots of gift 5 = 40
  • 7 lots of gift 6 = 42
  • 6 lots of gift 7 = 42
  • 5 lots of gift 8 = 40
  • 4 lots of gift 9 = 36
  • 3 lots of gift 10 = 30
  • 2 lots of gift 11 = 22
  • 1 lot of gift 12 = 12

These values also add to 364.

This amounts to multiplying each value in the sequence 12 to 1 by those numbers in reverse order, 1 to 12, then adding the resulting products:

You may notice that the first six terms in the addition above repeat, but in reverse, after the first occurrence of 42 (the meaning of life!), so the calculation can be shortened to:

This seems like a nice simplification.

But as nice as this is, it doesn’t help us with generalising to any value of n, such as 100, since even if the summation sequence can be halved for any sequence, for large values of n, that’s still a lot of numbers to multiply and add.

We’ll start exploring how to generalise to any value of n in part 3.