File: dlassq.go

package info (click to toggle)
golang-gonum-v1-gonum 0.15.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 18,792 kB
  • sloc: asm: 6,252; fortran: 5,271; sh: 377; ruby: 211; makefile: 98
file content (131 lines) | stat: -rw-r--r-- 3,188 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright ©2015 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gonum

import "math"

// Dlassq updates a sum of squares represented in scaled form. Dlassq returns
// the values scl and smsq such that
//
//	scl^2*smsq = X[0]^2 + ... + X[n-1]^2 + scale^2*sumsq
//
// The value of sumsq is assumed to be non-negative.
//
// Dlassq is an internal routine. It is exported for testing purposes.
func (impl Implementation) Dlassq(n int, x []float64, incx int, scale float64, sumsq float64) (scl, smsq float64) {
	// Implementation based on Supplemental Material to:
	// Edward Anderson. 2017. Algorithm 978: Safe Scaling in the Level 1 BLAS.
	// ACM Trans. Math. Softw. 44, 1, Article 12 (July 2017), 28 pages.
	// DOI: https://doi.org/10.1145/3061665
	switch {
	case n < 0:
		panic(nLT0)
	case incx <= 0:
		panic(badIncX)
	case len(x) < 1+(n-1)*incx:
		panic(shortX)
	}

	if math.IsNaN(scale) || math.IsNaN(sumsq) {
		return scale, sumsq
	}

	if sumsq == 0 {
		scale = 1
	}
	if scale == 0 {
		scale = 1
		sumsq = 0
	}

	if n == 0 {
		return scale, sumsq
	}

	// Compute the sum of squares in 3 accumulators:
	//  - abig: sum of squares scaled down to avoid overflow
	//  - asml: sum of squares scaled up to avoid underflow
	//  - amed: sum of squares that do not require scaling
	// The thresholds and multipliers are:
	//  - values bigger than dtbig are scaled down by dsbig
	//  - values smaller than dtsml are scaled up by dssml
	var (
		isBig            bool
		asml, amed, abig float64
	)
	for i, ix := 0, 0; i < n; i++ {
		ax := math.Abs(x[ix])
		switch {
		case ax > dtbig:
			ax *= dsbig
			abig += ax * ax
			isBig = true
		case ax < dtsml:
			if !isBig {
				ax *= dssml
				asml += ax * ax
			}
		default:
			amed += ax * ax
		}
		ix += incx
	}
	// Put the existing sum of squares into one of the accumulators.
	if sumsq > 0 {
		ax := scale * math.Sqrt(sumsq)
		switch {
		case ax > dtbig:
			if scale > 1 {
				scale *= dsbig
				abig += scale * (scale * sumsq)
			} else {
				// sumsq > dtbig^2 => (dsbig * (dsbig * sumsq)) is representable.
				abig += scale * (scale * (dsbig * (dsbig * sumsq)))
			}
		case ax < dtsml:
			if !isBig {
				if scale < 1 {
					scale *= dssml
					asml += scale * (scale * sumsq)
				} else {
					// sumsq < dtsml^2 => (dssml * (dssml * sumsq)) is representable.
					asml += scale * (scale * (dssml * (dssml * sumsq)))
				}
			}
		default:
			amed += scale * (scale * sumsq)
		}
	}
	// Combine abig and amed or amed and asml if more than one accumulator was
	// used.
	switch {
	case abig > 0:
		// Combine abig and amed:
		if amed > 0 || math.IsNaN(amed) {
			abig += (amed * dsbig) * dsbig
		}
		scale = 1 / dsbig
		sumsq = abig
	case asml > 0:
		// Combine amed and asml:
		if amed > 0 || math.IsNaN(amed) {
			amed = math.Sqrt(amed)
			asml = math.Sqrt(asml) / dssml
			ymin, ymax := asml, amed
			if asml > amed {
				ymin, ymax = amed, asml
			}
			scale = 1
			sumsq = ymax * ymax * (1 + (ymin/ymax)*(ymin/ymax))
		} else {
			scale = 1 / dssml
			sumsq = asml
		}
	default:
		scale = 1
		sumsq = amed
	}
	return scale, sumsq
}