Skip to content

Commit

Permalink
Folding for multiplication.
Browse files Browse the repository at this point in the history
  • Loading branch information
corywalker committed Jul 16, 2017
1 parent fbd9f3a commit bcf2a71
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 200 deletions.
273 changes: 73 additions & 200 deletions expreduce/builtin_arithmetic.go
Expand Up @@ -31,21 +31,40 @@ func RationalAssertion(num Ex, den Ex) (r *Rational, isR bool) {
return NewRational(numInt.Val, denInt.Val), true
}

func typedRealPart(i *Integer, r *Rational, f *Flt) Ex {
type FoldFn int

const (
FoldFnAdd FoldFn = iota
FoldFnMul
)

func typedRealPart(fn FoldFn, i *Integer, r *Rational, f *Flt) Ex {
if f != nil {
toReturn := f
if r != nil {
toReturn.AddR(r)
if fn == FoldFnAdd {
toReturn.AddR(r)
} else if fn == FoldFnMul {
toReturn.MulR(r)
}
}
if i != nil {
toReturn.AddI(i)
if fn == FoldFnAdd {
toReturn.AddI(i)
} else if fn == FoldFnMul {
toReturn.MulI(i)
}
}
return toReturn
}
if r != nil {
toReturn := r
if i != nil {
toReturn.AddI(i)
if fn == FoldFnAdd {
toReturn.AddI(i)
} else if fn == FoldFnMul {
toReturn.MulI(i)
}
}
return toReturn
}
Expand All @@ -55,11 +74,13 @@ func typedRealPart(i *Integer, r *Rational, f *Flt) Ex {
return nil
}

func computeRealPart(e *Expression) (Ex, int) {
func computeRealPart(fn FoldFn, e *Expression) (Ex, int) {
var foldedInt *Integer
var foldedRat *Rational
var foldedFlt *Flt
for i := 1; i < len(e.Parts); i++ {
// TODO: implement short circuiting if we encounter a zero while
// multiplying.
asInt, isInt := e.Parts[i].(*Integer)
if isInt {
if foldedInt == nil {
Expand All @@ -69,7 +90,11 @@ func computeRealPart(e *Expression) (Ex, int) {
foldedInt = asInt.DeepCopy().(*Integer)
continue
}
foldedInt.AddI(asInt)
if fn == FoldFnAdd {
foldedInt.AddI(asInt)
} else if fn == FoldFnMul {
foldedInt.MulI(asInt)
}
continue
}
asRat, isRat := e.Parts[i].(*Rational)
Expand All @@ -78,7 +103,11 @@ func computeRealPart(e *Expression) (Ex, int) {
foldedRat = asRat.DeepCopy().(*Rational)
continue
}
foldedRat.AddR(asRat)
if fn == FoldFnAdd {
foldedRat.AddR(asRat)
} else if fn == FoldFnMul {
foldedRat.MulR(asRat)
}
continue
}
asFlt, isFlt := e.Parts[i].(*Flt)
Expand All @@ -87,12 +116,16 @@ func computeRealPart(e *Expression) (Ex, int) {
foldedFlt = asFlt.DeepCopy().(*Flt)
continue
}
foldedFlt.AddF(asFlt)
if fn == FoldFnAdd {
foldedFlt.AddF(asFlt)
} else if fn == FoldFnMul {
foldedFlt.MulF(asFlt)
}
continue
}
return typedRealPart(foldedInt, foldedRat, foldedFlt), i
return typedRealPart(fn, foldedInt, foldedRat, foldedFlt), i
}
return typedRealPart(foldedInt, foldedRat, foldedFlt), -1
return typedRealPart(fn, foldedInt, foldedRat, foldedFlt), -1
}

func getArithmeticDefinitions() (defs []Definition) {
Expand All @@ -116,7 +149,7 @@ func getArithmeticDefinitions() (defs []Definition) {
}

res := this
realPart, symStart := computeRealPart(this)
realPart, symStart := computeRealPart(FoldFnAdd, this)
if realPart != nil {
if symStart == -1 {
return realPart
Expand Down Expand Up @@ -268,192 +301,45 @@ func getArithmeticDefinitions() (defs []Definition) {
return &Integer{big.NewInt(1)}
}

multiplicands := this.Parts[1:len(this.Parts)]
// If this expression contains any floats, convert everything possible to
// a float
if ExArrayContainsFloat(multiplicands) {
for i, e := range multiplicands {
subint, isint := e.(*Integer)
subrat, israt := e.(*Rational)
if isint {
newfloat := big.NewFloat(0)
newfloat.SetInt(subint.Val)
multiplicands[i] = &Flt{newfloat}
} else if israt {
num := big.NewFloat(0)
den := big.NewFloat(0)
newquo := big.NewFloat(0)
num.SetInt(subrat.Num)
den.SetInt(subrat.Den)
newquo.Quo(num, den)
multiplicands[i] = &Flt{newquo}
}
}
}

// If there is a zero in the expression, return zero, except under
// special circumstances.
containsInfinity := MemberQ(multiplicands, NewExpression([]Ex{
&Symbol{"Alternatives"},
&Symbol{"Infinity"},
&Symbol{"ComplexInfinity"},
}), es)
for _, e := range multiplicands {
float, isFlt := e.(*Flt)
if isFlt {
if float.Val.Cmp(big.NewFloat(0)) == 0 {
if containsInfinity {
return &Symbol{"Indeterminate"}
}
return &Flt{big.NewFloat(0)}
}
}
integer, isInteger := e.(*Integer)
if isInteger {
if integer.Val.Cmp(big.NewInt(0)) == 0 {
if containsInfinity {
return &Symbol{"Indeterminate"}
}
return &Integer{big.NewInt(0)}
}
}
}

// Geometrically accumulate floating point values towards the end of the expression
//es.Debugf("Before accumulating floats: %s", m)
origLen := len(multiplicands)
offset := 0
var lastf *Flt = nil
var lastfj int = 0
for i := 0; i < origLen; i++ {
j := i - offset
e := multiplicands[j]
f, ok := e.(*Flt)
if ok {
if lastf != nil {
es.Debugf("Encountered float. i=%d, j=%d, lastf=%s, lastfj=%d", i, j, lastf, lastfj)
f.Val.Mul(f.Val, lastf.Val)
//lastf.Val = big.NewFloat(1)
multiplicands = append(multiplicands[:lastfj], multiplicands[lastfj+1:]...)
offset++
es.Debugf("After deleting: %s", this)
}
lastf = f
lastfj = i - offset
}
}
//es.Debugf(es.Pre() +"After accumulating floats: %s", m)

if len(multiplicands) == 1 {
f, fOk := multiplicands[0].(*Flt)
if fOk {
if f.Val.Cmp(big.NewFloat(0)) == 1 {
return f
}
}
i, iOk := multiplicands[0].(*Integer)
if iOk {
if i.Val.Cmp(big.NewInt(0)) == 1 {
return i
}
}
}

// Remove one Floats
/*
for i := len(multiplicands) - 1; i >= 0; i-- {
f, ok := multiplicands[i].(*Flt)
if ok && f.Val.Cmp(big.NewFloat(1)) == 0 {
multiplicands[i] = multiplicands[len(multiplicands)-1]
multiplicands[len(multiplicands)-1] = nil
multiplicands = multiplicands[:len(multiplicands)-1]
}
}
*/

// Geometrically accumulate integer values towards the end of the expression
var lasti *Integer = nil
for _, e := range multiplicands {
theint, ok := e.(*Integer)
if ok {
if lasti != nil {
theint.Val.Mul(theint.Val, lasti.Val)
lasti.Val = big.NewInt(1)
}
lasti = theint
res := this
realPart, symStart := computeRealPart(FoldFnMul, this)
if realPart != nil {
if symStart == -1 {
return realPart
}
}

// Geometrically accumulate rational values towards the end of the expression
var lastr *Rational = nil
for _, e := range multiplicands {
therat, ok := e.(*Rational)
if ok {
if lastr != nil {
therat.Num.Mul(therat.Num, lastr.Num)
therat.Den.Mul(therat.Den, lastr.Den)
lastr.Num = big.NewInt(1)
lastr.Den = big.NewInt(1)
res = NewExpression([]Ex{&Symbol{"Times"}})
rAsInt, rIsInt := realPart.(*Integer)
if rIsInt && rAsInt.Val.Cmp(big.NewInt(0)) == 0 {
containsInfinity := MemberQ(this.Parts[symStart:], NewExpression([]Ex{
&Symbol{"Alternatives"},
&Symbol{"Infinity"},
&Symbol{"ComplexInfinity"},
}), es)
if containsInfinity {
return &Symbol{"Indeterminate"}
}
lastr = therat
}
}

// If there is one Integer and one Rational left, merge the Integer into
// the Rational
if lasti != nil && lastr != nil {
lastr.Num.Mul(lastr.Num, lasti.Val)
// This will get cleaned up in the next step
lasti.Val = big.NewInt(1)
}

// Remove one Integers and Rationals
for i := len(multiplicands) - 1; i >= 0; i-- {
toRemove := false
theint, isInt := multiplicands[i].(*Integer)
if isInt {
toRemove = theint.Val.Cmp(big.NewInt(1)) == 0
return &Integer{big.NewInt(0)}
}
therat, isRat := multiplicands[i].(*Rational)
if isRat {
toRemove = therat.Num.Cmp(big.NewInt(1)) == 0 && therat.Den.Cmp(big.NewInt(1)) == 0
}
if toRemove && len(multiplicands) > 1 {
multiplicands[i] = multiplicands[len(multiplicands)-1]
multiplicands[len(multiplicands)-1] = nil
multiplicands = multiplicands[:len(multiplicands)-1]
if !(rIsInt && rAsInt.Val.Cmp(big.NewInt(1)) == 0) {
res.Parts = append(res.Parts, realPart)
}
res.Parts = append(res.Parts, this.Parts[symStart:]...)
}

// If one expression remains, replace this Times with the expression
if len(multiplicands) == 1 {
return multiplicands[0]
if len(res.Parts) == 2 {
return res.Parts[1]
}

// Automatically Expand negations (*-1), not (*-1.) of a Plus expression
if len(multiplicands) == 2 {
leftint, leftintok := multiplicands[0].(*Integer)
rightint, rightintok := multiplicands[1].(*Integer)
leftplus, leftplusok := HeadAssertion(multiplicands[0], "Plus")
rightplus, rightplusok := HeadAssertion(multiplicands[1], "Plus")
var theInt *Integer = nil
var thePlus *Expression = nil
if leftintok {
theInt = leftint
}
if rightintok {
theInt = rightint
}
if leftplusok {
thePlus = leftplus
}
if rightplusok {
thePlus = rightplus
}
if theInt != nil && thePlus != nil {
if theInt.Val.Cmp(big.NewInt(-1)) == 0 {
// Perhaps better implemented as a rule.
if len(res.Parts) == 3 {
leftint, leftintok := res.Parts[1].(*Integer)
rightplus, rightplusok := HeadAssertion(res.Parts[2], "Plus")
if leftintok && rightplusok {
if leftint.Val.Cmp(big.NewInt(-1)) == 0 {
toreturn := NewExpression([]Ex{&Symbol{"Plus"}})
addends := thePlus.Parts[1:len(thePlus.Parts)]
addends := rightplus.Parts[1:len(rightplus.Parts)]
for i := range addends {
toAppend := NewExpression([]Ex{
&Symbol{"Times"},
Expand All @@ -468,20 +354,7 @@ func getArithmeticDefinitions() (defs []Definition) {
}
}

if len(multiplicands) == 2 {
rational, isRational := RationalAssertion(multiplicands[0], multiplicands[1])
if isRational {
return rational.Eval(es)
}
rational, isRational = RationalAssertion(multiplicands[1], multiplicands[0])
if isRational {
return rational.Eval(es)
}
}

this.Parts = this.Parts[0:1]
this.Parts = append(this.Parts, multiplicands...)
return this
return res
},
SimpleExamples: []TestInstruction{
&TestComment{"Simplification rules apply automatically:"},
Expand Down
4 changes: 4 additions & 0 deletions expreduce/ex_integer.go
Expand Up @@ -69,3 +69,7 @@ func (this *Integer) AsBigFloat() *big.Float {
func (this *Integer) AddI(i *Integer) {
this.Val.Add(this.Val, i.Val)
}

func (this *Integer) MulI(i *Integer) {
this.Val.Mul(this.Val, i.Val)
}
9 changes: 9 additions & 0 deletions expreduce/ex_rational.go
Expand Up @@ -128,3 +128,12 @@ func (this *Rational) AddR(r *Rational) {
this.Num.Mul(this.Num, r.Den)
this.Num.Add(this.Num, tmp)
}

func (this *Rational) MulI(i *Integer) {
this.Num.Mul(this.Num, i.Val)
}

func (this *Rational) MulR(r *Rational) {
this.Num.Mul(this.Num, r.Num)
this.Den.Mul(this.Den, r.Den)
}
12 changes: 12 additions & 0 deletions expreduce/ex_real.go
Expand Up @@ -85,3 +85,15 @@ func (this *Flt) AddR(r *Rational) {
func (this *Flt) AddF(f *Flt) {
this.Val.Add(this.Val, f.Val)
}

func (this *Flt) MulI(i *Integer) {
this.Val.Mul(this.Val, i.AsBigFloat())
}

func (this *Flt) MulR(r *Rational) {
this.Val.Mul(this.Val, r.AsBigFloat())
}

func (this *Flt) MulF(f *Flt) {
this.Val.Mul(this.Val, f.Val)
}

0 comments on commit bcf2a71

Please sign in to comment.