MatrixOp wave averaging and getting it really fast

Hi,

I want to average some columns from some waves. These waves have many points.
I'm currently using fWaveAverage but need to get something quicker (IP7).

I've come up with the following test procedures (number of points are realistic, number of waves are larger):


Function DoStuff()

	variable timer

	variable numPoints = 2e5
	variable numCols   = 12
	variable numWaves  = 3

	Make/N=(numPoints, numCols)/O input1, input2, input3

	input1[][] = enoise(p*q)
	input2[][] = enoise(p*q)
	input3[][] = enoise(p*q)

	/// #1
	timer = startmstimer

	MatrixOp/O input1Single = col(input1, 1)
	MatrixOp/O input2Single = col(input2, 5)
	MatrixOp/O input3Single = col(input3, 10)

	fWaveAverage("input1Single;input2Single;input3Single;", "", 0, 0, "root:average1", "")
	Wave average1

	print stopmstimer(timer)/1e6

	Make/O/N=(numWaves, numPoints) matrix

	/// #2
	timer = startmstimer

	Multithread matrix[0][] = input1[q][1]
	Multithread matrix[1][] = input2[q][5]
	Multithread matrix[2][] = input3[q][10]

	MatrixOp/O average2 = averageCols(matrix)^t
	
	printf "%g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average2, 1)

	/// #3
	timer = startmstimer

	MatrixOp/FREE data = col(input1, 1)
	MultiThread matrix[0][] = data[q]

	MatrixOp/FREE data = col(input2, 5)
	MultiThread matrix[1][] = data[q]

	MatrixOp/FREE data = col(input3, 10)
	MultiThread matrix[2][] = data[q]

	MatrixOp/O average3 = averageCols(matrix)^t

	printf "%g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average3, 1)

	/// #4
	timer = startmstimer

	MatrixOP/FREE matrix = setRow(matrix, 0, col(input1, 1))
	MatrixOP/FREE matrix = setRow(matrix, 1, col(input2, 5))
	MatrixOP/FREE matrix = setRow(matrix, 2, col(input3, 10))

	MatrixOp/O average4 = averageCols(matrix)^t

	printf "%g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average4, 1)

		/// #5
	timer = startmstimer

	MatrixOp/O average5 = averageCols(setRow(setRow(setRow(matrix, 1, col(input2, 5)), 0, col(input1, 1)), 2, col(input3, 10)))^t

	printf "%g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average5, 1)
End


with the results

•dostuff() 0.023102 0.00567959, 1 0.0080306, 1 0.00736661, 1 0.0038697, 1

So 5 looks best, but the problem is that neither the column from the input waves nor the number of input waves are known beforehand.
Is there some way of construction the MatrixOP line from example 5 at runtime? Using something like Execute "MatrixOP" is a bit messy for my taste.

Thanks a lot for reading that far ;)
Thomas
Hello Thomas,

I don't like version (5) because it is difficult to read. I am also confused as to why you chose to use setRow() with ^t when setCol() is an available option and is more efficient for large number of points.

I also don't know that it is more efficient to combine the 3 columns as opposed to executing something like:

MatrixOP/O aa=averagecols(col(inwave,colnum))


A.G.
Igor wrote: I don't like version (5) because it is difficult to read.


I'm not fond of that either, very much write only code.

Igor wrote:
I am also confused as to why you chose to use setRow() with ^t when setCol() is an available option and is more efficient for large number of points.


Well in the end I want to have a 1D wave with the averages and averageCols returns a 1xm wave, so I thought transposing it is the right thing to do.
Also if I use setCol I have to transpose matrix again as I want to average not each input wave but the same points from all input waves.

I've added a two more tests:


#include <Waves Average>

