UNPKG

5.96 kBJavaScriptView Raw
1'use strict'
2
3function factory (type, config, load, typed) {
4 const abs = load(require('../arithmetic/abs'))
5 const add = load(require('../arithmetic/add'))
6 const pow = load(require('../arithmetic/pow'))
7 const conj = load(require('../complex/conj'))
8 const sqrt = load(require('../arithmetic/sqrt'))
9 const multiply = load(require('../arithmetic/multiply'))
10 const equalScalar = load(require('../relational/equalScalar'))
11 const larger = load(require('../relational/larger'))
12 const smaller = load(require('../relational/smaller'))
13 const matrix = load(require('../../type/matrix/function/matrix'))
14
15 /**
16 * Calculate the norm of a number, vector or matrix.
17 *
18 * The second parameter p is optional. If not provided, it defaults to 2.
19 *
20 * Syntax:
21 *
22 * math.norm(x)
23 * math.norm(x, p)
24 *
25 * Examples:
26 *
27 * math.abs(-3.5) // returns 3.5
28 * math.norm(-3.5) // returns 3.5
29 *
30 * math.norm(math.complex(3, -4)) // returns 5
31 *
32 * math.norm([1, 2, -3], Infinity) // returns 3
33 * math.norm([1, 2, -3], -Infinity) // returns 1
34 *
35 * math.norm([3, 4], 2) // returns 5
36 *
37 * math.norm([[1, 2], [3, 4]], 1) // returns 6
38 * math.norm([[1, 2], [3, 4]], 'inf') // returns 7
39 * math.norm([[1, 2], [3, 4]], 'fro') // returns 5.477225575051661
40 *
41 * See also:
42 *
43 * abs, hypot
44 *
45 * @param {number | BigNumber | Complex | Array | Matrix} x
46 * Value for which to calculate the norm
47 * @param {number | BigNumber | string} [p=2]
48 * Vector space.
49 * Supported numbers include Infinity and -Infinity.
50 * Supported strings are: 'inf', '-inf', and 'fro' (The Frobenius norm)
51 * @return {number | BigNumber} the p-norm
52 */
53 const norm = typed('norm', {
54 'number': Math.abs,
55
56 'Complex': function (x) {
57 return x.abs()
58 },
59
60 'BigNumber': function (x) {
61 // norm(x) = abs(x)
62 return x.abs()
63 },
64
65 'boolean': function (x) {
66 // norm(x) = abs(x)
67 return Math.abs(x)
68 },
69
70 'Array': function (x) {
71 return _norm(matrix(x), 2)
72 },
73
74 'Matrix': function (x) {
75 return _norm(x, 2)
76 },
77
78 'number | Complex | BigNumber | boolean, number | BigNumber | string': function (x) {
79 // ignore second parameter, TODO: remove the option of second parameter for these types
80 return norm(x)
81 },
82
83 'Array, number | BigNumber | string': function (x, p) {
84 return _norm(matrix(x), p)
85 },
86
87 'Matrix, number | BigNumber | string': function (x, p) {
88 return _norm(x, p)
89 }
90 })
91
92 /**
93 * Calculate the norm for an array
94 * @param {Matrix} x
95 * @param {number | string} p
96 * @returns {number} Returns the norm
97 * @private
98 */
99 function _norm (x, p) {
100 // size
101 const sizeX = x.size()
102
103 // check if it is a vector
104 if (sizeX.length === 1) {
105 // check p
106 if (p === Number.POSITIVE_INFINITY || p === 'inf') {
107 // norm(x, Infinity) = max(abs(x))
108 let pinf = 0
109 // skip zeros since abs(0) === 0
110 x.forEach(
111 function (value) {
112 const v = abs(value)
113 if (larger(v, pinf)) { pinf = v }
114 },
115 true)
116 return pinf
117 }
118 if (p === Number.NEGATIVE_INFINITY || p === '-inf') {
119 // norm(x, -Infinity) = min(abs(x))
120 let ninf
121 // skip zeros since abs(0) === 0
122 x.forEach(
123 function (value) {
124 const v = abs(value)
125 if (!ninf || smaller(v, ninf)) { ninf = v }
126 },
127 true)
128 return ninf || 0
129 }
130 if (p === 'fro') {
131 return _norm(x, 2)
132 }
133 if (typeof p === 'number' && !isNaN(p)) {
134 // check p != 0
135 if (!equalScalar(p, 0)) {
136 // norm(x, p) = sum(abs(xi) ^ p) ^ 1/p
137 let n = 0
138 // skip zeros since abs(0) === 0
139 x.forEach(
140 function (value) {
141 n = add(pow(abs(value), p), n)
142 },
143 true)
144 return pow(n, 1 / p)
145 }
146 return Number.POSITIVE_INFINITY
147 }
148 // invalid parameter value
149 throw new Error('Unsupported parameter value')
150 }
151 // MxN matrix
152 if (sizeX.length === 2) {
153 // check p
154 if (p === 1) {
155 // norm(x) = the largest column sum
156 const c = []
157 // result
158 let maxc = 0
159 // skip zeros since abs(0) == 0
160 x.forEach(
161 function (value, index) {
162 const j = index[1]
163 const cj = add(c[j] || 0, abs(value))
164 if (larger(cj, maxc)) { maxc = cj }
165 c[j] = cj
166 },
167 true)
168 return maxc
169 }
170 if (p === Number.POSITIVE_INFINITY || p === 'inf') {
171 // norm(x) = the largest row sum
172 const r = []
173 // result
174 let maxr = 0
175 // skip zeros since abs(0) == 0
176 x.forEach(
177 function (value, index) {
178 const i = index[0]
179 const ri = add(r[i] || 0, abs(value))
180 if (larger(ri, maxr)) { maxr = ri }
181 r[i] = ri
182 },
183 true)
184 return maxr
185 }
186 if (p === 'fro') {
187 // norm(x) = sqrt(sum(diag(x'x)))
188 let fro = 0
189 x.forEach(
190 function (value, index) {
191 fro = add(fro, multiply(value, conj(value)))
192 })
193 return abs(sqrt(fro))
194 }
195 if (p === 2) {
196 // not implemented
197 throw new Error('Unsupported parameter value, missing implementation of matrix singular value decomposition')
198 }
199 // invalid parameter value
200 throw new Error('Unsupported parameter value')
201 }
202 }
203
204 norm.toTex = {
205 1: `\\left\\|\${args[0]}\\right\\|`,
206 2: undefined // use default template
207 }
208
209 return norm
210}
211
212exports.name = 'norm'
213exports.factory = factory