Типы переменных и уравнений
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
.