1 """
2 High-level user-level commands controlling the entire simulation.
3 $Id: basic.py 11324 2010-07-28 17:12:56Z ceball $
4 """
5 __version__='$Revision: 11324 $'
6
7 import cPickle as pickle
8
9 from xml.parsers.expat import ExpatError
10
11 import os,sys,re,string,time,platform
12
13 import __main__
14
15
16 try:
17 import gzip
18 except ImportError:
19 pass
20
21 import param
22 from param.parameterized import PicklableClassAttributes, ParameterizedFunction
23 from param.parameterized import ParamOverrides
24 from param.external import OrderedDict
25 from param import normalize_path
26
27 import topo
28 from topo.base.sheet import Sheet
29 from topo.base.projection import ProjectionSheet
30 from topo.sheet import GeneratorSheet
31 from topo.misc.util import MultiFile
32 from topo.misc.picklemain import PickleMain
33 from topo.misc.genexamples import generate as _generate
34 from topo.base.functionfamily import PatternDrivenAnalysis
38 """
39 Generate the saved network target, as defined in
40 topo.misc.genexamples.
41 """
42 _generate(targets=[target])
43
44
45
46
47
48 from param.parameterized import ParameterizedMetaclass
61
62 @classmethod
85 return caller
86
87
88
89 -class Command(ParameterizedFunction):
90 """
91 Parameterized command: any error when the command is run (called)
92 will not raise an exception, but will instead generate a warning.
93 """
94 __metaclass__ = CommandMetaclass
95 __abstract = True
96
98
99
100 self.warning("%s failed: %s"%(self,e))
101
104
107 """
108 Raises an ImportError on any attempt to access an attribute, call,
109 or get an item.
110
111 Useful to delay an ImportError until the point of use, thus
112 allowing e.g. a class attribute to contain something from a
113 non-core external module (e.g. pylab).
114
115 Delaying an ImportError until the point of use allows users to be
116 informed of the possibility of having various extra functions on
117 installation of a missing package.
118 """
120 self.__module_name = module_name
122
123 raise ImportError, "No module named %s. Install %s to get this functionality."%(self.__module_name,self.__module_name)
124 return None
127
128
129
130
135
139 """
140 Returns an ImportErrorObject for any attribute request.
141
142 Instances of this class can be used in place of a module to delay
143 an import error until the point of use of an attribute of that
144 module.
145
146 See ImportErrorObject for more details.
147 """
149 self.__module_name = module_name
152
165
172
175 """Remove pending events from the simulator's event queue."""
176 topo.sim.event_clear()
177
178
179 -def pattern_present(inputs={},duration=1.0,plastic=False,overwrite_previous=False,apply_output_fns=True):
180 """
181 Present the specified test patterns for the specified duration.
182
183 Given a set of input patterns (dictionary of
184 GeneratorSheetName:PatternGenerator pairs), installs them into the
185 specified GeneratorSheets, runs the simulation for the specified
186 length of time, then restores the original patterns and the
187 original simulation time. Thus this input is not considered part
188 of the regular simulation, and is usually for testing purposes.
189
190 As a special case, if 'inputs' is just a single pattern, and not
191 a dictionary, it is presented to all GeneratorSheets.
192
193 If a simulation is not provided, the active simulation, if one
194 exists, is requested.
195
196 If this process is interrupted by the user, the temporary patterns
197 may still be installed on the retina.
198
199 If overwrite_previous is true, the given inputs overwrite those
200 previously defined.
201
202 If plastic is False, overwrites the existing values of Sheet.plastic
203 to disable plasticity, then reenables plasticity.
204
205 In order to to see the sequence of values presented, use the back arrow
206 history mechanism in the GUI. Note that the GUI's Activity window must
207 be open and the display parameter set to true (display=True).
208 """
209
210 topo.sim.run(0.0)
211
212
213 if not overwrite_previous:
214 save_input_generators()
215
216 if not plastic:
217
218 for sheet in topo.sim.objects(Sheet).values():
219 sheet.override_plasticity_state(new_plasticity_state=False)
220
221 if not apply_output_fns:
222 for each in topo.sim.objects(Sheet).values():
223 if hasattr(each,'measure_maps'):
224 if each.measure_maps:
225 each.apply_output_fns = False
226
227
228 generatorsheets = topo.sim.objects(GeneratorSheet)
229 if not isinstance(inputs,dict):
230 for g in generatorsheets.values():
231 g.set_input_generator(inputs)
232 else:
233 for each in inputs.keys():
234 if generatorsheets.has_key(each):
235 generatorsheets[each].set_input_generator(inputs[each])
236 else:
237 param.Parameterized().warning(
238 '%s not a valid Sheet name for pattern_present.' % each)
239
240 topo.sim.event_push()
241
242 topo.sim.run(duration)
243 topo.sim.event_pop()
244
245
246
247 if not plastic:
248 for sheet in topo.sim.objects(Sheet).values():
249 sheet.restore_plasticity_state()
250
251 if not apply_output_fns:
252 for each in topo.sim.objects(Sheet).values():
253 each.apply_output_fns = True
254
255
256 if not overwrite_previous:
257 restore_input_generators()
258
262 """When unpickled, prints version & release information about snapshot."""
275
278 """
279 Save a snapshot of the network's current state.
280
281 The snapshot is saved as a gzip-compressed Python binary pickle.
282
283 (xml snapshots are currently experimental, and will not be useful
284 for most users.)
285
286 As this function uses Python's 'pickle' module, it is subject to
287 the same limitations (see the pickle module's documentation) -
288 with the notable exception of class attributes. Python does not
289 pickle class attributes, but this function stores class attributes
290 of any Parameterized class that is declared within the topo
291 package. See the param.parameterized.PicklableClassAttributes
292 class for more information.
293 """
294 if not snapshot_name:
295 snapshot_name = topo.sim.basename() + ".typ"
296
297
298
299
300
301
302 topoPOclassattrs = PicklableClassAttributes(topo,exclusions=('plotting','tests','tkgui'),
303 startup_commands=topo.sim.startup_commands)
304
305 from topo.misc.commandline import global_params
306
307 topo.sim.RELEASE=topo.release
308 topo.sim.VERSION=topo.version
309
310
311
312
313
314
315
316
317
318
319
320 to_save = (_VersionPrinter(topo.release,topo.version),PickleMain(),global_params,topoPOclassattrs,topo.sim)
321
322 if not xml:
323 try:
324 snapshot_file=gzip.open(normalize_path(snapshot_name),'wb',compresslevel=5)
325 except NameError:
326 snapshot_file=open(normalize_path(snapshot_name),'wb')
327
328 pickle.dump(to_save,snapshot_file,2)
329 else:
330 snapshot_file=open(normalize_path(snapshot_name),'w')
331
332 try:
333 import gnosis.xml.pickle
334 gnosis.xml.pickle.dump(to_save,snapshot_file,2,allow_rawpickles=True)
335 except ImportError:
336 param.Parameterized().warning("Unable to import 'gnosis' module: no snapshot saved (xml snapshots unavailable).")
337
338
339 snapshot_file.close()
340
344
345 try:
346 import gnosis.xml.pickle
347 gnosis.xml.pickle.load(snapshot,allow_rawpickles=True,class_search=gnosis.xml.pickle.SEARCH_ALL)
348 except ExpatError:
349 snapshot.seek(0)
350 pickle.load(snapshot)
351 except ImportError:
352 pickle.load(snapshot)
353
411
416 """
417 Save the current simulation as a Topographica script.
418
419 Generates a script that, if run, would generate a simulation with
420 the same architecture as the one currently in memory. This can be
421 useful when defining networks in place, so that the same general
422 configuration can be recreated later. It also helps when
423 comparing two similar networks generated with different scripts,
424 so that the corresponding items can be matched rigorously.
425
426 Note that the result of this operation is usually just a starting
427 point for further editing, because it will not usually be runnable
428 as-is (for instance, some parameters may not have runnable
429 representations). Even so, this is usually a good start.
430 """
431 if not script_name:
432 script_name = topo.sim.basename() + "_script_repr.ty"
433
434 header = ("# Generated by Topographica %s on %s\n\n" %
435 (topo.release,time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())))
436 script = header+topo.sim.script_repr()
437
438 script_file = open(normalize_path(script_name),'w')
439 script_file.write(script)
440
441
442
443
444
445 try:
446 vc_topographica_dir = os.path.split(os.path.split(topo.__file__)[0])[0]
447 except:
448 vc_topographica_dir = None
454 import os
455 def temporarily_change_to_vc_topographica_dir(*args,**kw):
456 orig_path = os.getcwd()
457 if vc_topographica_dir is not None:
458 os.chdir(vc_topographica_dir)
459 try:
460 result = fn(*args,**kw)
461 finally:
462
463 if os.getcwd()!=orig_path:
464 os.chdir(orig_path)
465 return result
466 return temporarily_change_to_vc_topographica_dir
467
471
472
473 import os.path
474 vc_types = {'git':["status","diff",["log","-n1"],["svn","log","--limit=1"]],
475 'svn':["info","status","diff"],
476 'bzr':['info','status','diff']}
477 for vc_type,commands in vc_types.items():
478 if os.path.exists(".%s"%vc_type):
479 return vc_type,commands
480
483 """Save the version control status of the current code to the specified file."""
484 try:
485 import subprocess
486 file = open(normalize_path(filename),'w')
487 file.write("Information about working copy used for batch run\n\n")
488 file.write("topo.version=%s\n"% topo.version)
489 file.flush()
490 vctype,commands = _get_vc_commands()
491 for cmd in commands:
492 fullcmd = [vctype,cmd] if isinstance(cmd,str) else [vctype]+cmd
493
494
495
496
497
498
499
500
501
502 subprocess.Popen(fullcmd,stdout=file,stderr=subprocess.STDOUT)
503 except:
504 print "Unable to retrieve version control information."
505 finally:
506 file.close()
507
522
558
559
560
561
562
563 default_analysis_plotgroups=["Orientation Preference","Activity"]
591
592
593
594 -class run_batch(ParameterizedFunction):
595 """
596 Run a Topographica simulation in batch mode.
597
598 Features:
599
600 - Generates a unique, well-defined name for each 'experiment'
601 (i.e. simulation run) based on the date, script file, and
602 parameter settings. Note that very long names may be truncated
603 (see the max_name_length parameter).
604
605 - Allows parameters to be varied on the command-line,
606 to allow comparing various settings
607
608 - Saves a script capturing the simulation state periodically,
609 to preserve parameter values from old experiments and to allow
610 them to be reproduced exactly later
611
612 - Can perform user-specified analysis routines periodically,
613 to monitor the simulation as it progresses.
614
615 - Stores commandline output (stdout) in the output directory
616
617 A typical use of this function is for remote execution of a large
618 number of simulations with different parameters, often on remote
619 machines (such as clusters).
620
621 The script_file parameter defines the .ty script we want to run in
622 batch mode. The output_directory defines the root directory in
623 which a unique individual directory will be created for this
624 particular run. The optional analysis_fn can be any python
625 function to be called at each of the simulation iterations defined
626 in the analysis times list. The analysis_fn should perform
627 whatever analysis of the simulation you want to perform, such as
628 plotting or calculating some statistics. The analysis_fn should
629 avoid using any GUI functions (i.e., should not import anything
630 from topo.tkgui), and it should save all of its results into
631 files.
632
633 As a special case, a number can be passed for the times list, in
634 which case it is used to scale a default list of times up to
635 10000; e.g. times=2 will select a default list of times up to
636 20000. Alternatively, an explicit list of times can be supplied.
637
638 Any other optional parameters supplied will be set in the main
639 namespace before any scripts are run. They will also be used to
640 construct a unique topo.sim.name for the file, and they will be
641 encoded into the simulation directory name, to make it clear how
642 each simulation differs from the others.
643
644 If requested by setting snapshot=True, saves a snapshot at the
645 end of the simulation.
646
647 If available and requested by setting vc_info=True, prints
648 the revision number and any outstanding diffs from the version
649 control system.
650
651 Note that this function alters param.normalize_path.prefix so that
652 all output goes into the same location. The original value of
653 param.normalize_path.prefix is deliberately not restored at the
654 end of the function so that the output of any subsequent commands
655 will go into the same place.
656 """
657 output_directory=param.String("Output")
658
659 analysis_fn = param.Callable(default_analysis_function)
660
661 times = param.Parameter(1.0)
662
663 snapshot=param.Boolean(True)
664
665 vc_info=param.Boolean(True)
666
667 dirname_prefix = param.String(default="",doc="""
668 Optional prefix for the directory name (allowing e.g. easy
669 grouping).""")
670
671
672 max_name_length = param.Number(default=200,doc="""
673 The experiment's directory name will be truncated at this
674 number of characters (since most filesystems have a
675 limit).""")
676
677 name_time_format = param.String(default="%Y%m%d%H%M",doc="""
678 String format for the time included in the output directory
679 and file names. See the Python time module library
680 documentation for codes.
681
682 E.g. Adding '%S' to the default would include seconds.""")
683
684 save_global_params = param.Boolean(default=True,doc="""
685 Whether to save the script's global_parameters to a pickle in
686 the output_directory after the script has been loaded (for
687 e.g. future inspection of the experiment).""")
688
689 dirname_params_filter = param.Callable(param_formatter.instance(),doc="""
690 Function to control how the parameter names will appear in the
691 output_directory's name.""")
692
693
695 """
696 If s is greater than the max_name_length parameter, truncate it
697 (and indicate that it has been truncated).
698 """
699
700 return s if len(s)<=p.max_name_length else s[0:p.max_name_length-3]+'___'
701
702 - def __call__(self,script_file,**params_to_override):
703 p=ParamOverrides(self,params_to_override,allow_extra_keywords=True)
704
705 import os
706 import shutil
707
708
709 scriptbase= re.sub('.ty$','',os.path.basename(script_file))
710 prefix = ""
711 prefix += time.strftime(p.name_time_format)
712 prefix += "_" + scriptbase
713 simname = prefix
714
715
716
717
718
719
720
721 prefix += p.dirname_params_filter(p.extra_keywords())
722
723
724 from topo.misc.commandline import global_params
725 global_params.set_in_context(**p.extra_keywords())
726
727
728 if not os.path.isdir(normalize_path(p['output_directory'])):
729 os.mkdir(normalize_path(p['output_directory']))
730
731
732 dirname = self._truncate(p,p.dirname_prefix+prefix)
733 normalize_path.prefix = normalize_path(os.path.join(p['output_directory'],dirname))
734
735 if os.path.isdir(normalize_path.prefix):
736 print "Batch run: Warning -- directory already exists!"
737 print "Run aborted; wait one minute before trying again, or else rename existing directory: \n" + \
738 normalize_path.prefix
739
740 sys.exit(-1)
741 else:
742 os.mkdir(normalize_path.prefix)
743 print "Batch run output will be in " + normalize_path.prefix
744
745
746 if p['vc_info']:
747 _print_vc_info(simname+".diffs")
748
749 hostinfo = "Host: " + " ".join(platform.uname())
750 topographicalocation = "Topographica: " + os.path.abspath(sys.argv[0])
751 topolocation = "topo package: " + os.path.abspath(topo.__file__)
752 scriptlocation = "script: " + os.path.abspath(script_file)
753
754 starttime=time.time()
755 startnote = "Batch run started at %s." % time.strftime("%a %d %b %Y %H:%M:%S +0000",
756 time.gmtime())
757 command_used_to_start = string.join(sys.argv)
758
759
760
761 batch_output = open(normalize_path(simname+".out"),'w')
762 batch_output.write(command_used_to_start+"\n")
763 sys.stdout = MultiFile(batch_output,sys.stdout)
764
765 print
766 print hostinfo
767 print topographicalocation
768 print topolocation
769 print scriptlocation
770 print
771 print startnote
772
773 from topo.misc.commandline import auto_import_commands
774 auto_import_commands()
775
776
777 from topo.command.basic import save_script_repr
778 param.parameterized.script_repr_suppress_defaults=False
779
780
781 shutil.copy2(script_file, normalize_path.prefix)
782 shutil.move(normalize_path(scriptbase+".ty"),
783 normalize_path(simname+".ty"))
784
785
786
787 times=p['times']
788 if not isinstance(times,list):
789 times=[t*times for t in [0,50,100,500,1000,2000,3000,4000,5000,10000]]
790
791
792 error_count = 0
793 initial_warning_count = param.parameterized.warning_count
794 try:
795 execfile(script_file,__main__.__dict__)
796 global_params.check_for_unused_names()
797 if p.save_global_params:
798 _save_parameters(p.extra_keywords(),simname+".global_params.pickle")
799 print_sizes()
800 topo.sim.name=simname
801
802
803 for run_to in times:
804 topo.sim.run(run_to - topo.sim.time())
805 p['analysis_fn']()
806 save_script_repr()
807 elapsedtime=time.time()-starttime
808 param.Parameterized(name="run_batch").message(
809 "Elapsed real time %02d:%02d." % (int(elapsedtime/60),int(elapsedtime%60)))
810
811 if p['snapshot']:
812 save_snapshot()
813
814 except:
815 error_count+=1
816 import traceback
817 traceback.print_exc(file=sys.stdout)
818 sys.stderr.write("Warning -- Error detected: execution halted.\n")
819
820
821 print "\nBatch run completed at %s." % time.strftime("%a %d %b %Y %H:%M:%S +0000",
822 time.gmtime())
823 print "There were %d error(s) and %d warning(s)%s." % \
824 (error_count,(param.parameterized.warning_count-initial_warning_count),
825 ((" (plus %d warning(s) prior to entering run_batch)"%initial_warning_count
826 if initial_warning_count>0 else "")))
827
828
829 sys.stdout = sys.__stdout__
830 batch_output.close()
831
835 """
836 Resets activity of all Sheets and their connections to zero.
837 """
838
839
840
841
842 for s in topo.sim.objects(Sheet).values():
843 s.activity*=0.0
844 for c in s.in_connections:
845 if hasattr(c,'activity'):
846 c.activity*=0.0
847
851 """
852 Estimate the minimum memory needed for the Sheets in this Simulation, in bytes.
853
854 This estimate is a lower bound only, based primarily on memory for
855 the matrices used for activity and connections.
856 """
857 return sum([s.n_bytes() for s in topo.sim.objects(Sheet).values()])
858
866
869 """Format the results from n_conns() and n_bytes() for use in batch output."""
870 print "Defined %d-connection network; %0.0fMB required for weight storage." % \
871 (n_conns(),max(n_bytes()/1024.0/1024.0,1.0))
872
873
874 PatternDrivenAnalysis.pre_presentation_hooks.append(wipe_out_activity)
875 PatternDrivenAnalysis.pre_presentation_hooks.append(clear_event_queue)
876
877
878
879 import types
880 __all__ = list(set([k for k,v in locals().items()
881 if isinstance(v,types.FunctionType) or
882 (isinstance(v,type) and issubclass(v,ParameterizedFunction))
883 and not v.__name__.startswith('_')]))
884