1971년의 어느 날 Hanson은 이렇게 말했습니다. “오늘 저는 숫자의 제곱근을 구하는 방법을 가르쳐 드리겠습니다. 보다 빠른 방법 긴 분할 내가 가르쳐야 할 방식. 한 마디로 가르쳐 드리겠습니다. 그리고 당신은 결코 잊지 못할 것입니다.” 그런 다음 그는 칠판에 쓴다.
추측
그리고 밑줄을 긋습니다. 웃음이 잠잠해지면 추측을 원래 숫자, 평균, 반복으로 나누는 것에 대해 설명했다. 그는 아마 언급 뉴턴-랩슨 .
방정식의 근 또는 0을 찾는 뉴턴의 방법은 방정식의 도함수를 사용하여 프로세스 속도를 높입니다. 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 반복입니다. 단계는 다음 X 값으로 평가되는 while 루프에 나타납니다. Derivative 함수는 이 예제에서 2X를 제공하는 간단한 다항식에서 훌륭하게 작동합니다. NameExpr()은 평가하지 않고 f에서 수식을 가져오고 EvalExpr()이 Expr()을 찾을 때 Expr()은 NameExpr()을 실행하여 수식을 얻습니다. 단계는 x - f(x)/f'(x)로 설정됩니다.
while(1) 루프 내에서 단계를 수행하고 인쇄하고 계산합니다. 테스트는 현재 및 다음 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 스크립팅 인덱스에서 복합물을 검색하면 두 가지 다른 반쯤 구워진 예제가 생성됩니다. 하나는 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() 함수는 크기()라고 불러야 하지만 abs와 같은 비복소 버전과 일치합니다. - add() 및 sub()에 대한 추가 매개변수 및 convertExpression() 함수를 찾습니다. 이해하기 쉬운 pow() 함수에 대해 stackexchange/Michael Hardy에게 큰 박수를 보냅니다!
convertExpression 함수는 +, -, *, / 및 ^ 연산자가 있는 JSL 표현식을 복잡한 네임스페이스 함수를 호출하는 동등한 표현식으로 마사지합니다. JSL 이진 연산자는 ((a+b)+(c+d)) 를 이진 함수가 아닌 add(a,b,c,d) 로 바꾸는 것에 대해 매우 공격적입니다. 여기에는 4개의 인수가 있습니다. 이것이 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는 복소수 값으로 설정되고 초기 시작점은 실수 라인에서 벗어난 지점으로 설정됩니다. 완료되면 로그가 표시됩니다.
완료, V = -100+0i에 대한 x ^ 2 - V의 근은 0+10i입니다.
[0 0]
V=-100 + 0i는 실수 -100(허수 성분 없음)입니다. 0 + 10i는 실수 성분이 없는 순수한 허수 답입니다. 10i * 10i는 예상대로 100*(i^2) 또는 -100입니다. 복잡한 뿌리는 켤레 쌍으로 발생합니다. Newton의 방법은 출발점을 기준으로 하나를 찾았습니다. 다른 하나는 -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.세 개의 분지. 분지가 닿는 큰 점은 수렴하는 데 더 오래 걸립니다.
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.중앙의 이더넷 허브, 전원 스트립 아래. 마우스와 모니터, 키보드가 충분하지 않으므로 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, gig는 클러스터에 대한 정보의 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 조회 서비스를 제공하는 이유를 여전히 잘 모르겠습니다… GetAddrInfo를 기억하기 전에 주석 처리된 핑 코드를 사용했는데, 이는 좀 더 어색합니다. 우리 집은 몇 년 전보다 지금 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을 사용하여 사용 가능한 시스템을 기다리고 Python 명령에 사용자 정의 .py 파일과 사용자 정의 매개변수를 제공합니다. .py 파일은 아래에서 추가로 생성됩니다. 아래 5행의 던지기는 온전성 검사입니다. 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" || //
""
),매개변수 키워드는 인수를 readfunction에서 사용할 수 있도록 합니다. 이것이 여러 RunPrograms를 병렬로 가질 수 있는 방법이며 읽기 기능이 어떤 프로그램인지 알 수 있도록 합니다.
Parameter( evallist({imagenumber,machine}) ),read 함수는 출력을 캡처하고 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 파일을 생성합니다. 파이썬 프로그램에는 그림 크기와 출력 파일 이름 정보를 얻기 위해 명령줄을 구문 분석하는 init() 함수가 포함되어 있습니다. 데이터는 명령줄 매개변수와 방정식으로 구워지는 두 가지 방식으로 전달됩니다. 아마도 모든 정보가 베이크 인되었을 수 있지만, 코드에 매개변수를 베이크할 이유(예: oneStep 방정식을 코드로 컴파일)가 없다면 모든 RunPrograms에 대한 코드 사본이 하나만 필요합니다. 명령줄 매개변수에 의해 완전히 제어되었습니다. 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로 모든 것을 쉽게 잡을 수 있도록 정수를 따릅니다.
# 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개의 RunPrograms가 시작됩니다. 50 또는 60은 위의 방법과 같이 condat 값을 사용하여 동시에 실행됩니다.
launch( imagenumber );
write("Launched. ")
);마무리 ... 마지막 몇 개의 RunPrograms가 끝날 때까지 기다리십시오. unstick은 하나 또는 두 개를 찾을 수 있습니다.
while( N Items( imgNumToRP ) > 0,
Wait( .1 );
unstick();
);
write("\!ndone");레이블은 나중에 이미지 생성기를 위해 저장됩니다. 위의 JSL/Python은 이미 .png 형식으로 변환해야 하는 바이너리 데이터를 저장했습니다.
savetextfile("$desktop/imgNumToLabel.txt",char(imgNumToLabel));Rpdone은 10,000개의 종료된 RunPrograms를 보유하고 있습니다. 그냥 놔둬
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 );
바이너리 파일은 필요한 픽셀의 반복 횟수와 픽셀의 유역을 식별하는 작은 정수라는 두 개의 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
회색 * redmap[basinarray] (etc)는 크기가 조정된 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 위치에 추가됩니다... 조잡하지만 작동합니다. 가장자리에 그리지 마십시오. 행렬 인덱싱은 인덱스 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년이 정말 기대됩니다.