Sorry.
Here they are in the text.
File qrhh-4.ijs
qrhh=: 3 : 0
NB. QR decomposition using the Householder reflections.
A=.y
'm n'=.$A
Q=.=/~i.m
for_k. i.n do.
z=.(<"1(k+i.(m-k)),.k){A
nz=.%:+/|*:z
v=.-(({.z)+(nz*(*{.z))),}.z
nv=.%:+/|*:v
if. nv<2^_52 do. continue. end.
v=.v%nv
v=.,.v
for_j. i.n do.
a=.(<"1(k+i.(m-k)),.j){A
a=.,.a
a=.(a-+:v(+/ .*)(+|:v)(+/ .*)a)
a=.,a
A=.a (<"1(k+i.(m-k)),.j)}A
end.
for_j. i.m do.
q=.(<"1(k+i.(m-k)),.j){Q
Q=.(q-+:v(+/ .*)(+|:v)(+/ .*)q) (<"1(k+i.(m-k)),.j)}Q
end.
end.
Q=.+|:Q
R=.A*((i.m)<:/(i.n))
Q;R
)
testortho=: 3 : 0
I=.=/~i.#y
a=.>./|,(y(+/ .*)(+|:y))-I
b=.>./|,((+|:y)(+/ .*)y)-I
a,b
)
testqr=: 3 : 0
'q r'=.qrhh y
c=.>./|,(q(+/ .*)r)-y
c,testortho q
)
testinv=: 4 : 0
I=.=/~i.#x
a=.>./|,(x(+/ .*)y)-I
b=.>./|,(y(+/ .*)x)-I
a,b
)
qrinv=: 3 : 0
'q r'=.qrhh y
I=.=/~i.#y
(I rinv r)(+/ .*)(+|:q)
)
qrdiv=: 4 : 0
'q r'=.qrhh y
((+|:q)(+/ .*)x) rinv r
)
rinv=: 4 : 0
NB. Solve an upper triangular system of equations Rx=b as
NB. x=R^(-1)*b.
'b'=.x
'R'=.y
'm n'=.$R
'mm p'=.2{.($b),1
if. m~:mm do. [:'rinv, Incompatible dimensions.' end.
x=. (n,p) $ 0
for_i. <:n-i.n do.
x=.((i{b)%((<(i,i)){R)) i}x
if. i=0 do. break. end.
bb=.(i{.b)-(((<"1((i.i),.i)){R)(*/)(i{x))
b=.bb (i.i)}b
end.
x
)
File test_qrhh_3_1.ijs
NB. Test a few examples of solving linear least squares using
NB. the QR decomposition in qrhh using the Householder reflections.
NB. Example 1.
A=:p:i.32 32
B=:%.A
A testinv B
BB=:qrinv A
A testinv BB
NB. Example 2.
A=:p:i.100 100
B=:%.A
A testinv B
BB=:qrinv A
A testinv BB
NB. Example 3.
x=:_0.5+(i.1000)%999
b=:^x
X=:(1 o. (x */ >:i.10)) ,. (2 o. (x */ i.11))
,.c=:b %. X
0j15":>./|(X (+/ . *) c)-b
,.cc=: (,.b) qrdiv X
0j15":>./|(X (+/ . *) cc)-b
'q r'=: 128!:0 X
>./|,((|:q)(+/ .*)q)-(=/~i.1{$q)
NB. Example 4.
y=:_0.5+(i.1e6)%999999
0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){c)) + ((2 o. (y */
(i.11))) (+/ . *) ((10+i.11){c)))-(,.^y)
0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){cc)) + ((2 o. (y
*/ (i.11))) (+/ . *) ((10+i.11){cc)))-(,.^y)
NB. Example 5.
x=:_0.5+(i.1000)%999
c=:(^x) %. X=:(^ 0j1 * x */ i:10)
(i.5){"1(i.4){X
,.c
>./,|(X(+/ .*)c)-^x
y=:_0.5+(i.1e6)%999999
>./,|((^ 0j1 * y */ i:10)(+/ .*)c)-(^y)
]cc=:(,.^x) qrdiv X
>./,|(X(+/ .*)cc)-^x
>./,|((^ 0j1 * y */ i:10)(+/ .*)cc)-(^y)
NB. Sample run. Windows 10, i5, J805.
NB. NB. Test a few examples of solving linear least squares using
NB. NB. the QR decomposition in qrhh using the Householder reflections.
NB.
NB. NB. Example 1.
NB. A=:p:i.32 32
NB. B=:%.A
NB. A testinv B
NB. 1.48818e_9 1.05908e_6
NB. BB=:qrinv A
NB. A testinv BB
NB. 8.81073e_12 1.45519e_11
NB. NB. Example 2.
NB. A=:p:i.100 100
NB. B=:%.A
NB. A testinv B
NB. 1.21006e_5 0.0582391
NB. BB=:qrinv A
NB. A testinv BB
NB. 1.01622e_10 1.69166e_10
NB. NB. Example 3.
NB. x=:_0.5+(i.1000)%999
NB. b=:^x
NB. X=:(1 o. (x */ >:i.10)) ,. (2 o. (x */ i.11))
NB. ,.c=:b %. X
NB. 9.98124e8
NB. _1.40794e9
NB. 1.15836e9
NB. _6.34655e8
NB. 2.23796e8
NB. _3.80026e7
NB. _5.72319e6
NB. 4.98532e6
NB. _1.17017e6
NB. 105348
NB. 97984.1
NB. 1.97192e7
NB. _5.38456e7
NB. 6.1024e7
NB. _3.64392e7
NB. 7.47621e6
NB. 5.76293e6
NB. _5.64701e6
NB. 2.2945e6
NB. _487483
NB. 44448
NB. 0j15":>./|(X (+/ . *) c)-b
NB. 0.003757055930316
NB. ,.cc=: (,.b) qrdiv X
NB. 1.05954
NB. 1.17629
NB. _1.97503
NB. 1.59316
NB. _0.88378
NB. 0.357739
NB. _0.105075
NB. 0.0213878
NB. _0.00271282
NB. 0.000162104
NB. 1.26732
NB. 0.750993
NB. _1.96767
NB. 1.52989
NB. _0.850343
NB. 0.36627
NB. _0.122327
NB. 0.0307885
NB. _0.00551343
NB. 0.000627413
NB. _3.4154e_5
NB. 0j15":>./|(X (+/ . *) cc)-b
NB. 0.000000000000005
NB. 'q r'=: 128!:0 X
NB. >./|,((|:q)(+/ .*)q)-(=/~i.1{$q)
NB. 0.999904
NB. NB. Example 4.
NB. y=:_0.5+(i.1e6)%999999
NB. 0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){c)) + ((2 o.
(y */ (i.11))) (+/ . *) ((10+i.11){c)))-(,.^y)
NB. 0.003757055988523
NB. 0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){cc)) + ((2
o. (y */ (i.11))) (+/ . *) ((10+i.11){cc)))-(,.^y)
NB. 0.000000000000005
NB.
NB. NB. Example 5.
NB. x=:_0.5+(i.1000)%999
NB. c=:(^x) %. X=:(^ 0j1 * x */ i:10)
NB. (i.5){"1(i.4){X
NB. 0.283662j_0.958924 _0.210796j_0.97753 _0.653644j_0.756802
_0.936457j_0.350783 _0.989992j0.14112
NB. 0.274049j_0.961716 _0.219594j_0.975591 _0.659683j_0.751544
_0.938892j_0.344213 _0.989127j0.147063
NB. 0.264409j_0.964411 _0.228374j_0.973574 _0.66568j_0.746237
_0.94128j_0.337626 _0.988226j0.153001
NB. 0.254742j_0.967009 _0.237135j_0.971477 _0.671635j_0.740882
_0.943623j_0.331022 _0.987289j0.158934
NB. ,.c
NB. 4.05363e6j_2.63539e6
NB. _5.04244e7j3.27836e7
NB. 2.90519e8j_1.88887e8
NB. _1.02273e9j6.64963e8
NB. 2.44111e9j_1.58715e9
NB. _4.13678e9j2.6895e9
NB. 5.05136e9j_3.2837e9
NB. _4.39569e9j2.8568e9
NB. 2.60811e9j_1.69431e9
NB. _9.47205e8j6.14882e8
NB. 1.5859e8j_1.02848e8
NB. _1.84539e7j1.19961e7
NB. 7.13468e7j_4.62898e7
NB. _1.82918e8j1.19028e8
NB. 3.31731e8j_2.16376e8
NB. _3.96625e8j2.58975e8
NB. 3.10552e8j_2.02872e8
NB. _1.60195e8j1.04678e8
NB. 5.30711e7j_3.46859e7
NB. _1.03281e7j6.75135e6
NB. 904018j_591051
NB. >./,|(X(+/ .*)c)-^x
NB. 0.683384
NB. y=:_0.5+(i.1e6)%999999
NB. >./,|((^ 0j1 * y */ i:10)(+/ .*)c)-(^y)
NB. 0.683384
NB. ]cc=:(,.^x) qrdiv X
NB. _5.34697e_5j3.37098e_5
NB. 0.000929484j_0.000560383
NB. _0.00767342j0.0043707
NB. 0.0399318j_0.0210979
NB. _0.14652j0.0696962
NB. 0.401164j_0.162692
NB. _0.842944j0.258263
NB. 1.3613j_0.206456
NB. _1.58113j_0.228456
NB. 0.688643j1.03882
NB. 1.45135j0.0651759
NB. _0.279029j_1.15773
NB. _0.115054j0.31855
NB. _0.0147366j0.15007
NB. 0.0960854j_0.229403
NB. _0.0826808j0.150801
NB. 0.0417259j_0.0658458
NB. _0.0140469j0.020154
NB. 0.0031403j_0.00420601
NB. _0.000426684j0.000542159
NB. 2.6879e_5j_3.27488e_5
NB. >./,|(X(+/ .*)cc)-^x
NB. 9.09428e_15
NB. >./,|((^ 0j1 * y */ i:10)(+/ .*)cc)-(^y)
NB. 9.97174e_15
On Mon, May 3, 2021 at 4:00 PM Raul Miller <[email protected]> wrote:
> Nothing attached came through.
>
> You probably need to make your attachments be .txt files, to avoid spam
> filters.
>
> Thanks,
>
> --
> Raul
>
> On Mon, May 3, 2021 at 3:56 PM Imre Patyi <[email protected]> wrote:
> >
> > Thank you.
> >
> > I have played with implementing a QR algorithm in J,
> > called qrhh for QR via Householder reflections.
> > Please find it attached along with some tests.
> >
> > The given QR algorithm qrhh solves all the 5 test cases attached
> > while the built in %. solves none of them with comparable or even
> > acceptable accuracy.
> >
> > I am more and more convinced that the built-in %. is numerically very
> > unstable. It would be better to find a reputable QR implementation
> > that is numerically stable. I am sure there are nice implementations
> > of QR in C freely available that are numerically stable. Any
> > mathematically correct
> > QR algorithm can solve small examples, but to solve examples of
> > useful size, we need to have a numerically stable algorithm. Most
> > plain vanilla math algorithms have very little chance of being
> numerically
> > stable (i.e., they magnify the roundoff errors rather than keeping them
> > more or less constant), it is not for nothing that numerical analysts
> exist.
> >
> > The rosetta code site has a C implementation with Householder
> reflections.
> > I have not tried testing it, but it is likely to beat J's built-in %. by
> > several
> > orders of magnitude of accuracy if the implementation is correct.
> > The recursive algorithm given also on the same page of rosetta code
> > for J (and mentioned in this thread by Raul Miller) is based upon a
> clever
> > rewriting of the classical Gram--Schmidt orthogonalization process,
> > which is well-known to be numerically unstable and every book on
> > numerical analysis recommends against using it. While a numerically
> > unstable algorithm (of which 128!:0 is probably an example) can be
> accurate
> > on some examples, it often fails on similar examples for no clear reason.
> > See Example 3 in the attachment, where
> >
> > NB. 'q r'=: 128!:0 X
> > NB. >./|,((|:q)(+/ .*)q)-(=/~i.1{$q)
> > NB. 0.999904
> >
> > 128!:0 produces an "orthogonal" matrix Q with Q'Q-I having an entry
> > nearly 1 (all should be nearly zero). Here X is a 1000 by 21 real
> matrix,
> > with not too large or too small numbers. When %. solves a linear
> > least squares problem Ax=b, or QRx=b, it relies on Q'Q=I in order to
> solve
> > Rx=Q'b
> > by backward substitution. If Q'Q is not nearly I, it ( %. ) is likely to
> > fail badly.
> >
> > https://rosettacode.org/wiki/QR_decomposition#C
> >
> > https://code.jsoftware.com/wiki/Essays/QR_Decomposition
> >
> > https://en.wikipedia.org/wiki/QR_decomposition
> >
> > Book, "Numerical recipes in C".
> >
> > I would like to request that the J maintainers replace the current
> > %. with a numerically stable one. QR decomposition is a well
> > entrenched small program, and it would likely be easy enough to
> > find a suitable numerically stable replacement. It would cost you
> > little to fix it.
> >
> > Thank you very much.
> >
> > Yours sincerely,
> > Imre Patyi
> >
> >
> > On Sun, May 2, 2021 at 11:37 PM Henry Rich <[email protected]> wrote:
> >
> > > J does not use long double.
> > >
> > > It is possible that 8.07 used 8087 instructions with 80-bit
> > > floating-point; I don't remember.
> > >
> > > The QR algorithm in JE is a clever recursive algorithm that Roger
> > > invented. It uses +/ . * to do almost all the calculations;
> reciprocals
> > > are performed only at the bottom of the recursion, on 2x2 or 1x1
> matrices.
> > >
> > > At the lower levels of recursion it works with short, wide matrices
> that
> > > the blocking matrix-multiply code can't grip efficiently, so I added a
> > > version of +/ . * that does matrix multiplication via inner products
> two
> > > at a time: just the thing for such cases. The overall algorithm is
> > > unchanged.
> > >
> > > The differences you report look pretty small to me, and could be
> > > artifacts of differing orders of summation.
> > >
> > > I have seen references to what Python does, but I don't know what its
> > > qr() code is defined to do. If the matrix is ill-conditioned, perhaps
> > > some extra processing is performed.
> > >
> > > Henry Rich
> > >
> > >
> > >
> > ----------------------------------------------------------------------
> > For information about J forums see http://www.jsoftware.com/forums.htm
> ----------------------------------------------------------------------
> For information about J forums see http://www.jsoftware.com/forums.htm
>
NB. Test a few examples of solving linear least squares using
NB. the QR decomposition in qrhh using the Householder reflections.
NB. Example 1.
A=:p:i.32 32
B=:%.A
A testinv B
BB=:qrinv A
A testinv BB
NB. Example 2.
A=:p:i.100 100
B=:%.A
A testinv B
BB=:qrinv A
A testinv BB
NB. Example 3.
x=:_0.5+(i.1000)%999
b=:^x
X=:(1 o. (x */ >:i.10)) ,. (2 o. (x */ i.11))
,.c=:b %. X
0j15":>./|(X (+/ . *) c)-b
,.cc=: (,.b) qrdiv X
0j15":>./|(X (+/ . *) cc)-b
'q r'=: 128!:0 X
>./|,((|:q)(+/ .*)q)-(=/~i.1{$q)
NB. Example 4.
y=:_0.5+(i.1e6)%999999
0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){c)) + ((2 o. (y */
(i.11))) (+/ . *) ((10+i.11){c)))-(,.^y)
0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){cc)) + ((2 o. (y */
(i.11))) (+/ . *) ((10+i.11){cc)))-(,.^y)
NB. Example 5.
x=:_0.5+(i.1000)%999
c=:(^x) %. X=:(^ 0j1 * x */ i:10)
(i.5){"1(i.4){X
,.c
>./,|(X(+/ .*)c)-^x
y=:_0.5+(i.1e6)%999999
>./,|((^ 0j1 * y */ i:10)(+/ .*)c)-(^y)
]cc=:(,.^x) qrdiv X
>./,|(X(+/ .*)cc)-^x
>./,|((^ 0j1 * y */ i:10)(+/ .*)cc)-(^y)
NB. Sample run. Windows 10, i5, J805.
NB. NB. Test a few examples of solving linear least squares using
NB. NB. the QR decomposition in qrhh using the Householder reflections.
NB.
NB. NB. Example 1.
NB. A=:p:i.32 32
NB. B=:%.A
NB. A testinv B
NB. 1.48818e_9 1.05908e_6
NB. BB=:qrinv A
NB. A testinv BB
NB. 8.81073e_12 1.45519e_11
NB. NB. Example 2.
NB. A=:p:i.100 100
NB. B=:%.A
NB. A testinv B
NB. 1.21006e_5 0.0582391
NB. BB=:qrinv A
NB. A testinv BB
NB. 1.01622e_10 1.69166e_10
NB. NB. Example 3.
NB. x=:_0.5+(i.1000)%999
NB. b=:^x
NB. X=:(1 o. (x */ >:i.10)) ,. (2 o. (x */ i.11))
NB. ,.c=:b %. X
NB. 9.98124e8
NB. _1.40794e9
NB. 1.15836e9
NB. _6.34655e8
NB. 2.23796e8
NB. _3.80026e7
NB. _5.72319e6
NB. 4.98532e6
NB. _1.17017e6
NB. 105348
NB. 97984.1
NB. 1.97192e7
NB. _5.38456e7
NB. 6.1024e7
NB. _3.64392e7
NB. 7.47621e6
NB. 5.76293e6
NB. _5.64701e6
NB. 2.2945e6
NB. _487483
NB. 44448
NB. 0j15":>./|(X (+/ . *) c)-b
NB. 0.003757055930316
NB. ,.cc=: (,.b) qrdiv X
NB. 1.05954
NB. 1.17629
NB. _1.97503
NB. 1.59316
NB. _0.88378
NB. 0.357739
NB. _0.105075
NB. 0.0213878
NB. _0.00271282
NB. 0.000162104
NB. 1.26732
NB. 0.750993
NB. _1.96767
NB. 1.52989
NB. _0.850343
NB. 0.36627
NB. _0.122327
NB. 0.0307885
NB. _0.00551343
NB. 0.000627413
NB. _3.4154e_5
NB. 0j15":>./|(X (+/ . *) cc)-b
NB. 0.000000000000005
NB. 'q r'=: 128!:0 X
NB. >./|,((|:q)(+/ .*)q)-(=/~i.1{$q)
NB. 0.999904
NB. NB. Example 4.
NB. y=:_0.5+(i.1e6)%999999
NB. 0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){c)) + ((2
o. (y */ (i.11))) (+/ . *) ((10+i.11){c)))-(,.^y)
NB. 0.003757055988523
NB. 0j15":>./|(((1 o. (y */ (>:i.10))) (+/ . *) ((i.10){cc)) + ((2
o. (y */ (i.11))) (+/ . *) ((10+i.11){cc)))-(,.^y)
NB. 0.000000000000005
NB.
NB. NB. Example 5.
NB. x=:_0.5+(i.1000)%999
NB. c=:(^x) %. X=:(^ 0j1 * x */ i:10)
NB. (i.5){"1(i.4){X
NB. 0.283662j_0.958924 _0.210796j_0.97753 _0.653644j_0.756802
_0.936457j_0.350783 _0.989992j0.14112
NB. 0.274049j_0.961716 _0.219594j_0.975591 _0.659683j_0.751544
_0.938892j_0.344213 _0.989127j0.147063
NB. 0.264409j_0.964411 _0.228374j_0.973574 _0.66568j_0.746237
_0.94128j_0.337626 _0.988226j0.153001
NB. 0.254742j_0.967009 _0.237135j_0.971477 _0.671635j_0.740882
_0.943623j_0.331022 _0.987289j0.158934
NB. ,.c
NB. 4.05363e6j_2.63539e6
NB. _5.04244e7j3.27836e7
NB. 2.90519e8j_1.88887e8
NB. _1.02273e9j6.64963e8
NB. 2.44111e9j_1.58715e9
NB. _4.13678e9j2.6895e9
NB. 5.05136e9j_3.2837e9
NB. _4.39569e9j2.8568e9
NB. 2.60811e9j_1.69431e9
NB. _9.47205e8j6.14882e8
NB. 1.5859e8j_1.02848e8
NB. _1.84539e7j1.19961e7
NB. 7.13468e7j_4.62898e7
NB. _1.82918e8j1.19028e8
NB. 3.31731e8j_2.16376e8
NB. _3.96625e8j2.58975e8
NB. 3.10552e8j_2.02872e8
NB. _1.60195e8j1.04678e8
NB. 5.30711e7j_3.46859e7
NB. _1.03281e7j6.75135e6
NB. 904018j_591051
NB. >./,|(X(+/ .*)c)-^x
NB. 0.683384
NB. y=:_0.5+(i.1e6)%999999
NB. >./,|((^ 0j1 * y */ i:10)(+/ .*)c)-(^y)
NB. 0.683384
NB. ]cc=:(,.^x) qrdiv X
NB. _5.34697e_5j3.37098e_5
NB. 0.000929484j_0.000560383
NB. _0.00767342j0.0043707
NB. 0.0399318j_0.0210979
NB. _0.14652j0.0696962
NB. 0.401164j_0.162692
NB. _0.842944j0.258263
NB. 1.3613j_0.206456
NB. _1.58113j_0.228456
NB. 0.688643j1.03882
NB. 1.45135j0.0651759
NB. _0.279029j_1.15773
NB. _0.115054j0.31855
NB. _0.0147366j0.15007
NB. 0.0960854j_0.229403
NB. _0.0826808j0.150801
NB. 0.0417259j_0.0658458
NB. _0.0140469j0.020154
NB. 0.0031403j_0.00420601
NB. _0.000426684j0.000542159
NB. 2.6879e_5j_3.27488e_5
NB. >./,|(X(+/ .*)cc)-^x
NB. 9.09428e_15
NB. >./,|((^ 0j1 * y */ i:10)(+/ .*)cc)-(^y)
NB. 9.97174e_15
qrhh=: 3 : 0
NB. QR decomposition using the Householder reflections.
A=.y
'm n'=.$A
Q=.=/~i.m
for_k. i.n do.
z=.(<"1(k+i.(m-k)),.k){A
nz=.%:+/|*:z
v=.-(({.z)+(nz*(*{.z))),}.z
nv=.%:+/|*:v
if. nv<2^_52 do. continue. end.
v=.v%nv
v=.,.v
for_j. i.n do.
a=.(<"1(k+i.(m-k)),.j){A
a=.,.a
a=.(a-+:v(+/ .*)(+|:v)(+/ .*)a)
a=.,a
A=.a (<"1(k+i.(m-k)),.j)}A
end.
for_j. i.m do.
q=.(<"1(k+i.(m-k)),.j){Q
Q=.(q-+:v(+/ .*)(+|:v)(+/ .*)q) (<"1(k+i.(m-k)),.j)}Q
end.
end.
Q=.+|:Q
R=.A*((i.m)<:/(i.n))
Q;R
)
testortho=: 3 : 0
I=.=/~i.#y
a=.>./|,(y(+/ .*)(+|:y))-I
b=.>./|,((+|:y)(+/ .*)y)-I
a,b
)
testqr=: 3 : 0
'q r'=.qrhh y
c=.>./|,(q(+/ .*)r)-y
c,testortho q
)
testinv=: 4 : 0
I=.=/~i.#x
a=.>./|,(x(+/ .*)y)-I
b=.>./|,(y(+/ .*)x)-I
a,b
)
qrinv=: 3 : 0
'q r'=.qrhh y
I=.=/~i.#y
(I rinv r)(+/ .*)(+|:q)
)
qrdiv=: 4 : 0
'q r'=.qrhh y
((+|:q)(+/ .*)x) rinv r
)
rinv=: 4 : 0
NB. Solve an upper triangular system of equations Rx=b as
NB. x=R^(-1)*b.
'b'=.x
'R'=.y
'm n'=.$R
'mm p'=.2{.($b),1
if. m~:mm do. [:'rinv, Incompatible dimensions.' end.
x=. (n,p) $ 0
for_i. <:n-i.n do.
x=.((i{b)%((<(i,i)){R)) i}x
if. i=0 do. break. end.
bb=.(i{.b)-(((<"1((i.i),.i)){R)(*/)(i{x))
b=.bb (i.i)}b
end.
x
)
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm