diff --git a/examples/load_rubi.m b/examples/load_rubi.m index 0f0d7a3..ddcff80 100644 --- a/examples/load_rubi.m +++ b/examples/load_rubi.m @@ -1,4 +1,119 @@ +(* ::Package:: *) + +(* ::Title:: *) +(*Rubi (Rule-Based Integrator) Package*) (* Clone https://github.com/corywalker/rubi into the root dir of Expreduce. *) -Get["rubi/Integration Utility Functions.m"] -Get["rubi/9.1 Integrand simplification rules.m"] -Get["rubi/1.1.1 Linear binomial products.m"] + + + +BeginPackage["Rubi`"] + +Int::usage = +"Int[expn,var] returns the antiderivative (indefinite integral) of with respect to . +Int[list,var] returns a list of the antiderivatives of the elements of with respect to ."; + + +IntShowSteps::usage = "IntShowSteps[expn,var] shows all the rules and intermediate steps required to integrate with respect to , and returns Null."; + + +Dist::usage = "Dist[expn1,expn2,var] distributes over ."; +Subst::usage = "Subst[expn1,var,expn2] substitutes for in ."; + + +ShowSteps::usage = "If ShowSteps is True and the ShowSteps package has been loaded, integration steps are displayed."; +$StepCounter::usage = "If the ShowSteps package has been loaded and $StepCounter is an integer, it is incremented each time an integration rule is applied."; + + +$RuleColor::usage = "$RuleColor is the color used to display rules when showing integration steps. The default rule color is red." +$ConditionColor::usage = "$ConditionColor is the color used to display application conditions when showing integration steps. The default condition color is blue." + + +sin::usage = "Inert sine function"; +cos::usage = "Inert cosine function"; +tan::usage = "Inert tangent function"; +cot::usage = "Inert cotangent function"; +sec::usage = "Inert secant function"; +csc::usage = "Inert cosecant function"; + + +Begin["`Private`"] + +(*LoadRules[filename_String] := + Module[{object}, + object=PrintTemporary["Loading "<>filename<>".m..."]; + Get[NotebookDirectory[]<>filename<>".m"]; + NotebookDelete[object]; + Null]*) + +LoadRules[filename_String] := ( + Print["Loading "<>filename<>".m..."]; + Get["rubi/"<>filename<>".m"]; +) + + +Unprotect[Int]; Clear[Int]; + + +SetAttributes [Int, {Listable}]; + + +ShowSteps = Global`$LoadShowSteps===True; + + +(*LoadRules["ShowStep routines"];*) +LoadRules["Integration utility functions"]; +LoadRules["9.1 Integrand simplification rules"]; + + +LoadRules["1.1.1 Linear binomial products"]; +LoadRules["1.1.3 General binomial products"]; + +LoadRules["1.2.1 Quadratic trinomial products"]; +LoadRules["1.2.2 Quartic trinomial products"]; +LoadRules["1.2.3 General trinomial products"]; +LoadRules["1.2.4 Improper trinomial products"]; + +LoadRules["1.1.4 Improper binomial products"]; +LoadRules["1.3 Miscellaneous algebraic functions"]; + + +LoadRules["9.3 Piecewise linear functions"]; +LoadRules["2 Exponentials"]; +LoadRules["3 Logarithms"]; +LoadRules["4.1 Sine"]; +LoadRules["4.2 Tangent"]; +LoadRules["4.3 Secant"]; +LoadRules["4.4 Miscellaneous trig functions"]; +LoadRules["5 Inverse trig functions"]; +LoadRules["6 Hyperbolic functions"]; +LoadRules["7 Inverse hyperbolic functions"]; +LoadRules["8 Special functions"]; +LoadRules["9.2 Derivative integration rules"]; +LoadRules["9.4 Miscellaneous integration rules"]; + + +(*FixIntRules[];*) + + +(*If[Global`$LoadShowSteps===True, StepFunction[Int]];*) + + +Protect[Int]; + + +End []; +EndPackage []; + +(*Helper debug functions in global context.*) + +printDownValues[args_, sym_] := + Print /@ Map[{#, MatchQ[args, #]} &, + Map[List @@ (#[[1]][[1]]) &, DownValues[sym]]]; +isPartialMatch[args_, dv_] := MatchQ[args, List @@ (dv[[1]][[1]])]; +isFullMatch[args_, dv_, sym_] := + Replace[args, dv /. sym -> List] =!= args; +printPartitalMatchingDownValues[args_, sym_, n_] := + Select[DownValues[sym], isPartialMatch[args, #] &, n]; +printMatchingDownValues[args_, sym_, n_] := + Select[DownValues[sym], isFullMatch[args, #, sym] &, n]; + diff --git a/examples/test_rubi.m b/examples/test_rubi.m index a166e02..4a03486 100644 --- a/examples/test_rubi.m +++ b/examples/test_rubi.m @@ -9,13 +9,15 @@ res = Int[testp[[1]], testp[[2]]]; (*Print[res];*) (*Print[res === testp[[4]]];*) - If[res === testp[[4]], Print[thei]]; + (*If[res === testp[[4]], Print[thei]];*) + If[res =!= testp[[4]], Print[thei]]; ); While[testi <= Length[testproblems], - (*Print["hi ", testi]*) - If[(testi>34&&testi<47)||MemberQ[{50, 52, 53, 54, 55, 56, 57, 58, 59, 211, 214, 215, 216, 218, 222, 223, 224, 225, 228, 229, 231, 232, 233}, testi] || (testi>=160&&testi<=166), Null, + Print["Testing ", testi, " ", testproblems[[testi]]] + (*If[(testi>34&&testi<47)||MemberQ[{50, 52, 53, 54, 55, 56, 57, 58, 59, 211, 214, 215, 216, 218, 222, 223, 224, 225, 228, 229, 231, 232, 233}, testi] || (testi>=160&&testi<=166), Null, runRubiTest[testi]; - ]; + ];*) + runRubiTest[testi]; testi = testi+1; ]; diff --git a/expreduce/builtin_system.go b/expreduce/builtin_system.go index e1fbe33..27c1455 100644 --- a/expreduce/builtin_system.go +++ b/expreduce/builtin_system.go @@ -182,7 +182,15 @@ func applyModuleFn(this *Expression, es *EvalState) (Ex, bool) { rhs = rhs.Eval(es) } es.defined[pl.uniqueName] = Def{ - downvalues: []Expression{*NewExpression([]Ex{&Symbol{"System`Rule"}, &Symbol{pl.uniqueName}, rhs})}, + downvalues: []DownValue{ + DownValue{ + rule: *NewExpression([]Ex{ + &Symbol{"System`Rule"}, + &Symbol{pl.uniqueName}, + rhs, + }), + }, + }, } } else { es.defined[pl.uniqueName] = Def{} @@ -341,9 +349,9 @@ func GetSystemDefinitions() (defs []Definition) { &Symbol{"System`RuleDelayed"}, NewExpression([]Ex{ &Symbol{"System`HoldPattern"}, - dv.Parts[1], + dv.rule.Parts[1], }), - dv.Parts[2], + dv.rule.Parts[2], })) } return res diff --git a/expreduce/definition.go b/expreduce/definition.go index 3245e35..f6e9315 100644 --- a/expreduce/definition.go +++ b/expreduce/definition.go @@ -2,8 +2,13 @@ package expreduce import "bytes" +type DownValue struct { + rule Expression + specificity int +} + type Def struct { - downvalues []Expression + downvalues []DownValue attributes Attributes defaultExpr Ex @@ -15,8 +20,12 @@ func CopyDefs(in map[string]Def) map[string]Def { out := make(map[string]Def) for k, v := range in { newDef := Def{} - for _, rule := range v.downvalues { - newDef.downvalues = append(newDef.downvalues, *rule.DeepCopy().(*Expression)) + for _, dv := range v.downvalues { + newDv := DownValue{ + rule: *dv.rule.DeepCopy().(*Expression), + specificity: dv.specificity, + } + newDef.downvalues = append(newDef.downvalues, newDv) } out[k] = newDef } @@ -26,8 +35,8 @@ func CopyDefs(in map[string]Def) map[string]Def { func (this *Def) String() string { var buffer bytes.Buffer buffer.WriteString("{") - for i, e := range this.downvalues { - buffer.WriteString(e.String()) + for i, dv := range this.downvalues { + buffer.WriteString(dv.rule.String()) if i != len(this.downvalues)-1 { buffer.WriteString("\n") } @@ -39,8 +48,8 @@ func (this *Def) String() string { func (def *Def) StringForm(form string, context *String, contextPath *Expression) string { var buffer bytes.Buffer buffer.WriteString("{") - for i, e := range def.downvalues { - buffer.WriteString(e.StringForm(form, context, contextPath)) + for i, dv := range def.downvalues { + buffer.WriteString(dv.rule.StringForm(form, context, contextPath)) if i != len(def.downvalues)-1 { buffer.WriteString("\n") } diff --git a/expreduce/evalstate.go b/expreduce/evalstate.go index fb7367b..db2e5e2 100644 --- a/expreduce/evalstate.go +++ b/expreduce/evalstate.go @@ -209,7 +209,7 @@ func (this *EvalState) GetDef(name string, lhs Ex) (Ex, bool, *Expression) { } this.Debugf("Inside GetDef(\"%s\",%s)", name, lhs) for i := range this.defined[name].downvalues { - def := this.defined[name].downvalues[i] + def := this.defined[name].downvalues[i].rule defStr, lhsDefStr := "", "" started := int64(0) @@ -268,7 +268,7 @@ func (this *EvalState) DefineAttrs(sym *Symbol, rhs Ex) { func (this *EvalState) MarkSeen(name string) { if !this.IsDef(name) { newDef := Def{ - downvalues: []Expression{}, + downvalues: []DownValue{}, } this.defined[name] = newDef } @@ -288,7 +288,7 @@ func ruleSpecificity(lhs Ex, rhs Ex) int { // Condition rules will be ranked in order of definition, not // specificity. I'm not entirely sure if this is correct, but it seems // to be the case for all the Rubi rules. - specificity = 40 + specificity = 100 } return specificity } @@ -336,7 +336,13 @@ func (this *EvalState) Define(lhs Ex, rhs Ex) { this.Debugf("Inside es.Define(\"%s\",%s,%s)", name, lhs, rhs) if !this.IsDef(name) { newDef := Def{ - downvalues: []Expression{*NewExpression([]Ex{&Symbol{"System`Rule"}, lhs, rhs})}, + downvalues: []DownValue{ + DownValue{ + rule: *NewExpression([]Ex{ + &Symbol{"System`Rule"}, lhs, rhs, + }), + }, + }, } this.defined[name] = newDef return @@ -344,13 +350,13 @@ func (this *EvalState) Define(lhs Ex, rhs Ex) { // Overwrite identical rules. for i := range this.defined[name].downvalues { - existingRule := this.defined[name].downvalues[i] + existingRule := this.defined[name].downvalues[i].rule existingLhs := existingRule.Parts[1] if IsSameQ(existingLhs, lhs, &this.CASLogger) { existingRhsCond := maskNonConditional(existingRule.Parts[2]) newRhsCond := maskNonConditional(rhs) if IsSameQ(existingRhsCond, newRhsCond, &this.CASLogger) { - this.defined[name].downvalues[i].Parts[2] = rhs + this.defined[name].downvalues[i].rule.Parts[2] = rhs return } } @@ -361,16 +367,21 @@ func (this *EvalState) Define(lhs Ex, rhs Ex) { var tmp = this.defined[name] newSpecificity := ruleSpecificity(lhs, rhs) for i := range this.defined[name].downvalues { - thisSpecificity := ruleSpecificity( - this.defined[name].downvalues[i].Parts[1], - this.defined[name].downvalues[i].Parts[2], - ) - if thisSpecificity < newSpecificity { + if this.defined[name].downvalues[i].specificity == 0 { + this.defined[name].downvalues[i].specificity = ruleSpecificity( + this.defined[name].downvalues[i].rule.Parts[1], + this.defined[name].downvalues[i].rule.Parts[2], + ) + } + if this.defined[name].downvalues[i].specificity < newSpecificity { newRule := *NewExpression([]Ex{&Symbol{"System`Rule"}, lhs, rhs}) tmp.downvalues = append( tmp.downvalues[:i], append( - []Expression{newRule}, + []DownValue{DownValue{ + rule: newRule, + specificity: newSpecificity, + }}, this.defined[name].downvalues[i:]..., )..., ) @@ -378,7 +389,7 @@ func (this *EvalState) Define(lhs Ex, rhs Ex) { return } } - tmp.downvalues = append(tmp.downvalues, *NewExpression([]Ex{&Symbol{"System`Rule"}, lhs, rhs})) + tmp.downvalues = append(tmp.downvalues, DownValue{rule: *NewExpression([]Ex{&Symbol{"System`Rule"}, lhs, rhs})}) this.defined[name] = tmp } @@ -389,7 +400,8 @@ func (this *EvalState) ClearAll() { func (this *EvalState) Clear(name string) { _, ok := this.defined[name] if ok { - delete(this.defined, name) + this.defined[name] = Def{} + //delete(this.defined, name) } } diff --git a/expreduce/interp.go b/expreduce/interp.go index 8854d59..569f4e1 100644 --- a/expreduce/interp.go +++ b/expreduce/interp.go @@ -172,6 +172,7 @@ var unaryOps = map[int]string{ 13: "Not", 115: "Factorial", 117: "Function", + 15: "Plus", } var binaryOps = map[int]string{ @@ -349,6 +350,14 @@ func ParserExprConv(expr *wl.Expression) Ex { set.Parts[2], }) return e + case 137: + return NewExpression([]Ex{ + NewExpression([]Ex{ + &Symbol{"System`Derivative"}, + NewInt(1), + }), + ParserExprConv(expr.Expression), + }) } log.Fatalf("System`UnParsed: %+v %+v %+v", expr.Token, expr.Case, expr) return nil