InglêsFrancêsEspanhol

favicon do OnWorks

PDL :: Indexingp - Online na nuvem

Execute PDL :: Indexingp no provedor de hospedagem gratuita OnWorks no Ubuntu Online, Fedora Online, emulador online do Windows ou emulador online do MAC OS

Este é o comando PDL :: Indexingp que pode ser executado no provedor de hospedagem gratuita OnWorks usando uma de nossas várias estações de trabalho online gratuitas, como Ubuntu Online, Fedora Online, emulador online do Windows ou emulador online do MAC OS

PROGRAMA:

NOME


PDL :: Indexing - Introdução à indexação e divisão de piddles.

VISÃO GERAL


Esta página do manual deve servir como um primeiro tutorial sobre os recursos de indexação e encadeamento de
PDL.

Como todas as linguagens vetorizadas, o PDL automatiza o loop em matrizes usando uma variante de
notação matemática vetorial. O loop automático é chamado de "threading", em parte
porque, em última análise, o PDL implementará o processamento paralelo para acelerar os loops.

Grande parte da flexibilidade e poder do PDL depende dos recursos de indexação e encadeamento de
a extensão Perl. A indexação permite o acesso aos dados de um piddle de uma forma muito flexível
caminho. O threading fornece vetorização eficiente de operações simples.

Os valores de um piddle são armazenados compactamente como valores digitados em um único bloco de memória,
não (como em uma lista de listas Perl normal) como escalares Perl individuais.

Nas seções a seguir, muitos "métodos" são chamados - esses são operadores Perl que
aplicam-se a PDLs. No shell perldl (ou pdl2), você pode descobrir mais sobre cada método
digitando "?" seguido pelo nome do método.

Dimensão listas
Um piddle (variável PDL), em geral, é uma matriz N-dimensional onde N pode ser 0 (para um
escalar), 1 (por exemplo, para uma amostra de som) ou valores mais altos para imagens e mais complexos
estruturas. Cada dimensão do piddle tem um tamanho inteiro positivo. O "perl"
intérprete trata cada piddle como um tipo especial de escalar Perl (um objeto Perl abençoado,
na verdade - mas você não precisa saber disso para usá-los), que podem ser usados ​​em qualquer lugar que você puder
colocar um escalar normal.

Você pode acessar as dimensões de um piddle como uma lista Perl e de outra forma determinar o tamanho
de uma charada com vários métodos. Os mais importantes são:

nelem - o número total de elementos em um PDL
ndims - retorna o número de dimensões em um PDL
dims - retorna a lista de dimensões de um PDL como uma lista Perl
dim - retorna o tamanho de uma dimensão particular de um PDL

Indexação e Fluxo de dados
PDL mantém uma noção de "fluxo de dados" entre um piddle e subcampos indexados desse
piddle. Quando você produz um subcampo indexado ou elemento único de um piddle pai, o
filho e pai permanecem ligados até que você os desconecte manualmente. Isso permite que você
representam os mesmos dados de maneiras diferentes em seu código - por exemplo, você pode considerar
uma imagem RGB simultaneamente como uma coleção de valores (R, G, B) em uma imagem 3 x 1000 x 1000,
e como três planos de cores separados de 1000 x 1000 armazenados em variáveis ​​diferentes. Modificando
qualquer uma das variáveis ​​muda a memória subjacente, e as mudanças são refletidas em todos
representações dos dados.

Existem dois métodos importantes que permitem controlar as conexões de fluxo de dados entre uma criança
e PDL pai:

cópia - força uma cópia explícita de um PDL
sever - interrompe a conexão de fluxo de dados entre um PDL e seus pais (se houver)

Enfiando e Dimensão Order
A maioria das operações PDL atuam nas primeiras dimensões de seus argumentos piddle. Para
exemplo, "soma" soma todos os elementos ao longo da primeira dimensão na lista (dimensão 0).
Se você alimentar um piddle tridimensional, a primeira dimensão é considerada o
dimensão "ativa" e as dimensões posteriores são dimensões de "rosca" porque são
simplesmente deu um loop. Existem várias maneiras de transpor ou reordenar a lista de dimensões de
um PDL. Essas técnicas são muito rápidas, pois não afetam os dados subjacentes, apenas
alterar a maneira como o PDL acessa os dados. As principais funções de ordenação de dimensão são:

mv - move uma dimensão específica para outro lugar na lista de dimensões
xchg - troca duas dimensões na lista de dimensões, deixando o resto sozinho
reordenar - permite a mistura de atacado das dimensões
agrupar - agrupa duas ou mais pequenas dimensões em uma maior
squeeze - elimina quaisquer dimensões de tamanho 1

Físico e manequim Dimensões
· Threading de nível de Perl de documento

· Threadids

· Atualização e descrição correta da fatia

· Novas funções em slice.pd (affine, lag, splitdim)

· Retrabalho do parágrafo sobre segmentação explícita

Indexação e segmentação com PDL


Grande parte da flexibilidade e do poder do PDL depende dos recursos de indexação e loop de
a extensão Perl. A indexação permite o acesso aos dados de um objeto PDL de uma forma muito flexível
caminho. Threading fornece funcionalidade de loop implícito eficiente (uma vez que os loops são
implementado como código C otimizado).

Objetos Pdl (posteriormente chamados de "pdls") são objetos Perl que representam
matrizes e operações sobre eles. Em contraste com o estilo Perl @x simples, lista os dados do array
é armazenado de forma compacta em um único bloco de memória, ocupando assim muito menos memória e
permitindo o uso de código C rápido para implementar operações (por exemplo, adição, etc) em pdls.

PDS pode crianças
Central para muitos dos recursos de indexação do PDL é a relação de "pai" e
"filho" entre pdls. Muitos dos comandos de indexação criam um novo pdl a partir de um pdl existente.
O novo pdl é o "filho" e o antigo é o "pai". Os dados do novo pdl são
definido por uma transformação que especifica como gerar (computar) seus dados a partir do
dados dos pais. A relação entre o pdl filho e seu pai costuma ser bidirecional,
o que significa que as alterações nos dados da criança são propagadas de volta para os pais. (Nota: você
veja, já estamos apontando em nossa terminologia para os novos recursos de fluxo de dados. O tipo
de fluxo de dados que é usado pelos comandos de indexação (sobre os quais você aprenderá em um minuto)
está sempre em operação, não apenas quando você ativou explicitamente o fluxo de dados em seu pdl
dizendo "$ a-> doflow". Para obter mais informações sobre o fluxo de dados, consulte o manual de fluxo de dados
página.)

Outra maneira de interpretar os pdls criados por nossos comandos de indexação é visualizá-los como um
tipo de ponteiro inteligente que aponta de volta para alguma parte ou todos os dados de seu pai.
Portanto, não é surpreendente que os dados dos pais (ou uma parte deles) mudem quando
manipulado através deste "ponteiro". Após essas observações introdutórias que, esperançosamente,
preparou você para o que está por vir (ao invés de confundi-lo demais), vamos mergulhar
imediatamente e comece com uma descrição dos comandos de indexação e alguns exemplos típicos
como eles podem ser usados ​​em programas PDL. Vamos ilustrar ainda mais o ponteiro / fluxo de dados
analogias no contexto de alguns dos exemplos posteriores.

Existem duas implementações diferentes desta relação de `` ponteiro inteligente '': a primeira
um, que é um pouco mais lento, mas funciona para qualquer transformação, é simplesmente fazer o
transformação para a frente e para trás, conforme necessário. A outra é considerar a criança
piddle um piddle `` virtual '', que apenas armazena um ponteiro para o pai e acessa
informações para que as rotinas que usam o jogo infantil acessem diretamente os dados
no pai. Se o piddle virtual é dado a uma rotina que não pode usá-lo, PDL
fisicaliza de forma transparente o piddle virtual antes de permitir que a rotina o use.

Atualmente (1.94_01) todas as transformações que são `` afins '', ou seja, os índices dos dados
item no piddle pai são determinados por uma transformação linear (+ constante) do
os índices do piddle infantil resultam em piddles virtuais. Todas as outras rotinas de indexação (por exemplo
"-> índice (...)") resulta em piddles físicos. Todas as rotinas compiladas pelo PP podem aceitar afins
piddles (exceto aquelas rotinas que passam ponteiros para funções de biblioteca externa).

Observe que o fato de algo ser afim ou não não afeta a semântica do que você faz
de qualquer forma: ambos

$ a-> índice (...). = 5;
$ a-> fatia (...). = 5;

altere os dados em $ a. A afinidade, no entanto, tem um impacto significativo na memória
uso e desempenho.

Cortando PDS
Provavelmente, a aplicação mais importante do conceito de PDLs pai / filho é o
representação de fatias retangulares de um PDL físico por um PDL virtual. Tendo conversado
tempo suficiente sobre os conceitos, vamos ser mais específicos. Suponha que estejamos trabalhando com um PDL 2D
representando uma imagem 5x5 (é excepcionalmente pequena para que possamos imprimi-la sem preencher
várias telas cheias de dígitos;).

