wolfram mathematica graficar funciones español buscador wolfram-mathematica mathematica-frontend

wolfram-mathematica - mathematica - wolfram alpha en español



Guardar Definiciones consideradas peligrosas (2)

SaveDefinitions es una buena opción de Manipulate . Hace que Manipulate almacene cualquier definición utilizada para su creación dentro del panel Manipulate. Una Manipulación hecha de esta manera se puede copiar en un cuaderno vacío y aún funcionará por sí misma. Además, su cuaderno de trabajo que contiene muchos de estos Manipulados tampoco se convierte en una serie de cajas rosadas con mensajes de error impresos debajo de él al abrirlos. ¡Genial!

Sin embargo, toda esta bondad tiene su lado oscuro que puede morderte muy fuerte si no eres consciente de ello. He tenido esto en un cuaderno en el que había estado trabajando durante unos días, pero le presento un ejemplo de ejemplo de juguete paso a paso que recrea el problema.

En este escenario, usted desea crear una Manipulate muestre un gráfico de una función ondulada agradable, por lo que define esto (por favor haga un tamaño de ventana como este, esto es importante):

La definición es buena, así que la guardamos para la próxima vez y la convertimos en una celda de inicialización. A continuación agregamos la Manipulate , y la ejecutamos también.

f[x_] := x^2 Manipulate[ Plot[n f[x], {x, -3, 3}], {n, 1, 4}, SaveDefinitions -> True ]

Todo funciona muy bien, el Manipulado realmente brilla, es un buen día.

Solo siendo tu yo paranoico, verificas si la definición es correcta:

Sí, todo sigue funcionando. Multa. Pero ahora se te ocurre que una mejor función ondulada sería un seno, así que cambias la definición, ejecutas y eres paranoico, verifica:

Todo sigue bien. Estás listo para el trabajo duro de un día, guardas tu trabajo y lo dejas. [Salir del kernel]

Día siguiente. Comienzas tu trabajo de nuevo. Evalúas las celdas de inicialización en tu cuaderno. La definición sigue siendo buena? Comprobar.

Ahora, desplácese hacia abajo hasta su cuadro Manipular (no es necesario volver a ejecutar gracias a las SaveDefinitions ), juegue un poco con el control deslizante. Y desplácese hacia arriba.

Siendo el paranoico tú, una vez más verificas la definición de f:

¡He aquí que alguien ha cambiado la definición a tus espaldas! Y nada se ejecuta entre la primera y la segunda verificación de Information (?) Según los números de In [] ( In[1] : def of f, In[2] primero, ¿ In[3] segundo?).

¿Que pasó? Bueno, es el Manipulate por supuesto. Un FullForm revela su estructura interna:

Manipulate[Plot[n*f[x],{x, -3, 3}],{{n, 2.44}, 1, 4},Initialization:>{f[x_] := x^2}]

Ahí tienes al culpable. La parte de inicialización del cuadro define f nuevamente, pero es la versión anterior porque no reevaluamos el Manipulate después de modificar su definición. Tan pronto como el cuadro de manipulación aparece en la pantalla, se evalúa y recupera su definición anterior. A nivel mundial

Por supuesto, en este ejemplo de juguete queda inmediatamente claro que algo extraño está sucediendo. En mi caso, tenía un módulo más grande en un cuaderno más grande en el que yo, después de un poco de depuración, había cambiado una pequeña parte. Parecía funcionar, pero al día siguiente, el mismo error que me había molestado antes de volver a golpearlo. Me tomó un par de horas antes de darme cuenta de que uno de los varios Manipulados que solía estudiar el problema en cuestión desde todos los lados estaba haciendo esto.

Claramente, estoy tentado de decir, este es un comportamiento no deseado. Ahora, para la pregunta obligatoria: ¿qué podemos hacer para evitar que se produzca este comportamiento detrás de su espalda de Manipulate además de volver a ejecutar cada Manipulate en su cuaderno cada vez que cambie una definición que puedan usar?


