1971년 어느 날 Mr Hanson은 “오늘은 숫자의 제곱근을 구하는 방법을 알려드리겠습니다. 보다 빠른 방법 긴 분할 내가 가르쳐야 할 길. 한 마디로 가르쳐 드리겠습니다. 그리고 당신은 결코 잊지 못할 것입니다.” 그런 다음 그는 칠판에 씁니다.
추측하다
그리고 밑줄을 긋습니다. 웃음이 잦아들자 그는 추측을 원래 숫자, 평균, 반복으로 나누는 것에 대해 설명했다. 그는 아마 언급 뉴턴-랩슨 .
방정식의 근 또는 영점을 찾기 위한 뉴턴의 방법은 방정식의 도함수를 사용하여 프로세스 속도를 높입니다. 10의 제곱근은 X^2 == 10 또는 X^2 – 10 == 0이 되는 X의 값을 찾는 것과 같습니다. X^2 – 10의 도함수는 2X이며, X의 연속 값을 찾기 위한 루프의 핵심은
Xnew = X - ( (X^2 - 10) / (2X) )
가지고 놀면 정확히 추측하다 다음과 같이 초기 추측값이 2로 수렴됩니다.
2 – 초기 추측
3.5 – 적용 x = (x + 10/x)/2
3.17857142857143
3.16231942215088
3.16227766044414
3.16227766016838
3.16227766016838 – 안정적인 답변을 위한 6번의 반복!
다음은 시연할 JSL 프로그램입니다.
f = Expr( x ^ 2 - V );
step = Eval Expr( x - Expr( Name Expr( f ) ) / Expr( Derivative( Name Expr( f ), x ) ) );
V = 1e2; // find the sqrt of V
x = -8; // initial guess.
xnext = .; // set up loop
n = 0; // counter
While( 1,
xnext = step;
If( Abs( x - xnext ) < Abs( 1e-14 * x ),
Break()
);
x = xnext;
Write( "\!n", n, " ", x );
n = n + 1;
);
Write( "\!ndone, a root of " || Char( Name Expr( f ) ) || " for V = " || Char( v ) || " is ", x );
첫 번째 줄은 V의 제곱근을 찾는 데 도움이 되는 표현식 f를 정의합니다. 두 번째 줄인 단계는 한 주기에 대한 Newton-Raphson 반복입니다. step은 다음 X 값으로 평가되는 while 루프에 나타납니다. Derivative 함수는 간단한 다항식에서 훌륭하게 작동하며 이 예제에서는 2X를 제공합니다. NameExpr()은 평가하지 않고 f에서 수식을 가져오고 EvalExpr()이 Expr()을 찾을 때 Expr()은 NameExpr()이 (수식을 얻기 위해) 실행되도록 합니다. 단계는 x - f(x)/f'(x)로 설정됩니다.
while(1) 루프 내에서 단계가 수행되고 인쇄되고 계산됩니다. 테스트는 현재 X 값과 다음 X 값의 차이를 확인합니다. X의 현재 크기에서 값이 매우 작으면 더 가까워지지 않고 매우 가까운 두 숫자 사이에서 진동할 수 있으므로 while 루프에서 벗어납니다.
0 -10.25
1 -10.0030487804878
2 -10.0000004646115
3 -10
V = 100에 대한 x ^ 2 - V의 근은 -10입니다.
양수 및 음수 제곱근이 모두 있음을 기억하십시오! 시작 값은 수렴이 이어지는 루트를 결정합니다. -8 오버샷에서 -10.25로 시작한 다음 -10으로 안착했습니다.
(왜 미분 f'으로 나누나요? 직관적으로 직선 f의 기울기가 0에 가까울 때 f의 Y 결과를 변경하려면 다음 점이 더 멀어져야 하며 기울기로 나누면 됩니다.)
위의 JSL은 불가능한 요청을 잘 처리하지 못합니다. sqrt(-100)을 찾으라는 요청을 받으면 JSL은 루프를 돌며 찾을 수 없는 복소수를 찾습니다.
복소수
JSL 스크립팅 색인에서 complex를 검색하면 서로 다른 두 가지 미숙한 예가 생성됩니다. 하나는 JSL 클래스를 기반으로 하고 다른 하나는 네임스페이스 및 매트릭스를 기반으로 합니다. 매트릭스 버전이 강력한 솔루션에 더 가깝고 아마도 더 빠를 것이라고 생각합니다. 클래스 버전은 감질나지만 사용하는 데 문제가 있었습니다(속도, 메모리 문제). JMP에는 연산자에 대한 사용자 오버로드가 없으므로 둘 중 어느 것도 사용하기 쉽지 않습니다.
이 JSL은 이전 JSL과 매우 유사합니다. 복소수 산술, 거의 같은 수식을 처리하기 위한 많은 양의 코드가 있으며 이제는 sqrt(-100)도 처리합니다.
ns = New Namespace("complex", // adapted from scripting index example
{
make = Function( {a, b}, a || b ),
add = Function( {x, y, t=[0 0], u=[0 0], v=[0 0], w=[0 0]}, // an example has 5 terms
if( Is Number( x ),x = x || 0);
if( Is Number( y ),y = y || 0);
x + y + t + u + v + w;
),
sub = Function( {x, y, t=[0 0], u=[0 0], v=[0 0], w=[0 0]}, // an example has 3 terms. this sub,add trick is good enough for a demo.
if( Is Number( x ),x = x || 0);
if( Is Number( y ),y = y || 0);
x - y - t - u - v - w;
),
mul = Function( {x, y},
if( Is Number( x ),x = x || 0);
if( Is Number( y ),y = y || 0);
local:z = J( 1, 2 );
local:z[1] = x[1] * y[1] - x[2] * y[2];
local:z[2] = x[1] * y[2] + x[2] * y[1];
local:z;
),
div = Function( {x, y},
if( Is Number( x ),x = x || 0);
if( Is Number( y ),y = y || 0);
local:z = J( 1, 2 );
local:d = (y[1] ^ 2 + y[2] ^ 2);
local:z[1] = (x[1] * y[1] + x[2] * y[2]) / local:d;
local:z[2] = (x[2] * y[1] - x[1] * y[2]) / local:d;
local:z;
),
// https://math.stackexchange.com/questions/1397437/formula-for-raising-a-complex-number-to-a-power
// If a,b are real, then a+ib=r(cosθ+i*sinθ) where r^2=a^2+b^2 and tanθ=b/a, and (a + i*b)^N = r^N(cos(Nθ) + i*sin(Nθ))
pow = function( {c, n},
if( Is Number( c ),c = c || 0);
local:a = c[1];
local:b = c[2];
local:theta = ATan( local:b, local:a );
local:r = Sqrt( local:a ^ 2 + local:b ^ 2 );
local:r ^ n * (Cos( n * local:theta ) || Sin( n * local:theta ));
),
abs = function( {x},
Sqrt( x[1] ^ 2 + x[2] ^ 2 )
),
toChar = Function( {x,w=20,d=14}, // rounding (to 14 places) is not a good idea if the numbers are small ( 1e-9 will lose lots of precision)
Char( round(x[1],d),w ) || "+" || Char( round(x[2],d),w ) || "i"
),
convertExpression = function( {e},
substitute( Name Expr( e ),
Expr( Add() ), Expr( complex:add() ),
Expr( Subtract() ), Expr( complex:sub() ),
Expr( Multiply() ), Expr( complex:mul() ),
Expr( Divide() ), Expr( complex:div() ),
Expr( Power() ), Expr( complex:pow() )
)
)
}
);위에서 x^2 또는 x^3 등에 대한 pow() 함수를 추가했습니다. 여기서 x는 복소수, abs() 함수는 magnitude()라고 해야 하지만 복소수가 아닌 버전과 일치하는 abs() 함수입니다. -add() 및 sub() 및 convertExpression() 함수에 대한 추가 매개변수를 찾습니다. 이해하기 쉬운 pow() 기능을 제공한 stackexchange/Michael Hardy에게 큰 박수를 보냅니다!
convertExpression 함수는 +, -, *, / 및 ^ 연산자가 있는 JSL 표현식을 복합 네임스페이스 함수를 호출하는 동등한 표현식으로 마사지합니다. JSL 이진 연산자는 ((a+b)+(c+d)) 를 이진 함수가 아닌 add(a,b,c,d) 로 바꾸는 데 매우 공격적입니다. 여기에는 네 개의 인수가 있습니다. 이것이 complex:add 및 :sub 함수에 추가 옵션 인수가 있는 이유입니다.
JSL의 다음 비트는 루프를 함수로 이동하여 향후 개발을 예상합니다.
findzero = function( {x},
{xnext, n},
xnext = .; // set up loop
n = 0; // counter
While( n < 200,
xnext = step;
If( complex:Abs( complex:sub( x, xnext ) ) < 1e-14 * complex:abs( x ),
Break()
);
x = xnext;
n = n + 1;
);
If( n == 200, x = [. .] );
Eval List( {x, n} );
);findzero는 시작점 x를 취하고 반복 솔루션 x와 반복 횟수 n이 포함된 목록을 반환합니다.
그리고 함수를 호출하는 코드… 공식과 도함수는 복소수에 대해 동일합니다.
f = Expr( x ^ 2 - V );
step = Eval Expr( x - Expr( Name Expr( f ) ) / Expr( Derivative( Name Expr( f ), x ) ) );
step = complex:convertExpression( Name Expr( step ) );
V = complex:make( -100, 0 ); // find the sqrt of V==-100
x = complex:make( 0, 1 ); // initial guess. Does not like 0i for a start value.
{x, n} = findzero( x );
Write( "\!ndone, a root of " || Char( Name Expr( f ) ) || " for V = " || complex:toChar( v, 10, 5 ) || " is ", complex:toChar( x, 10, 5 ) );
Eval( complex:convertExpression( Name Expr( f ) ) );세 번째 줄은 +, -, *, /, ^ 연산자를 실수 버전에서 복소수 버전으로 변경하기 위해 convertExpression 함수를 호출합니다. V는 복소수 값으로 설정되고 초기 시작점은 실수선을 벗어난 지점으로 설정됩니다. 완료되면 로그에 표시됩니다.
완료, x ^ 2의 근 - V = -100+0i에 대한 V는 0+10i입니다.
[0 0]
V=-100 + 0i는 실수 -100(허수 성분 없음)입니다. 0 + 10i는 실수 성분이 없는 순수 허수 답입니다. 10i * 10i는 예상대로 100*(i^2) 또는 -100입니다. 복소수 근은 켤레 쌍에서 발생합니다. 뉴턴의 방법은 시작점을 기반으로 하나를 찾았습니다. 다른 하나는 -10i에 있습니다.
코드를 수정하여 복소 평면의 여러 점에 대한 반복 횟수 n을 추적하고 아마도 등고선 플롯으로 사용하고 2차 대신 3차를 사용하는 그래프를 작성해 보겠습니다.
f = Expr( x ^ 3 - V );
step = Eval Expr( x - Expr( Name Expr( f ) ) / Expr( Derivative( Name Expr( f ), x ) ) );
step = complex:convertExpression( Name Expr( step ) );
V = complex:make( -2, 0 ); // find the sqrt of V==-2
dt = New Table( "complex",New Column( "xre" ),New Column( "yim" ),New Column( "iterations" ),New Column( "basinr" ),New Column( "basini" ));
stepsize=1/32;
for(xreal=-2,xreal<=2,xreal+=stepsize,
for(yimag=-2,yimag<=2,yimag+=stepsize,
x = complex:make( xreal, yimag ); // initial guess. Does not like 0i for a start value.
{x, n} = findzero( x );
dt<<ADDROWS></ADDROWS>같은 루트를 반복하는 복소 평면의 모든 점은 유역을 형성합니다. 위에서 그래프 빌더는 루트를 식별하기 위해 실제 값과 허수 값을 단일 값으로 결합합니다.
이미지는 빨간색, 청록색 및 진한 파란색의 세 가지 유역을 보여줍니다. 모든 빨간색 시작점은 동일한 값으로 수렴됩니다. 더 큰 도트 크기는 수렴하는 데 더 오래 걸립니다. 회색 점은 200회 반복 후에도 수렴되지 않았습니다.
Three basins. Larger dots where the basins touch take longer to converge.Three basins. Larger dots where the basins touch take longer to converge.Three basins. Larger dots where the basins touch take longer to converge.Three basins. Larger dots where the basins touch take longer to converge.세 개의 분지. 유역이 닿는 큰 점이 수렴하는 데 더 오래 걸립니다.
JSL 복소수 구현으로 이미지 크기를 16K에서 65K 포인트로 두 배로 늘리는 데 10분 정도 걸립니다. 4Kx2K 포인트의 비트맵 10,000개를 만들고 싶기 때문에 다른 접근 방식이 필요합니다.
베오울프 클러스터
ㅏ 베오울프 클러스터 대부분 디스플레이나 키보드가 없지만 제어 컴퓨터에 LAN으로 연결된 오래된 컴퓨터 더미입니다. 필자의 경우 제어 컴퓨터는 JSL을 사용하여 클러스터에 수행할 작업을 알리고 결과를 수집합니다. Linux 기반 클러스터는 JMP가 생성하는 Python 프로그램을 실행하고 있습니다. JMP를 실행하는 Windows 컴퓨터는 RunProgram의 PLINK를 사용하여 각 컴퓨터에서 하나 이상의 SSH 세션을 열고 필요한 프로그램과 매개변수를 제공합니다. Python 프로그램이 실행된 다음 결과를 NFS 디스크에 저장한 다음 종료됩니다. JMP는 RunProgram 종료를 확인하면 결과를 수집하고 다음 RunProgram을 시작합니다. 10,000개의 RunPrograms 후에는 5분짜리 비디오를 만들기에 충분한 이미지가 있습니다.
Ethernet hub in center, below power-strip. Not enough mice and monitors and keyboards, SSH is best.Ethernet hub in center, below power-strip. Not enough mice and monitors and keyboards, SSH is best.Ethernet hub in center, below power-strip. Not enough mice and monitors and keyboards, SSH is best.Ethernet hub in center, below power-strip. Not enough mice and monitors and keyboards, SSH is best.파워 스트립 아래 중앙의 이더넷 허브. 마우스와 모니터, 키보드가 충분하지 않은 경우 SSH가 가장 좋습니다.
Python에는 기본 복소수 데이터 유형이 있으므로 위의 JSL 중 일부는 사라집니다. 이제 JSL은 다음과 같습니다. 약간의 문자열로 포함된 일부 Python을 볼 수 있습니다.
F = Expr( // f and e parameters are driven by the frame animation time
f6 * xOld ^ e6 + f5 * xOld ^ e5 + f4 * xOld ^ e4 + f3 * xOld ^ e3 + f2 * xOld ^ e2 + f1 * xOld ^ e1 - 1
); // <<<<<<<<<<< supply equation here <<<<<<<<<<<<<<
scale = 2 / 1;
xresolution = 1920 * scale;
yresolution = 1080 * scale;
// keep aspect ratio by chopping shorter dimension...
xcenter = -1;
ycenter = 0;
delta = 3;
ytop = ycenter - delta * yresolution / xresolution;
xleft = xcenter - delta;
xright = xcenter + delta;
ybottom = ycenter + delta * yresolution / xresolution;
위의 비트에는 방정식과 일부 비디오 크기 매개변수가 포함되어 있습니다. 아래 비트는 네트워크를 설명합니다. Hostkey, server, cpus, running, gigs는 클러스터에 대한 정보의 condat(연결 데이터) 목록에 대한 인덱스입니다.
// named indexes for condat
hostkey = 1; // from a failed connection, this makes the connection work
server = 2; // the machine names
cpus = 3; // from the lscpu command, below
running = 4; // dynamic: how many are running on this cpu, right now
gigs = 5; // from the free command, below
// the avahi-daemon may need to be stop/start/status sometimes,
// unfortunately that is hard to do except at server keyboard if you can't connect...
데몬의 문제는 여전히 존재하지만 이제 -2(등) 접미사가 있는 시스템을 알리고 있다는 것을 알고 있으며 디스플레이를 연결하지 않고도 문제를 해결할 수 있습니다.
condat = {
{"4e:c3:8a:3b:e7:d4:bd:0d:85:62:36:40:17:15:b4:a3", "dell790a.local", 4, 0, 22},
{"00:c2:37:09:dc:d7:cf:fb:33:e3:14:e3:d6:fb:39:be", "dell790b.local", 4, 0, 14},
{"54:c8:1a:6a:50:e3:51:02:ed:1b:f8:fa:bd:f4:e4:17", "dell7010a.local", 8, 0, 30},
{"66:0d:ca:3c:1f:11:eb:45:d4:92:d0:0b:ae:01:b8:6b", "dell7010b.local", 8, 0, 30},
{"94:84:2f:a5:56:2d:db:16:b5:80:a9:0d:87:07:ee:53", "dell9010.local", 8, 0, 30},
{"b2:b7:b3:c3:83:63:11:e7:56:e7:c9:c2:e6:fc:f0:c9", "thinkb.local", 8, 0, 30},
{"2b:ca:fc:45:f0:64:cd:ac:70:ce:93:73:e8:84:2d:75", "thinkc.local", 8, 0, 30},
{"62:b7:67:23:8d:86:fe:80:1a:62:ec:d8:41:1b:b4:1b", "thinkd.local", 8, 0, 30},
{"8e:54:ee:b0:25:67:b8:5c:4d:ad:5e:e9:71:87:fd:71", "netflix.local", 2, 0, 30} // host for vm
};아래의 JSL은 condat 목록을 살펴보고 dell9010과 같은 이름을 점으로 구분된 IP 주소(또는 IPV6 동급)로 변환합니다. 나중에 조회 실패를 방지합니다. 클러스터는 약 3일 동안 실행되었으며 아내의 데스크톱이 DNS 조회 서비스를 제공하는 이유를 아직 잘 모르겠습니다… 우리 집은 몇 년 전보다 지금 IPV6로 인해 훨씬 더 행복해졌습니다. 때로는 IPV6 주소가 대괄호 안에 속합니다.
// on my local network, the lookups for xxx.local fail randomly,
// and more often when busy. pre-lookup the dotted ip now.
//
// better way? Get Addr Info( "thinkd.local" )[3][4]
//
for( itry = 1, itry < 10, itry += 1,
nprocs = 0;
MAXCPU = 0;
good = 0;
for( iCondat = 1, iCondat <= N Items( condat ), iCondat += 1,
nprocs += condat[iCondat][cpus]; // nprocs will divide up the work (stepsize)
condat[iCondat][running] = 0;
MAXCPU = Max( MAXCPU, condat[iCondat][cpus] ); // MAXCPU loops needed later
if( Starts With( condat[iCondat][server], "[" ),
good += 1, // else
// txt = Run Program(
// Executable( "PING.EXE"/*path probably not needed*/ ),
// Options( {"-n 1", condat[iCondat][server]} ),
// ReadFunction( "text" )
// );
// ip = Regex( txt, "\[\[([^]]+)\]]\", "\0" ); // typ: [2603:6081:1a02:1200:8973:e7be:605c:f753]
try(
ip = "[" || Get Addr Info( condat[iCondat][server] )[3][4] || "]";
if( !Is Missing( ip ),
Write( "\!n", condat[iCondat][server] || " --> " || ip );
condat[iCondat][server] = ip;
good += 1;
, //
Write( "\!n", condat[iCondat][server] || " lookup failed ", Left( txt, 70 ) )
); //
, // catch
show(exception_msg,iCondat,condat[iCondat][server],Get Addr Info( condat[iCondat][server] ));
);
);
);
if( good == N Items( condat ),
Break(),
Wait( .1 )
);
);
Write( "\!n",good," of ", N Items( condat )," names resolved." );다음 비트는 자격 증명에 관한 것입니다. ppk는 JMP 시스템의 개인용 키입니다. 해당 키의 공개 부분은 클러스터의 각 시스템에 있습니다. Plink는 원격 시스템에 대한 SSH 연결에 서명하기 위해 개인 비트가 필요합니다. if(0) 코드가 있으므로 모든 기계에서 실험할 수 있습니다. 하루가 끝나면 모든 종료 명령을 보낼 수 있습니다. condat 목록에 이 JSL 스크립트(위의 9행)를 실행 중인 VirtualBox를 실행 중인 Linux 시스템이 있는 경우 종료 명령에 주의하십시오.
remoteuser = "c@";
localkey = " -i c:\Users\v1\Desktop\keys\private.ppk ";
// test code examples...
if( 0, // sequential test using command
//command = " \!"python3.6 -m pip install numpy";
//command = " \!"python3.6 -m pip --version";
//command = " \!"python3.6 -m pip install -U pip";
//command = " \!"python3.6 --version";
command = " \!"hostname -A";
command = " \!"top -b -n 2|head -n 15";
// check if the common nfs mount is set up
//command = " \!"ls /mnt/nfsc"; // fstab: closet.local:/mnt/nfs /mnt/nfsc/ nfs defaults 0 0
// cycle the power; -r means restart. for this to work, must be sudo and not need password
//command = " \!"sudo shutdown -r now"; // visudo: c ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown
//command = " \!"sudo shutdown now"; // visudo: c ALL=(ALL) NOPASSWD: /sbin/poweroff, /sbin/reboot, /sbin/shutdown
// info about number of cores
//command = " \!"lscpu | egrep '^(Model name|Thread|CPU\(s\):)'";
// info about memory
//command = " \!"free -h";
for( iCondat = 1, iCondat <= N Items( condat ), iCondat += 1,
result = runprogram(
executable( "plink" ),
options(
localkey ||
/**/ " -hostkey " || condat[iCondat][hostkey] || /* this line hides the host key */
" " || remoteuser || condat[iCondat][server] || command
),
write function( Function( {this}, this << Write( "n\r\n" ) ) ),
readfunction( "blob" )
);
result = Regex( Blob To Char( result ), "(\r\n)+", "\r\n", GLOBALREPLACE );
Write( "\!n\!n", iCondat, " ", condat[iCondat][server], " ", result );
Wait( 0 );
);
Write( "\!n\!ntest done" );
);다음 비트는 RunProgram의 많은 복사본을 동시에 실행하는 것입니다. ImgNum은 이러한 각 연관 배열의 키이며 Python 프로그램이 작업해야 하는 이미지의 번호입니다. ImgNumToRP는 실행 중인 RunProgram 개체를 보유합니다. 해당 개체는 종료될 때 imgNumToRPdone으로 이동됩니다. imgNumToResult는 Python 프로그램의 텍스트 출력, imgNumToMachine은 기계의 ID, imgNumToLabel은 그림의 레이블을 보유합니다. (레이블은 나중에 적용되며 자세한 내용은 아래에 있습니다.)
imgNumToRP = [=> ]; // holder for active RunProgram objects
imgNumToRPdone = [=> ]; // holder for RunProgram objects that need a moment to terminate
imgNumToRPresult = [=> ""]; // result strings from RunProgram objects
imgNumToMachine = [=> 0]; // which machine is this running on?
imgNumToLabel = [=>""]; // f(x) labels for graphs
nextImageNumberToMakePicture = 0;
// also: keep track of zeros from previous image
Unstick 함수는 RunProgram이 종료된 후 마지막으로 ReadFunction을 호출하지 않는 RunProgram을 처리합니다.
unstick = function({},{imagenumber},
for( imagenumber = imgNumToRP << first, !Is Empty( imagenumber ), imagenumber = imgNumToRPnext,
imgNumToRPnext = imgNumToRP << next( imagenumber );
if( (imgNumToRP[imagenumber]) << canread == 0 & (imgNumToRP[imagenumber]) << canwrite == 0 & (imgNumToRP[imagenumber]) << isreadeof == 1,
imgNumToRPresult[imagenumber] = "\!n" || char(imagenumber) || ": " || imgNumToRPresult[imagenumber] || "\!n"; // NoInterleave save this with count and basin
imgNumToRPdone[imagenumber] = imgNumToRP[imagenumber];
imgNumToRP << removeitem( imagenumber );
//if(imgNumToMachine[imagenumber]!=machine,throw("b machine not match"||char(machine)||","||char(imgNumToMachine[imagenumber])));
condat[imgNumToMachine[imagenumber]][running] = condat[imgNumToMachine[imagenumber]][running] - 1;
imgNumToMachine[imagenumber] = 0;
try(
write("\!nunsticking ",imagenumber);
process( imagenumber ); // must be after...
,
show(exception_msg);
);
Break(); // restart with <<FIRST></FIRST>아래의 findmachine 기능은 너무 많은 SSH 세션이 시작되지 않은 시스템을 찾습니다. unstick() 함수는 고정된 케이스의 10%를 찾기 위해 주기적으로 호출됩니다.
findMachine = function( {},{iCondat,n}, // find a machine that is not filled up
n=0;
while( 1,
for( iCondat = 1, iCondat <= N Items( condat ), iCondat += 1,
if( condat[iCondat][running] < condat[iCondat][cpus],
Return( iCondat )
)
);
if(mod(n+=1,60)==0,
write("wait for machine...");
// look for stuck
unstick();
);
Wait( 1 ); // a full second is fine; the processes run for 10s of seconds
)
);JMP가 RunProgram이 완료되었음을 감지하면 프로세스 함수가 호출됩니다. Python 프로그램은 출력 끝에 "sentinel"이라고 쓰고 이 코드는 모든 것이 원활하게 실행되고 있는지 조잡한 확인으로 확인합니다. Bindata는 Python 프로그램이 NFS 디스크의 팜 디렉터리에 생성하는 이진 파일입니다. Windows 및 JMP는 \\VBOXSVR 경로에서 NFS 디스크를 인식하지만 Linux 및 Python 프로그램은 //mnt 경로에서 인식합니다. 이 코드는 바이너리 데이터에서 그림을 생성하려고 했지만 사후 처리 단계로 분리되었습니다. 이제 수식에 대해 발견된 0의 수만 보고합니다. 바이너리 형식은 파이썬 코드에 설명되어 있습니다. JMP는 이를 읽고 여기에서 두 번 사용된 blobtomatrix로 로드합니다. 마지막에 다시 시작하면 주석이 제거되고 필요한 경우 작동할 수 있습니다. 나는 그것을 주석 해제하는 것을 잊었고 필요하지 않았습니다.
// process output from one thread in the farm
process = function( {imagenumber},
{bindata, start, stop, balanceBytes, matdata, countArray,basinArray,zeros},
//show("process",imagenumber);
if( Contains( imgNumToRPresult[imagenumber], "sentinel" ),
// process a file. maybe not this one if it is out of order... FIXME...nextImageNumberToMakePicture
bindata = Load Text File( "\\VBOXSVR\nfsc\farm\binary" || char(imagenumber) || ".generated", blob() );
matdata = blobtomatrix(
bindata,
"uint",
2,
"little",
2
);
countArray = J( yresolution, xresolution, 0 );
basinArray = J( yresolution, xresolution, 0 );
countArray[0] = matdata[1::nrows(countArray)*ncols(countArray), 1];
basinArray[0] = matdata[1::nrows(countArray)*ncols(countArray), 2];
balanceBytes = length(bindata) - 4*nrows(countArray)*ncols(countArray);
zeros = blobtomatrix(blobpeek(bindata,length(bindata)-balanceBytes),"float",8,"little",4);
write("\!nimagenumber=",imagenumber, if(!ismatrix(zeros)," zeros ***"," zeros="||char(nrows(zeros))));
deletefile("\\VBOXSVR\nfsc\farm\testpython"||char(imagenumber)||".py"); // source no longer needed
sentinels += 1;//
, // else
Write( imgNumToRPresult[imagenumber] );
Write( "\!nrelaunch disabled" || char(imagenumber) );
//launch( imagenumber );
)
);시작 기능은 다른 RunProgram을 시작합니다. findmachine을 사용하여 사용 가능한 시스템을 기다리고 사용자 지정 .py 파일과 사용자 지정 매개 변수를 Python 명령에 제공합니다. .py 파일은 아래에서 추가로 생성됩니다. 아래 5행의 throw는 온전한 검사입니다. JSL은 논리적 오류를 감시하기 위해 추가 코드를 작성해야 할 정도로 복잡합니다.
// launch a thread in the farm
launch = function( {imagenumber},{command,machine},
machine = findMachine();
//show("launch",imagenumber,machine);
if(imgNumToMachine[imagenumber]!=0,throw("image number already on machine"||char(imagenumber)||","||char(machine)));
imgNumToMachine[imagenumber] = machine;
condat[machine][running] = condat[machine][running] + 1;
//command = "\[python3 /mnt/nfsc/farm/testpython]\"||char(imagenumber)||"\[.py]\";
command = "\[hostname -A;python3 /mnt/nfsc/farm/testpython]\"||char(imagenumber)||"\[.py]\";
imgNumToRP[imagenumber] = runprogram(
executable( "plink" ),
options(
localkey || /* -v */ " -hostkey " || condat[machine][hostkey] || " " || remoteuser || condat[machine][server] || " " || command || //
" -o " || Char( 0 ) || // offset
" -s " || Char( 1 ) || // step
" -x " || Char( xresolution ) || //
" -y " || Char( yresolution ) || //
" -t " || Char( ytop ) || //
" -l " || Char( xleft ) || //
" -r " || Char( xright ) || //
" -b " || Char( ybottom ) || //
" -f " || "/mnt/nfsc/farm/binary" || char(imagenumber) || ".generated" || //
""
),매개변수 키워드는 인수를 read함수에서 사용할 수 있도록 합니다. 이것은 여러 RunProgram을 병렬로 가질 수 있는 방법이며 읽기 기능이 어떤 것인지 알 수 있도록 합니다.
Parameter( evallist({imagenumber,machine}) ),readfunction은 출력을 캡처하고 RunProgram이 완료되었음을 감지하기 위해 시도합니다(성공한 시간의 약 90%). 이 코드가 실행될 때 RunProgram이 여전히 종료되고 계속 실행 중이기 때문에 핸들이 제거되는 것이 아니라 imgNumToRPDone으로 이동됩니다. 그래야 깔끔하게 끝낼 수 있습니다. 아래의 Parm은 위의 Parameter 값을 받습니다. 아래에 또 다른 온전성 검사(던지기)가 있습니다.
readfunction(
function( {this, parm}, {txt, imagenumber,machine},
{imagenumber,machine} = parm ;
//show("readfunc",imagenumber,machine);
txt = "";
//write("readfun("||char(imagenumber)||")... ");
While( this << canread, txt = txt || (this << read) );
if( Length( txt ) > 0,
//show(txt);
imgNumToRPresult[imagenumber] = imgNumToRPresult[imagenumber] || txt
);
if( this << isreadeof & imgNumToRP << Contains( imagenumber ), // this *almost* always works, about 1:100 fail to get the readeof in the readrunction
imgNumToRPresult[imagenumber] = "\!n" || char(imagenumber) || ": " || imgNumToRPresult[imagenumber] || "\!n"; // NoInterleave save this with count and basin
process( imagenumber );
imgNumToRPdone[imagenumber] = imgNumToRP[imagenumber];
imgNumToRP << removeitem( imagenumber );
if(imgNumToMachine[imagenumber]!=machine,throw("a machine not match"||char(machine)||","||char(imgNumToMachine[imagenumber])));
condat[machine][running] = condat[machine][running] - 1;
imgNumToMachine[imagenumber] = 0;
);
)
)
);
(imgNumToRP[imagenumber]) << writeeof;
);makePythonCode 함수는 구운 oneStep 방정식으로 .py 파일을 생성합니다. Python 프로그램에는 그림 크기와 출력 파일 이름 정보를 얻기 위해 명령줄을 구문 분석하는 init() 함수가 포함되어 있습니다. 데이터는 두 가지 방식으로 전달됩니다. 명령줄 매개변수와 방정식에서 구워집니다. 아마도 모든 정보가 베이크되었을 수 있지만 코드에 매개변수를 베이크할 이유가 없는 경우(코드에 oneStep 방정식을 컴파일하는 것과 같은) 모든 RunPrograms에 대해 하나의 코드 사본만 필요합니다. 명령줄 parms에 의해 완전히 제어되었습니다. Python 코드는 단일 이미지에서 작업하는 여러 컴퓨터를 원할 경우 문제의 일부를 수행할 수 있도록 작성되었습니다. 전체 이미지를 한 번에 만들고 나중에 부분을 재결합하지 않는 것이 전반적으로 더 빠릅니다.
makePythonCode = function({imagenumber,oneStep},{filename,python},
python =
"\[
import cmath, math, sys, getopt
import numpy as np
def main():
offset, step, ixdim, iydim, top, bottom, left, right, filename = init() # NoInterleave offset will be 0 and step will be 1
iymax = iydim - 1
ixmax = ixdim - 1
yrange = top - bottom
xrange = right - left
result = np.zeros( (int(math.ceil((iydim*ixdim-offset)/step)),2),dtype=np.int16) # count, basin # NoInterleave simpler, no change required though
iresult = 0
ix = offset # NoInterleave like iy, now begins at zero
iy = 0
while ix > ixmax: # normalize before loop starts
ix = ix - ixdim
iy = iy + 1
y = bottom + yrange * iy / iymax # after iy is normalized, before loop
roundedComplexToBasinId = {}
basinIdToBasinSize = {} # NoInterleave this might be useful, but needs to be appended to file (now a constant size for main chunk)
nextBasinId = 1 # reserve basin 1 for "did not converge"
while iy <= iymax:
x = left + xrange * ix / ixmax
xOld = complex(x,y) #xOld - (xOld ** 5 - 1) / (5 * xOld ** 4)
count = 0
try: ## catch zero divide, rarely
for irun in range(1,1000): # NoInterleave prev stopped at 100
###xNew = xOld - (xOld ** 5 - 1) / (5 * xOld ** 4)
]\" || // maintain the indent and newlines!다음 줄은 베이크 인 비트입니다...OneStep은 위의 ### 주석 두 줄처럼 xOld 값과 Newton의 방정식에서 xNew를 설정하는 유효한 Python 문의 JSL 문자열입니다.
oneStep || "\[
count += 1
delta = abs(xNew - xOld)
dist = abs(xOld)
if delta < 1e-15 * dist:
break
xOld = xNew
xOld = complex( round(xOld.real,10), round(xOld.imag,10) )
if irun >= 999:
basinId = 1 # arbitrary, but available always. did not converge.
else:
try: ## catch new basin discovered, rarely
basinId = roundedComplexToBasinId[xOld]
except KeyError as e:
nextBasinId += 1
roundedComplexToBasinId[xOld] = nextBasinId
basinId = nextBasinId
basinIdToBasinSize[basinId] = 0
### print(offset, count, xOld, basinId)
except ZeroDivisionError as e:
basinId = -1
### print(offset, e)
try:
result[iresult,0] = count
except:
print("oops",ix,iy)
if basinId >= 0:
try:
basinIdToBasinSize[basinId] += 1
except KeyError as e:
basinIdToBasinSize[basinId] = 1 # happens with basinId==1
result[iresult,1] = basinId
iresult += 1
ix = ix + step # advance
### offset = offset + step # needed for return value
while ix > ixmax: # re-normalize
ix = ix - ixdim
iy = iy + 1
y = bottom + yrange * iy / iymax다음은 정수와 복식의 이진 파일을 작성하는 파이썬 코드입니다. JMP가 blobToMatrix로 모든 항목을 쉽게 가져올 수 있도록 double이 정수를 따릅니다.
# output results
fileobj = open(filename, mode='wb') # NoInterleave maybe prepend a descriptor
result.tofile(fileobj) # NoInterleave append after this constant size block
# NoInterleave append roundedComplexToBasinId and basinIdToBasinSize rather than printing them, they should be same dimension
# 4 8-byte doubles: real,imag,basin,size
temp = np.zeros( (len(roundedComplexToBasinId),4),dtype=np.float64)
itemp = 0
for iz in roundedComplexToBasinId.keys():
temp[itemp,0] = iz.real
temp[itemp,1] = iz.imag
temp[itemp,2] = roundedComplexToBasinId[iz]
temp[itemp,3] = basinIdToBasinSize[roundedComplexToBasinId[iz]]
itemp += 1
temp.tofile(fileobj)
fileobj.close()
print("sentinel"); # so JSL can test if this ran to the end...
def init():
offset = 0
step = 1
xdim = 3
ydim = 2
top = -1.0
bottom = 1.0
left = -1.0
right = 1.0
filename = "temp.binary"
options, remainder = getopt.getopt(sys.argv[1:], 's:x:y:o:t:b:l:r:f:')
for opt, arg in options:
if opt == '-o':
offset = int(arg) # offset is the starting point
elif opt == '-s':
step = int(arg) # step to the next point
elif opt == '-x':
xdim = int(arg) # xdim field int size
elif opt == '-y':
ydim = int(arg) # ydim field int size
elif opt == '-t':
top = float(arg) # top of field user coords
elif opt == '-b':
bottom = float(arg) # bot field user coords
elif opt == '-l':
left = float(arg) # left field user coords
elif opt == '-r':
right = float(arg) # right field user coords
elif opt == '-f':
filename = arg # write binary file
print(" xdim=",xdim,
" ydim=",ydim,
" offset=",offset,
" step=",step,
" top=",top,
" bottom=",bottom,
" left=",left,
" right=",right)
return offset, step, xdim, ydim, top, bottom, left, right, filename
if __name__ == "__main__":
main()
]\";Python 프로그램은 NFS 디렉토리(Windows에서 알 수 있듯이)에 고유 번호로 저장됩니다. 반환된 파일 이름은 사용되지 않습니다. 파일 이름의 Linux 버전은 시작 기능에 의해 생성되고 Plink를 통해 전달됩니다. (우리는 지금 JSL로 돌아왔습니다...)
filename = Save Text File( "\\VBOXSVR\nfsc\farm\testpython"||char(imagenumber)||".py", python ); // NoInterleave - unique file name for each picture because the equation changes
return(filename);
);
아래 루프는 10,000개의 프레임을 생성했습니다.
for( imagenumber = 0, imagenumber < 10000, imagenumber += 1, //<<<<< frame loop <<<<<<
write("\!nloop",imagenumber);
// for X^5 - 1 there are 5 roots; 1 real and 4 complex
// the python expression for x(n+1) = x(n) - F / F' is
// xNew = xOld - (xOld ** 5 - 1) / (5 * xOld ** 4)공식 및 OneStep을 Python 문으로 변환 – 실제 값을 F1...F6 및 E1...E6 매개변수로 대체하기 때문에 복잡해 보입니다. 마지막에 숨겨진 것은 JSL의 ^에서 Python의 **로의 변경입니다. 이는 JSL이 **를 표현식으로 표현할 방법이 없기 때문에 표현식이 아닌 텍스트로 수행됩니다. F1...E6은 표현식으로 처리됩니다. Sin( imagenumber / 200 )은 -1에서 1까지 대략 10개의 완전한 사인파입니다. 사인파는 약수가 다르기 때문에 주파수가 다릅니다. 요인은 -2에서 2로 원활하게 이동하고 지수는 1...6 목표에 대해 +/- 1만큼 원활하게 이동합니다. 따라서 한 사진에서 다음 사진까지의 방정식은 매우 유사하지만 예측할 수 없을 정도로 조금씩 다릅니다.
FF = Derivative( Name Expr( F ), xOld );
// onestep will be inserted into python below
oneStep = substitute( // "xNew = xOld - (xOld ** 5 - 1) / (5 * xOld ** 4)"
char( // "xNew = xOld - (xOld ^ 5 - 1) / (5 * xOld ^ 4)"
substitute( // char() adds the parens around F and FF
Expr( xNew = xOld - F / FF ), // build the statement with symbols, then
Expr( F ), Name Expr( F ), // replace F and FF symbols with the content
Expr( FF ), Name Expr( FF ), // of the function and derivative
Expr( F6 ), round(2 * Sin( imagenumber / (200+5) ) / 2,3),
Expr( F5 ), round(2 * Sin( imagenumber / (200+7) ) / 2,3),
Expr( F4 ), round(2 * Sin( imagenumber / (200+11) ) / 2,3),
Expr( F3 ), round(2 * Sin( imagenumber / (200+13) ) / 2,3),
Expr( F2 ), round(2 * Sin( imagenumber / (200+17) ) / 2,3),
Expr( F1 ), round(2 * Sin( imagenumber / (200+19) ) / 2,3),
Expr( E6 ), round(6.0 + Sin( imagenumber / (200+23) ) / 2,3),
Expr( E5 ), round(5.0 + Sin( imagenumber / (200+29) ) / 2,3),
Expr( E4 ), round(4.0 + Sin( imagenumber / (200+31) ) / 2,3),
Expr( E3 ), round(3.0 + Sin( imagenumber / (200+37) ) / 2,3),
Expr( E2 ), round(2.0 + Sin( imagenumber / (200+41) ) / 2,3),
Expr( E1 ), round(1.0 + Sin( imagenumber / (200+47) ) / 2,3)
) // sub F,FF
), // char
"^", "**"
); // substitute ^ in JSL to ** in pythonimgNumToLabel 연관 배열은 나중에 이미지에 레이블을 지정하는 데 사용될 수 있습니다. 단순화 xOld->X...
imgNumToLabel[imagenumber]=substitute(regex(oneStep,"xNew = xOld - \((.*?)\) / ","\1")," ** ","^"," * ","·","xOld","X");
write("\!n",imgNumToLabel[imagenumber]);
//"/mnt/nfsc/farm/binary" || char(imagenumber) || ".generated"기존 파일에 대한 테스트는 프로그램을 다시 시작해야 할 때 속도를 높이기 위해 개발 중에 사용되었습니다. 제대로 작동하지 않을 수 있으며 최종 실행에 사용되지 않았습니다(3일!).
if(fileexists("\\VBOXSVR\nfsc\farm\binary" || char(imagenumber) || ".generated"),
write("Exists ","\\VBOXSVR\nfsc\farm\binary" || char(imagenumber) || ".generated");
continue();
);
makePythonCode(imagenumber,oneStep);이것은 잘못된 버려진 코드처럼 보입니다.
n = 0; // eventually this becomes the offset // NoInterleave offset is always 0
sentinels = 0; // NoInterleave "sentinel" might be all it returns
//write("Launching... ");여기에서 10,000개의 RunProgram이 시작됩니다. 위의 condat 값을 사용하여 50 또는 60이 동시에 실행됩니다.
launch( imagenumber );
write("Launched. ")
);마무리...마지막 몇 개의 RunProgram이 끝날 때까지 기다리십시오. unstick은 하나 또는 두 개를 찾을 수 있습니다.
while( N Items( imgNumToRP ) > 0,
Wait( .1 );
unstick();
);
write("\!ndone");레이블은 나중에 이미지 생성기에 저장됩니다. 위의 JSL/Python은 여전히 .png 형식으로 변환해야 하는 바이너리 데이터를 이미 저장했습니다.
savetextfile("$desktop/imgNumToLabel.txt",char(imgNumToLabel));Rpdone은 10,000개의 종료된 RunProgram을 보유하고 있습니다. 가자.
imgNumToRPdone = 0;// clean out // NoInterleave picture generation happens after all count and basin are created
이미지 생성기는 지금 실행할 수도 있고 나중에 실행할 수도 있습니다...
include("pythonFarmDisplay.jsl");이것이 Beowulf 코드의 끝입니다. 그림 생성기는 그것을 사용할 수 있었지만 사용하지 않았습니다. 비디오가 한 프레임에서 다음 프레임까지 동일한 색상을 사용하기를 원합니다. 색상은 매력의 유역을 나타내며 유역의 모든 지점이 유인되는 0은 동일한 색상을 가져야 합니다. 그러나 방정식은 모든 프레임에서 조금씩 변경되므로 0이 이동합니다. 그림 생성기는 이전 그림의 0을 다음 그림과 일치시키려고 시도합니다. 때때로 그들은 점프하지만 충분합니다.
사진을 만드는 JSL은 다음과 같습니다. 먼저 분지의 색상표를 만듭니다. 가장 밝은 버전의 색상입니다. 반복 횟수가 커지면 나중에 어두워집니다.
colors=
hlscolor(0,L,1)|| // dark red
hlscolor(.05,L,0.8)|| // red
hlscolor(.15,L,0.9)|| // orn
hlscolor(.35,L,1.0)|| // yel
hlscolor(.50,L,1.0)|| // grn
hlscolor(.65,L,1.0)|| // cyn
hlscolor(.80,L,0.7)|| // blu
hlscolor(.95,L,1.0)|| // mag
hlscolor(1.0,L,0.0); // gry
redmap=j(ncols(colors),1,0);
greenmap=j(ncols(colors),1,0);
bluemap=j(ncols(colors),1,0);
for(i=1,i<=ncols(colors),i+=1,
{r,g,b}=colortorgb(colors[i]);
m = max(r,g,b);
redmap[i] = r/m;
greenmap[i] = g/m;
bluemap[i] = b/m;
);이전에 저장한 이미지 레이블을 다시 로드해야 하며 그림 크기가 일치해야 합니다. 이 코드는 이전과 동일해야 하며 공통 매개변수 파일로 추출되어야 합니다. 너무 늦었어!
imgNumToLabel=Include( "$desktop/pythonFarmDisplayLabels.jsl" );
scale = 2 / 1;
xresolution = 1920 * scale;
yresolution = 1080 * scale;
// keep aspect ratio by chopping shorter dimension...
xcenter = -1;
ycenter = 0;
delta = 3;
ytop = ycenter - delta * yresolution / xresolution;
xleft = xcenter - delta;
xright = xcenter + delta;
ybottom = ycenter + delta * yresolution / xresolution;
분지의 색상은 약간 다른 그림/방정식에서 추적해야 합니다. 이것은 이전 그림의 0이 있는 곳입니다.
prevzeros = []; // what came before the first one
NFS 디스크가 너무 작았습니다. 여기에는 309GB의 압축되지 않은 픽셀 정보가 있습니다.
//filepat = "\\VBOXSVR\nfsc\farm\binary^imagenumber^.generated";
// manually moved the files (running out of space)...
filepat = "f:\newtonmethod\generated\binary^imagenumber^.generated";
countarray = J( yresolution, xresolution, 0 );
basinarray = J( yresolution, xresolution, 0 );
바이너리 파일은 2개의 JSL 매트릭스, 즉 픽셀에 필요한 반복 횟수와 픽셀의 유역을 식별하는 작은 정수로 풀립니다. 그 뒤에 유역 번호를 복잡한 좌표로 가져가는 이진 데이터(균형 바이트)가 조금 더 있습니다. 복잡한 좌표는 한 사진에서 다음 사진으로 일치되는 것입니다.
For( imagenumber = 1, imagenumber < 10000, imagenumber += 1,
file = Eval Insert( filepat );
If( File Exists( file ),
bindata = Load Text File( file, blob() );
matdata = Blob To Matrix( bindata, "uint", 2, "little", 2 );
countArray[0] = matdata[1 :: yresolution * xresolution, 1];
basinArray[0] = matdata[1 :: yresolution * xresolution, 2];
balanceBytes = Length( bindata ) - 4 * yresolution * xresolution;
zeros = Blob To Matrix( Blob Peek( bindata, Length( bindata ) - balanceBytes ), "float", 8, "little", 4 );
//Show( zeros );
if(zeros[1, 3]==2,
zeros = [9e99 9e99 1 0] |/ zeros; // python code should have put this in
);
// re-assign the basin numbers to line up with prevzeros
If( N Rows( prevzeros ) == 0,
prevzeros = zeros
);
KDTable은 가장 가까운 이웃을 찾습니다. 복잡한 평면은 훌륭한 이웃입니다.
kdt = KDTable( prevzeros[0, 1 :: 2] ); // 1::2 means the real and imag cols
newbasin = (1 :: N Rows( zeros )) + 10000;
toomany = N Rows( zeros ) > N Rows( prevzeros );
For( i = 1, i <= N Rows( zeros ), i += 1,
If( zeros[i, 3] != i,
Throw( "basin index assumption wrong" )
); // zeros are sorted by basin number
{indx, dist} = kdt << knearestrows( 2, zeros[i, 1 :: 2] );
If( N Cols( dist ) > 1 & dist[1] < dist[2] / 10, // assume 10X closer is a good match
newbasin[i] = indx[1];
kdt << removerows( indx[1] );//
, /*else*/ !toomany, // if there are toomany new zeros, don't assign them this way
newbasin[i] = indx[1];
kdt << removerows( indx[1] );
);
);<
/*
zerobasin newbasin
1 4 change 1s to 4s in basinArray and zeros
2 3 2s to 3s
3 10001 too many case! no basin assigned. becomes 7, below
4 2
5 5
6 6
7 1
*/
unassigned = Loc( newbasin > 10000 );
If( N Rows( unassigned ) > 0,새 사진은 이전 사진보다 0이 더 많으므로 마지막 대야에 넣습니다.
newbasin[unassigned] = newbasin[unassigned] - 10000 + N Rows( zeros )
);
basinArray = Shape( newbasin[basinArray], N Rows( basinArray ), N Cols( basinArray ) );
zeros[0, 3] = Shape( newbasin[zeros[0, 3]], N Rows( zeros ), 1 );
ri = Rank Index( zeros[0, 3] );
zeros[0, 0] = zeros[ri, 0]; // zeros are sorted by basin number
0은 위에 정렬됩니다. 행 1은 색상 1을 얻습니다.
prevzeros = zeros;
높은 카운트의 사진은 비디오를 더 매끄럽게 만들기 위해 폐기됩니다.
countarray[loc(countarray>200)] = 200; // 150 is typical big count, 999 is not converged
rangecount = Range( countarray );
countarray=countarray-3; // 3 or 4 is typical minimum count, make it bright (0)
countarray[loc(countarray<0)]=0;
GrayArray는 카운트에서 조정된 어둠입니다.
grayArray = (1 - (countArray /*- rangeCount[1]*/) / (200 /*rangeCount[2] - rangeCount[1]*/)) ^ 8;
rangebasin = range(basinarray);
//basinArray = basinArray / (7);
nc=ncols(colors);
basinarray[loc(basinarray>nc)]=ncols(colors); // if these exist they could be mapped to gray, perhaps
basinarray[loc(basinarray<1)]=ncols(colors); // ditto
gray * redmap[basinarray](등)는 스케일링된 rgb 색상 구성요소입니다.
red = grayArray :*</img> shape(redmap[basinarray], N Rows( basinArray ), N Cols( basinArray ) );//(1 + Sin( 2 * Pi() * basinArray + (0 * 2 * Pi() / 3) )) / 2;
green = grayArray :*</img> shape(greenmap[basinarray], N Rows( basinArray ), N Cols( basinArray ) );//(1 + Sin( 2 * Pi() * basinArray + (1 * 2 * Pi() / 3) )) / 2;
blue = grayArray :*</img> shape(bluemap[basinarray], N Rows( basinArray ), N Cols( basinArray ) );//(1 + Sin( 2 * Pi() * basinArray + (2 * 2 * Pi() / 3) )) / 2;
0..1 색상 값의 오버플로/언더플로 수정…
red[Loc( red < 0 )] = 0;
red[Loc( red > 1 )] = 1;
green[Loc( green < 0 )] = 0;
green[Loc( green > 1 )] = 1;
blue[Loc( blue < 0 )] = 0;
blue[Loc( blue > 1 )] = 1;
+ 마커는 제로 위치에 추가됩니다… 조잡하지만 작동합니다. 가장자리에 그리지 마십시오. 행렬 인덱싱은 인덱스 0에 대해 전체 행 또는 열을 수행합니다!
crosssize = floor(yresolution / 100);
For( iz = 1, iz <= N Rows( zeros ), iz += 1,
real = Round( xresolution * Interpolate( Num( zeros[iz, 1] ), xleft, 0, xright, 1 ) );
imag = Round( yresolution * Interpolate( Num( zeros[iz, 2] ), ytop, 0, ybottom, 1 ) );
if(crosssize+5 < imag < nrows(red)-crosssize-5 & crosssize+5 < real < ncols(red)-crosssize-5,
red[imag - 1, real - crosssize :: real + crosssize] = 0.5;
red[imag + 1, real - crosssize :: real + crosssize] = 0.5;
red[imag - 2, real - crosssize :: real + crosssize] = 0.5;
red[imag + 2, real - crosssize :: real + crosssize] = 0.5;
red[imag - crosssize :: imag + crosssize, real - 1] = 0.5;
red[imag - crosssize :: imag + crosssize, real + 1] = 0.5;
red[imag - crosssize :: imag + crosssize, real - 2] = 0.5;
red[imag - crosssize :: imag + crosssize, real + 2] = 0.5;
green[imag - 1, real - crosssize :: real + crosssize] = 0.5;
green[imag + 1, real - crosssize :: real + crosssize] = 0.5;
green[imag - 2, real - crosssize :: real + crosssize] = 0.5;
green[imag + 2, real - crosssize :: real + crosssize] = 0.5;
green[imag - crosssize :: imag + crosssize, real - 1] = 0.5;
green[imag - crosssize :: imag + crosssize, real + 1] = 0.5;
green[imag - crosssize :: imag + crosssize, real - 2] = 0.5;
green[imag - crosssize :: imag + crosssize, real + 2] = 0.5;
blue[imag - 1, real - crosssize :: real + crosssize] = 0.5;
blue[imag + 1, real - crosssize :: real + crosssize] = 0.5;
blue[imag - 2, real - crosssize :: real + crosssize] = 0.5;
blue[imag + 2, real - crosssize :: real + crosssize] = 0.5;
blue[imag - crosssize :: imag + crosssize, real - 1] = 0.5;
blue[imag - crosssize :: imag + crosssize, real + 1] = 0.5;
blue[imag - crosssize :: imag + crosssize, real - 2] = 0.5;
blue[imag - crosssize :: imag + crosssize, real + 2] = 0.5;
red[imag, real - crosssize :: real + crosssize] = 1;
red[imag - crosssize :: imag + crosssize, real] = 1;
green[imag, real - crosssize :: real + crosssize] = 1;
green[imag - crosssize :: imag + crosssize, real] = 1;
blue[imag, real - crosssize :: real + crosssize] = 1;
blue[imag - crosssize :: imag + crosssize, real] = 1;
);
);
label = imgNumToLabel[imagenumber];하단에 레이블이 추가됩니다. 레이블의 작은 비트맵을 만든 다음 더 큰 이미지에 복사합니다.
if(1,
//label = char(imagenumber)||" zeros="||char(N Rows( zeros ))||" "||label;
labelpic = textbox(label,setwrap(3000),<<SETFONTSIZE></SETFONTSIZE>
이미지는 여기에서 만들어집니다. 그런 다음 이름을 지정하여 동영상에 보관할지 여부를 결정합니다.
img = New Image( "rgb", {red, green, blue} );
if(6<=nrows(zeros)<=8 & 2<=rangebasin[1]<=4 & 6<=rangebasin[2]<=8 & 3<=rangecount[1]<=4 & rangecount[2]<200,
img << saveimage( "f:/newtonmethod/png3/keeper" || Char( 1e6 + imagenumber ) || ".png", "png" );
,
img << saveimage( "f:/newtonmethod/png3/zapped" || Char( 1e6 + imagenumber ) || ".png", "png" );
);
//img<<SCALE></SCALE>그게 다야! 여기 비디오가 있습니다. 나는 그것이 일어나는 곳에서 깜박이는 어두운 프레임과 아래에 바쁜 캡션이 있는 과학적인 버전만 만들기로 결정했습니다. 예쁜 버전은 더 매끄럽고 더 최면에 걸릴 수 있습니다. 하드웨어와 연결 속도가 있는 경우 4K 비디오입니다. 그렇지 않으면 YouTube에서 축소합니다.
포스팅을 하다보니 2020년이 2시간이나 남았습니다. 2021년이 정말 기대됩니다.