In [1]:
import plotly.offline as py
from plotly.graph_objs import *
import numpy as np
from numpy import pi, cos, sin, exp, log, sqrt
py.init_notebook_mode()
def curve(rfun,tmin=-2,tmax=2,tpts=200,color='black'):
    domain = np.linspace(tmin,tmax,tpts)
    r = [[rfun(t)[i] for t in domain] for i in range(3)]
    trace = Scatter3d(x=r[0],y=r[1],z=r[2],mode='lines',
                      line=Line(color=color,width=3))
    return(trace)
def snake(u,v):
    lu, lv = len(u), len(v)
    path = []

    i, j = 0, 0
    istep, jstep = 1, 1
    while(i < lu):
        while(0 <= j < lv):
            path.append((u[i],v[j]))
            j += jstep
        j -= jstep
        i += istep
        jstep *= -1

    i -= istep
    i -= istep
    istep *= -1
    while(0 <= j < lv):
        while(0 <= i < lu):
            path.append((u[i],v[j]))
            i += istep
        i -= istep
        j += jstep
        istep *= -1
    return path
def surface(rfun,tmin=-2,tmax=2,tpts=20,umin=-2,umax=2,upts=20,color='black'):
    t, u = np.linspace(tmin,tmax,tpts), np.linspace(umin,umax,upts)
    path = snake(t,u)
    r = [[rfun(t,u)[i] for (t,u) in path] for i in range(3)]
    trace = Scatter3d(x=r[0],y=r[1],z=r[2],mode='lines',
                      line=Line(color=color,width=3))
    return(trace)
In [2]:
from math import copysign
#minimum of f(x,y,z)=x^3-y^2*z constrained to x^2+y^2+z^2=1 is -1
#maximum of f(x,y,z)=x^3-y^2*z constrained to x^2+y^2+z^2=1 is 1

#show the seven critical level surfaces and the constraint surface:
#A: x^3-y^2*z=-1 (minimum critical level)
#B: x^3-y^2*z=-2/3/sqrt(3) (intermediate critical level)
#C: x^3-y^2*z=-2/sqrt(31) (intermediate critical level)
#D: x^3-y^2*z=0 (intermediate critical level)
#E: x^3-y^2*z=2/sqrt(31) (intermediate critical level)
#F: x^3-y^2*z=2/3/sqrt(3) (intermediate critical value)
#G: x^3-y^2*z=1 (maximum critical level)
#H: x^2+y^2+z^2=1 (constraint)

#Each of A,B,C,E,F,G is tangent to H at one or more critical points
#D is not tangent to H, which can happen when the gradient of f is zero.

constraint = surface(lambda t,u: (sin(u)*cos(t),sin(u)*sin(t),cos(u)),color='gray',tmin=0,tmax=2*pi,umin=0,umax=pi)
h=lambda y,z,m:y*y*z+m
root3=lambda x: copysign(abs(x)**(1/3),x)
for (m,c) in ((-1,'purple'),
              (-2/3/sqrt(3),'blue'),
              (-2/sqrt(31),'cyan'),
              (0,'green'),
              (2/sqrt(31),'yellow'),
              (2/3/sqrt(3),'orange'),
              (1,'red')):
    level = surface(lambda t,u: (root3(h(t,u,m)),t,u),color=c,tmin=-1,tmax=1,umin=-1,umax=1)
    py.iplot(Figure(data=Data([constraint,level])))
In [6]:
#Show the constrained critical points:
cp=[[-2/sqrt(31),-2/sqrt(31),2/sqrt(31),2/sqrt(31),0,0,0,0,0,0,1,-1],
    [3*sqrt(2/31),-3*sqrt(2/31),3*sqrt(2/31),-3*sqrt(2/31),sqrt(2/3),-sqrt(2/3),sqrt(2/3),-sqrt(2/3),0,0,0,0],
    [3/sqrt(31),3/sqrt(31),-3/sqrt(31),-3/sqrt(31),1/sqrt(3),1/sqrt(3),-1/sqrt(3),-1/sqrt(3),1,-1,0,0]]