pdl> $ im = sequência (5,5)
pdl> p $ im

[
[0 1 2 3 4]
[5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]
]

pdl> ajuda vars
Variáveis ​​PDL no pacote principal ::

Nome Tipo Dimensão Fluxo Estado Mem
-------------------------------------------------- --------------
$ im Double D [5,5] P 0.20Kb

[aqui pode ser apropriado falar rapidamente sobre o comando "help vars" que fornece
informações sobre pdls no shell interativo "perldl" ou "pdl2" que acompanha o PDL. ]

Agora suponha que queremos criar um pdl 1-D que faz referência apenas a uma linha da imagem, digamos
linha 2; ou um pdl que representa todas as linhas pares da imagem (imagine que temos que lidar com
quadros pares e ímpares de uma imagem entrelaçada devido a algum comportamento peculiar de nosso quadro
agarrador). Como outra aplicação frequente de fatias, podemos criar um pdl que
representa uma região retangular da imagem com as partes superior e inferior invertidas. Todos estes
efeitos (e muitos mais) podem ser facilmente alcançados com a poderosa função de fatia:

pdl> $ line = $ im-> slice (':, (2)')
pdl> $ even = $ im-> slice (':, 1: -1: 2')
pdl> $ area = $ im-> slice ('3: 4,3: 1')
pdl> help vars # ou apenas PDL-> vars
Variáveis ​​PDL no pacote principal ::

Nome Tipo Dimensão Fluxo Estado Mem
-------------------------------------------------- --------------
$ par duplo D [5,2] -C 0.00Kb
$ im Double D [5,5] P 0.20Kb
$ linha Double D [5] -C 0.00Kb
$ area Double D [2,3] -C 0.00Kb

Todos os três pdls "filhos" são filhos de $ im ou do outro (bastante equivalente)
ponteiros de interpretação para dados de $ im. Operações apenas no acesso a pdls virtuais
aquelas partes dos dados conforme especificado pelo argumento para cortar. Então, podemos apenas imprimir
linha 2:

pdl> p $ linha
[10 11 12 13 14]

Observe também a diferença no "Estado de fluxo" da área $ acima e abaixo:

pdl> p $ area
pdl> help $ area
Esta variável é Double D [2,3] VC 0.00Kb

O seguinte demonstra que $ im e $ linha realmente se comportam como você esperaria de um
objeto semelhante a um ponteiro (ou na imagem do fluxo de dados: as mudanças nos dados de $ line são
propagado de volta para $ im):

pdl> $ im ++
pdl> p $ linha
[11 12 13 14 15]
PDL> $ linha + = 2
pdl> p $ im

[
[1 2 3 4 5]
[6 7 8 9 10]
[13 14 15 16 17]
[16 17 18 19 20]
[21 22 23 24 25]
]

