Usando variables \def calculadas por \FPeval dentro de tikz

Usando variables \def calculadas por \FPeval dentro de tikz

Hola, soy un principiante absoluto.

Tomé una plantilla de tikz para dibujar redes neuronales e intenté modificarla para poder cambiar el número de capas automáticamente, pero produce errores extraños.

Estoy usando estos paquetes y no tengo idea si es importante

\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}

Se me ocurrió la siguiente macro en un archivo .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
}

Lo que estoy tratando de hacer es hacer que el desplazamiento y de la capa oculta sea igual a la mitad de la diferencia entre el número de neuronas de entrada y las neuronas de la capa oculta. Creé una variable \nodeoffset, que después de su uso muestra claramente el texto "yshift=0.5cm" cuando usas \mln{2.5cm}{2}{3}

Cuando trato de usarlo en la parte

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

arroja un error como "Error del paquete pgfkeys: no conozco la clave '/tikz/yshift=0.5cm' y voy a ignorarla. Quizás la escribiste mal".

¿Cómo consigo el parámetro allí correctamente?

Respuesta1

Se enfrenta al llamado problema de expansión. TikZ no expande completamente la macro \nodeoffset. Puedes hacer que se expanda reemplazando

\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) {};

Ejemplo 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}

ingrese la descripción de la imagen aquí

Sin embargo, me gustaría sugerir algunas modificaciones/mejoras.

  1. \tikzstyleestá en desuso, utilice la sintaxis correspondiente \tikzset(ver más abajo) en su lugar.
  2. En realidad no lo necesitas fpaquí.
  3. Usar positioning.
  4. Los \defs al principio no parecen tener ningún propósito.

Hay muchas más cosas que se pueden cambiar, pero esta es una versión 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}

información relacionada