func main() { flag.Parse() if flag.NArg() == 0 { log.Fatal("missing file name") } name := flag.Args()[0] f, err := os.Open(name) if err != nil { log.Fatal(err) } defer f.Close() var r io.Reader if path.Ext(name) == ".gz" { gz, err := gzip.NewReader(f) if err != nil { log.Fatal(err) } name = strings.TrimSuffix(name, ".gz") r = gz } else { r = f } var aDok *sparse.DOK switch path.Ext(name) { case ".mtx": aDok, err = readMatrixMarket(r) case ".rsa": log.Fatal("reading of Harwell-Boeing format not yet implemented") default: log.Fatal("unknown file extension") } if err != nil { log.Fatal(err) } a := sparse.NewCSR(aDok) n, _ := a.Dims() x := make([]float64, n) for i := range x { x[i] = 1 } b := make([]float64, n) sparse.MulMatVec(1, false, a, x, 1, 0, b, 1) for i := range x { x[i] = 0 } result, err := iterative.Solve(a, b, x, nil, &iterative.CG{}) if err != nil { log.Fatal(err) } if len(result.X) > 10 { fmt.Println("Solution[:10]:", result.X[:10]) } else { fmt.Println("Solution:", result.X) } }
func iterate(method Method, a sparse.Matrix, b *mat64.Vector, settings *Settings, ctx *Context, stats *Stats) error { bNorm := mat64.Norm(b, 2) if bNorm == 0 { bNorm = 1 } op := method.Init(ctx) for { switch op { case NoOperation: case ComputeAp: ctx.Ap.ScaleVec(0, ctx.Ap) sparse.MulMatVec(ctx.Ap, 1, false, a, ctx.P) stats.MatVecMultiplies++ case ComputeAq: ctx.Aq.ScaleVec(0, ctx.Aq) sparse.MulMatVec(ctx.Aq, 1, false, a, ctx.Q) stats.MatVecMultiplies++ case SolvePreconditioner: // TODO(vladimir-ch): Add preconditioners. // Z = Residual ctx.Z.CopyVec(ctx.Residual) stats.PrecondionerSolves++ case CheckConvergence: stats.Iterations++ stats.Residual = mat64.Norm(ctx.Residual, 2) / bNorm fmt.Println(stats.Residual) if stats.Residual < settings.Tolerance { return nil } if stats.Iterations == settings.Iterations { return errors.New("iterative: reached iteration limit") } } op = method.Iterate(ctx) } }
func Solve(a sparse.Matrix, b, xInit *mat64.Vector, settings *Settings, method Method) (result Result, err error) { stats := Stats{ StartTime: time.Now(), } dim, c := a.Dims() if dim != c { panic("iterative: matrix is not square") } if xInit != nil && dim != xInit.Len() { panic("iterative: mismatched size of the initial guess") } if b.Len() != dim { panic("iterative: mismatched size of the right-hand side vector") } if xInit == nil { xInit = mat64.NewVector(dim, nil) } if settings == nil { settings = DefaultSettings(dim) } ctx := Context{ X: mat64.NewVector(dim, nil), Residual: mat64.NewVector(dim, nil), } // X = xInit ctx.X.CopyVec(xInit) if mat64.Norm(ctx.X, math.Inf(1)) > 0 { // Residual = Ax sparse.MulMatVec(ctx.Residual, 1, false, a, ctx.X) stats.MatVecMultiplies++ } // Residual = Ax - b ctx.Residual.SubVec(ctx.Residual, b) if mat64.Norm(ctx.Residual, 2) >= settings.Tolerance { err = iterate(method, a, b, settings, &ctx, &stats) } result = Result{ X: ctx.X, Stats: stats, Runtime: time.Since(stats.StartTime), } return result, err }
func Solve(a sparse.Matrix, b, xInit []float64, settings *Settings, method Method) (result Result, err error) { stats := Stats{ StartTime: time.Now(), } dim := len(xInit) if dim == 0 { panic("iterative: invalid dimension") } r, c := a.Dims() if r != c { panic("iterative: matrix is not square") } if c != dim { panic("iterative: mismatched size of the matrix") } if len(b) != dim { panic("iterative: mismatched size of the right-hand side vector") } if settings == nil { settings = DefaultSettings(dim) } ctx := Context{ X: make([]float64, dim), Residual: make([]float64, dim), } copy(ctx.X, xInit) copy(ctx.Residual, b) if floats.Norm(ctx.X, math.Inf(1)) > 0 { sparse.MulMatVec(-1, false, a, ctx.X, 1, 1, ctx.Residual, 1) stats.MatVecMultiplies++ } if floats.Norm(ctx.Residual, 2) >= settings.Tolerance { err = iterate(method, a, b, settings, &ctx, &stats) } result = Result{ X: ctx.X, Stats: stats, Runtime: time.Since(stats.StartTime), } return result, err }
func iterate(method Method, a sparse.Matrix, b []float64, settings *Settings, ctx *Context, stats *Stats) error { dim := len(ctx.X) bNorm := floats.Norm(b, 2) if bNorm == 0 { bNorm = 1 } request := method.Init(ctx) for { switch request { case NoOperation: case ComputeAp: ctx.Ap = resize(ctx.Ap, dim) sparse.MulMatVec(1, false, a, ctx.P, 1, 0, ctx.Ap, 1) stats.MatVecMultiplies++ case SolvePreconditioner: ctx.Z = resize(ctx.Z, dim) copy(ctx.Z, ctx.Residual) stats.PrecondionerSolves++ case CheckConvergence: stats.Iterations++ stats.Residual = floats.Norm(ctx.Residual, 2) / bNorm fmt.Println(stats.Residual) if stats.Residual < settings.Tolerance { return nil } if stats.Iterations == settings.Iterations { return errors.New("iterative: reached iteration limit") } } request = method.Iterate(ctx) } }
func main() { flag.Parse() if flag.NArg() == 0 { log.Fatal("missing file name") } name := flag.Args()[0] f, err := os.Open(name) if err != nil { log.Fatal(err) } defer f.Close() var r io.Reader if path.Ext(name) == ".gz" { gz, err := gzip.NewReader(f) if err != nil { log.Fatal(err) } name = strings.TrimSuffix(name, ".gz") r = gz } else { r = f } // blas64.Use(cgo.Implementation{}) blas64.Use(native.Implementation{}) var aDok *sparse.DOK switch path.Ext(name) { case ".mtx": aDok, err = readMatrixMarket(r) case ".rsa": log.Fatal("reading of Harwell-Boeing format not yet implemented") default: log.Fatal("unknown file extension") } if err != nil { log.Fatal(err) } a := sparse.NewCSR(aDok) n, _ := a.Dims() // Create the right-hand side so that the solution is [1 1 ... 1]. x := make([]float64, n) for i := range x { x[i] = 1 } xVec := mat64.NewVector(n, x) bVec := mat64.NewVector(n, make([]float64, n)) sparse.MulMatVec(bVec, 1, false, a, xVec) result, err := iterative.Solve(a, bVec, nil, nil, &iterative.CG{}) if err != nil { log.Fatal(err) } if result.X.Len() > 10 { fmt.Println("Solution[:10]:", result.X.RawVector().Data[:10]) } else { fmt.Println("Solution:", result.X.RawVector()) } }