Я не думаю, что вам нужно автоматически генерировать все это.
Из ваших комментариев я понимаю, что функции d0
, d1
, ... генерируются из большой разреженной матрицы вспособ суммирования всего входного массива x
(с коэффициентами), но принципиально пропускающий суммирование нулевых коэффициентов, что дает вам большой выигрыш в производительности, потому что матрица огромна.Это будет правильная оценка?
Если это так, я все еще не думаю, что вам нужно генерировать код для этого.
Давайте посмотрим.Я предполагаю, что ваша гигантская разреженная матрица имеет интерфейс для получения значений ячеек, и он выглядит примерно так:
let getMatrixCell (i: int) (j: int) : double
let maxI: int
let maxJ: int
Тогда ваш код автогенерации может выглядеть примерно так:
let generateDFunction (i: int) =
printfn "let d%d (x: double[]) =" i
printfn " [|"
for j in 0..maxJ do
let cell = getMatrixCell i j
if cell <> 0 then
printfn " %f * x.[%d]" cell j
printfn " |]"
printfn " |> Array.sum"
Что может привести к чему-то вроде этого:
let d25 (x : array<double>) =
[|
-1.0 * x.[25]
1.0 * x.[3]
|]
|> Array.sum
Обратите внимание, что я здесь упрощаю: в вашем примере файла похоже, что функции также умножают отрицательные коэффициенты на x.[i]
.Но, возможно, я также слишком усложняю, потому что похоже, что все коэффициенты всегда либо 1
, либо -1
.Но это все несущественно с моей точки зрения.
Теперь в комментариях было предложено, что вы не генерируете функции d0
, d1
, ..., а вместо этого работаете напрямую с матрицей,Например, это было бы наивной реализацией такого предложения:
let calculateDFunction (i: int) (x: double[]) =
[| for j in 0..maxJ -> (getMatrixCell i j) * x.[j] |] |> Array.sum
Вы тогда утверждали, что это решение будет слишком медленным, потому что оно всегда перебирает весь массив x
, который огромен, нобольшинство коэффициентов равны нулю, поэтому это не обязательно.
И тогда ваш способ решения этой проблемы заключался в использовании промежуточного шага сгенерированного кода: вы генерируете функции, которые касаются только ненулевых индикаторов, а затем компилируете и используете эти функции.
Но вот в чем дело: да, вам нужен этот промежуточный шаг, чтобы избавиться от ненулевых признаков, , но это не обязательно должен быть сгенерированный и скомпилированный код !
Вместо этого вы можете заранее подготовить списки / массивы ненулевых указателей:
let indicies =
[| for i in 0..maxI ->
[ for j in 0..maxJ do
let cell = getMatrixCell i j
if cell <> 0 then yield (j, cell)
]
|]
Это даст массив indicies : Array<int list>
, где каждый индекс k
соответствует вашей автоматически сгенерированной функции dk
,и содержит список ненулевых матричных указателей вместе с их значениями в матрице.Например, функция d22
, которую я дал выше, будет представлена 22-м элементом indicies
:
indicies.[22] = [ (25, -1.0), (3, 1.0) ]
На основе этой промежуточной структуры вы можете затем вычислить любую функцию dk
:
let calculateDFunction (k: int) (x: double[]) =
[| for (j, coeff) in indicies.[k] -> coeff * x.[j] |] |> Array.sum
На самом деле, если производительность важна для вас (как это видно из комментариев), вам, вероятно, следует покончить со всеми этими промежуточными массивами: сотни или тысячи распределений кучи на каждой итерации определенно не помогают,Вы можете суммировать с изменяемой переменной вместо:
let calculateDFunction (k: int) (x: double[]) =
let sum = 0.0
for (j, coeff) in indicies.[k] do
sum <- sum + coeff * x.[j]
sum