Observe como as operações de atribuição nos pdls virtuais filhos mudam o pdl físico pai
e vice-versa (no entanto, a atribuição básica "=" não, use ". =" para obter esse efeito.
Veja abaixo os motivos). Os PDLs filhos virtuais são algo como "links ativos" para o
PDL pai "original". Como dito anteriormente, eles podem ser pensados ​​para funcionar de forma semelhante a um
Ponteiro C. Mas, em contraste com um ponteiro C, eles carregam muito mais informações. Em primeiro lugar, eles
especificar a estrutura dos dados que eles representam (a dimensionalidade do novo pdl) e
em segundo lugar, especifique como criar esta estrutura a partir dos dados pais (a forma como isso funciona
está enterrado no interior do PDL e não é importante para você saber de qualquer maneira (a menos que você
deseja hackear o núcleo no futuro ou gostaria de se tornar um guru PDL em geral (para um
definição desta estranha criatura veja PDL :: Internals)).

Os exemplos anteriores demonstraram o uso típico da função de fatia. Desde o
a funcionalidade de fatiar é tão importante aqui está uma explicação da sintaxe para a string
argumento para cortar:

$ vpdl = $ a-> slice ('ind0, ind1 ...')

onde "ind0" especifica o que fazer com o índice nº 0 do pdl $ a, etc. Cada elemento do
A lista separada por vírgulas pode ter uma das seguintes formas:

':' Use toda a dimensão

'n' Use apenas o índice "n". A dimensão desse índice no PDL virtual resultante é 1.
Um exemplo envolvendo esses dois primeiros formatos de índice:

pdl> $ column = $ im-> slice ('2 ,:')
pdl> $ row = $ im-> slice (':, 0')
pdl> coluna p $

[
[3]
[8]



]

pdl> p $ row

[
[1 2 3 4 5]
]

pdl> help $ column
Esta variável é Double D [1,5] VC 0.00Kb

pdl> help $ row
Esta variável é Double D [5,1] VC 0.00Kb

'(n)' Use apenas o índice "n". Esta dimensão é removida do pdl resultante (contando com o
fato de que uma dimensão de tamanho 1 sempre pode ser removida). A distinção entre este
caso e o anterior torna-se importante em atribuições onde a mão esquerda e a direita
lado tem que ter dimensões adequadas.

pdl> $ line = $ im-> slice (':, (0)')
pdl> help $ line
Esta variável é Double D [5] -C 0.00Kb

pdl> p $ linha
[1 2 3 4 5]

Identificou a diferença em relação ao exemplo anterior?

'n1:n2' or 'n1:n2:n3'
Pegue o intervalo de índices de "n1" a "n2" ou (segunda forma) pegue o intervalo de
índices de "n1" a "n2" com o passo "n3". Um exemplo de uso deste formato é
a definição anterior da subimagem composta por linhas pares.

pdl> $ even = $ im-> slice (':, 1: -1: 2')

Este exemplo também demonstra que os índices negativos funcionam como funcionam para normais
Matrizes de estilo Perl por contagem regressiva a partir do final da dimensão. Se "n2" for
menor que "n1" (no exemplo -1 é equivalente ao índice 4) os elementos no
PDL virtuais são efetivamente revertidos em relação a seu pai.

'* [n]'
Adicione uma dimensão fictícia. O tamanho desta dimensão será 1 por padrão ou igual a
"n" se o argumento numérico opcional for fornecido.

Agora, isso é realmente algo um pouco estranho à primeira vista. O que é um manequim
dimensão? Uma dimensão fictícia insere uma dimensão onde não havia uma antes. Quão
isso é feito? Bem, no caso da nova dimensão ter tamanho 1, pode ser facilmente
explicado pela maneira como você pode identificar um vetor (com elementos "m") com um
Matriz "(1, m)" ou "(m, 1)". O mesmo é obviamente válido para objetos de dimensões superiores.
Mais interessante é o caso de dimensões fictícias de tamanho maior do que um (por exemplo
"fatia ('* 5 ,:')"). Isso funciona da mesma maneira que uma chamada para a função fictícia cria
uma nova dimensão fictícia. Portanto, continue lendo e verifique sua explicação abaixo.

'([n1: n2 [: n3]] = i)'
[Ainda não implementado ??????] Com um argumento como este você faz generalizado
diagonais. O diagonal será dimensão não. "i" do novo pdl de saída e (se
parte opcional entre colchetes especificada) se estenderá ao longo da faixa de índices
especificado da respectiva dimensão do PDL pai. Em geral, um argumento como este
só faz sentido se houver outros argumentos como esse na mesma chamada de slice.
A parte entre colchetes é opcional para este tipo de argumento. Todos os argumentos deste
tipo que especifica a mesma dimensão de destino "i" tem que se relacionar com o mesmo número de
índices em sua dimensão pai. A melhor maneira de explicar é provavelmente dar um
exemplo, aqui fazemos um pdl que se refere aos elementos ao longo da diagonal do espaço de
seu pdl pai (um cubo):

$ cubo = zeros (5,5,5);
$ sdiag = $ cubo-> fatia ('(= 0), (= 0), (= 0)');

O comando acima cria um pdl virtual que representa a diagonal ao longo do
dimensão dos pais no. 0, 1 e 2 e torna sua dimensão 0 (a única dimensão) de
isto. Você usa a sintaxe estendida se os tamanhos das dimensões pai
deseja construir a diagonal a partir de tamanhos diferentes ou deseja reverter o
sequência de elementos na diagonal, por exemplo

$ rect = zeros (12,3,5,6,2);
$vpdl = $rect->slice('2:7,(0:1=1),(4),(5:4=1),(=1)');

Assim, os elementos de $ vpdl serão relacionados aos de seu pai da maneira que pudermos
expresso como:

vpdl (i, j) = rect (i + 2, j, 4,5-j, j) 0 <= i <5, 0 <= j <2

[trabalhar na nova função de índice: "$ b = $ a-> index ($ c);" ???? ]

e guarante que os mesmos estão diferente tipos of atribuições in PDL
Os exemplos anteriores já mostraram que pdls virtuais podem ser usados ​​para operar em ou
acessar porções de dados de um PDL pai. Eles também podem ser usados ​​como lvalues ​​em atribuições
(como o uso de "++" em alguns dos exemplos acima já demonstrou). Para explícito
atribuições aos dados representados por um PDL virtual você tem que usar o sobrecarregado ". ="
operador (que neste contexto chamamos propagado atribuição) Por que você não pode usar o
operador de atribuição normal "="?

Bem, você definitivamente ainda pode usar o operador '=', mas não faria o que você deseja. Esse
é devido ao fato de que o operador '=' não pode ser sobrecarregado da mesma forma que outros
operadores de atribuição. Se tentássemos usar '=' para tentar atribuir dados a uma parte de um
PDL físico por meio de PDL virtual não obteríamos o efeito desejado (em vez do
variável que representa o PDL virtual (uma referência a uma coisa abençoada) seria após o
atribuição apenas contém a referência a outra coisa abençoada que se comportaria para
atribuições futuras como uma cópia "física" do rvalue original [na verdade, ainda não
claro e assunto das discussões na lista de discussão dos desenvolvedores de PDL]. Nesse sentido
quebraria a conexão do PDL com o pai [não é este comportamento em certo sentido o
oposto do que acontece no fluxo de dados, onde ". =" interrompe a conexão com o pai? ]

Por exemplo

pdl> $ line = $ im-> slice (':, (2)')
pdl> $ line = zeros(5);
PDL> $ linha ++;
pdl> p $ im

[
[1 2 3 4 5]
[6 7 8 9 10]
[13 14 15 16 17]
[16 17 18 19 20]
[21 22 23 24 25]
]

pdl> p $ linha
[1 1 1 1 1]

Mas usando ". ="

pdl> $ line = $ im-> slice (':, (2)')
pdl> $ linha. = zeros(5)
pdl> $ line ++
pdl> p $ im

[
[1 2 3 4 5]
[6 7 8 9 10]
[1 1 1 1 1]
[16 17 18 19 20]
[21 22 23 24 25]
]

pdl> imprimir $ linha
[1 1 1 1 1]

Além disso, você pode substituir

PDL> $ linha. = 0;

para a atribuição acima (o zero é convertido em um piddle escalar, sem dimensões, portanto
pode ser atribuído a qualquer piddle).

Um bom recurso nas versões recentes do perl são as sub-rotinas lvalue (ou seja, versões 5.6.xe
superior incluindo todos os perls atualmente suportados pelo PDL). Isso permite usar o
sintaxe de divisão em ambos os lados da atribuição:

pdl> $ im-> slice (':, (2)'). = zeros(5) -> xvals-> float

Relacionado ao recurso de subatribuição lvalue, há uma pequena armadilha para os incautos: perls recentes
introduziu um "recurso" que quebra o uso do PDL de subs lvalue para atribuições de fatias quando
rodando sob o depurador perl, "perl -d". No depurador, o uso acima fornece uma
erro como: "Não é possível retornar uma sub-rotina temporária de lvalue ..." Portanto, você deve usar a sintaxe
como isso:

pdl> ($ pdl = $ im-> slice (':, (2)')). = zeros(5) -> xvals-> float

que funciona com e sem o depurador, mas é indiscutivelmente desajeitado e difícil de ler.

Observe que pode haver um problema com atribuições como esta quando lvalue e rvalue pdls
consulte as partes sobrepostas de dados no pdl pai:

# reverter os elementos da primeira linha de $ a
($ tmp = $ a-> slice (':, (1)')). = $ a-> slice ('- 1: 0, (1)');

Atualmente, os dados pai no lado direito das atribuições não são copiados antes do
loop de atribuição (interno) continua. Portanto, o resultado desta atribuição dependerá
na sequência em que os elementos são atribuídos e quase certamente não faz o que tu
procurado. Portanto, a semântica é atualmente indefinido por enquanto e sujeito a alterações a qualquer momento. Para
obter o comportamento desejado, use

($ tmp = $ a-> slice (':, (1)')). = $ a-> slice ('- 1: 0, (1)') -> copiar;

que faz uma cópia física da fatia ou

($ tmp = $ a-> slice (':, (1)')). = $ a-> slice ('- 1: 0, (1)') -> servidor;

que retorna a mesma fatia, mas rompe a conexão da fatia com seu pai.

Outros funções que manipular dimensões
Tendo falado extensivamente sobre a função de fatia, deve-se notar que esta não é a
apenas função de indexação PDL. Existem funções de indexação adicionais que também são úteis
(especialmente no contexto de threading, sobre o qual falaremos mais tarde). Aqui está uma lista
e alguns exemplos de como usá-los.

"manequim"
insere uma dimensão fictícia do tamanho que você especificar (padrão 1) no local escolhido.
Você não pode esperar para ouvir como isso é alcançado? Bem, todos os elementos com índice "(X, x, Y)"
("0 <= x
pdl (onde "X" e "Y" se referem ao grupo de índices antes e depois da localização
onde a dimensão fictícia foi inserida.)

Este exemplo calcula a coordenada x do centroide de uma imagem (mais tarde iremos
aprender que não precisamos realmente da dimensão fictícia, graças à magia do implícito
threading; mas usando dimensões fictícias, o código também funcionaria em um mundo sem threads;
embora, depois de trabalhar com threads PDL, você não gostaria de viver sem eles
novamente).

# centróide
($ xd, $ yd) = $ im-> escurece;
$ xc = soma ($ im * xvals (zeros ($ xd)) -> dummy (1, $ yd)) / soma ($ im);

Vamos explicar como isso funciona com mais detalhes. Primeiro, o produto:

$ xvs = xvals (zeros ($ xd));
imprimir $ xvs-> dummy (1, $ yd); # repetir a linha $ yd vezes
$ prod = $ im * xvs-> dummy (1, $ yd); # formar o produto inteligente de pixels com
# a linha repetida de valores x

O resto é então somar os resultados do produto em pixels e
normalizando com a soma de todos os valores de pixel na imagem original, calculando assim
a coordenada x do "centro de massa" da imagem (interpretando os valores dos pixels como
massa local) que é conhecido como o centróide de uma imagem.

A seguir está uma (do ponto de vista do consumo de memória) uma conversão muito barata de
escala de cinza para RGB, ou seja, cada pixel contém agora um triplo de valores em vez de um escalar.
Os três valores no triplo são, felizmente, todos iguais para uma imagem cinza, então
que nosso truque funciona bem no sentido de que mapeia todos os três membros do triplo para o
mesmo elemento de origem:

# uma escala de cinza barata para conversão RGB
$ rgb = $ grey-> dummy (0,3)

Infelizmente, este truque não pode ser usado para converter suas fotos P / B antigas em coloridas
da maneira que você gostaria. :(

Observe que o uso de memória de piddles com dimensões fictícias é especialmente sensível a
a representação interna. Se o piddle pode ser representado como um afim virtual
(`` vaffine '') piddle, apenas as estruturas de controle são armazenadas. Mas se $ b em

$ a = zeros(10000);
$ b = $ a-> dummy (1,10000);

é tornado físico por alguma rotina, você verá que o uso de memória do seu programa
aumentou repentinamente em 100Mb.

"diagonal"
substitui duas dimensões (que devem ser do mesmo tamanho) por uma dimensão que
faz referência a todos os elementos ao longo da "diagonal" ao longo dessas duas dimensões. Aqui nós
tem dois exemplos que devem parecer familiares para quem já fez alguns
álgebra. Em primeiro lugar, faça uma matriz de unidade:

# matriz de unidade
$ e = zeros (float, 3, 3); # tornar tudo zero
($ tmp = $ e-> diagonal (0,1)). = 1; # definir os elementos ao longo da diagonal para 1
print $ e;

Ou a outra diagonal:

($ tmp = $ e-> slice (': - 1: 0') -> diagonal (0,1)). = 2;
print $ e;

(Você notou como usamos a função de fatia para reverter a sequência de linhas antes
definindo a diagonal da nova criança, definindo assim a diagonal cruzada do
pai?) Ou um mapeamento do espaço de matrizes diagonais para o campo sobre o qual
as matrizes são definidas, o traço de uma matriz:

# traço de uma matriz
$ trace = sum ($ mat-> diagonal (0,1)); # soma todos os elementos diagonais

"xchg" e "mv"
xchg troca ou "transpõe" as duas dimensões especificadas. Um simples
exemplo:

# transpor uma matriz (sem reorganizar explicitamente os dados e
# fazendo uma cópia)
$ prod = $ ax $ a-> xchg (0,1);

$ prod agora deve estar bem próximo da matriz unitária se $ a for uma matriz ortogonal.
Freqüentemente, "xchg" será usado no contexto de threading, mas falaremos mais sobre isso posteriormente.

mv funciona de maneira semelhante. Ele move uma dimensão (especificada por seu número no
pai) para uma nova posição no novo pdl filho:

$ b = $ a-> mv (4,0); # tornar a 5ª dimensão de $ a a primeira na
# novo filho $ b

A diferença entre "xchg" e "mv" é que "xchg" apenas muda a posição de dois
dimensões entre si, enquanto "mv" insere a primeira dimensão no lugar de
segundo, movendo as outras dimensões de acordo.

"clump"
reduz várias dimensões em uma. Seu único argumento especifica quantas dimensões
do PDL de origem deve ser recolhido (começando do primeiro). Um (reconhecidamente
irrealista) exemplo é um PDL 3D que contém dados de uma pilha de arquivos de imagem que você
acabei de ler. No entanto, os dados de cada imagem realmente representam um tempo 1D
série e só foi organizado dessa forma porque foi digitalizado com uma moldura
agarrador. Então, para tê-lo novamente como uma série de sequências de tempo, você diz

pdl> $ seqs = $ stack->moita(2)
pdl> ajuda vars
Variáveis ​​PDL no pacote principal ::

Nome Tipo Dimensão Fluxo Estado Mem
-------------------------------------------------- --------------
$ seqs Double D [8000,50] -C 0.00Kb
$ stack Double D [100,80,50] P 3.05Mb

Por mais irrealista que possa parecer, nosso software de microscópio confocal grava dados (às vezes)
Por aqui. Mas com mais freqüência você usa clump para alcançar um certo efeito ao usar implícito
ou segmentação explícita.

chamadas para indexação funções pode be acorrentado
Como você deve ter notado em alguns dos exemplos acima, chamadas para as funções de indexação
pode ser bem encadeado, uma vez que todas essas funções retornam um objeto filho recém-criado.
No entanto, ao fazer manipulações de índice extensas em uma cadeia, certifique-se de acompanhar o que
você está fazendo, por exemplo

$ a-> xchg (0,1) -> mv (0,4)

move a dimensão 1 de $ a para a posição 4, uma vez que quando o segundo comando é executado, o
dimensão original 1 foi movida para a posição 0 do novo filho que chama o "mv"
função. Acho que você entendeu (apesar das minhas explicações complicadas).

Propagado atribuições ('. =') e manequim dimensões
Um aspecto secundário relacionado à indexação é a atribuição de pdls contendo dimensões fictícias de
tamanho maior que 1. Essas atribuições (usando ". =") são proibidas porque vários elementos
do ponto lvalue PDL para o mesmo elemento do pai. Como consequência, o valor de
esses elementos pais são potencialmente ambíguos e dependeriam da sequência em que
a implementação faz as atribuições aos elementos. Portanto, uma tarefa como esta:

$ a = pdl [1,2,3];
$ b = $ a-> dummy (1,4);
$ b. = yvals (zeros (3,4));

pode produzir resultados inesperados e os resultados são explicitamente indefinido por PDL porque
quando o PDL obtém recursos de computação paralela, o resultado atual pode mudar.

Do ponto de vista do fluxo de dados, a introdução do manequim maior que um
dimensões é considerado uma transformação irreversível (semelhante à terminologia em
termodinâmica) que impede a propagação para trás da atribuição a um pai (que você
solicitou explicitamente usando a atribuição ". ="). Um problema semelhante a ter em atenção
ocorre no contexto de rosqueamento, onde às vezes dimensões fictícias são criadas implicitamente
durante o loop de linha (veja abaixo).

Razões for da pai / filho (ou "ponteiro") conceito
[isso vai ter que esperar um pouco]

XXXXX sendo memória eficiente
XXXXX no contexto de threading
XXXXX maneira muito flexível e poderosa de acessar porções de dados PDL
(de uma forma muito mais geral do que permite sec, etc)
Implementação eficiente de XXXXX
XXXXX diferença para a seção / em, etc.

Como funciona o dobrador de carta de canal para fazer coisas físico novamente
[XXXXX preencha mais tarde, quando tudo estiver um pouco mais resolvido]

** Quando necessário (função C lib de interface de rotina xsub)
** Como alcançado (-> físico)
** Como testar (isfísico (explique como funciona atualmente))
** -> copiar e -> servidor

Enfiando


No parágrafo anterior sobre indexação, já mencionamos o termo ocasionalmente, mas
agora é realmente hora de falar explicitamente sobre "encadeamento" com pdls. O termo threading tem
muitos significados diferentes em diferentes campos da computação. Dentro da estrutura do PDL,
provavelmente poderia ser definido vagamente como um recurso de loop implícito. Está implícito porque
você não especifica nada como fechar for-loops, mas em vez disso, os loops são automaticamente
(ou 'magicamente') gerado pelo PDL com base nas dimensões dos pdls envolvidos. Esse
deve dar-lhe uma primeira ideia do porquê as funções de manipulação de índice / dimensão que você encontrou
nos parágrafos anteriores são especialmente importantes e úteis no contexto de
threading. O outro ingrediente para threading (além dos pdls envolvidos) é um
função que está ciente de encadeamento (geralmente, essas são funções compiladas PDL :: PP) e
que os pdls são "encadeados". Muito sobre a terminologia e agora vamos tentar
lançar alguma luz sobre o que tudo isso significa.

Implícito segmentação - a primeiro exemplo
Existem duas variantes ligeiramente diferentes de rosqueamento. Começamos com o que chamamos
"threading implícito". Vamos pegar um exemplo prático que envolve o loop de uma função
sobre muitos elementos de um PDL. Suponha que temos uma imagem RGB que queremos converter para cinza
escala. A imagem RGB é representada por um pdl 3-dim "im (3, x, y)", onde a primeira dimensão
contém os três componentes de cor de cada pixel e "x" e "y" são a largura e a altura de
a imagem, respectivamente. Em seguida, precisamos especificar como converter um triplo de cor em um determinado
pixel em um valor de cinza (para ser um exemplo realista, deve representar o
intensidade com a qual nossas células oculares insensíveis à cor detectariam essa cor para alcançar
o que chamaríamos de uma conversão natural da cor para a escala de cinza). Uma aproximação que
funciona muito bem é calcular a intensidade do cinza de cada tripleto RGB (r, g, b) como um
soma ponderada

valor cinza = 77/256 * r + 150/256 * g + 29/256 * b =
interno ([77,150,29] / 256, [r, g, b])

onde a última forma indica que podemos escrever isso como um produto interno do vetor 3
compreendendo os pesos para os componentes vermelho, verde e azul com o vetor 3 contendo o
componentes de cor. Tradicionalmente, podemos ter escrito uma função como a seguinte para
processar a imagem inteira:

my @ dims = $ im-> dims;
# aqui normalmente verifique se o primeiro dim tem o tamanho correto (3), etc
$ cinza = zeros (@dims [1,2]); # faça o pdl para a imagem cinza resultante
$ w = pdl [77,150,29] / 256; # o vetor de pesos
para ($ j = 0; $ j
para ($ i = 0; $ i
# calcula o valor do pixel
$ tmp = inner ($ w, $ im-> slice (':, (i), (j)'));
set ($ gray, $ i, $ j, $ tmp); # e defina-o na imagem em escala de cinza
}
}

Agora escrevemos o mesmo usando threading (observando que "interno" é uma função que reconhece threading
definido no pacote PDL :: Primitive)

$ cinza = interno ($ im, pdl ([77,150,29] / 256));

Acabamos com um one-liner que cria automaticamente o cinza pdl $ com o
número e tamanho das dimensões e executa os loops automaticamente (estes loops são
implementado como código C rápido nos internos do PDL). Bem, ainda te devemos um
explicação de como essa 'mágica' é alcançada.

Como funciona o dobrador de carta de canal parece da exemplo trabalho ?
A primeira coisa a notar é que cada função que está ciente de threading (estes estão sem
funções de exceção compiladas a partir de descrições concisas por PDL :: PP, mais tarde chamado apenas de PP-
funções) espera um número definido (mínimo) de dimensões (nós as chamamos de dimensões centrais)
de cada um de seus argumentos PDL. A função interna espera duas dimensões unidimensionais (entrada)
parâmetros a partir dos quais calcula um parâmetro de dimensão zero (saída). Nós escrevemos isso
simbolicamente como "interno ((n), (n), [o] ())" e chame-o de "interno" assinatura, onde n representa
o tamanho dessa dimensão. n sendo igual no primeiro e no segundo parâmetro significa que
essas dimensões devem ter o mesmo tamanho em qualquer chamada. Como um exemplo diferente, pegue o
produto externo que leva dois vetores 1D para gerar uma matriz 2D, simbolicamente escrita como
"externo ((n), (m), [o] (n, m))". O "[o]" em ambos os exemplos indica que este (aqui terceiro)
argumento é um argumento de saída. No último exemplo, as dimensões do primeiro e do segundo
argumento não tem que concordar, mas você vê como eles determinam o tamanho das duas dimensões
do PDL de saída.

Este é o ponto em que o threading finalmente entra no jogo. Se você chamar funções PP com
PDLS que tem mais do que as dimensões do núcleo necessárias, as primeiras dimensões do PDL
argumentos são usados ​​como as dimensões do núcleo e as dimensões extras adicionais são rosqueadas
sobre. Vamos demonstrar isso primeiro com nosso exemplo acima

$ cinza = interno ($ im, $ w); # w é o vetor de peso de cima

Neste caso, $ w é 1D e, portanto, fornecido apenas a dimensão central, $ im é 3D, mais
especificamente "(3, x, y)". A primeira dimensão (de tamanho 3) é a dimensão central necessária
que corresponde (conforme exigido por inner) a primeira (e única) dimensão de $ w. O segundo
dimensão é a primeira dimensão da rosca (de tamanho "x") e a terceira é aqui a segunda
dimensão da rosca (de tamanho "y"). O pdl de saída é criado automaticamente (conforme solicitado por
definindo $ gray como "null" antes da invocação). As dimensões de saída são obtidas por
anexando o laço dimensões (aqui "(x, y)") para as dimensões de saída do núcleo (aqui 0D) para
produzir as dimensões finais do PDL criado automaticamente (aqui "0D + 2D = 2D" para produzir uma saída 2D
do tamanho "(x, y)").

Portanto, o comando acima chama a funcionalidade central que calcula o produto interno de dois
Vetores 1D "x * y" vezes com $ w e todas as fatias 1D da forma "(':, (i), (j)')" de $ im e
define os respectivos elementos da saída pdl "$ gray (i, j)" para o resultado de cada
computação. Poderíamos escrever isso simbolicamente como

$ cinza (0,0) = f ($ w, $ im (:, (0), (0)))
$ cinza (1,0) = f ($ w, $ im (:, (1), (0)))
.
.
.
$ cinza (x-2, y-1) = f ($ w, $ im (:, (x-2), (y-1)))
$ cinza (x-1, y-1) = f ($ w, $ im (:, (x-1), (y-1)))

Mas isso é feito automaticamente pelo PDL sem escrever nenhum loop Perl explícito. Nós vemos
que o comando realmente cria um PDL de saída com as dimensões corretas e define o
elementos de fato para o resultado do cálculo para cada pixel da imagem de entrada.

Quando ainda mais pdls e dimensões extras estão envolvidos, as coisas ficam um pouco mais complicadas.
Vamos primeiro dar as regras gerais de como as dimensões da rosca dependem das dimensões de
pdls de entrada permitindo que você descubra a dimensionalidade de um pdl de saída criado automaticamente
(para qualquer conjunto de pdls de entrada e dimensões centrais da função PP em questão). o
as regras gerais provavelmente parecerão um pouco confusas à primeira vista, para que possamos definir
para ilustrar o uso com um conjunto de exemplos adicionais (que esperamos também
demonstrar que existem de fato muitas situações práticas onde o threading entra
extremamente útil).

A chamada for codificação disciplina
Antes de apontarmos os outros detalhes técnicos do threading, observe esta chamada para
disciplina de programação ao usar threading:

A fim de preservar a legibilidade humana, POR FAVOR comente qualquer expressão não trivial em seu
código envolvendo threading. Mais importante ainda, para qualquer sub-rotina, inclua informações em
o começo sobre o que você espera que as dimensões representem (ou intervalos de dimensões).

Como um aviso, olhe para esta função não documentada e tente adivinhar o que pode estar acontecendo:

pesquisa secundária {
minha ($ im, $ paleta) = @_;
my $ res;
índice ($ paleta-> xchg (0,1),
$ im-> long-> dummy (0, ($ palette-> dim) [0]),
($ res = nulo));
return $ res;
}

Você concorda que pode ser difícil descobrir as dimensões esperadas, o propósito de
a rotina, etc? (Se você quiser descobrir o que este trecho de código faz, veja abaixo)

Como funciona o dobrador de carta de canal para descobrir Fora da laço dimensões
Existem algumas regras que permitem descobrir o número e o tamanho do loop
dimensões (e se o tamanho de seus pdls de entrada está de acordo com as regras de segmentação).
As dimensões de qualquer argumento pdl são divididas em dois grupos a seguir: Núcleo
dimensões (conforme definido pela função PP, consulte Apêndice B para obter uma lista de primitivas PDL)
e dimensões extras que compreendem todas as dimensões restantes desse pdl. Por exemplo
chamar uma função "func" com a assinatura "func ((n, m), [o] (n))" com um pdl
"a (2,4,7,1,3)" as "f ($ a, ($ o = null))" resulta na divisão semântica das dimensões de a
em: dimensões do núcleo "(2,4)" e dimensões extras "(7,1,3)".

As dimensões do núcleo R0 são identificadas com as primeiras N dimensões do respectivo pdl
argumento (e são obrigatórios). Quaisquer outras dimensões são dimensões extras e usadas para
determinar as dimensões do loop.

R1 O número de dimensões de loop (implícitas) é igual ao número máximo de
dimensões assumidas sobre o conjunto de argumentos PDL.

R2 O tamanho de cada uma das dimensões do loop é derivado do tamanho do respectivo
dimensões dos argumentos PDL. O tamanho de uma dimensão de loop é dado pelo
tamanho máximo encontrado em qualquer um dos pdls com essa dimensão extra.

R3 Para todos os pdls que têm uma determinada dimensão extra, o tamanho deve ser igual ao tamanho de
a dimensão do laço (conforme determinado pela regra anterior) ou 1; caso contrário, você levanta um
Exceção de tempo de execução. Se o tamanho da dimensão extra em um pdl for um, ele é
tratado implicitamente como uma dimensão fictícia de tamanho igual ao tamanho de dim do loop quando
realizando o loop de linha.

R4 Se um pdl não tem uma dimensão de loop, no loop de thread este pdl é tratado como se
tendo uma dimensão fictícia de tamanho igual ao tamanho dessa dimensão do laço.

R5 Se a criação automática de saída for usada (definindo o pdl relevante para "PDL-> nulo" antes
invocação) o número de dimensões do PDL criado é igual à soma do
número de dimensões de saída do núcleo + número de dimensões de loop. O tamanho do núcleo
dimensões de saída são derivadas da dimensão relevante de PDLs de entrada (conforme especificado
na definição da função) e os tamanhos das outras dimensões são iguais ao
tamanho da dimensão do loop da qual é derivado. O PDL criado automaticamente será
físico (a menos que o fluxo de dados esteja em operação).

Neste contexto, observe que você pode ter o problema de atribuição a pdls contendo
dimensões fictícias maiores que um (veja acima). Embora seus pdl (s) de saída não contenham
quaisquer dimensões fictícias em primeiro lugar, elas podem terminar com fictícios criados implicitamente
dimensões de acordo com R4.

Como exemplo, suponha que temos uma função PP (aqui não especificada) com a assinatura:

função ((m, n), (m, n, o), (m), [o] (m, o))

e você o chama com 3 pdls "a (5,3,10,11)", "b (5,3,2,10,1,12)" e "c (5,1,11,12)" Como

função ($ a, $ b, $ c, ($ d = nulo))

então o número de dimensões do loop é 3 (por "R0 + R1" de $ be $ c) com tamanhos
"(10,11,12)" (por R2); as duas dimensões principais de saída são "(5,2)" (a partir da assinatura de
func) resultando em uma saída 5-dimensional pdl $ c de tamanho "(5,2,10,11,12)" (ver R5) e
(o criado automaticamente) $ d é derivado de "($ a, $ b, $ c)" de uma forma que pode ser expressa
em pseudo-código PDL como

$ d (:,:, i, j, k). = func ($ a (:,:, i, j), $ b (:,:,:, i, 0, k), $ c (:, 0, j, k))
com 0 <= i <10, 0 <= j <= 11, 0 <= k <12

Se analisarmos a conversão de cor para escala de cinza novamente com essas regras em mente, notamos
outra grande vantagem do threading implícito. Podemos chamar a conversão com um pdl
representando um pixel (im(3)), uma linha de pixels rgb ("im (3, x)"), uma imagem colorida adequada
("im (3, x, y)") ou uma pilha inteira de imagens RGB ("im (3, x, y, z)"). Contanto que $ im seja do
formulário "(3, ...)" o pdl de saída criado automaticamente conterá o número correto de
dimensões e conter os dados de intensidade como esperamos, uma vez que os loops foram
realizado implicitamente graças a implicitamente segmentação. Você pode facilmente se convencer de que
chamar com um pixel de cor $ cinza é 0D, com uma linha resulta em cinza 1D (x), com uma imagem
obtemos "cinza (x, y)" e, finalmente, uma pilha de imagens convertidas "cinza (x, y, z)".

Vamos preencher essas regras gerais com um pouco mais de vida, examinando mais alguns
exemplos. O leitor pode tentar descobrir formulações equivalentes com for-
loop e comparar a flexibilidade dessas rotinas usando threading implícito para o
formulação explícita. Além disso, especialmente ao usar várias dimensões de rosca, é um
exercício útil para verificar a velocidade relativa fazendo alguns testes de benchmark (que ainda
tem de fazer).

O primeiro na linha é um exemplo de centróide ligeiramente retrabalhado, agora codificado com segmentação em
mente.

# mult encadeado para calcular coords de centroide, funciona para pilhas também
$ xc = sumover (($ im * xvals (($ im-> dims) [0])) ->moita(2)) /
soma total ($ im->moita(2));

Vamos analisar o que está acontecendo passo a passo. Primeiro o produto:

$ prod = $ im * xvals (zeros (($ im-> dims) [0]))

Isso funcionará realmente para $ im sendo um, dois, três e dimensões superiores. Se $ im for
unidimensional é apenas um produto comum (no sentido de que cada elemento de $ im é
multiplicado pelo respectivo elemento de "xvals (...)"), se $ im tiver mais dimensões
rosqueamento adicional é feito adicionando dimensões fictícias apropriadas a "xvals (...)"
de acordo com R4. Mais importante, as duas operações sumover mostram um primeiro exemplo de como
para fazer uso dos comandos de manipulação de dimensão. Uma rápida olhada na assinatura de sumover
irá lembrá-lo de que ele apenas "engolirá" a primeira dimensão de um determinado PDL de entrada.
Mas e se quisermos realmente calcular a soma de todos os elementos dos dois primeiros
dimensões? Bem, nada nos impede de passar um PDL virtual para o sumover que neste
caso é formado pela aglutinação das duas primeiras dimensões do "pdl pai" em uma. De
ponto de vista do pdl pai, a soma agora é calculada sobre as duas primeiras dimensões,
exatamente como queríamos, embora sumover tenha acabado de fazer o trabalho conforme especificado por sua assinatura. Conseguiu
isso?

Outra pequena sutileza de escrever o código assim: usamos intencionalmente
"soma ($ pdl->moita(2)) "em vez de" sum ($ pdl) "para que possamos passar apenas uma imagem
"(x, y)" ou uma pilha de imagens "(x, y, t)" nesta rotina e obter apenas um
x-coordiante ou um vetor de coordenadas x (de tamanho t) em troca.

Outro conjunto de operações comuns é o que se poderia chamar de "operações de projeção". Esses
as operações tomam um ND pdl como entrada e retornam um (N-1) -D pdl "projetado". Essas operações
são frequentemente executados com funções como soma, produção, mínimo e máximo. Usando
novamente imagens como exemplos, podemos querer calcular o valor máximo de pixels para cada linha
de uma imagem ou pilha de imagens. Nós sabemos como fazer isso

# máximas de linhas (em função do número da linha e tempo)
máximo ($ stack, ($ ret = null));

Mas e se você quiser calcular máximos por coluna quando o encadeamento implícito sempre se aplica
a funcionalidade central para a primeira dimensão e tópicos sobre todas as outras? Como podemos nós
conseguir isso, em vez disso, a funcionalidade principal é aplicada à segunda dimensão e
o encadeamento é feito sobre os outros. Você consegue adivinhar? Sim, fazemos um PDL virtual que tem
a segunda dimensão do "pdl pai" como sua primeira dimensão usando o comando "mv".

# máximos de colunas (em função do número da coluna e tempo)
máximo ($ pilha-> mv (1,0), ($ ret = nulo));

e calcular todas as somas de sub-fatias sobre a terceira dimensão agora é quase muito fácil

# somas de pixels no tempo (assumindo que o tempo é o terceiro escurecimento)
soma total ($ stack-> mv (2,0), ($ ret = null));

Finalmente, se você deseja aplicar a operação a todos os elementos (como max sobre todos os elementos ou
soma sobre todos os elementos), independentemente das dimensões do PDL em questão "aglomeração" vem
a calhar. Como um exemplo, veja a definição de "soma" (conforme definido em "Ufunc.pm"):

subsoma {
PDL :: Ufunc :: sumover ($ name-> clump (-1), ($ tmp = null));
return $ tmp-> at (); # retorna um número Perl, não um PDL 0D
}

Já mencionamos que todas as operações básicas suportam encadeamento e atribuição não
exceção. Então, aqui estão algumas tarefas encadeadas

pdl> $ im = zeros (byte, 10,20)
PDL> $ linha = exp (-rvals(10) ** 2/9)
# tarefa encadeada
pdl> $ im. = $ line # definir cada linha de $ im para $ linha
pdl> $ im2. = 5 # definir todos os elementos de $ im2 para 5

Agora você provavelmente já viu como funciona e o que faz, não é?

Para terminar os exemplos neste parágrafo, aqui está uma função para criar uma imagem RGB a partir de
o que é chamado de imagem de paleta. A imagem da paleta consiste em duas partes: uma imagem de
índices em uma tabela de pesquisa de cores e na própria tabela de pesquisa de cores. [descreva como
funciona] Vamos usar uma função PP que não encontramos ainda no anterior
exemplos. É a função de índice apropriadamente nomeada, assinatura "((n), (), [o] ())" (ver Apêndice
B) com a funcionalidade principal de que "index (pdl (0,2,4,5), 2, ($ ret = null))" retornará o
elemento com índice 2 do primeiro pdl de entrada. Nesse caso, $ ret conterá o valor 4.
Então aqui está o exemplo:

# uma pesquisa de índice encadeado para gerar uma imagem RGB, RGBA ou YMCK
# de uma imagem de paleta (representada por uma tabela de pesquisa $ palette e
# uma imagem de índice de cores $ im)
# você pode dizer apenas manequim(0) uma vez que as regras de rosqueamento o tornam adequado
pdl> index ($ palette-> xchg (0,1),
$ im-> long-> dummy (0, ($ palette-> dim) [0]),
($ res = nulo));

Vamos analisar e explicar as etapas envolvidas. Supondo que estamos lidando com um RGB
a tabela de consulta $ palette tem o tamanho "(3, x)". Primeiro trocamos as dimensões da paleta
de modo que o loop é feito sobre a primeira dimensão de $ palette (de tamanho 3 que representa r,
componentes g e b). Agora olhando para $ im, adicionamos uma dimensão fictícia de tamanho igual ao
comprimento do número de componentes (no caso que estamos discutindo aqui, poderíamos ter apenas
usou o número 3, pois temos 3 componentes de cores). Podemos usar uma dimensão fictícia, pois
para componentes de cor vermelha, verde e azul, usamos o mesmo índice da imagem original,
por exemplo, assumindo que um certo pixel de $ im tinha o valor 4, então a pesquisa deve produzir o
triplo

[paleta (0,4), paleta (1,4), paleta (2,4)]

para os novos componentes vermelho, verde e azul da imagem de saída. Espero que agora você tenha
algum tipo de ideia do que o trecho de código acima deve fazer (muitas vezes é
bastante complicado para descrever em detalhes como um pedaço de código de threading funciona; simplesmente vai em frente
e experimente um pouco para ter uma ideia melhor).

Se você leu as regras de encadeamento com atenção, então deve ter notado que não
tem que declarar explicitamente o tamanho da dimensão fictícia que criamos para $ im; quando nós
criá-lo com tamanho 1 (o padrão) as regras de rosqueamento fazem com que ele se ajuste automaticamente
o tamanho desejado (pela regra R3, em nosso exemplo o tamanho seria 3 assumindo uma paleta de
tamanho "(3, x)"). Uma vez que situações como esta ocorrem com frequência na prática, é por isso que
regra R3 foi introduzida (a parte que faz as dimensões do tamanho 1 caber na rosca
loop dim size). Então, podemos apenas dizer

pdl> index ($ palette-> xchg (0,1), $ im-> long->manequim(0), ($ res = nulo));

Novamente, você pode se convencer de que esta rotina criará a saída correta se chamada
com um pixel ($ im é 0D), uma linha ($ im é 1D), uma imagem ($ im é 2D), ..., uma pesquisa RGB
tabela (paleta é "(3, x)") e tabela de pesquisa RGBA (paleta é "(4, x)", consulte, por exemplo, OpenGL).
Essa flexibilidade é alcançada pelas regras de rosqueamento que são feitas para fazer o certo
coisa na maioria das situações.

Para encerrar tudo mais uma vez, a idéia geral é a seguinte. Se você deseja alcançar
girando sobre certas dimensões e ter o core funcionalidade aplicado a outro
conjunto especificado de dimensões, você usa os comandos de manipulação de dimensão para criar um (ou
de várias) virtual PDL (s) de modo que do ponto de vista do principal PDL (s) você consegue o que
que você deseja (tendo sempre a assinatura da função em questão e R1-R5 em mente!).
Fácil, não é?

saída autocriação e Função PP chamada convenções
Neste ponto, temos que nos desviar para alguns detalhes técnicos que têm a ver com o aspecto geral
chamar convenções de funções PP e a criação automática de argumentos de saída.
Basicamente, existem duas maneiras de invocar rotinas PDL, a saber

$ resultado = func ($ a, $ b);

e

func ($ a, $ b, $ result);

Se você estiver usando apenas encadeamento implícito, a variável de saída pode ser automaticamente
criado por PDL. Você sinaliza isso para a função PP definindo o argumento de saída para um
tipo especial de PDL que é retornado de uma chamada para a função "PDL-> null" que retorna
um pdl essencialmente "vazio" (para aqueles interessados ​​em detalhes há um sinalizador no C pdl
estrutura para isso). As dimensões do PDL criado são determinadas pelas regras de
threading implícito: as primeiras dimensões são as dimensões principais de saída para as quais o
dimensões de rosqueamento são anexadas (que por sua vez são determinadas pelas dimensões do
insira PDLS conforme descrito acima). Então você pode dizer

func ($ a, $ b, ($ result = PDL-> null));

or

$ result = func ($ a, $ b)

que são exatamente equivalente.

Esteja avisado que você pode não use a criação automática de saída ao usar threading explícito (para
razões explicadas na seção seguinte sobre explícito segmentação, a segunda variante de
rosqueamento).

Em loops "restritos", você provavelmente deseja evitar a criação implícita de um PDL temporário em
cada etapa do loop que vem junto com o estilo "funcional", mas sim dizer

# cria pdl de saída de tamanho apropriado apenas na primeira chamada
$ resultado = nulo;
para (0 ... $ n) {
func ($ a, $ b, $ result); # em todas, exceto na primeira invocação $ result
func2 ($ b); # está definido e tem o tamanho certo para
# obtém a saída fornecida, as dimerizações de $ b não mudam
twiddle ($ result, $ a); # faça algo de $ result a $ a para iteração
}

A mensagem para levar para casa desta seção mais uma vez: esteja ciente da limitação de produção
criação ao usar explícito segmentação.

Explícito segmentação
Tendo até agora falado apenas sobre o primeiro sabor de threading, agora é hora de
apresentar a segunda variante. Em vez de embaralhar dimensões o tempo todo e
contando com as regras de threading implícito para fazer tudo certo, você às vezes pode querer
especifique de uma maneira mais explícita como executar o loop de thread. Provavelmente não é também
surpreendente que esta variante do jogo seja chamada explícito segmentação. Agora, antes de nós
criar a impressão errada: também não é implicitamente or explícito; os dois sabores fazem
misturar. Mas mais sobre isso mais tarde.

As duas funções mais usadas com threading explícito são thread e unthread. Nós começamos
com um exemplo que ilustra o uso típico do primeiro:

[# ** este é o pior exemplo possível para começar]
# mas pode ser usado para mostrar que $ mat + = $ linha é diferente de
# $ mat->fio(0) + = $ linha
# threading explícito para adicionar um vetor a cada coluna de uma matriz
pdl> $ mat = zeros (4,3)
pdl> $ line = pdl (3.1416,2, -2)
pdl> ($ tmp = $ mat->fio(0)) + = $ linha

Neste exemplo, "$ mat->fio(0) "diz ao PDL que você deseja a segunda dimensão deste
PDL a ser encadeado primeiro levando a um loop de encadeamento que pode ser expresso como

para (j = 0; j <3; j ++) {
para (i = 0; i <4; i ++) {
mat (i, j) + = src (j);
}
}

"thread" recebe uma lista de números como argumentos que especificam explicitamente quais dimensões
enfie primeiro. Com a introdução do threading explícito, as dimensões de um PDL são
divididos conceitualmente em três grupos diferentes, os dois últimos dos quais já temos
encontrados: dimensões da rosca, dimensões do núcleo e dimensões extras.

Conceitualmente, é melhor pensar nas dimensões de um pdl que foram especificadas em
uma chamada para "enfiar" como sendo retirado do conjunto de dimensões normais e colocado em um
pilha separada. Então, supondo que temos um pdl "a (4,7,2,8)" dizendo

$ b = $ a-> thread (2,1)

cria um novo pdl virtual de dimensão "b (4,8)" (que chamamos de escurecimentos restantes) que
também tem 2 dimensões de rosca de tamanho "(2,7)". Para os fins deste documento, escrevemos
que simbolicamente como "b (4,8) {2,7}". Uma diferença importante em relação aos exemplos anteriores, onde
apenas o rosqueamento implícito foi usado é o fato de que as dimensões do núcleo são comparadas com
da remanescente dimensões que não são necessariamente as primeiras dimensões do PDL. Nós
irá agora especificar como a presença de dimensões de rosca muda as regras R1-R5 para rosca
loops (que se aplicam ao caso especial onde nenhum dos argumentos PDL tem qualquer thread
dimensões).

As dimensões do núcleo T0 são comparadas com o primeiro n remanescente dimensões do PDL
argumento (observe a diferença para R1). Mais longe remanescente dimensões e guarante que os mesmos estão extra
dimensões e são usados ​​para determinar o implicitamente laço dimensões.

T1a O número de implicitamente laço dimensões é igual ao número máximo de extras
dimensões assumidas sobre o conjunto de argumentos PDL.

T1b O número de explícito laço dimensões é igual ao número máximo de discussão
dimensões assumidas sobre o conjunto de argumentos PDL.

T1c O número total de laço dimensões é igual à soma de explícito laço dimensões
e implicitamente laço dimensões. No loop da linha, explícito laço dimensões e guarante que os mesmos estão
encadeado primeiro seguido por implicitamente laço dimensões.

T2 O tamanho de cada um dos laço dimensões é derivado do tamanho do respectivo
dimensões dos argumentos PDL. É dado pelo tamanho máximo encontrado em qualquer pdls
tendo esta dimensão de rosca (para explícito laço dimensões) ou dimensão extra (para
implicitamente laço dimensões).

T3 Esta regra se aplica a qualquer explícito laço dimensão bem como qualquer implicitamente laço
dimensão. Para todos os pdls que têm um determinado thread / extra dimensão o tamanho deve ser
igual ao tamanho do respectivo explícito implícito laço dimensão ou 1; de outra forma
você levanta uma exceção de tempo de execução. Se o tamanho de um thread / extra dimensão de um PDL é um
é implicitamente tratado como uma dimensão fictícia de tamanho igual ao explícito implícito
laço dimensão.

T4 Se um PDL não tiver um thread / extra dimensão que corresponde a um
explícito implícito laço dimensão, no loop de thread, este PDL é tratado como se tivesse
uma dimensão fictícia de tamanho igual ao tamanho dessa dimensão do loop.

T4a Todos os pdls que têm fio dimensões deve ter o mesmo número de discussão
dimensões.

A autocriação de saída T5 não pode ser usada se qualquer um dos argumentos pdl tiver qualquer fio
dimensões. Caso contrário, R5 se aplica.

As mesmas restrições se aplicam em relação às dimensões fictícias implícitas (criadas por
aplicação de T4) conforme já mencionado na seção sobre segmentação implícita: se houver
o pdls de saída tem uma dimensão fictícia (explícita ou implicitamente criada) maior que um a
exceção de tempo de execução será levantada.

Vamos demonstrar essas regras em funcionamento em um caso genérico. Suponha que temos um (aqui
não especificado) Função PP com a assinatura:

função ((m, n), (m), (), [o] (m))

e você o chama com 3 pdls "a (5,3,10,11)", "b (3,5,10,1,12)", "c(10) "e um PDL de saída
"d (3,11,5,10,12)" (que pode aqui não ser criado automaticamente) como

func ($ a-> thread (1,3), $ b-> thread (0,3), $ c, $ d-> thread (0,1))

A partir da assinatura de func e da chamada acima, os pdls dividem-se nos seguintes grupos de
dimensões do núcleo, extra e rosca (escritas na forma "pdl (escurecimento do núcleo) {escurecimento da linha} [extra
escurece] "):

a (5,10) {3,11} [] b(5) {3,1} [10,12] c () {} [10] d(5) {3,11} [10,12]

Com isso, para nos ajudar (é em geral útil escrever os argumentos como este
quando você começa a brincar com threading e quer acompanhar o que está acontecendo) nós
deduza ainda que o número de dimensões de loop explícitas é 2 (por T1b de $ a e $ b)
com tamanhos "(3,11)" (por T2); 2 dimensões de loop implícitas (por T1a de $ be $ d) de tamanho
"(10,12)" (por T2) e os elementos de são calculados a partir dos pdls de entrada de uma forma que pode
ser expresso em pseudo-código pdl como

para (l = 0; l <12; l ++)
para (k = 0; k <10; k ++)
para (j = 0; j <11; j ++) efeito de tratá-lo como dim falso (índice j)
para (i = 0; i <3; i ++) |
d (i, j,:, k, l) = func (a (:, i,:, j), b (i,:, k, 0, l), c (k))

Ugh, este exemplo não foi realmente fácil em termos de contabilidade. Ele serve principalmente como um
exemplo de como descobrir o que está acontecendo quando você encontra uma aparência complicada
expressão. Mas agora é realmente hora de mostrar que o encadeamento é útil, dando um pouco mais
dos nossos chamados exemplos "práticos".

[Os exemplos a seguir precisarão de algumas explicações adicionais no futuro. Para o
momento, tente viver com os comentários nos fragmentos de código. ]

1 exemplo:

*** inverso da matriz representada por eigvecs e eigvals
** dada uma matriz simétrica M = A ^ T x diag (lambda_i) x A
** => inverso M ^ -1 = A ^ T x diag (1 / lambda_i) x A
** primeiro $ tmp = diag (1 / lambda_i) * A
** então A ^ T * $ tmp por produto interno rosqueado
# manipulação de índice para que as matrizes sejam impressas corretamente no PDL
$ inv. = $ evecs * 0; # apenas copie para obter a saída de tamanho apropriado
$ tmp. = $ evecs; # inicializar, sem propagação reversa
($ tmp2 = $ tmp->fio(0)) / = $ evals; # divisão encadeada
# e agora uma multiplicação de matriz disfarçada
PDL :: Primitive :: inner ($ evecs-> xchg (0,1) -> thread (-1,1),
$ tmp-> thread (0, -1),
$ inv-> thread (0,1));
# alternativa para matriz mult usando encadeamento implícito,
# primeiro xchg apenas para transpor
PDL :: Primitive :: inner ($ evecs-> xchg (0,1) ->manequim(1)
$ tmp-> xchg (0,1) ->manequim(2)
($ inv = nulo));

2 exemplo:

# produto externo por multiplicação encadeada
# enfatizar que precisamos fazer isso com uma chamada explícita para my_biop1
# ao usar encadeamento explícito
$ res = zeros (($ a-> escurece) [0], ($ b-> escurece) [0]);
my_biop1($a->thread(0,-1),$b->thread(-1,0),$res->(0,1),"*");
# coisa semelhante por encadeamento implícito com PDL criado automaticamente
$ res = $ a->manequim(1) * $ b->manequim(0);

3 exemplo:

# uso diferente de thread e unthread para embaralhar uma série de
# dimensões de uma vez sem muitas chamadas para -> xchg e -> mv

# use thread / unthread para embaralhar as dimensões
# apenas experimente e compare o PDL filho com seu pai
$ trans = $ a-> thread (4,1,0,3,2) -> unthread;

4 exemplo:

# calcula algumas caixas delimitadoras
# $ bb manterá BB como [xmin, xmax], [ymin, ymax], [zmin, zmax]
# usamos novamente thread e unthread para embaralhar as dimensões ao redor
pdl> $ bb = zeros (duplo, 2,3);
pdl> mínimo ($ vértices->fio(0) -> aglomerar->desenfiar(1), $ bb-> ​​fatia ('(0) ,:'));
pdl> máximo ($ vértices->fio(0) -> aglomerar->desenfiar(1), $ bb-> ​​fatia ('(1) ,:'));

5 exemplo:

# calcular uma sequência auto-racionada (ou seja, auto-normalizada) de imagens
# usa encadeamento explícito e uma divisão encadeada implicitamente
$ stack = read_image_stack ();
# calcular a média (por pixel médio) das primeiras $ n + 1 imagens
$ aver = zeros ([pilha-> escurece] -> [0,1]); # torna o pdl de saída
soma ($ stack-> slice (":,:, 0: $ n") -> thread (0,1), $ aver);
$ aver / = ($ n + 1);
$ stack / = $ aver; # normaliza a pilha fazendo uma divisão encadeada
# implícito versus explícito
# alternativamente calcule $ aver com encadeamento implícito e autocriação
soma total ($ stack-> slice (":,:, 0: $ n") -> mv (2,0), ($ aver = null));
$ aver / = ($ n + 1);
#

Implícito contra explícito segmentação
Neste parágrafo, vamos ilustrar quando o encadeamento explícito é preferível em vez de
threading implícito e vice-versa. Mas, novamente, esta não é provavelmente a melhor maneira de
colocando o caso desde que você já sabe: os dois sabores se misturam. Então, é mais sobre como
para obter o melhor dos dois mundos e, de qualquer forma, dentro do melhor das tradições Perl: TIMTOWTDI!

[Desculpe, isso ainda precisa ser preenchido em uma versão posterior; consulte os exemplos acima
ou escolha alguns novos]

Finalmente, este pode ser um bom lugar para justificar todos os detalhes técnicos que temos feito
por cerca de algumas páginas: por que threading?

Bem, o código que usa threading deve ser (consideravelmente) mais rápido do que o código que usa
for-loops explícitos (ou construções Perl semelhantes) para obter a mesma funcionalidade.
Especialmente em supercomputadores (com recursos de computação vetorial / processamento paralelo) PDL
o threading será implementado de uma forma que aproveite as vantagens das facilidades adicionais
dessas máquinas. Além disso, é uma construção conceitualmente simples (embora técnica
detalhes podem estar envolvidos às vezes) e podem grandemente reduzir a complexidade sintática de
Código PDL (mas mantenha a advertência para documentação em mente). Quando você estiver confortável
com o segmentação maneira de pensar (e codificar), não deve ser muito difícil
entender o código que outra pessoa escreveu (desde que ele tenha lhe dado uma ideia do que
as dimensões de entrada esperadas são, etc.). Como uma dica geral para aumentar o desempenho de seu
código: se você tiver que introduzir um loop em seu código, tente reformular o problema para
que você pode usar threading para executar o loop (como com qualquer coisa, há exceções para
esta regra prática; mas os autores deste documento tendem a pensar que estes são raros
casos;).

PDL :: PP


An fácil maneira para definir funções que e guarante que os mesmos estão consciente of indexação e segmentação (E da universo e
tudo)
PDL: PP faz parte da distribuição de PDL. É usado para gerar funções que estão cientes de
indexação e regras de encadeamento de descrições muito concisas. Pode ser útil para você se
você deseja escrever suas próprias funções ou se deseja fazer a interface de funções de um
biblioteca externa para que eles suportem indexação e threading (e talvez fluxo de dados também,
consulte PDL :: Dataflow). Para obter mais detalhes, consulte PDL :: PP.

Apêndice A


afim transformações - a especial classe of simples e poderoso transformações
[Isso também é algo a ser adicionado em versões futuras. Já temos o general
Rotina make_affine em PDL? É possível que façamos referência a outro homem apropriado
página daqui]

Apêndice B


assinaturas of padrão PDL :: PP compilado funções
Uma seleção de assinaturas de primitivas PDL para mostrar quantas dimensões PP compilou
funções devoram (e, portanto, você pode descobrir o que será encadeado). O máximo de
essas funções são as básicas definidas em "primitive.pd"

# funções em primitive.pd
#
soma ((n), [o] ())
prodover ((n), [o] ())
valores do eixo ((n)) no local
interno ((n), (n), [o] ())
externo ((n), (m), [o] (n, m))
innerwt ((n), (n), (n), [o] ())
interno2 ((m), (m, n), (n), [o] ())
inner2t ((j, n), (n, m), (m, k), [o] ())
índice (1D, 0D, [o])
mínimo (1D, [o])
máximo (1D, [o])
wstat ((n), (n), (), [o], ())
assgn ((), ())

# operações básicas
operações binárias ((), (), [o] ())
operações unárias ((), [o] ())

AUTOR & DIREITOS AUTORAIS


Copyright (C) 1997 Christian Soeller ([email protegido]) E Tuomas J. Lukka
([email protegido]) Todos os direitos reservados. Embora destinado ao lançamento como uma página de manual
com a distribuição PDL padrão, não é de domínio público. A permissão é concedida a
distribuir gratuitamente cópias textuais deste documento, desde que nenhuma modificação fora
de formatação, e que este aviso permaneça intacto. Você está autorizado e
encorajados a usar seu código e seus derivados em seu próprio código-fonte para diversão ou para
lucro como você achar melhor.

Use PDL :: Indexingp online usando serviços onworks.net


Servidores e estações de trabalho gratuitos

Baixar aplicativos Windows e Linux

  • 1
    Image Downloader
    Image Downloader
    Rastrear e baixar imagens usando
    Selênio usando python3 e PyQt5.
    Motor de busca suportado: Google, Bing,
    Baidu. Entrada de palavras-chave do teclado
    ou entrada de...
    Baixar Image Downloader
  • 2
    Plugin Eclipse Tomcat
    Plugin Eclipse Tomcat
    O plug-in Eclipse Tomcat fornece
    integração simples de um servlet tomcat
    container para o desenvolvimento de java
    Aplicativos da web. Você pode se juntar a nós para
    discussão ...
    Baixe o plug-in Eclipse Tomcat
  • 3
    Área de trabalho do WebTorrent
    Área de trabalho do WebTorrent
    WebTorrent Desktop é para streaming
    torrents no Mac, Windows ou Linux. Isto
    conecta-se ao BitTorrent e
    Peers WebTorrent. Agora não há
    precisa esperar por ...
    Baixar WebTorrent Desktop
  • 4
    GenX
    GenX
    GenX é um programa científico para refinar
    reflexividade de raios-x, nêutron
    refletividade e raio-x de superfície
    dados de difração usando o diferencial
    algoritmo de evolução ....
    Baixar GenX
  • 5
    pspp4windows
    pspp4windows
    O PSPP é um programa de estatística
    análise de dados amostrados. é grátis
    substituição do programa proprietário
    SPSS. PSPP tem base em texto e
    gráfico nós...
    Baixar pspp4windows
  • 6
    Extensões Git
    Extensões Git
    Extensões Git é uma ferramenta de IU independente
    para gerenciar repositórios Git. Isso também
    integra-se com o Windows Explorer e
    Microsoft Visual Studio
    (2015/2017/2019). º...
    Baixar extensões do Git
  • Mais "

Comandos Linux

Ad