Типы переменных и уравнений
IR Symbolics отражает Julia AST, но поддерживает собственное выполнение математических действий согласно математической семантике. Основой IR является тип Sym, который определяет символьную переменную. Зарегистрированные (математические) функции в Sym (или объектах istree) возвращают выражение, которое istree. Например, op1 = x+y — это один символьный объект, op2 = 2z — другой, а op1*op2 — еще один объект дерева. Затем в верхней части Equation, обычно записываемое как op1 ~ op2, определяет символьное уравнение между двумя операциями.
Типы
Sym, Term и FnType содержатся в SymbolicUtils.jl. Обратите внимание, что в Symbolics мы всегда используем Sym{Real}, Term{Real} и FnType{Tuple{Any}, Real}. Чтобы получить аргументы объекта istree, используйте arguments(t::Term), а чтобы получить операцию, используйте operation(t::Term). Однако обратите внимание, что никогда не следует выполнять диспетчеризацию Term или тестировать isa Term. Вместо этого нужно использовать SymbolicUtils.istree, чтобы проверить, определены ли arguments и operation.
#
Symbolics.@variables — Macro
Определяет одну или несколько неизвестных переменных.
@variables t α σ(..) β[1:2]
@variables w(..) x(t) y z(t, α, x)
expr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)
(..) означает, что значение должно быть оставлено без вызова.
Symbolics поддерживает создание переменных, обозначающих массив некоторого размера.
julia> @variables x[1:3]
1-element Vector{Symbolics.Arr{Num, 1}}:
x[1:3]
julia> @variables y[1:3, 1:6] # поддержка тензоров
1-element Vector{Symbolics.Arr{Num, 2}}:
y[1:3,1:6]
julia> @variables t z(t)[1:3] # также работает для зависимых переменных
2-element Vector{Any}:
t
(z(t))[1:3]
Символ или выражение, представляющее массив, можно преобразовать в массив символов или выражений с помощью функции scalarize.
julia> Symbolics.scalarize(z)
3-element Vector{Num}:
(z(t))[1]
(z(t))[2]
(z(t))[3]
Обратите внимание, что @variables возвращает вектор всех определенных переменных.
@variables также может принимать значения символов времени выполнения с помощью оператора интерполяции $, и в этом случае @variables не присваивает значение автоматически, а только возвращает вектор символьных переменных. Здесь применяется и весь остальной синтаксис.
julia> a, b, c = :runtime_symbol_value, :value_b, :value_c
(:runtime_symbol_value, :value_b, :value_c)
julia> vars = @variables t $a $b(t) $c(t)[1:3]
4-element Vector{Any}:
t
runtime_symbol_value
value_b(t)
(value_c(t))[1:3]
julia> (t, a, b, c)
(t, :runtime_symbol_value, :value_b, :value_c)
#
Symbolics.Equation — Type
struct Equation
Отношение равенства между двумя выражениями.
Поля
-
lhs: выражение в левой части уравнения. -
rhs: выражение в правой части уравнения.
#
Base.:~ — Method
~(lhs, rhs) -> Any
Создает Equation из двух экземпляров Num или Num и Number.
Примеры
julia> using Symbolics
julia> @variables x y;
julia> @variables A[1:3, 1:3] B[1:3, 1:3];
julia> x ~ y
x ~ y
julia> x - y ~ 0
x - y ~ 0
julia> A ~ B
(broadcast(~, A, B))[1:3,1:3]
julia> A .~ 3x
(broadcast(~, A, 3x))[1:3,1:3]
Примечание о функциях, ограниченных Number
Объекты Sym и Term НЕ являются подтипами Number. Symbolics предоставляет простой тип-оболочку Num, который является подтипом Real. Num заключает в оболочку либо Sym, либо Term, либо любой другой объект, определяет тот же набор операций, что и символьные выражения, и направляет их к значениям, которые он заключает. Для распаковки Num можно использовать функцию Symbolics.value.
По умолчанию макросы @variables возвращают объекты, заключенные в Num, чтобы можно было вызывать функции, ограниченные Number или Real.
using Symbolics
@variables t x y z(t);
Symbolics.operation(Symbolics.value(x + y))
+ (generic function with 562 methods)
Symbolics.operation(Symbolics.value(z))
z
Symbolics.arguments(Symbolics.value(x + y))
2-element Vector{Any}:
x
y
Обратите внимание, что Julia преобразует иррациональные числа, такие как π и ℯ, в Float64 всякий раз, когда они используются в арифметике с другими числами, включая целые. Выражение типа 2π будет немедленно преобразовано в тип float, поэтому выражение типа 2π * x оставит символьный x, умноженный на Float64. Возможно, целесообразно иметь также символьное представление π, которое можно получить с помощью Num(π). В универсальном программировании можно просто переопределить переменную π так, чтобы она имела тот же тип, что и другой аргумент, как показано ниже.
function f(x)
let π=oftype(x, π)
1 + (2//3 + 4π/5) * x
end
end
f(t)
1 + t*((2//3) + (4//5)*π)
Это будет работать для любых входных данных с плавающей запятой, а также для символьных входных данных.
Функции проверки
#
SymbolicUtils.istree — Function
istree(x)
Возвращает true, если x — это член. Если имеет значение, operation, arguments также должны быть определены для x.
#
SymbolicUtils.operation — Function
operation(x)
Если x является членом, как определено с помощью istree(x), operation(x) возвращает его начало, если x представляет вызов функции, например, начало представляет собой вызываемую функцию.
#
SymbolicUtils.arguments — Function
arguments(x)
Получает аргументы x, которые должны быть определены, если istree(x) имеет значение true.