Skip to content

Commit

Permalink
Beginning of Complex support. Currently broken.
Browse files Browse the repository at this point in the history
  • Loading branch information
corywalker committed Jan 10, 2018
1 parent 0489bb1 commit b577e78
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 18 deletions.
14 changes: 14 additions & 0 deletions expreduce/builtin_atoms.go
Expand Up @@ -15,6 +15,20 @@ func getAtomsDefinitions() (defs []Definition) {
return this
},
})
defs = append(defs, Definition{
Name: "Complex",
legacyEvalFn: func(this *Expression, es *EvalState) Ex {
if len(this.Parts) != 3 {
return this
}
rAsInt, rIsInt := this.Parts[1].(*Integer)
iAsInt, iIsInt := this.Parts[2].(*Integer)
if rIsInt && iIsInt {
return NewComplex(rAsInt.Val, iAsInt.Val).Eval(es)
}
return this
},
})
defs = append(defs, Definition{Name: "String"})
defs = append(defs, Definition{Name: "Real"})
defs = append(defs, Definition{Name: "Integer"})
Expand Down
4 changes: 4 additions & 0 deletions expreduce/builtin_expression.go
Expand Up @@ -68,6 +68,10 @@ func GetExpressionDefinitions() (defs []Definition) {
if IsRational {
return NewSymbol("System`Rational")
}
_, IsComplex := this.Parts[1].(*Complex)
if IsComplex {
return NewSymbol("System`Complex")
}
asExpr, IsExpression := this.Parts[1].(*Expression)
if IsExpression {
return asExpr.Parts[0]
Expand Down
1 change: 1 addition & 0 deletions expreduce/builtin_list.go
Expand Up @@ -11,6 +11,7 @@ func (this *Expression) ToStringList(params ToStringParams) (bool, string) {
var buffer bytes.Buffer
buffer.WriteString("{")
for i, e := range this.Parts[1:] {
params.previousHead = "<TOPLEVEL>"
buffer.WriteString(e.StringForm(params))
if i != len(this.Parts[1:])-1 {
buffer.WriteString(", ")
Expand Down
1 change: 1 addition & 0 deletions expreduce/evalstate.go
Expand Up @@ -133,6 +133,7 @@ func (es *EvalState) Init(loadAllDefs bool) {
es.MarkSeen("System`$Failed")
es.MarkSeen("System`Null")
es.MarkSeen("System`C")
es.MarkSeen("System`Complex")
es.MarkSeen("System`Integers")

es.MarkSeen("System`Exp")
Expand Down
72 changes: 72 additions & 0 deletions expreduce/ex_complex.go
@@ -0,0 +1,72 @@
package expreduce

import "fmt"
import "math/big"
import "hash/fnv"

type Complex struct {
Re *big.Int
Im *big.Int
needsEval bool
}

func (this *Complex) Eval(es *EvalState) Ex {
if this.Im.Cmp(big.NewInt(0)) == 0 {
return NewInteger(this.Re)
}
this.needsEval = false
return this
}

func (this *Complex) StringForm(params ToStringParams) string {
if params.form == "FullForm" {
return fmt.Sprintf("Complex[%d, %d]", this.Re, this.Im)
}
return fmt.Sprintf("(%d + %d*I)", this.Re, this.Im)
}

func (this *Complex) String() string {
context, contextPath := DefaultStringFormArgs()
return this.StringForm(ToStringParams{form: "InputForm", context: context, contextPath: contextPath})
}

func (this *Complex) IsEqual(other Ex, cl *CASLogger) string {
otherConv, otherIsComplex := other.(*Complex)
if !otherIsComplex {
return "EQUAL_FALSE"
}
if (this.Re.Cmp(otherConv.Re) != 0) || (this.Im.Cmp(otherConv.Im) != 0) {
return "EQUAL_FALSE"
}
return "EQUAL_TRUE"
}

func (this *Complex) DeepCopy() Ex {
tmpn := big.NewInt(0)
tmpn.Set(this.Re)
tmpd := big.NewInt(0)
tmpd.Set(this.Im)
return &Complex{tmpn, tmpd, this.needsEval}
}

func (this *Complex) Copy() Ex {
return this.DeepCopy()
}

func (this *Complex) NeedsEval() bool {
return this.needsEval
}

func NewComplex(r *big.Int, i *big.Int) *Complex {
return &Complex{r, i, true}
}

func (this *Complex) Hash() uint64 {
h := fnv.New64a()
h.Write([]byte{82, 226, 223, 39, 113, 26, 149, 249})
nBytes, _ := this.Re.MarshalText()
h.Write(nBytes)
dBytes, _ := this.Im.MarshalText()
h.Write(dBytes)
return h.Sum64()
}
1 change: 1 addition & 0 deletions expreduce/ex_expression.go
Expand Up @@ -203,6 +203,7 @@ func (this *Expression) Eval(es *EvalState) Ex {
es.trace = NewExpression([]Ex{NewSymbol("System`List")})
}
oldHash := curr.Parts[i].Hash()
//fmt.Println(curr, i)
curr.Parts[i] = curr.Parts[i].Eval(es)
if es.HasThrown() {
return es.thrown
Expand Down
3 changes: 3 additions & 0 deletions expreduce/iterspec.go
Expand Up @@ -39,6 +39,9 @@ func tryIterParam(e Ex) (Ex, bool) {
if _, isRat := e.(*Rational); isRat {
return e, true
}
if _, isComp := e.(*Complex); isComp {
return e, true
}
return nil, false
}

Expand Down
25 changes: 21 additions & 4 deletions expreduce/matchq.go
Expand Up @@ -23,6 +23,7 @@ var intSym = NewSymbol("System`Integer")
var strSym = NewSymbol("System`String")
var symSym = NewSymbol("System`Symbol")
var ratSym = NewSymbol("System`Rational")
var complexSym = NewSymbol("System`Complex")

func NewMatchIter(a Ex, b Ex, pm *PDManager, es *EvalState) (matchIter, bool) {
patternHead := ""
Expand Down Expand Up @@ -144,7 +145,7 @@ func NewMatchIter(a Ex, b Ex, pm *PDManager, es *EvalState) (matchIter, bool) {
_, aIsString := a.(*String)
_, aIsSymbol := a.(*Symbol)
aRational, aIsRational := a.(*Rational)
bRational, bIsRational := b.(*Rational)
aComplex, aIsComplex := a.(*Complex)
aExpression, aIsExpression := a.(*Expression)
bExpression, bIsExpression := b.(*Expression)

Expand Down Expand Up @@ -175,6 +176,8 @@ func NewMatchIter(a Ex, b Ex, pm *PDManager, es *EvalState) (matchIter, bool) {
headEx = symSym
} else if aIsRational {
headEx = ratSym
} else if aIsComplex {
headEx = complexSym
}

if IsBlankTypeOnly(b) {
Expand All @@ -192,8 +195,11 @@ func NewMatchIter(a Ex, b Ex, pm *PDManager, es *EvalState) (matchIter, bool) {
return &dummyMatchIter{newPm}, true
}
return nil, false
} else if aIsExpression && bIsRational {
matchq, newPm := isMatchQRational(bRational, aExpression, pm, es)
}

// Handle special case for matching Complex[a, b]
if aIsComplex && bIsExpression {
matchq, newPm := isMatchQComplex(aComplex, bExpression, pm, es)
if matchq {
return &dummyMatchIter{newPm}, true
}
Expand Down Expand Up @@ -241,7 +247,7 @@ func NewMatchIter(a Ex, b Ex, pm *PDManager, es *EvalState) (matchIter, bool) {
}

if !assumingHead {
if aIsFlt || aIsInteger || aIsString || aIsSymbol || aIsRational {
if aIsFlt || aIsInteger || aIsString || aIsSymbol || aIsRational || aIsComplex {
if IsSameQ(a, b, &es.CASLogger) {
return &dummyMatchIter{nil}, true
}
Expand Down Expand Up @@ -284,6 +290,17 @@ func isMatchQRational(a *Rational, b *Expression, pm *PDManager, es *EvalState)
b, pm, es)
}

func isMatchQComplex(a *Complex, b *Expression, pm *PDManager, es *EvalState) (bool, *PDManager) {
return IsMatchQ(
NewExpression([]Ex{
NewSymbol("System`Complex"),
NewInteger(a.Re),
NewInteger(a.Im),
}),

b, pm, es)
}

type assignedIterState struct {
formI int
assnI int
Expand Down
6 changes: 6 additions & 0 deletions expreduce/qfunctions.go
Expand Up @@ -58,6 +58,12 @@ func numberQ(e Ex) bool {
if ok {
return true
}
// This currently causes ((0 + 1*I)*Private`a + (0 + -1*I)*Private`a) to
// segfault.
/*_, ok = e.(*Complex)
if ok {
return true
}*/
return false
}

Expand Down
14 changes: 7 additions & 7 deletions expreduce/resources.go

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions expreduce/resources/atoms.m
Expand Up @@ -48,6 +48,18 @@
]
};

Complex::usage = "`Complex` is the head for the atomic rational type.";
(a : (_Integer|_Real|_Rational)) * Complex[real_, im_] * rest___ := Complex[a * real, a * im] * rest;
(a : (_Integer|_Real|_Rational)) + Complex[real_, im_] + rest___ := Complex[a + real, im] + rest;
Complex[x_,y_] + Complex[u_,v_] + rest___ := Complex[x+u,y+v] + rest;
Complex[x_,y_] * Complex[u_,v_] * rest___ := Complex[x*u-y*v,x*v+y*u] + rest;
Attributes[Complex] = {Protected};
Tests`Complex = {
ESimpleExamples[
ESameTest[Complex[-16, 28], (4 + 8I)(2 + 3I)]
]
};

String::usage = "`String` is the head for the atomic string type.";
Attributes[String] = {Protected};
Tests`String = {
Expand Down Expand Up @@ -100,9 +112,9 @@
Im[x_Real] := 0;
Im[x_Rational] := 0;
Im[a_Integer * x_Integer?Positive^y_Rational] := 0;
Im[x_] := Print["Call to Im not implemented!", x];
Im[x_Complex] := x[[2]];

Re[x_Integer] := x;
Re[x_Real] := x;
Re[x_Rational] := x;
Re[x_] := Print["Call to Re not implemented!", x];
Re[x_Complex] := x[[1]];
4 changes: 2 additions & 2 deletions expreduce/resources/power.m
Expand Up @@ -59,7 +59,7 @@
Power[-1, 1/2] := I;
Power[Rational[a_?Positive,b_?Positive], 1/2] := Power[a, 1/2] * Power[b, -1/2];
Power[Power[x_, y_Rational], -1] := Power[x, -y];
I^e_Integer := Switch[Mod[e, 4],
Complex[0,1]^e_Integer := Switch[Mod[e, 4],
0, 1,
1, I,
2, -1,
Expand Down Expand Up @@ -215,8 +215,8 @@
]
};

(*TODO: actually use Complex atom type*)
I::usage = "`I` is the imaginary number representing `Sqrt[-1]`.";
I := Complex[0, 1];
Attributes[I] = {Locked, Protected, ReadProtected};
Tests`I = {
ESimpleExamples[
Expand Down
4 changes: 2 additions & 2 deletions expreduce/resources/solve.m
Expand Up @@ -82,8 +82,8 @@
(* Available at: http://www.research.ed.ac.uk/portal/files/413486/Solving_Symbolic_Equations_%20with_PRESS.pdf *)
Solve[eqn_Equal, var_Symbol] := Module[{isolated},
If[containsOneOccurrence[eqn, var],
isolated = isolate[Rule @@ eqn, var];
If[AllTrue[isolated, (Head[#] == Rule)&], Return[{#}& /@ isolated]];
isolated = {#}& /@ isolate[Rule @@ eqn, var];
If[AllTrue[isolated, (Head[#[[1]]] == Rule)&], Return[isolated]];
Print["isolation procedure failed"];
Return[isolated]];
Print["Solve found no solutions"];
Expand Down
4 changes: 3 additions & 1 deletion expreduce/sameq.go
Expand Up @@ -13,10 +13,12 @@ func IsSameQ(a Ex, b Ex, cl *CASLogger) bool {
_, bIsSymbol := b.(*Symbol)
_, aIsRational := a.(*Rational)
_, bIsRational := b.(*Rational)
_, aIsComplex := a.(*Complex)
_, bIsComplex := b.(*Complex)
_, aIsExpression := a.(*Expression)
_, bIsExpression := b.(*Expression)

if (aIsFlt && bIsFlt) || (aIsString && bIsString) || (aIsInteger && bIsInteger) || (aIsSymbol && bIsSymbol) || (aIsRational && bIsRational) {
if (aIsFlt && bIsFlt) || (aIsString && bIsString) || (aIsInteger && bIsInteger) || (aIsSymbol && bIsSymbol) || (aIsRational && bIsRational) || (aIsComplex && bIsComplex) {
// a and b are identical raw types
if aIsFlt && bIsFlt {
// This is important, without it e.g. NestWhileList[(# + 3/#)/2 &, 1.0, UnsameQ, 2] never converges
Expand Down

0 comments on commit b577e78

Please sign in to comment.