Usando variáveis ​​\def calculadas por \FPeval dentro do tikz

Usando variáveis ​​\def calculadas por \FPeval dentro do tikz

Olá, sou um iniciante absoluto.

Peguei um modelo tikz para desenhar redes neruais e tentei modificá-lo para poder alterar o número de camadas automaticamente, mas gerou erros estranhos.

Estou usando esses pacotes e não tenho ideia se isso importa

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[czech]{babel}
\usepackage[margin=2cm, a4paper]{geometry}
\usepackage[T1]{fontenc}
\usepackage{fontawesome}
\usepackage{xcolor}
\usepackage{graphicx}
\usepackage{array}
\usepackage[protrusion,expansion]{microtype}
\usepackage{tikz}
\usepackage[nomessages]{fp}

Eu criei a seguinte macro em um arquivo .sty separado:

\newcommand{\mln}[3]{
    \def\layersep{#1}
    \def\inpneuroncount{#2}
    \def\hidneuroncount{#3}

    \FPeval{result}{clip((\hidneuroncount-\inpneuroncount)/2)}
    \def\nodeoffset{yshift=\result cm}
    \nodeoffset

    \begin{tikzpicture}[shorten >=1pt,->,draw=black!50, node distance=\layersep]
        \tikzstyle{every pin edge}=[<-,shorten <=3pt]
        \tikzstyle{neuron}=[circle,fill=black!25,minimum size=15pt,inner sep=0pt]
        \tikzstyle{input neuron}=[neuron, fill=green!50];
        \tikzstyle{output neuron}=[neuron, fill=red!50];
        \tikzstyle{hidden neuron}=[neuron, fill=blue!50];
        \tikzstyle{annot} = [text width=4em, text centered]

        % Draw the input layer nodes
        \foreach \name / \y in {1,...,\inpneuroncount}
        % This is the same as writing \foreach \name / \y in {1/1,2/2,3/3,4/4}
            \node[input neuron, pin=left:Input \#\y] (I-\name) at (0,-\y) {};

        % Draw the hidden layer nodes
        \foreach \name / \y in {1,...,\hidneuroncount}
            \path[\nodeoffset]
                node[hidden neuron] (H-\name) at (\layersep,-\y cm) {};

        % Draw the output layer node
        \node[output neuron,pin={[pin edge={->}]right:Output}, right of=H-1] (O) {};

        % Connect every node in the input layer with every node in the
        % hidden layer.
        \foreach \source in {1,...,\inpneuroncount}
            \foreach \dest in {1,...,\hidneuroncount}
                \path (I-\source) edge (H-\dest);

        % Connect every node in the hidden layer with the output layer
        \foreach \source in {1,...,\hidneuroncount}
            \path (H-\source) edge (O);

        % Annotate the layers
        \node[annot,above of=H-1, node distance=1cm] (hl) {Hidden layer};
        \node[annot,left of=hl] {Input layer};
        \node[annot,right of=hl] {Output layer};
    \end{tikzpicture}
    % End of code
}

O que estou tentando fazer é fazer com que o deslocamento y da camada oculta seja igual à metade da diferença entre o número de neurônios de entrada e os neurônios da camada oculta. Criei uma variável \nodeoffset, que após o uso renderiza claramente o texto "yshift=0,5cm" quando você usa \mln{2,5cm}{2}{3}

Quando tento usá-lo na parte

% Draw the hidden layer nodes
    \foreach \name / \y in {1,...,\hidneuroncount}
        \path[\nodeoffset]

ele gera um erro como "Erro de pacote pgfkeys: não conheço a chave '/tikz/yshift=0,5cm' e vou ignorá-la. Talvez você tenha digitado incorretamente."

Como faço para obter o parâmetro corretamente?

Responder1

Você está enfrentando o chamado problema de expansão. TikZ não expande totalmente a macro \nodeoffset. Você pode expandi-lo substituindo

\path[\nodeoffset] node[hidden neuron] (H-\name) at (\layersep,-\y cm) {};

por

\path[style/.expanded=\nodeoffset] node[hidden neuron] (H-\name) at (\layersep,-\y cm) {};

Exemplo completo:

\documentclass{article}
\usepackage{tikz}
\usepackage[nomessages]{fp}
\newcommand{\mln}[3]{
    \def\layersep{#1}
    \def\inpneuroncount{#2}
    \def\hidneuroncount{#3}

    \FPeval{result}{clip((\hidneuroncount-\inpneuroncount)/2)}
    \def\nodeoffset{yshift=\result cm}
    \nodeoffset

    \begin{tikzpicture}[shorten >=1pt,->,draw=black!50, node distance=\layersep]
        \tikzstyle{every pin edge}=[<-,shorten <=3pt]
        \tikzstyle{neuron}=[circle,fill=black!25,minimum size=15pt,inner sep=0pt]
        \tikzstyle{input neuron}=[neuron, fill=green!50];
        \tikzstyle{output neuron}=[neuron, fill=red!50];
        \tikzstyle{hidden neuron}=[neuron, fill=blue!50];
        \tikzstyle{annot} = [text width=4em, text centered]

        % Draw the input layer nodes
        \foreach \name / \y in {1,...,\inpneuroncount}
        % This is the same as writing \foreach \name / \y in {1/1,2/2,3/3,4/4}
            \node[input neuron, pin=left:Input \#\y] (I-\name) at (0,-\y) {};

        % Draw the hidden layer nodes
        \foreach \name / \y in {1,...,\hidneuroncount}
            \path[style/.expanded=\nodeoffset]
                node[hidden neuron] (H-\name) at (\layersep,-\y cm) {};

        % Draw the output layer node
        \node[output neuron,pin={[pin edge={->}]right:Output}, right of=H-1] (O) {};

        % Connect every node in the input layer with every node in the
        % hidden layer.
        \foreach \source in {1,...,\inpneuroncount}
            \foreach \dest in {1,...,\hidneuroncount}
                \path (I-\source) edge (H-\dest);

        % Connect every node in the hidden layer with the output layer
        \foreach \source in {1,...,\hidneuroncount}
            \path (H-\source) edge (O);

        % Annotate the layers
        \node[annot,above of=H-1, node distance=1cm] (hl) {Hidden layer};
        \node[annot,left of=hl] {Input layer};
        \node[annot,right of=hl] {Output layer};
    \end{tikzpicture}
    % End of code
}
\begin{document}
\mln{8em}{5}{4}
\end{document}

insira a descrição da imagem aqui

No entanto, gostaria de sugerir algumas modificações/melhorias.

  1. \tikzstyleestá obsoleto, use a \tikzsetsintaxe correspondente (veja abaixo).
  2. Na verdade, você não precisa fpaqui.
  3. Usar positioning.
  4. Os \defs no início realmente não parecem ter um propósito.

Há muito mais coisas que podem ser alteradas, mas esta é uma versão parcialmente modificada.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\newcommand{\mln}[3]{
    \begin{tikzpicture}[shorten >=1pt,->,draw=black!50, node distance=#1]
        \tikzset{every pin edge/.style={<-,shorten <=3pt},
          neuron/.style={circle,fill=black!25,minimum size=15pt,inner
            sep=0pt},
          input neuron/.style={neuron, fill=green!50},
          output neuron/.style={neuron, fill=red!50},
          hidden neuron/.style={neuron, fill=blue!50},
          annot/.style={text width=4em, text centered},
        my offset/.style={yshift={((#3-#2)/2)*1cm}}}
        \begin{scope}[local bounding box=diag]
          % Draw the input layer nodes
          \foreach \name / \y in {1,...,#2}
          % This is the same as writing \foreach \name / \y in {1/1,2/2,3/3,4/4}
              \node[input neuron, pin={[alias=auxI]left:Input \#\y}] (I-\name) at (0,-\y) {};

          % Draw the hidden layer nodes
          \foreach \name / \y in {1,...,#3}
              {\path[my offset]
                  node[hidden neuron] (H-\name) at (#1,-\y cm) {};}

          % Draw the output layer node
          \node[output neuron,pin={[pin edge={->},alias=auxO]right:Output}, right=of H-1] (O) {};

          % Connect every node in the input layer with every node in the
          % hidden layer.
          \foreach \source in {1,...,#2}
              \foreach \dest in {1,...,#3}
                  \path (I-\source) edge (H-\dest);

          % Connect every node in the hidden layer with the output layer
          \foreach \source in {1,...,#3}
              \path (H-\source) edge (O);
        \end{scope}
        % Annotate the layers
        \path ([yshift=1ex]diag.north-|H-1.center) node[anchor=south,annot] (hl) {Hidden layer};
        \path ([yshift=1ex]diag.north-|auxI.west) node[anchor=south west,annot]  {Input layer};
        \path ([yshift=1ex]diag.north-|auxO.east) node[anchor=south east,annot]  {Output layer};
    \end{tikzpicture}
    % End of code
}
\begin{document}
\mln{8em}{5}{4}
\end{document}

informação relacionada