Aquí hay un intento. La idea es identificar símbolos con DownValues o algún otro ...Values dentro de su código manipulado, y renombrarlos automáticamente usando variables / símbolos únicos en lugar de ellos. La idea aquí puede ejecutarse de manera bastante elegante con la ayuda de la funcionalidad de símbolos de clonación, que encuentro útil de vez en cuando. La función de clone continuación clonará un símbolo dado, produciendo un símbolo con las mismas definiciones globales:

Clear[GlobalProperties]; GlobalProperties[] := {OwnValues, DownValues, SubValues, UpValues, NValues, FormatValues, Options, DefaultValues, Attributes}; Clear[unique]; unique[sym_] := ToExpression[ ToString[Unique[sym]] <> StringReplace[StringJoin[ToString /@ Date[]], "." :> ""]]; Attributes[clone] = {HoldAll}; clone[s_Symbol, new_Symbol: Null] := With[{clone = If[new === Null, unique[Unevaluated[s]], ClearAll[new]; new], sopts = Options[Unevaluated[s]]}, With[{setProp = (#[clone] = (#[s] /. HoldPattern[s] :> clone)) &}, Map[setProp, DeleteCases[GlobalProperties[], Options]]; If[sopts =!= {}, Options[clone] = (sopts /. HoldPattern[s] :> clone)]; HoldPattern[s] :> clone]]

Hay varias alternativas de cómo implementar la función en sí. Una es introducir la función con otro nombre, tomando los mismos argumentos que Manipulate , digamos myManipulate . UpValues otro: sobrecargar suavemente Manipulate través de UpValues de algún envoltorio personalizado, que presentaré. Lo llamaré CloneSymbols . Aquí está el código:

ClearAll[CloneSymbols]; CloneSymbols /: Manipulate[args___,CloneSymbols[sd:(SaveDefinitions->True)],after:OptionsPattern[]]:= Unevaluated[Manipulate[args, sd, after]] /. Cases[ Hold[args], s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :> clone[s], Infinity, Heads -> True];

Aquí hay un ejemplo de uso:

f[x_] := Sin[x]; g[x_] := x^2;

Tenga en cuenta que para usar la nueva funcionalidad, uno tiene que envolver la opción SaveDefinitions->True en el contenedor CloneSymbols :

Manipulate[Plot[ f[n g[x]], {x, -3, 3}], {n, 1, 4}, CloneSymbols[SaveDefinitions -> True]]

Esto no afectará las definiciones de los símbolos originales en el código dentro de Manipulate , ya que fueron sus clones las definiciones que se guardaron y usaron en la inicialización ahora. Podemos ver el FullForm para esta Manipulate para confirmar que:

Manipulate[Plot[f$37782011751740542578125[Times[n,g$37792011751740542587890[x]]], List[x,-3,3]],List[List[n,1.9849999999999999`],1,4],RuleDelayed[Initialization, List[SetDelayed[f$37782011751740542578125[Pattern[x,Blank[]]],Sin[x]], SetDelayed[g$37792011751740542587890[Pattern[x,Blank[]]],Power[x,2]]]]]

En particular, puede cambiar las definiciones de funciones para decir

f[x_]:=Cos[x]; g[x_]:=x;

Luego mueva el control deslizante del Manipulate producido arriba, y luego verifique las definiciones de funciones

?f Global`f f[x_]:=Cos[x] ?g Global`g g[x_]:=x

Este Manipulate es razonablemente independiente de cualquier cosa y puede copiarse y pegarse de manera segura. Lo que sucede aquí es lo siguiente: primero encontramos todos los símbolos con DownValues , SubValues o UpValues no triviales (es probable que también se puedan agregar OwnValues ), y usamos Cases y clone para crear sus clones sobre la marcha. Luego reemplazamos de forma léxica todos los símbolos clonados con sus clones dentro de Manipulate , y luego dejamos que Manipulate guarde las definiciones de los clones. De esta manera, hacemos una "instantánea" de las funciones involucradas, pero no afectamos a las funciones originales de ninguna manera.

La singularidad de los clones (símbolos) se ha abordado con la función unique . Sin embargo, tenga en cuenta que si bien los Manipulate -s obtenidos de esta manera no amenazan las definiciones de funciones originales, en general seguirán dependiendo de ellas, por lo que no se pueden considerar totalmente independientes de nada. Uno tendría que caminar por el árbol de dependencias y clonar todos los símbolos allí, y luego reconstruir sus interdependencias, para construir una "instantánea" totalmente independiente en Manipular. Esto es factible pero más complicado.

EDITAR

Por solicitud de @Sjoerd, agrego código para un caso en el que queremos que nuestros Manipulate -s se actualicen a los cambios de la función, pero no queremos que interfieran activamente y cambien las definiciones globales. Sugiero una variante de una técnica de "puntero": volveremos a reemplazar los nombres de las funciones con nuevos símbolos, pero, en lugar de clonar esos nuevos símbolos después de nuestras funciones, usaremos la opción de Initialization Manipulate para simplemente hacer esos símbolos "punteros" a nuestras funciones, por ejemplo como Initialization:>{new1:=f,new2:=g} . Claramente, la reevaluación de dicho código de inicialización no puede dañar las definiciones de f o g , y al mismo tiempo nuestros Manipulate -s responderán a los cambios en esas definiciones.

El primer pensamiento es que simplemente podríamos reemplazar los nombres de las funciones por nuevos símbolos y dejar que la inicialización de Manipulate haga el resto automáticamente. Desafortunadamente, en ese proceso, recorre el árbol de dependencias y, por lo tanto, también se incluirán las definiciones de nuestras funciones, que es lo que intentamos evitar. Entonces, en su lugar, construiremos explícitamente la opción Initialize . Aquí está el código:

ClearAll[SavePointers]; SavePointers /: Manipulate[args___,SavePointers[sd :(SaveDefinitions->True)], after:OptionsPattern[]] := Module[{init}, With[{ptrrules = Cases[Hold[args], s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :> With[{pointer = unique[Unevaluated[s]]}, pointer := s; HoldPattern[s] :> pointer], Infinity, Heads -> True]}, Hold[ptrrules] /. (Verbatim[HoldPattern][lhs_] :> rhs_ ) :> (rhs := lhs) /. Hold[defs_] :> ReleaseHold[ Hold[Manipulate[args, Initialization :> init, after]] /. ptrrules /. init :> defs]]]

Con las mismas definiciones que antes:

ClearAll[f, g]; f[x_] := Sin[x]; g[x_] := x^2;

Aquí hay un FullForm de Manipulate producido:

In[454]:= FullForm[Manipulate[Plot[f[n g[x]],{x,-3,3}],{n,1,4}, SavePointers[SaveDefinitions->True]]] Out[454]//FullForm= Manipulate[Plot[f$3653201175165770507872[Times[n,g$3654201175165770608016[x]]], List[x,-3,3]],List[n,1,4],RuleDelayed[Initialization, List[SetDelayed[f$3653201175165770507872,f],SetDelayed[g$3654201175165770608016,g]]]]

Los nuevos símbolos generados sirven como "punteros" a nuestras funciones. Los Manipulate construidos con este enfoque responderán a las actualizaciones de nuestras funciones y, al mismo tiempo, serán inofensivos para las definiciones de las funciones principales. El precio a pagar es que no son independientes y no se mostrarán correctamente si las funciones principales no están definidas. Por lo tanto, uno puede usar el envoltorio SavePointers o SavePointers , dependiendo de lo que se necesite.


La respuesta es usar la celda de inicialización como inicialización para la Manipulate :

Manipulate[ Plot[n f[x], {x, -3, 3}], {n, 1, 4}, Initialization :> FrontEndTokenExecute["EvaluateInitialization"]]

También puedes usar DynamicModule :

DynamicModule[{f}, f[x_] := x^2; Manipulate[Plot[n f[x], {x, -3, 3}], {n, 1, 4}]]

No necesita SaveDefinitions -> True en este caso.

EDITAR

En respuesta al comentario de Sjoerd. Con la siguiente técnica simple, no necesita copiar la definición en todas partes y actualizar todas las copias si cambia la definición (pero aún debe volver a evaluar su código para obtener Manipulate actualizada):

DynamicModule[{f}, f[x_] := x^2; list = Manipulate[Plot[n^# f[x], {x, -3, 3}], {n, 2, 4}] & /@ Range[3]]; list // Row