Function DoStuff()
 
	variable timer
 
	variable numPoints = 2e5
	variable numCols   = 12
	variable numWaves  = 3
 
	Make/N=(numPoints, numCols)/O input1, input2, input3
 
	input1[][] = enoise(p*q)
	input2[][] = enoise(p*q)
	input3[][] = enoise(p*q)
 
	/// #1
	timer = startmstimer
 
	MatrixOp/O input1Single = col(input1, 1)
	MatrixOp/O input2Single = col(input2, 5)
	MatrixOp/O input3Single = col(input3, 10)
 
	fWaveAverage("input1Single;input2Single;input3Single;", "", 0, 0, "root:average1", "")
	Wave average1
 
	printf "1: %g, %g\r", stopmstimer(timer)/1e6, 1
 
	Make/O/N=(numWaves, numPoints) matrix
 
	/// #2
	timer = startmstimer
 
	Multithread matrix[0][] = input1[q][1]
	Multithread matrix[1][] = input2[q][5]
	Multithread matrix[2][] = input3[q][10]
 
	MatrixOp/O average2 = averageCols(matrix)^t
 
	printf "2: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average2, 1)
 
	/// #3
	timer = startmstimer
 
	MatrixOp/FREE data = col(input1, 1)
	MultiThread matrix[0][] = data[q]
 
	MatrixOp/FREE data = col(input2, 5)
	MultiThread matrix[1][] = data[q]
 
	MatrixOp/FREE data = col(input3, 10)
	MultiThread matrix[2][] = data[q]
 
	MatrixOp/O average3 = averageCols(matrix)^t
 
	printf "3: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average3, 1)
 
	/// #4
	timer = startmstimer
 
	MatrixOP/FREE matrix = setRow(matrix, 0, col(input1, 1))
	MatrixOP/FREE matrix = setRow(matrix, 1, col(input2, 5))
	MatrixOP/FREE matrix = setRow(matrix, 2, col(input3, 10))
 
	MatrixOp/O average4 = averageCols(matrix)^t
 
	printf "4: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average4, 1)
 
	/// #5
	timer = startmstimer
 
	MatrixOp/O average5 = averageCols(setRow(setRow(setRow(matrix, 1, col(input2, 5)), 0, col(input1, 1)), 2, col(input3, 10)))^t
 
	printf "5: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average5, 1)

	Make/O/N=(numPoints, numWaves) matrix

	/// #6
	timer = startmstimer

	MatrixOP/FREE matrix = setCol(matrix, 0, col(input1, 1))
	MatrixOP/FREE matrix = setCol(matrix, 1, col(input2, 5))
	MatrixOP/FREE matrix = setCol(matrix, 2, col(input3, 10))

	MatrixOp/O average6 = averageCols(matrix^t)^t
 
	printf "6: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average6, 1) 

	/// #7
	timer = startmstimer

	MatrixOp/O average7 = averageCols(setCol(setCol(setCol(matrix, 1, col(input2, 5)), 0, col(input1, 1)), 2, col(input3, 10))^t)^t

	printf "7: %g, %g\r", stopmstimer(timer)/1e6, EqualWaves(average1, average7, 1)
End


and these now run with

•dostuff() 1: 0.0247083, 1 2: 0.00551995, 1 3: 0.00792857, 1 4: 0.00731161, 1 5: 0.00384668, 1 6: 0.00865464, 1 7: 0.00467676, 1
This is how I would do it, but I am using Igor 6.37 not Igor 7.

It was a little difficult to understand what you wanted; I hope I got it right.

Generally I noticed that FastOP is faster than MatrixOP for some of the operations. I wanted to MultiThread the heaviest calculation, but apparently that has almost no effect on MatrixOP calculations. I also noticed that it was faster to do one operation at a time with FastOP and MatrixOP instead of multiple operations, e.g. it's faster to do a=a+b and then a=a+c, than it is to do them in one step a=a+b+c.

If you want to use EqualWaves, I suggest doing it outside the timed sequence, since EqualWaves took a not insignificant amount of time to calculate when I tested it.


Function DoStuff()
 
	variable numRows=2e5
	variable numCols=12
	variable numWaves=30
 
	//	Creates the input waves
 	Make/O/FREE/WAVE/N=(numWaves) InputWaves
 	MultiThread InputWaves[]=DoStuffCreateInputWaves(p, numRows, numCols)		//	2.6 seconds
	
	//	Starts the timer
	Variable t=StartMSTimer

	//	Sums the rows of each input wave, one at a time. I meant to use multithreading, but it showed no improvement in speed
	Duplicate/O/FREE/WAVE InputWaves, SumRowWaves		//	0.02 ms
	SumRowWaves[]=DoStuffSumRows(InputWaves[p])			//	121 ms

	//	Adds all the summed rows together
	Wave SumRowWave=SumRowWaves[0]
	Duplicate/O SumRowWave, AverageWave	//	0.5 ms
	Variable i=0
	for (i=1; i<numWaves; i+=1)
		Wave SumRowWave=SumRowWaves[i]
		FastOP AverageWave=AverageWave+SumRowWave
	endfor	//	6.5 ms
	
	//	Normalizes with the total number of columns. If the number of columns are not the same for all waves, you will have to calculate the sum instead
	Variable a=1/(numWaves*numCols)
	FastOP AverageWave=(a)*AverageWave		//	0.14 ms
	
	//	Displays the elapsed time in miliseconds. 127 ms overall for 30 waves
	Print(StopMSTimer(t)/1000)
	Print(" ")
end

ThreadSafe Function/WAVE DoStuffCreateInputWaves(WaveNum, numRows, numCols)
Variable WaveNum, numRows, numCols
	Make/O/FREE/N=(numRows, numCols) InputWave
	InputWave[][] = enoise(p*q)
	Return InputWave
end

Function/WAVE DoStuffSumRows(InputWave)
Wave InputWave
	MatrixOP/O/FREE SumRowWave=sumRows(InputWave)
	Return SumRowWave
end
thomas_braun wrote:
Well in the end I want to have a 1D wave with the averages and averageCols returns a 1xm wave, so I thought transposing it is the right thing to do.
Also if I use setCol I have to transpose matrix again as I want to average not each input wave but the same points from all input waves.


FWIW, I just added one further optimization in MatrixOP for the special case of transposing 1D arrays.

A.G.

@olelytken: Thanks for your solution, but I want to average along the number of input waves and not along the number of points in each wave. Regarding
EqualWaves
, I *think* it is executed after
stopmstimer
.

@AG: Thanks, I'll give it a try.