Searching for a solution to this problem, there were surprisingly few discussions on this problem, and most of those I did find I couldn’t get to work, but eventually I found a solution using ActiveWindow.SmallScroll, which has the twin benefits of being very simple, and obvious how it works. Example VBA code is shown below.

Edit 2 Dec. 2021: Modifications to the code for generating the images resulted in very short cycle times, so I added some code to “sleep” between cycles, so the cycle time was at least 1 second. Sleep is a Windows function, so has to be “declared” at the top of the code module. I have also added code to time each iteration, so the sleep period is reduced as the time for generating the image increases. An added benefit of this code is that it has removed the white screen flashing between each iteration, so the resulting video is much smoother.

```
#If VBA7 Then ' Excel 2010 or later
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal Milliseconds As LongPtr)
#Else ' Excel 2007 or earlier
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal Milliseconds As Long)
#End If
Sub Zoomin()
Dim Startfact As Double, inc As Double, steps As Long, Fact As Double
Worksheets("Sheet4").Activate
With Application.ActiveSheet
Startfact = Range("Start").Value
inc = Range("Factor").Value
steps = Range("steps").Value
Fact = Startfact
For i = 1 To steps
STime = Timer()
Range("Scale").Value = Fact
Range("Iteration") = i
ActiveWindow.SmallScroll down:=1
ActiveWindow.SmallScroll up:=1
Fact = Fact * inc
TimeDiff = (Timer() - STime) * 1000
If TimeDiff < 950 Then
Sleep (1000 - TimeDiff)
Else
Sleep (50)
End If
Next i
End With
End Sub
```

An example animation generated with this routine (plus a Python user defined function to generate the images) is shown below. More details of the code for generating the images will be provided in a later post:

With the faster image generation I have added a second video zooming in 48 times, with a final magnification of 2.8E+14 times, which is about as far as you can get with the resolution allowed with 64 bit float values:

]]>The new file is located (as before) at:

For more information on the last major release see Numerical Integration With Tanh-Sinh Quadrature v 5.0. For more background information and numerous examples search this site for Tanh-Sinh, or select Numerical Integration from the categories drop down.

As always, if you have any questions or comments, please leave a comment below.

]]>Haitian Fight Song, here played by Danny Thompson in Norway in 1968

and Bert Jansch and Danny Thompson playing Thames Lighterman (great pictures as well as great music):

]]>Bert Jansch composition. Recorded at the BBC for John Peel’s Night Ride, broadcast Dec 18, 1968. Apologies for poor sound quality: taken from 50 year old 1/4″ 4-track mono tape running at 3 3/4 ips. The title refers to Pentangle roadie Bobby Cadman, whose previous occupation had been Thames Lighterman. Bert’s song “One For Jo” is also about him, addressed to Bobby’s wife.

Note that all the examples below were taken from the manual for Release 2.0.2. The current release is 3.4.3, and includes several additional examples, and considerably more background information.

The first example plots a simple line graph using hard coded data. I have added a modified version that will transfer the data from a selected row on the spreadsheet.

```
import matplotlib.pyplot as plt
from matplotlib import colors
import pyxll
from pyxll import xl_func, xl_arg, xl_return
from pyxll import plot
@xl_func
def MPLTute_1():
# Create the figure
fig = plt.subplots()[0]
# Plot the data
plt.plot([1,2,3,4])
plt.plot([0,2,4,6])
plt.ylabel('some numbers')
# Display the figure in Excel
plot(fig)
@xl_func
@xl_arg('data','float[]')
def MPLTute_1a(data):
fig, ax = plt.subplots()
ax.plot(data)
ax.set(ylabel='some numbers')
plot(fig)
```

The first function follows the code in the tutorial as closely as possible, with the addition of a second plotted line. The second function, in addition to reading the data to be plotted from the spreadsheet, follows the coding used in the pyxll examples more closely.

The next example plots XY data, read from the spreadsheet as a list of lists. The string ‘ro’ plots the line as red circles for each data point (see the manual for details):

```
@xl_func
@xl_arg('data','float[][]')
def MPLTute_2(data):
fig, ax = plt.subplots()
ax.plot(data[0], data[1], 'ro')
ax.set_xbound(0, 6)
ax.set_ybound(0, 20)
ax.set(ylabel='some numbers')
plot(fig)
```

Function 3 plots 3 lines, each with a different format string. The lines are hard coded functions of the range “t”. The lines are created with a single .plot, as a sequence of 3 sets of X values, Y values, line format string:

```
@xl_func
def MPLTute_3():
t = np.arange(0., 5., 0.2)
fig, ax = plt.subplots()
ax.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plot(fig)
```

Function 4 plots one of the lines from the previous example, but instead of using a single string to format the line, a dictionary is passed from the spreadsheet, allowing multiple format properties to be specified:

```
@xl_func
@xl_arg('props','dict<str, var>')
def MPLTute_4(props):
x = np.arange(0., 5., 0.2)
y = x**2
fig, ax = plt.subplots()
ax.plot(x, y, **props)
plot(fig)
```

The spreadsheet includes a list of available properties, but see the manual for full details:

Function 5 plots a histogram, using the plt.hist method:

```
@xl_func
def MPLTute_5():
np.random.seed(19680801)
mu, sigma = 100, 15
x = mu + sigma * np.random.randn(10000)
fig, ax = plt.subplots()
# the histogram of the data
# Changed from the tutorial example: "n, bins, patches = plt.hist(x, 50, normed=1, facecolor='g', alpha=0.75)"
# because use of normed now raises an error.
n, bins, patches = plt.hist(x, 50, density=1, facecolor='g', alpha=0.75)
plt.xlabel('Smarts')
plt.ylabel('Probability')
plt.title('Histogram of IQ')
plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
plt.axis([40, 160, 0, 0.03])
plt.grid(True)
plot(fig)
```

The comment noting the change from the code in the tutorial refers to the document for Release 2.02. The current tutorial (Release 3.4.3) has been corrected.

Function 6 illustrates the addition of text and graphics to the graph:

```
@xl_func
def MPLTute_6():
fig, ax = plt.subplots()
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),)
plt.ylim(-2,2)
plot(fig)
```

Finally Functions 7 and 7a illustrate the use of different axis types, and returning multiple graphs from a single function:

```
@xl_func
@xl_arg('scaletype', 'int')
def MPLTute_7(scaletype):
fig, ax = plt.subplots()
from matplotlib.ticker import NullFormatter # useful for `logit` scale
# Fixing random state for reproducibility
np.random.seed(19680801)
# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))
# plot with selected axes scale
if scaletype == 1:
# plt.figure(1)
# # linear
# plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)
# log
elif scaletype == 2:
# plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)
# symmetric log
elif scaletype == 3:
# plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.01)
plt.title('symlog')
plt.grid(True)
# logit
elif scaletype == 4:
# plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
else:
return 'scaletype must be between 1 and 4'
# Format the minor tick labels of the y-axis into empty strings with
# `NullFormatter`, to avoid cumbering the axis with too many labels.
plt.gca().yaxis.set_minor_formatter(NullFormatter())
# Adjust the subplot layout, because the logit one may take more space
# than usual, due to y-tick labels like "1 - 10^{-3}"
# Not required, Excel version returns only 1 chart
# plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
# wspace=0.35)
plot(fig)
@xl_func
def MPLTute_7a():
# Multiplot version
fig, ax = plt.subplots()
from matplotlib.ticker import NullFormatter # useful for `logit` scale
# Fixing random state for reproducibility
np.random.seed(19680801)
# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))
# plot with various axes scales
fig = plt.figure(1)
# # linear
plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)
# log
plt.subplot(222)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)
# symmetric log
plt.subplot(223)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.01)
plt.title('symlog')
plt.grid(True)
# logit
plt.subplot(224)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)
# Format the minor tick labels of the y-axis into empty strings with
# `NullFormatter`, to avoid cumbering the axis with too many labels.
plt.gca().yaxis.set_minor_formatter(NullFormatter())
# Adjust the subplot layout, because the logit one may take more space
# than usual, due to y-tick labels like "1 - 10^{-3}"
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
wspace=0.35)
plot(fig)
```

To search for alternate channels that may provide the conda package you’re

looking for, navigate to https://anaconda.org and use the search bar at the top of the page.

This provides the link: https://anaconda.org/haasad/pypardiso

which provides the command line:

conda install -c haasad pypardiso

Ensure that the Intel MKL library is installed before installing pypardiso.

The pypardiso Github site is at:https://github.com/haasad/PyPardisoProject

This provides the following documentation:

I have now modified my py_spsolve function to use the PyPardiso solver by default:

```
@xl_func(disable_function_wizard_calc=True)
@xl_func(category = "Scipy-Linalg")
@xl_arg('v', 'numpy_array', ndim=1)
@xl_arg('i', 'numpy_array', ndim=1)
@xl_arg('j', 'numpy_array', ndim=1)
@xl_arg('ya', 'numpy_array', ndim=1)
@xl_return('numpy_array')
def py_spsolve(v, i, j, ya, out = 0, use_pardiso = True, permc_spec = 'MMD_AT_PLUS_A'):
"""
Solve a matrix equation Ax = y in sparse COO format.
:param v: value vector
:param i: row index vector
:param j: column index vector
:param ya: y vector
"""
timea = np.zeros((1,2))
stime =time.perf_counter()
n = ya.size
A = ssp.coo_matrix((v,(i,j)),shape=(n,n)).tocsc()
timea[0,0] = time.perf_counter()-stime
if use_pardiso:
res = spsolve(A, ya)
else:
res = sspla.spsolve(A, ya, permc_spec)
timea[0,1] = time.perf_counter()-stime
if out == 1:
return timea
else:
return res
```

“The Irish music community, and indeed the much larger community throughout the world who found such inspiration in his work, will have learned with great sadness today of the passing of Paddy Moloney. […] Paddy, with his extraordinary skills as an instrumentalist, notably the uilleann pipes and bodhrán, was at the forefront of the renaissance of interest in Irish music, bringing a greater appreciation of Irish music and culture internationally.”

In my opinion some of their best work was produced with other World renowned artists, of which the performance below with Sinead O’Connor is an exceptional example:

For more on the history of the Chieftains see:

]]>The sparse solver function spsolve has an optional argument called permc_spec that defines “how to permute the columns of the matrix for sparsity preservation”. My timings for the previous post found that the argument NATURAL had very much better performance than any other alternative. This time, NATURAL performed as before, but all the other options had substantially improved, so that it had become the second slowest for the matrix used in the previous post, and the slowest for larger matrices.

The solvers have been compared for solving the stiffness matrix for 3D building frames of increasing size. The table below shows the timing results for the four spsolve options, the banded solver, two iterative solver options, and the PyPardiso solver. Each option has been timed for a system with 14742 equations (as in the previous post), 29487 equations, and 56705 equations.

Two times are reported for the Pardiso function: the time for the first calculation, and a much faster time for subsequent calculations, provided the stiffness matrix is unchanged.

- The MMD_AT_PLUS_A option is now the fastest of the four spsolve options.
- The Pardiso function is substantially faster than any spsolve option on the first run, especially for the larger equation systems, and of the order of 50 times faster again on subsequent runs with the same stiffness matrix.
- The banded solver was slower than Pardiso for the two smaller systems, and failed with a memory error for the third.
- The two iterative solvers were faster than Pardiso, especially for the largest system, but returned solutions with significant differences from the “exact” solver solutions. Also note that if the same stiffness matrix was to be used with multiple load cases then Pardiso would be very much quicker.

In summary the Pardiso package was substantially faster than any spsolve option, especially for very large systems, was also faster than the banded solver, and was able to handle larger systems than the banded solver.

In the next post I will look at procedures for installing the Pardiso package, and calling the function from Excel using pyxll.

]]>Python 3.10 is now available–but should you switch to it immediately? And if not now, when?

The short answer is, no, you probably don’t want to switch immediately; quite possibly you

can’tswitch immediately. To understand why, we need to consider Python packaging, the software development process, and take a look at the history of past releases.We can then make a guess about when Python 3.10 will actually be usable.

The problems with a new major Python release

I’m writing this article on October 5th, 2021, the day after 3.10 was released… and far too early to start using Python 3.10.

As with many open source projects, Python, Python libraries, and most of the toolchain and packages mentioned in this article are based on volunteer labor. None of this should be read as a complaint towards the people doing the maintenance, they’re doing hugely valuable work for free, and everything takes time. In addition, because upgrades involve so many different groups, coordination and releases take even more time.

With that in mind, let’s consider the problems with using 3.10 today: …

by Itamar Turner-Trauring

]]>

This has been a fun little restoration project. My friend and music journalist Simon Jones handed me an old VHS tape recently. On it he tells me, “is the holy grail for Steeleye Span fans. A very rare recording of a TV appearance they made in 1970.” His request was simple, “could you copy the programme from the tape?” The transfer process was simple enough, if a little convoluted. I still have a couple of fully functioning JVC S-VHS decks, so that helped a whole lot! The one I plumped for is a HR-S8700EK. A S-VHS deck with a built in digital time base corrector, which I would use to stabilise the video image. The audio and video outputs from this were hooked up to a Pioneer DVR-720H DVD/Hard Disk recorder and the programme was recorded digitally. Normally I would capture the tape directly to my PC with a Terratec Firewire capture card but in this case there was some tape deterioration and the resultant picture disturbance upset the capture card and interrupted the capture. The Pioneer unit was far more tolerant to the signal and recorded it cleanly. The disk recording was burned to a DVD which was transferred to my editing PC. Here it was upscaled to 1080p, pillar-boxed and set in a 16:9 frame to look better on modern widescreen monitors and colour corrected, just a tad. The Music Room programme has the ATV ident and countdown clock at the beginning so its likely the recording came from ATV themselves, perhaps as someone’s archive copy. If anyone has details on that please let me know.

Tim Vickerstaff

Live At My Father’s Place, Roslyn, New York, 20th April 1973

]]>00:00 One Misty Moisty Morning

04:59 The Ups And Downs

09:54 Oak Tree, Pigeon At The Gate

13:58 Sheep-Crook And Black Dog

20:18 Spotted Cow

24:41 Lady Isabelle Barnet’s Jigs

28:11 Rogues In A Nation

33:26 Cam Ye Oer Frae France

38:18 Alison Gross

43:31 Robbery With Violins

47:40 Gaudete

51:36 Royal Forester

57:05 Mason’s Apron