points=Scatter3d(x=cp[0],y=cp[1],z=cp[2],mode='markers')
py.iplot(Figure(data=Data([constraint,points])))
In [3]:
help(Scatter3d)
Help on class Scatter3d in module plotly.graph_objs.graph_objs:

class Scatter3d(PlotlyDict)
 |  Valid attributes for 'scatter3d' at path [] under parents ():
 |  
 |      ['textpositionsrc', 'showlegend', 'ysrc', 'line', 'z', 'legendgroup',
 |      'name', 'uid', 'x', 'visible', 'mode', 'textsrc', 'text', 'zsrc',
 |      'marker', 'textfont', 'surfaceaxis', 'type', 'textposition', 'xsrc',
 |      'scene', 'y', 'projection', 'opacity', 'hoverinfo', 'stream', 'error_x',
 |      'error_z', 'surfacecolor', 'error_y']
 |  
 |  Run `<scatter3d-object>.help('attribute')` on any of the above.
 |  '<scatter3d-object>' is the object at []
 |  
 |  Method resolution order:
 |      Scatter3d
 |      PlotlyDict
 |      builtins.dict
 |      PlotlyBase
 |      builtins.object
 |  
 |  Methods inherited from PlotlyDict:
 |  
 |  __copy__(self)
 |  
 |  __deepcopy__(self, memodict={})
 |  
 |  __dir__(self)
 |      Dynamically return the existing and possible attributes.
 |  
 |  __getattr__(self, key)
 |      Python only calls this when key is missing!
 |  
 |  __getitem__(self, key)
 |      Calls __missing__ when key is not found. May mutate object.
 |  
 |  __init__(self, *args, **kwargs)
 |  
 |  __missing__(self, key)
 |      Mimics defaultdict. This is called from __getitem__ when key DNE.
 |  
 |  __setattr__(self, key, value)
 |      Maps __setattr__ onto __setitem__
 |  
 |  __setitem__(self, key, value, _raise=True)
 |      Validates/Converts values which should be Graph Objects.
 |  
 |  force_clean(self, **kwargs)
 |      Recursively remove empty/None values.
 |  
 |  get_data(self, flatten=False)
 |      Returns the JSON for the plot with non-data elements stripped.
 |  
 |  get_ordered(self, **kwargs)
 |      Return a predictable, OrderedDict version of self.
 |  
 |  help(self, attribute=None, return_help=False)
 |      Print help string for this object or an attribute of this object.
 |      
 |      :param (str) attribute: A valid attribute string for this object.
 |      :param (bool) return_help: Return help_string instead of printing it?
 |      :return: (None|str)
 |  
 |  strip_style(self)
 |      Recursively strip style from the current representation.
 |      
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |      
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json. Note that a key tagged as
 |      style, but with an array as a value may still be considered data.
 |  
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |      
 |      :param (int) level: The number of indentations to start with.
 |      :param (int) indent: The indentation amount.
 |      :param (str) eol: The end of line character(s).
 |      :param (bool) pretty: Curtail long list output with a '..' ?
 |      :param (int) max_chars: The max characters per line.
 |      
 |      Example:
 |      
 |          print(obj.to_string())
 |  
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |      
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |      
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |      
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from builtins.dict:
 |  
 |  __contains__(self, key, /)
 |      True if D has a key k, else False.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  fromkeys(iterable, value=None, /) from builtins.type
 |      Returns a new dict with keys from iterable and values equal to value.
 |  
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |  
 |  items(...)
 |      D.items() -> a set-like object providing a view on D's items
 |  
 |  keys(...)
 |      D.keys() -> a set-like object providing a view on D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |  
 |  values(...)
 |      D.values() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from builtins.dict:
 |  
 |  __hash__ = None
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from PlotlyBase:
 |  
 |  to_graph_objs(self, **kwargs)
 |      Everything is cast into graph_objs. Here for backwards compat.
 |  
 |  validate(self)
 |      Everything is *always* validated now. keep for backwards compat.


In []: