rcas 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/CITATION.cff +17 -0
  3. data/DESIGN.md +783 -0
  4. data/LICENSE +21 -0
  5. data/MANUAL.md +6265 -0
  6. data/README.md +267 -0
  7. data/bin/rcas +9 -0
  8. data/bin/rcas-app +9 -0
  9. data/bin/rcas-chat +9 -0
  10. data/lib/rcas/algebraic.rb +481 -0
  11. data/lib/rcas/analysis.rb +966 -0
  12. data/lib/rcas/app/launcher.rb +203 -0
  13. data/lib/rcas/app/public/app.css +402 -0
  14. data/lib/rcas/app/public/app.js +449 -0
  15. data/lib/rcas/app/public/index.html +46 -0
  16. data/lib/rcas/app/server.rb +220 -0
  17. data/lib/rcas/app/window.rb +94 -0
  18. data/lib/rcas/app/worksheet.rb +290 -0
  19. data/lib/rcas/app.rb +168 -0
  20. data/lib/rcas/background.rb +758 -0
  21. data/lib/rcas/chat/assistant.rb +199 -0
  22. data/lib/rcas/chat/picker.rb +164 -0
  23. data/lib/rcas/chat/repl.rb +583 -0
  24. data/lib/rcas/chat/session.rb +137 -0
  25. data/lib/rcas/chat/settings.rb +71 -0
  26. data/lib/rcas/chat/style.rb +30 -0
  27. data/lib/rcas/chat/tool.rb +53 -0
  28. data/lib/rcas/chat/ui.rb +316 -0
  29. data/lib/rcas/chat/usage.rb +62 -0
  30. data/lib/rcas/chat/workspace.rb +132 -0
  31. data/lib/rcas/chat.rb +54 -0
  32. data/lib/rcas/coefficients.rb +170 -0
  33. data/lib/rcas/combinatorics.rb +274 -0
  34. data/lib/rcas/complex_parts.rb +160 -0
  35. data/lib/rcas/constants.rb +129 -0
  36. data/lib/rcas/core_ext.rb +35 -0
  37. data/lib/rcas/decide.rb +501 -0
  38. data/lib/rcas/decompositions.rb +241 -0
  39. data/lib/rcas/differentiate.rb +144 -0
  40. data/lib/rcas/discussion.rb +558 -0
  41. data/lib/rcas/distributions.rb +980 -0
  42. data/lib/rcas/dixon.rb +95 -0
  43. data/lib/rcas/docs.rb +321 -0
  44. data/lib/rcas/domains.rb +728 -0
  45. data/lib/rcas/expand.rb +174 -0
  46. data/lib/rcas/expression.rb +613 -0
  47. data/lib/rcas/factor.rb +605 -0
  48. data/lib/rcas/finite_field.rb +577 -0
  49. data/lib/rcas/fourier.rb +118 -0
  50. data/lib/rcas/fps.rb +678 -0
  51. data/lib/rcas/fraction.rb +126 -0
  52. data/lib/rcas/functions.rb +1136 -0
  53. data/lib/rcas/gcd.rb +112 -0
  54. data/lib/rcas/geometry.rb +266 -0
  55. data/lib/rcas/groebner.rb +162 -0
  56. data/lib/rcas/hold.rb +277 -0
  57. data/lib/rcas/hypothesis.rb +364 -0
  58. data/lib/rcas/inequalities.rb +689 -0
  59. data/lib/rcas/integral_functions.rb +260 -0
  60. data/lib/rcas/integrate.rb +1589 -0
  61. data/lib/rcas/integrate_substitutions.rb +434 -0
  62. data/lib/rcas/interpolate.rb +40 -0
  63. data/lib/rcas/irb.rb +146 -0
  64. data/lib/rcas/laplace.rb +159 -0
  65. data/lib/rcas/latex.rb +556 -0
  66. data/lib/rcas/lattice.rb +172 -0
  67. data/lib/rcas/linear_algebra.rb +117 -0
  68. data/lib/rcas/linear_program.rb +416 -0
  69. data/lib/rcas/lint.rb +79 -0
  70. data/lib/rcas/matrix.rb +531 -0
  71. data/lib/rcas/matrix_multiply.rb +202 -0
  72. data/lib/rcas/multimodular.rb +286 -0
  73. data/lib/rcas/named_polynomials.rb +274 -0
  74. data/lib/rcas/number_theory.rb +443 -0
  75. data/lib/rcas/numerics.rb +825 -0
  76. data/lib/rcas/ode.rb +488 -0
  77. data/lib/rcas/openmath/objects.rb +364 -0
  78. data/lib/rcas/openmath/phrasebook.rb +551 -0
  79. data/lib/rcas/openmath/popcorn.rb +518 -0
  80. data/lib/rcas/openmath/xml.rb +309 -0
  81. data/lib/rcas/openmath.rb +49 -0
  82. data/lib/rcas/petkovsek.rb +165 -0
  83. data/lib/rcas/piecewise.rb +488 -0
  84. data/lib/rcas/plot.rb +763 -0
  85. data/lib/rcas/plot3d.rb +419 -0
  86. data/lib/rcas/poly_matrix.rb +318 -0
  87. data/lib/rcas/poly_recurrence.rb +117 -0
  88. data/lib/rcas/polynomial.rb +466 -0
  89. data/lib/rcas/precision.rb +925 -0
  90. data/lib/rcas/printer.rb +150 -0
  91. data/lib/rcas/product.rb +155 -0
  92. data/lib/rcas/q_difference.rb +296 -0
  93. data/lib/rcas/q_functions.rb +158 -0
  94. data/lib/rcas/q_summation.rb +308 -0
  95. data/lib/rcas/q_zeilberger.rb +199 -0
  96. data/lib/rcas/random.rb +506 -0
  97. data/lib/rcas/rational_function.rb +186 -0
  98. data/lib/rcas/recurrence.rb +323 -0
  99. data/lib/rcas/render.rb +431 -0
  100. data/lib/rcas/results.rb +192 -0
  101. data/lib/rcas/scalar.rb +219 -0
  102. data/lib/rcas/series.rb +726 -0
  103. data/lib/rcas/simplify.rb +649 -0
  104. data/lib/rcas/solve.rb +2002 -0
  105. data/lib/rcas/special.rb +163 -0
  106. data/lib/rcas/statistics.rb +175 -0
  107. data/lib/rcas/steps.rb +835 -0
  108. data/lib/rcas/summation.rb +532 -0
  109. data/lib/rcas/trig.rb +264 -0
  110. data/lib/rcas/van_hoeij.rb +241 -0
  111. data/lib/rcas/vector.rb +175 -0
  112. data/lib/rcas/vector_calculus.rb +411 -0
  113. data/lib/rcas/version.rb +5 -0
  114. data/lib/rcas/zeilberger.rb +358 -0
  115. data/lib/rcas.rb +91 -0
  116. data/package.json +8 -0
  117. metadata +206 -0
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RCAS
4
+ # The Laplace transform and its inverse.
5
+ #
6
+ # laplace(exp(3*t), t, s) # => 1/(s - 3)
7
+ # laplace(t**2, t, s) # => 2/s**3
8
+ # laplace(exp(-t)*sin(2*t), t, s) # => 2/((1 + s)**2 + 4)
9
+ # inverse_laplace(1/(s**2 + 1), s, t) # => sin(t)
10
+ #
11
+ # The transform turns differentiation into multiplication by s, which is
12
+ # why it solves linear differential equations by algebra. rcas computes it
13
+ # from the table plus two rules: the first shift, exp(a*t)*f(t) at s - a,
14
+ # and multiplication by t, which differentiates in s. The inverse goes
15
+ # through partial fractions, so it covers the rational transforms a course
16
+ # produces.
17
+ #
18
+ # Sources (keys: MANUAL.md, Sources): the table and the shift rules as in
19
+ # [BD12, ch. 6]; partial fractions from rational_function.rb.
20
+ module Laplace
21
+ class Error < ArgumentError; end
22
+
23
+ module_function
24
+
25
+ # ---- the transform -----------------------------------------------------------
26
+
27
+ def transform(f, t, s)
28
+ t = Expression.lift(t)
29
+ s = Expression.lift(s)
30
+ f = Expression.lift(f).simplify
31
+ constant, terms = Simplify.termize(f, simplify: true)
32
+ result = Scalar.zero?(Num.new(constant)) ? Num.new(0) : (Num.new(constant) / s)
33
+ terms.each do |factors, coefficient|
34
+ # a factor free of t is a constant of the transform: a*sin(t) is
35
+ # a/(1 + s**2) (the fourth review, L7, found it refused)
36
+ constants, moving = factors.partition { |base, exp| !base.variables.include?(t.name) && !Expression.lift(exp).variables.include?(t.name) }
37
+ term = Simplify.rebuild_product(1, moving.to_h)
38
+ scale = Simplify.rebuild_product(coefficient, constants.to_h)
39
+ result += scale * term_transform(term, t, s)
40
+ end
41
+ result.simplify
42
+ end
43
+
44
+ # A single term: t**n times something the table knows.
45
+ def term_transform(term, t, s)
46
+ power, rest = split_power(term, t)
47
+ base = table_transform(rest, t, s)
48
+ raise Error, "laplace: no transform known for #{term}" if base.nil?
49
+ return base if power.zero?
50
+ (Num.new((-1)**power) * base.diff(s, power)).simplify
51
+ end
52
+
53
+ # t**n * rest => [n, rest]
54
+ def split_power(term, t)
55
+ coefficient, factors = Simplify.factorize(term)
56
+ power = factors[t]
57
+ return [0, term] unless power.is_a?(Integer) && power.positive?
58
+ rest = factors.reject { |base, _| base == t }
59
+ [power, Simplify.rebuild_product(coefficient, rest)]
60
+ end
61
+
62
+ # 1, exp(a t), sin/cos(w t), sinh/cosh(w t), and exp(a t) times those.
63
+ def table_transform(f, t, s)
64
+ f = f.simplify
65
+ return f / s unless Solve.depends?(f, t) # a constant a is a/s, not 1/s
66
+ shift, rest = split_exponential(f, t)
67
+ shifted = shift ? s - shift : s
68
+ value =
69
+ if !Solve.depends?(rest, t) then rest / shifted
70
+ elsif rest.is_a?(Fn) && (rate = linear_rate(rest.args.first, t))
71
+ case rest.name
72
+ when :sin then rate / (shifted**2 + rate**2)
73
+ when :cos then shifted / (shifted**2 + rate**2)
74
+ when :sinh then rate / (shifted**2 - rate**2)
75
+ when :cosh then shifted / (shifted**2 - rate**2)
76
+ end
77
+ end
78
+ value&.simplify
79
+ end
80
+
81
+ # exp(a*t) * rest => [a, rest]
82
+ def split_exponential(f, t)
83
+ coefficient, factors = Simplify.factorize(f)
84
+ exponent = factors[Simplify.exp_base]
85
+ return [nil, f] if exponent.nil?
86
+ rate = linear_rate(Expression.lift(exponent), t) or return [nil, f]
87
+ rest = factors.reject { |base, _| base == Simplify.exp_base }
88
+ [rate, Simplify.rebuild_product(coefficient, rest)]
89
+ end
90
+
91
+ # a*t => a, anything else => nil
92
+ def linear_rate(u, t)
93
+ coefficients = Solve.polynomial_coefficients(u, t)
94
+ return nil unless coefficients && coefficients.size == 2 && Scalar.zero?(coefficients[0])
95
+ coefficients[1]
96
+ end
97
+
98
+ # ---- the inverse --------------------------------------------------------------
99
+
100
+ def inverse(f, s, t)
101
+ s = Expression.lift(s)
102
+ t = Expression.lift(t)
103
+ f = Expression.lift(f).simplify
104
+ pieces = begin
105
+ RationalFunction.apart(f, s)
106
+ rescue DomainError
107
+ f
108
+ end
109
+ constant, terms = Simplify.termize(pieces, simplify: true)
110
+ raise Error, "inverse_laplace: #{f} has a constant part, which is a delta" unless constant.zero?
111
+ terms.map { |factors, coefficient| Num.new(coefficient) * piece_inverse(Simplify.rebuild_product(1, factors), s, t) }
112
+ .reduce(Num.new(0)) { |a, b| a + b }.simplify
113
+ end
114
+
115
+ def piece_inverse(piece, s, t)
116
+ numerator = RationalFunction.numer(piece)
117
+ denominator = RationalFunction.denom(piece)
118
+ raise Error, "inverse_laplace: #{piece} is not a proper rational function of #{s}" unless Solve.depends?(denominator, s)
119
+ factorization = denominator.to_poly(QQ[s.name]).factor
120
+ raise Error, "inverse_laplace: #{piece} has more than one kind of factor; give it as partial fractions" unless factorization.factors.size == 1
121
+
122
+ factor, multiplicity = factorization.factors.first
123
+ unit = factorization.unit
124
+ degree = factor.degree
125
+ coefficients = (0..degree).map { |k| factor.coeff(k).value }
126
+ case degree
127
+ when 1 then linear_inverse(numerator, coefficients, multiplicity, unit, s, t)
128
+ when 2 then quadratic_inverse(numerator, coefficients, multiplicity, unit, s, t)
129
+ else raise Error, "inverse_laplace: the denominator factor #{factor} has degree #{degree}"
130
+ end
131
+ end
132
+
133
+ # c/(a*s + b)**n => c * t**(n-1) * exp(-b/a * t) / (a**n * (n-1)!)
134
+ def linear_inverse(numerator, coefficients, multiplicity, unit, s, t)
135
+ raise Error, "inverse_laplace: the numerator #{numerator} is not constant" if Solve.depends?(numerator, s)
136
+ b, a = coefficients
137
+ rate = Num.new(Simplify.normalize_number(Rational(-b, a)))
138
+ scale = (numerator / (unit * Num.new(a)**multiplicity * RCAS.factorial(multiplicity - 1))).simplify
139
+ (scale * t**(multiplicity - 1) * Fn.new(:exp, [rate * t])).simplify
140
+ end
141
+
142
+ # (A*s + B) / ((s - p)**2 + w**2) => exp(p*t) * (A*cos(w*t) + (B + A*p)/w * sin(w*t))
143
+ def quadratic_inverse(numerator, coefficients, multiplicity, unit, s, t)
144
+ raise Error, "inverse_laplace: a repeated quadratic factor is not supported" unless multiplicity == 1
145
+ c, b, a = coefficients
146
+ centre = Num.new(Simplify.normalize_number(Rational(-b, 2 * a)))
147
+ square = Num.new(Simplify.normalize_number(Rational(c, a) - Rational(b, 2 * a)**2))
148
+ raise Error, "inverse_laplace: the quadratic factor is not irreducible" unless square.value.positive?
149
+ omega = RCAS.sqrt(square).simplify
150
+ linear = Solve.polynomial_coefficients(numerator, s) or raise Error, "inverse_laplace: #{numerator} is not linear in #{s}"
151
+ slope = linear[1] || Num.new(0)
152
+ offset = linear[0]
153
+ scale = (Num.new(1) / (unit * Num.new(a))).simplify
154
+ cosine = slope
155
+ sine = ((offset + slope * centre) / omega).simplify
156
+ (scale * Fn.new(:exp, [centre * t]) * (cosine * Fn.new(:cos, [omega * t]) + sine * Fn.new(:sin, [omega * t]))).simplify
157
+ end
158
+ end
159
+ end