18. МАТЕМАТИКА
Язык программирования ACL является языком, с одной стороны, интерпретируемым, а с другой -- в известной степени специальным, то есть направленным на определенный класс задач, которые его автору приходится решать относительно часто. И хотя на ACL можно выполнить любые вычисления, тем не менее есть резон какие-то вычисления общего порядка выполнять запуская готовые команды. В основном это касается вычислений в длинных циклах, но часто речь может идти и просто о вычислениях специального вида. Использование команд не только ускоряет работу программы, но и делает код более читаемым. Все готовые вычисления реализуются в рамках одной команды называемой #mathematics, а короче #ma. Эта команда в принципе может иметь сколько угодно операций и число ее операций растет от версии к версии. В этом разделе рассмотрены все операции, разработанные к моменту написания курса. Операции можно условно разделить на группы. Первая группа операций -- это векторные операции. Под вектором будем понимать часть вещественного массива, начиная с индекса, задаваемого параметром [b] и с числом элементов [le]. Когда в операции используются и другие векторы, то они описываются другими параметрами. Итак рассмотрим векторные операции.
ВЕКТОРНЫЕ ОПЕРАЦИИ
#ma [op=vic; b=; le=;]
Эта операция выполняет инициализацию вектора константой. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Постоянное значение должно быть задано в переменной [C]. В результате операции все элементы выбранной части массива r() будут равны [C].
#ma [op=via; b=; le=;]
Эта операция выполняет инициализацию вектора арифметической прогрессией. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Первое значение задается в переменной [C]. Шаг к следующему значению задается в переменной [D]. В результате операции все элементы выбранной части массива r() будут равны C + D*i, где i есть разность индексов текущего и первого элементов, то есть он равен 0 для первого элемента.
#ma [op=vig; b=; le=;]
Эта операция выполняет инициализацию вектора функцией Гаусса. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Первое значение аргумента задается в переменной [A]. Шаг к следующему значению аргумента задается в переменной [B]. Положение центра кривой задается в переменной [C]. Полуширина, то есть ширина на половине высоты задается в переменной [D]. В результате операции все элементы выбранной части массива r() будут равны значениям функции exp(-a1*(A+B*i-C)^2), где i есть разность индексов текущего и первого элементов, то есть он равен 0 для первого элемента, a1 - коэффициент, вычисляемый из полуширины кривой D.
#ma [op=vaс; b=; le=;]
Эта операция выполняет добавление к вектору константы. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Постоянное значение должно быть задано в переменной [C]. В результате операции ко всем элементам выбранной части массива r() добавляется константа [C].
#ma [op=vmс; b=; le=;]
Эта операция выполняет умножение вектора на константу. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Постоянное значение должно быть задано в переменной [C]. В результате операции все элементы выбранной части массива r() умножаются на константу [C].
#ma [op=vex; b=; le=;]
Эта операция вычисляет экспоненту от значений вектора поэлементно. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. В результате операции все элементы выбранной части массива r() получают новые значения.
#ma [op=vsi; b=; le=;]
Эта операция вычисляет синус от значений вектора поэлементно. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. В результате операции все элементы выбранной части массива r() получают новые значения.
#ma [op=vco; b=; le=;]
Эта операция вычисляет косинус от значений вектора поэлементно. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. В результате операции все элементы выбранной части массива r() получают новые значения.
#ma [op=vpo; b=; le=;]
Эта операция вычисляет степень от значений вектора поэлементно. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Показатель степени должен находиться в переменной C. Проверка на неправильные операции не проводится. Пользователь должен заботиться об этом сам. В результате операции все элементы выбранной части массива r() получают новые значения, то есть x превращается в x^C.
#ma [op=vba; b=; le=;]
Эта специальная операция выполняет преобразование вектора из байтов в ASCII коды. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Предполагается, что в нем записаны байты, то есть целые числа в диапазоне от -128 до 127. В результате преобразования отрицательные числа заменяются на положительные прибавлением 256.
#ma [op=vva; b=; le=; trx=;]
Эта операция выполняет сложение вектора с вектором поэлементно. Первый вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Второй вектор такой же длины смещен относительно первого на разность индексов [trx]. Результат сложения записывается вместо первого вектора. Параметр [trx] может быть отрицателен. Для правильной работы векторы не должны перекрываться. Для вычитания векторов достаточно второй вектор предварительно умножить на -1.
#ma [op=vvm; b=; le=; trx=;]
Эта операция выполняет умножение вектора на вектор поэлементно. Первый вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Второй вектор такой же длины смещен относительно первого на разность индексов [trx]. Результат умножения записывается вместо первого вектора. Параметр [trx] может быть отрицателен. Для правильной работы векторы не должны перекрываться.
#ma [op=vvs; b=; le=; trx=;]
Эта операция выполняет скалярное умножение вектора на вектор. Первый вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Второй вектор такой же длины смещен относительно первого на разность индексов [trx]. Результат умножения записывается в переменную [C].
#ma [op=var; b=; le=;]
Эта операция ограничивает область значений вектора. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Если значение элементов вектора меньше, чем значение переменной V0, то оно заменяется на V0. Соответственно, если больше V1, то заменяется на V1.
#ma [op=vor; b=; le=; mo=;]
Эта операция упорядочивает элементы вектора по возрастанию, если [mo] равно 1, или убыванию, если [mo] равно 0. Вектор находится в реальном массиве r() и определяется параметрами [b] и [le]. Одновременно в целом массиве i() начиная с первого элемента указываются индексы элементов, какие они имели до упорядочивания. Таким образом, если необходимо упорядочить аналогичным образом другие векторы, то это уже можно сделать используя массив i(). При этом первым надо поставить тот индекс, который находится в i(1) и так далее.
#ma [op=cwm; b=; le=; mo=;]
Эта операция выполняет расчет параметров максимума (фокуса) кривой, заданной некоторой системой значений реального массива r(). Параметры есть [b] как первый индекс элемента массива r(), и [le] как число рассматриваемых значений. В результате операции будут вычислены минимальное и максимальное значения, центр HM-области, и ширина HM-области. Эти значения будут записаны в переменные A,B,C,D. HM-область (half-maximum) -- это область, внутри которой значения элементов массива больше чем B/2 если параметр [mo] равен 0 или B-(B-A)/2, если [mo] равен 1.
Вторая группа операций -- это матричные операции. Под матрицей также понимается часть вещественного массива r() с начальным индексом, задаваемым параметров [b], но размерность матрицы задается параметрами [nx] (число колонок) и [ny] (число строк).
МАТРИЧНЫЕ ОПЕРАЦИИ
#ma [op=mtr; b=; nx=; ny=;]
Эта операция выполняет транспонирование матрицы. Матрица находится в реальном массиве r() с первого номера, задаваемого параметром [b]. Число строк задается [ny] а число столбцов [nx]. В результате операции в той же части реального массива будет записана транспонированная матрица у которой колонки станут строками, а строки колонками. Эта операция очень удобна при чтении чисел из файла в свободном формате (из колонок) в том случае, когда необходимо выделить отдельные функции из многих функций. После транспонирования значения каждой функции записаны слитно.
#ma [op=mii; b=; nx=; ny=; mo=;]
Эта операция выполняет инверсию индексов матрицы. В результате операции последний элемент каждой строки становится первым и последняя строка становится первой. Как обычно, матрица описывается параметрами [nx], [ny] и [b]. Кроме того, параметр [mo] определяет модификации. Если [mo=0;], оба индекса инвертируются, если [mo=1;], то только первый индекс инвертируется, то есть строки, если [mo=2;], то только второй индекс инвертируется, то есть колонки. Результирующая матрица записывается на то же место. Заметим, что предполагается обычный в математике порядок записи матрицы строками.
#ma [op=mss; b=; nx=; ny=;]
Эта операция выполняет суммирование строк и колонок матрицы, которая имеет форму m[nx,ny]. В результате вычисляется вектор v[nx] как сумма по всем k для m[nx,k] который записывается сразу за матрицей, затем вычисляется вектор v[ny] как сумма по всем k для m[k,ny] который записывается после предыдущего вектора. А следующий элемент реального массива равен полной сумме всех элементов матрицы. Это можно себе представить как интегрирование функции f(x,y) сначала по y с результатом A(x), затем по x с результатом B(y) и затем по x и y одновременно. В важном частном случае, если [ny] = 1 и матрица превращается в вектор, операция работает иначе. В этом случае вычисляется только сумма по всем элементам вектора и результат записывается за вектором. Это эквивалентно интегралу от функции f(x). В новой версии программы описанный выше режим работает при значении параметра [emp=0;]. Если [emp=1;] то выполняется суммирование только по второму индексу при условии, что ny > 1. А при [emp=2;] выполняется суммирование только по первому индексу. Каждый раз результат суммирования записывается сразу за матрицей а за ним полная сумма. Новые режимы позволяют упростить программирование и ускорить счет в тех случаях, когда двойное суммирование не является необходимым.
#ma [op=mmi; n=; b=; nx=; ny=; sav=; wid=; hei=;
file=; form=;]
Эта операция выполняет интерполяцию матрицы в матрицу. Это довольно сложная операция, потому что размер матрицы может быть произвольно большим. Начальная матрица имеет размеры [nx] и [ny], в то время как новая матрица имеет размеры [wid] и [hei]. Предполагается, что исходная матрица описывает прямоугольную область с xi1, yi1, xi2, yi2, в то время как интерполированная матрица описывает прямоугольную область xo1, yo1, xo2, yo2. Эти значения должны быть заданы в реальном массиве r() начиная с индекса задаваемого [n] в следующем порядке xi1,xi2,yi1,yi2,xo1,xo2,yo1,yo2. Обычно исходная матрица берется из файла с именем [file]. Однако в случае [file=here;] матрица должна быть размещена в реальном массиве r() стартуя с индекса [b]. Обычно результирующая матрица записывается в файл с именем [form] . Однако в случае [form=here;] матрица записывается в реальный массив r() стартуя с индекса [sav]. Предполагается что шаг сетки точек задается по формуле dx=(x2-x1)/(nx-1). Используется линейная интерполяция. Если размер области интеполированной матрицы превышают размеры области исходной матрицы, то значения в точках за пределами исходной матрицы задаются как средние значения исходной матрицы.
#ma [op=mmr; b=; le=;]
Эта операция выполняет вращение квадратной матрицы вокруг ее центра. Предполагается, что квадратная матрица записана в реальном массиве r() начиная с индекса [b]. Размерность матрицы равна [le]*[le]. Из этой матрицы получается другая матрица, такой же размерности, элементы которой вычисляются из элементов исходной матрицы по закону
xo=xn*cos(A)-yn*sin(A), yo=xn*sin(A)+yn*cos(A).
Здесь угол A равен значению переменной [A], xn,yn - координаты новой матрицы, то есть индексы элемента, отсчитываемые от середины. Если [le] четное, то эти координаты полуцелые. Вычисленные координаты xo,yo старой матрицы используются для определения индексов 4-х ближайших элементов, из которых производится интерполяция. Если индексы выходят за пределы их изменения, то соответствующие элементы полагаются равными нулю. Операция эквивалентна вращению черно-белой картинки, которую задает матрица. Новая матрица записывается в том же массиве сразу за исходной, то есть начиная с элемента [b]+[le]*[le].
#ma [op=mcg; b=; sav=; nx=; ny=; n=i; file=; form=;]
Эта операция выполняет свертку (или развертку) матрицы с гауссианом. Предполагается, что матрица используется в вычислениях типа FFT (быстрого преобразования Фурье) и она имеет размерности [nx] и [ny] как целые степени 2, то есть 64, 128, 256, 512, 1024. Однако [ny] может быть равно 1 (см. далее). Светка (или развертка) вычисляется методом двойного FFT и результатом является матрица таких же размеров. Параметры гауссиана (sigmaX) и (sigmaY) измеряются в единицах равных размеру одного пиксела изображения. Гауссиан задается по каждому измерению своим преобразованием Фурье
G(q) = exp( -(sigma*q)^2/2 ).
Свертка выполняется при условии, если параметр (epsilon) = 0. В этом случае фурье-изображение матрицы умножается на G(q). В противном случае если (epsilon) > 0, выполняется развертка при разумной регуляризации, а именно, Фурье изображение матрицы умножается на множитель
1 / sqrt( G(q)^2 + epsilon^2 ).
Значения (sigmaX), (sigmaY), (epsilon) должны быть заданы в элементах реального массива r(i), r(i+1) и r(i+2),, где (i) определяется параметром [n]. Замечу, что если (sigmaX) = 0 или (sigmaY) = 0, то по соответствующему направлению ничего не делается. Исходная матрица читается из файла с именем [file]. Если [file=here;], матрица читается из реального массива начиная с индекса [b]. Вычисленная матрица записывается в файл, имя которого задается [form]. Если [form=here;] матрица будет записана в реальный массив r() стартуя с индекса [sav].
Значение [ny=1;] также возможно и означает одномерный случай, когда матрица совпадает с вектором размера [nx].
# C=p1; D=p2; #ma [op=mip; b=; nx=; ny=;]
Эта операция выполняет преобразование матрицы, которое условно можно назвать размножением периода. Предполагается, что матрица записана в массив r(), начиная с индекса [b] и имеет размеры [nx] и [ny] по горизонтали и вертикали. Необходимо выделить центральную часть этой матрицы внутри прямоугольника с размерами p1 (по горизонтали) и p2 (по вертикали) и переопределить все элементы матрицы вне этого прямоугольника из условия периодичности. То есть сдвигать координаты точки по периодам, до тех пор, пока они не попадут в центральный прямоугольник и потом определить значение точки внутри прямоугольника интерполяцией. Сначала определяются области по вертикали, потом по горизонтали. Периоды в общем случае могут не равняться целому числу шагов матрицы, они задаются в переменных C и D в единицах размера пиксела матрицы. Применение такой процедуре можно найти в разных задачах. Например, пусть вам надо задать периодическую картинку в большой матрице. Тогда достаточно просто исходно задать нулевую матрицу, определить один период и использовать процедуру. Или у вас уже задана периодическая матрица, но с результате ее преобразований периодичность по краям исказилась. Эта процедура позволяет исправить искажения. В графике аналогичная процедура называется заполнением области текстурой. Задается произвольная область, задается картинка и картинка копируется в области периодически начиная с левого верхнего угла. Тут было необходимо копировать именно из центра области.
#ma [op=fno; b=; nx=; ny=; mo=;]
Эта операция выполняет специальную нормировку матрицы. Матрица определяется параметрами [b], [nx], [ny], как и выше. Однако в этом случае каждая строка размером [nx] трактуется как вектор v(j). Определяются минимальное v_min и максимальное v_max значения этого вектора и [ny] значений v_min и [ny] значений v_max записываются сразу за матрицей, начиная с индекса [b]+[nx]*[ny]. Затем если [mo=0;], то каждый вектор нормируется согласно формуле [v(j) - v_min]/[v_max - v_min]. Нормированные значения могут быть использованы для графики. Если [mo] не равно нулю, то матрица остается той же самой и операция используется только для определения минимальных и максимальных значений.
#ma [op=ffi; b=; nx=; ny=; n=; wid=; c=;]
Эта операция выполняет интерполяцию строк матрицы, описывающих функции на неравномерной сетке в строки матрицы, описывающие функции на равномерной сетке и в произвольном интервале с любым числом точек, включая одну точку. Интерполяция линейная в пределах интервала содержащего новую точку. Если новая точка находится за пределами области определения функций, то берется соответствующее граничное значение. Предполагается, что исходная матрица записана в реальном массиве r() начиная с индекса [b] и имеет размеры [nx] и [ny], в то время как новая матрица имеет размеры [wid] и [ny] и записывается в r(), начиная с индекса [n]. Строка аргументов исходной матрицы записана в r() непосредственно перед ней, а аргументы новой матрицы определяются по начальному и конечному значениям, которые должны быть заданы в r() начиная с индекса, задаваемого параметром [c]. Эта операция удобна при конвертировании внешних таблиц в таблицы, используемые в расчете.
#ma [op=fis; b=; nx=; ny=; siz=;]
Эта операция выполняет интерполяцию строк матрицы кубическим сплайном. Каждая строка исходной матрицы представляет собой значения функции, определенной на системе значений аргумента с переменным шагом. Матрица имеет размеры [nx] и [ny] и записана в реальном массиве r() начиная с индекса [b]. Аргументы должны предшествовать матрице и система точек начинается с индекса [b]-[nx]. Результатом интерполяции является новая матрица, которая имеет размеры [siz] and [ny]. Она определяет ту же самую систему функций, но на новой системе точек с постоянным шагом начиная с первой старой точки до последней старой точки. Новая матрица записывается сразу за старой матрицей, так что для нее первый элемент массива r() начинается с [b]+[nx]*[ny]. Другими словами, эта операция меняет представление системы функций с переменного шага на постоянный шаг с использованием кубического сплайна.
#ma [op=smh; file=; le=; twi=; the=; ord=; b=;]
Эта операция выполняет поиск области максимума (фокуса) внутри 16-битного изображения, записанного детектором. Это достаточно специальная операция, но она может быть полезной в некоторых случаях. Параметры означают следующее: [file] -- имя файла, [le] -- размер заголовка в байтах, [twi],[the] -- размеры изображения в пикселах, [ord] -- порядок байтов в двухбайтовом представлении чисел, [ord=0;] для Low Byte Second и [ord=1;] для Low Byte First. Результат будет записан в реальный массив r(), начиная с индекса [b]. Результат представляет собой 5 чисел: x и y координаты фокуса, значение максимума в фокусе, а также x и y компоненты FWHM (полная ширина на половине высоты) для пика фокуса. Все координаты измеряются в пикселах.
#ma [op=rfh; file=; le=; twi=; the=; ord=; b=;
wid=; hei=; xs=; ys=;]
Эта операция выполняет чтение фрагмента внутри 16-битного изображения, записанного детектором. Это достаточно специальная операция, но она может быть полезной в некоторых случаях. Параметры означают следующее: [file] -- имя файла, [le] -- размер заголовка в байтах, [twi],[the] -- размеры изображения в пикселах, [ord] -- порядок байтов в двухбайтовом представлении чисел, [ord=0;] для Low Byte Second и [ord=1;] для Low Byte First. Результат будет записан в реальный массив r(), начиная с индекса [b]. Результат представляет собой прямоугольную матрицу чисел шириной [wid] и высотой [hei]. Если трактовать исходную картинку как записанную снизу вверх, то [xs] и [ys] задают координаты левой нижней точки фрагмента, начальные значения (1,1). Если ширина или высота фрагмента равны единице, то результатом будет часть столбца или строки исходной матрицы.
#ma [op=tom; le=; siz=; n=; b=;]
Эта операция выполняет преобразование матрицы в матрицу, используемое в томографии. Исходная матрица имеет размерность [le]*[siz]. Она должна быть записана по столбцам в реальном массиве, начиная с индекса [n]. Порядок записи 11 21 31 ... 12 22 ... Вычисляемая матрица квадратная и имеет размерность [le]*[le] и будет записана в массиве r(), начиная с индекса [b]. Для получения правильного результата необходимо, чтобы матрицы не перекрывались. Элементы новой матрицы с координатами xn,yn отсчитываемыми от середины области изменения индексов, получаются из элементов старой матрицы следующим образом. Каждый столбец старой матрицы ставится в соответствие значению угла A=PI*m/[siz], m=0,....[siz]-1. Координата каждого столбца получается из координат xn,yn по закону x=xn*cos(A)+yn*sin(A). Для этой координаты определяются два ближайших элемента, между которыми производится интерполяция. Если индекс элемента выходит за рамки, то элемент равен нулю. Полученные таким образом значения для всех столбцов суммируются.
Третья группа операций имеет дело с комплексными числами. Хотя комплексных чисел в ACL нет, их можно понимать как пару рядом стоящих вещественных чисел. И с ними можно проводить вычисления по законам комплексных чисел. Реализованы следующие операции над комплексными массивами.
ОПЕРАЦИИ С КОМПЛЕКСНЫМИ ЧИСЛАМИ
#ma [op=mcc; b=; nx=;]
Эта операция умножает комплексный массив на комплексный массив. Первый комплексный массив находится реальном массиве r(), начиная с элемента с индексом [b]. Он имеет структуру r,i,r,i,r,i и размер [nx] комплексных значений. Второй комплексный массив расположен сразу после первого. Результат умножения будет помещен на место первого массива, так что первый массив будет потерян.
#ma [op=dcc; b=; nx=;]
Эта операция делит комплексный массив на комплексный массив. Первый комплексный массив находится реальном массиве r() начиная с элемента с индексом [b]. Он имеет структуру r,i,r,i,r,i и размер [nx] комплексных значений. Второй комплексный массив расположен сразу после первого. Результат деления будет помещен на место первого массива, так что первый массив будет потерян в памяти. Чтобы избежать деления на ноль операция делается следующим образом c1*conjg(c2)/(abs(c2)^2+E^2). Поэтому должна быть определена переменная [E], которая должна иметь достаточно малое значение.
#ma [op=cen; b=; n=; trx=;]
Эта операция преобразует комплексный массив чисел из экспоненциальной в нормальную форму. Размер массива определяется параметром [num]. В экспоненциальной форме каждый элемент массива задается модулем и фазой, причем в реальном массиве r() сначала записывается [n] значений модуля, начиная с индекса, определяемого параметром [beg], а сразу вслед за ним [n] значений фазы для тех же комплексных чисел. Эти данные должны быть заданы до начала операции. В результате операции в реальный массив r() записывается [n] комплексных чисел в режиме r,i,r,i,... причем первый элемент записывается в индекс [b]+[trx]. Очевидно старая и новая записи не должны перекрываться, поэтому [trx] по модулю должно быть больше 2*[n]. Однако возможная ощибка не отслеживается, будьте внимательны. Данная операция удобна, если вам необходимо задавать комплексный массив через зависимость модуля или фазы от какого-либо параметра. Так как в расчетах фурье-преобразования используется нормальная форма, то такая операция необходима.
#ma [op=cne; b=; n=; nx=; ny=; trx=;]
Эта операция обратна предыдущей и она преобразует комплексный массив чисел из нормальной в экспоненциальную форму. Размер массива определяется параметром [n]. В экспоненциальной форме каждый элемент массива задается модулем и фазой, причем в реальном массиве r() сначала записывается [n] значений модуля, начиная с индекса, определяемого параметром [b]+[trx], а сразу вслед за ним [n] значений фазы для тех же комплексных чисел. Эти данные являются результатом операции. А до начала операции в реальный массив r() записывается [n] комплексных чисел в режиме r,i,r,i,... причем первый элемент записывается в индекс [b]. Очевидно старая и новая записи не должны перекрываться, поэтому [trx] по модулю должно быть больше 2*[n]. Однако возможная ощибка не отслеживается, будьте внимательны. Данная операция удобна, если вам необходимо проанализировать графически комплексный массив через зависимость модуля или фазы от какого-либо параметра. Так как в расчетах фурье-преобразования используется нормальная форма, то такая операция необходима. Для удобства графического представления, а также возможно и для других целей массив реальных чисел, описывающих фазу выпрямляется, то есть ликвидируются возможные скачки на 2 пи. Эти скачки неизбежны, так как фаза определяется через arctan(i/r) с учетом конкретных знаков r и i. Алгоритм выпрямления фазы зависит от параметра [nx]. Значение этого параметра должно находиться между 1 и [n] а его смысл в том, что он задает стартовую точку для выпрямления фазы как в сторону возрастания индекса, так и в сторону убывания. Эта стартовая точка должна находиться в области предполагаемого наименьшего изменения фазы. Например, для симметричной параболической фазы она находится в середине области, то есть [nx]=[n]/2. Это значение для [nx] автоматически устанавливается без указания на ошибку, если исходное значение [nx] находится вне указанного интервала. При конвертировании двумерного массива механизм выпрямления фазы нужно отключать. Он отключается, если [ny]=10101. Случайно такое значение поставить маловероятно, поэтому можно не заботиться если не надо отключать выпрямление. Но если надо, то его надо ставить принудительно.
#ma [op=fft; b=; nx=;]
Эта операция означает Фурье преобразование в бесконечных пределах для функции, которая равна нулю на бесконечности. Расчет проводится с использованием алгоритма FFT (быстрого преобразования Фурье) для суммы
fq[n] = sum{m = 0,...,N-1} exp( sign(k)*2*pi*i*n*m/N )*fx[m]
где N = 2^abs(k) и k -- целое, n = 0,...,N-1
Параметр k задается [nx]. При этом k может быть отрицательным, но его абсолютное значение не может быть больше 18. При k < 0 вычисляется переход из x пространства в q пространство. При этом функция в x-пространстве должна быть задана на системе точек x[n]=D*(n+(1-N)/2), где D - шаг сетки точек - задается в программе в указанной переменной [D]. При этом функция определяется в симметричных пределах на области X = D*N, а для преобразования интеграла к сумме используются некоторые дополнительные умножения на фазовые множители. Преобразования не зависят от значения D и оно необходимо только для умножения на сумму и получения ответа в физической размерной системе координат. Ответ получается на системе точек q[n]=d*(n+(1-N)/2), где d = 2п/X, а полная область в q пространстве равна Q = 2п/D. Если k > 0, то все наоборот. Функция должна быть задана на системе точек q[n]=d*(n+(1-N)/2), где d = 2п/X, ответ получается на системе точек x[n]=D*(n+(1-N)/2), где D = X/N, а сумму надо умножать на множитель d/2п = 1/X. Этот множитель также следует задать в переменной [D]. То есть переменная [D] есть параметр операции. Комплексный массив fx[m] должен быть записан в реальный массив r(), начиная с элемента с индексом [b] и упорядоченный как (r,i,r,i, и так далее). Вычисленное фурье-изображение будет помещено к той же части реального массива r().
#ma [op=com;] K A B C K A B C K A B C K A B C . . .
Это операция общего программирования арифметики комплексных чисел. Она программирует методом записи команд как в первых компьютерах. Каждая команда содержит 4 целых числа K A B C, причем первое число задает код операции, а остальные три - указания на размещение аргументов в реальном массиве, то есть на три комплексных числа, причем над первыми двумя производится операция, а результат записывается в третье число. Другими словами, комплексные числа записаны в реальном массиве r(), а целые числа A, B, C - это индексы для реальной части рассматриваемых комплексных чисел, мнимые части следуют за ними. Реализованы следующие коды операций: 1 - сложение, 2 - вычитание, 3 - умножение, 4 - деление, 5 - exp(), 6 - cos(), 7 - sin(), 8 - tan(), 9 - pow(,), 10 - log(). Для функций второй аргумент не используется. Исключением является функция pow(), для которой используется реальная часть второго аргумента, то есть комплексное число возводится только в реальную степень. Как это работает. Пусть вам необходимо вычислить выражение a=a+b*exp(c)/(d+e); где a,b,c,d,e - комплексные числа. Вы вводите еще два числа f и g и все числа размещаете в реальном массиве так что a=(r(1),r(2)), b=(r(3),r(4)), . . ., g=(r(13),r(14)). Тогда выражение можно вычислить следующей системой команд
#ma [op=com;] 5 5 0 11 3 11 3 11 1 7 9 13 4 11 13 11 1 1 11 1
Такой способ, несмотря на свою абстрактность и ненаглядность, позволяет провести вычисления быстрее и записать их более компактно по сравнению с обычным расписыванием комплексной арифметики.
Наконец, есть еще четвертая группа операций, представленная пока всего одной операцией, которую условно можно назвать специальными вычислениями.
СПЕЦИАЛЬНЫЕ ВЫЧИСЛЕНИЯ
Специальные вычисления не являются элементами языка программирования в буквальном смысле этого слова. Все такие вычисления можно сделать с помощью других команд ACL. Но часто это не удобно по разным причинам. Одной из причин может быть массовость таких вычислений. Другой -- необходимость выполнить вычисления быстро. В связи с этим данная группа операций тоже постепенно увеличивается в числе. Первая операция
#ma [op=box; wid=; hei=; sty=; bot=; top=; uni=; b=;]
Она выполняет вспомогательные вычисления координат точек для рисования осей прямоугольного графика, внутри которого будет нарисована функция. Такие вычисления проводятся при выполнении операций научной графики в командах #w и #g . Эти операции будут описаны в последующих главах. Данная операция позволяет написать программу, аналогичную #w [op=pf;], но в рамках универсальной графики #eg с возможностью комбинировать много графиков и других элементов непосредственно на ACL, что (хотя и в сложнее для программиста) открывает большие возможности создания графиков типографского качества. Перед вызовом данной операции должны быть заданы параметры [wid], [hei], определяющие область графика (осей), [sty], [bot], [top], задающие видности осей и размеров рисок, аналогично указанным выше командам, [uni] - может быть нулем для автоматической разметки осей и не нулем для ручной разметки, [b] - указывает первый индекс массива r(), в котором должны быть заданы вещественные значения. Если [uni]=0, то таких значений 4, а именно, xmi, xma, fmi,fma, а если [uni]=1, то таких значений 11 и они точно те же, как в команде #w [op=pf;] и других, где размечаются оси. В результате выполнения операции i(1) равно полному числу точек для рисования линий, а i(2) равно полному числу текстов с числовыми значениями при метках. Обозначим эти числа временно как N и M. Тогда числа r(1), ..., r(2*N) содержат координаты точек для линий по правилу x1,y1,x2,y2,.... Числа i(3), ..., i(N+1) указывают на видность сегментов. Если i(3)=1,, то первый сегмент (между 1-й и 2-й точками) соединяется линией, если i(3)=0, то не соединяется и сегмент невидим. Если i(4)=1,, то второй сегмент (между 2-й и 3-й точками) соединяется линией и так далее. Далее в числах r(2*N+1), ..., r(2*N+4*M) содержится информация о текстах. На каждый текст 4 числа. Первые два - это координаты, в которых текст должен быть помещен, третье - это число, которое должно быть показано текстом, четвертое - указание к какой оси текст относится (0 - левая ось, 1 - нижняя, 2 - правая, 3 - верхняя). В зависимости от принадлежности к оси текст ставится либо началом, либо концом, либо серединой в указанную точку. Наконец, 4 числа начиная с r(2*N+4*M+1) указывают xbeg, cx, ybeg, cy - начальные значения осей и масштабные коэффициенты. Эти данные используются для рисования кривой функции. Примером применения этой операции может служить суперкоманда ##pfFCH.
Другой тип специальных вычислений связан с моделированием движения твердых шаров внутри заданного объема. Эту задачу можно полностью решить на ACL, но если шаров много, то скорость вычислений неудовлетворительная. Дело в том, что такой процесс реализуется с использованием команды анимационной графики и в перерыве между быстрой сменой кадров желательно выполнить все вычисления быстро, то есть необходима максимальная скорость. Поэтому написаны три небольшие программы на Java, которые запускаются в работу как отдельные операции. Предполагается, что число шаров задано в переменной N, а диаметр шаров -- в переменной D. Соответственно в первых 4*N элементах реального массива r() записаны параметры для каждого шара, а именно, две координаты x,y центра и две компоненты X,Y скорости (точнее приращение координат за одни шаг). Первая операция (ball system movement)
#ma [op=bsm;]
просто выполняет передвижение шаров на один шаг, то есть для каждого шара его координаты меняются по закону x=x+X, y=y+Y. Вторая операция (ball system interaction)
#ma [op=bsi;]
выполняет расчет возможного взаимного соударения системы шаров. Алгоритм вычислений стандартный - две частицы, для которых расстояние между их центрами меньше диаметра, обмениваются проекциями скоростей на ось, соединяющую их центры. Проверяются все комбинации пар частиц. Использование этой операции заметно ускоряет процесс. Третья операция (ball system reflection)
#ma [op=bsr;]
выполняет расчет возможного отражения шаров от границ области, в которой они находятся. Предполагается, что область описывается ломаной линией, соединяющей точки на плоскости. Эти точки x,y необходимо задать в реальном массиве r() начиная с индекса, который является значением переменной K. Число прямолинейных участков должно быть записано в переменной N1, а число заданных точек должно быть на 1 больше. Если область имеет достаточно сложный контур, то использование этой операции также существенно ускоряет процесс.
СПЕЦИАЛЬНЫЕ ВЫЧИСЛЕНИЯ В ВИДЕ ПРОЦЕДУР
В языке ACL есть дополнительный набор готовых вычислений, которые не оформлены как операции команды #ma , а вызываются как обычные процедуры языка, но этим процедурам не надо писать куски ACL кода в скобках #pro ... @ . Они имеют специальные названия, которые интерпретатор понимает сразу и сразу выполняет. Код этих процедур написан на языке Java, аналогично всем процедурам, которые вызываются при исполнении команд ACL. Признаться, я даже не могу объяснить почему я не сделал специальную команду, а решил вызывать такие процедуры через команду #e . Но так сделано. Причина видимо в том, что указанные программы являются частью моей личной научной работы и они не являются элементами языка ACL для всех.
По этой причине я совсем не собирался упоминать про них в описании ACL. Многие из указанных программ достаточно сложны и имеют собственное описание. Однако среди них есть такие, которые могут пригодиться кому-то еще кроме меня. По этой причине я решил частично дополнить описание и рассказать про синтаксис таким программ. Итак эти процедуры имеют название, которое начинается с символа \ (юникод 92), после которого идет номер из двух разрядов (ноль писать обязательно), вот пример
#e [определение параметров] _\nn
Реальные параметры передаются в программу либо через элементы массива r(), либо через переменные, а целые могут передаваться либо через целый массив, либо через параметры. Сейчас есть процедуры от \01 до \06. Они решают разные задачи, которые мне приходилось решать в своей практике. Описание этих задач нужно читать в другом месте. Некоторые из программ настолько сложны, что их описание занимает объем целой статьи. Кроме того, для их использования надо хорошо знать раздел физики, называемый рентгеновская оптика. Тем не менее, я для себя буду здесь записывать справочник по использованию процедур.
\00 -- расчет коэффициента прохождения для многокристального монохроматора на плоскости энергия-угол
\01 -- программа рентгеновской инлайн оптики, т.е. перенос волновой функции через объекты и воздух
\02 -- программа метода стоячих волн, угловая зависимость рентгеновского отражения и выхода вторичных процессов
\03 -- программа расчета лауэ-дифракции в кристалле методоми геометрической оптики
\04 -- расчет набора стандартных функций для использования в ACL программах одномерной инлайн оптики
\05 -- программа трехволновой дифракции в кристаллах
\06 -- программа четырехволновой дифракции в кристаллах