| 67 | | def print_logo(filename, position, y, f_height, maxwidth=None): |
|---|
| 68 | | global poster, pagewidth |
|---|
| 69 | | if maxwidth is None: |
|---|
| 70 | | maxwidth=pagewidth/10.0 |
|---|
| 71 | | logo = PIL.Image.open(filename) |
|---|
| 72 | | logo_height = 0.9*f_height |
|---|
| 73 | | hw_ratio = float(logo.size[1])/logo.size[0] |
|---|
| 74 | | logo_width = min(maxwidth, logo_height/hw_ratio) |
|---|
| 75 | | logo_height = logo_width*hw_ratio |
|---|
| 76 | | y = y + (f_height-logo_height)/2.0 |
|---|
| 77 | | if position == 'left': |
|---|
| 78 | | x = 2*margins['left'] |
|---|
| 79 | | else: |
|---|
| 80 | | x = pageright-logo_width-margins['right'] |
|---|
| 81 | | poster.drawInlineImage(logo, x, y, height=logo_height, width=logo_width) |
|---|
| 82 | | |
|---|
| 83 | | def make_title(styles,title,authors,institutions,logo_left,logo_right): |
|---|
| 84 | | """Returns the y position of the bottom of the title (including bottom margin).""" |
|---|
| 85 | | global poster, margins, pageheight, pagewidth, pagetop |
|---|
| 86 | | title_frame = Frame(margins['left'], pageheight/2.0, pagewidth, 20*cm, |
|---|
| 87 | | showBoundary=True) |
|---|
| 88 | | author_str = ", ".join([str(a) for a in authors]) |
|---|
| 89 | | inst_str = ", ".join(["<super>%d</super>%s" % (i+1,inst) for i, inst in enumerate(institutions)]) |
|---|
| 90 | | |
|---|
| 91 | | title_paragraph = Paragraph(title, styles['Title']) |
|---|
| 92 | | p_width = 10*cm |
|---|
| 93 | | title_paragraph.wrap(p_width, 1e12) |
|---|
| 94 | | while len(title_paragraph.breakLines([p_width,1e12]).lines) > 1: |
|---|
| 95 | | p_width *= 1.1 |
|---|
| 96 | | |
|---|
| 97 | | title_components = [title_paragraph, |
|---|
| 98 | | Paragraph(author_str, styles['Authors']), |
|---|
| 99 | | Paragraph(inst_str, styles['Affiliations']) |
|---|
| 100 | | ] |
|---|
| 101 | | f_height = 0 |
|---|
| 102 | | for p in title_components: |
|---|
| 103 | | f_height += p.wrap(p_width,pageheight)[1] + p.getSpaceAfter() + p.getSpaceBefore() |
|---|
| 104 | | |
|---|
| 105 | | x = margins['left'] + pagewidth/2.0 - p_width/2.0 |
|---|
| 106 | | y = pagetop - f_height |
|---|
| 107 | | |
|---|
| 108 | | poster.roundRect(margins['left'],y,pagewidth,f_height,2*cm, fill=1) |
|---|
| 109 | | title_frame = Frame(x, y, p_width, f_height, showBoundary=False) |
|---|
| 110 | | title_frame.addFromList(title_components, poster) |
|---|
| 111 | | |
|---|
| 112 | | print_logo(logo_left, "left", y, f_height) |
|---|
| 113 | | print_logo(logo_right, "right", y, f_height) |
|---|
| 114 | | |
|---|
| 115 | | |
|---|
| 116 | | |
|---|
| 117 | | title_bottom = y - titlesep |
|---|
| 118 | | return title_bottom |
|---|
| 119 | | |
|---|
| 120 | | def make_text_frame(styles,text,x,y,width,height,_debug): |
|---|
| | 67 | |
|---|
| | 68 | |
|---|
| | 69 | |
|---|
| | 70 | |
|---|
| | 71 | def make_text_frame(styles, text, x, y, width, height, _debug): |
|---|
| 280 | | def make_example(styles,x_r,y,width,height,_debug=False): |
|---|
| 281 | | """ Make a frame showing the VAbenchmarks.py script, together with figure.""" |
|---|
| 282 | | global poster |
|---|
| 283 | | styles['Title'].alignment = TA_LEFT |
|---|
| 284 | | styles['Code'].leftIndent = 0 |
|---|
| 285 | | checkout_pyNN() |
|---|
| 286 | | f = open(os.path.join('pyNN_%s' % VERSION,'test','VAbenchmarks.py'),'r') |
|---|
| 287 | | example_script = f.read() |
|---|
| 288 | | f.close() |
|---|
| 289 | | lines = example_script.split('\n') |
|---|
| 290 | | nlines = len(lines) |
|---|
| 291 | | maxlength = 0 |
|---|
| 292 | | for line in lines: |
|---|
| 293 | | if len(line) > maxlength: |
|---|
| 294 | | maxlength = len(line) |
|---|
| 295 | | example_script = colourize(example_script) |
|---|
| 296 | | |
|---|
| 297 | | scale_style(styles['Code'], 10.0/styles['Code'].fontSize) |
|---|
| 298 | | |
|---|
| 299 | | pad = 0.5*cm |
|---|
| 300 | | code_flowable = XPreformatted(example_script, styles['Code']) |
|---|
| 301 | | |
|---|
| 302 | | paragraph_list = [Paragraph("Example", styles['Title']), |
|---|
| 303 | | code_flowable] |
|---|
| 304 | | |
|---|
| 305 | | code_height = styles['Code'].leading+styles['Code'].spaceAfter+styles['Code'].spaceBefore |
|---|
| 306 | | f_height = nlines * code_height |
|---|
| 307 | | f_height += styles['Title'].leading + styles['Title'].spaceBefore + styles['Title'].spaceAfter |
|---|
| 308 | | f_height /= 2.0 |
|---|
| 309 | | |
|---|
| 310 | | lines_in_first_frame = int(nlines - f_height/code_height) |
|---|
| 311 | | maxlength = 0 |
|---|
| 312 | | for line in lines[:lines_in_first_frame]: |
|---|
| 313 | | if len(line) > maxlength: |
|---|
| 314 | | maxlength = len(line) |
|---|
| 315 | | f_width1 = stringWidth("m"*maxlength, styles['Code'].fontName, styles['Code'].fontSize, 'UTF-8') + pad |
|---|
| 316 | | maxlength = 0 |
|---|
| 317 | | for line in lines[lines_in_first_frame:]: |
|---|
| 318 | | if len(line) > maxlength: |
|---|
| 319 | | maxlength = len(line) |
|---|
| 320 | | f_width2 = stringWidth("m"*maxlength, styles['Code'].fontName, styles['Code'].fontSize, 'UTF-8') + pad |
|---|
| 321 | | f_height += 2*pad |
|---|
| 322 | | |
|---|
| 323 | | x = x_r - f_width1 - f_width2 |
|---|
| 324 | | |
|---|
| 325 | | poster.roundRect(x,y-f_height,f_width1+f_width2,f_height,1*cm,fill=1) |
|---|
| 326 | | frame1 = Frame(x, y-f_height, f_width1, f_height, showBoundary=_debug, |
|---|
| 327 | | leftPadding=pad, rightPadding=0, bottomPadding=pad, |
|---|
| 328 | | topPadding=pad) |
|---|
| 329 | | frame1.add(Paragraph("Example", styles['Title']), poster) |
|---|
| 330 | | flowables = frame1.split(code_flowable, poster) |
|---|
| 331 | | |
|---|
| 332 | | frame1.addFromList(flowables, poster) |
|---|
| 333 | | frame2 = Frame(x + f_width1, y-f_height, f_width2, f_height, showBoundary=_debug, |
|---|
| 334 | | leftPadding=pad, rightPadding=pad, bottomPadding=pad, |
|---|
| 335 | | topPadding=pad) |
|---|
| 336 | | frame2.addFromList(flowables, poster) |
|---|
| 337 | | #frame3 = Frame(x + 2*f_width, y-f_height, f_width, f_height, showBoundary=_debug, |
|---|
| 338 | | # leftPadding=0, rightPadding=pad, bottomPadding=pad, |
|---|
| 339 | | # topPadding=pad) |
|---|
| 340 | | #frame3.addFromList(flowables, poster) |
|---|
| 341 | | |
|---|
| 342 | | |
|---|
| 343 | | return x, y-f_height, f_width1+f_width2,f_height |
|---|
| 344 | | |
|---|
| 345 | | def make_example_figure(filename,x,y,width,height,_debug=False): |
|---|
| 346 | | global poster |
|---|
| 347 | | pad = 0.5*cm |
|---|
| 348 | | img = Image(filename, width=width-2*pad, height=height-2*pad, kind='proportional', lazy=0) |
|---|
| 349 | | f_height = img.drawHeight + 2*pad |
|---|
| 350 | | figure_frame = Frame(x, y-f_height, |
|---|
| 351 | | width, f_height, |
|---|
| 352 | | showBoundary=_debug, |
|---|
| 353 | | leftPadding=pad, rightPadding=pad, |
|---|
| 354 | | bottomPadding=pad, topPadding=pad) |
|---|
| 355 | | #poster.roundRect(x,y-f_height,width,f_height,1*cm) |
|---|
| 356 | | figure_frame.add(img, poster) |
|---|
| 357 | | |
|---|
| 419 | | def paint_background(background, photo_credit=None): |
|---|
| 420 | | global poster |
|---|
| 421 | | try: # first assume it is a colour |
|---|
| 422 | | poster.setFillColor(getattr(colors,background)) |
|---|
| 423 | | poster.rect(0,0,poster._pagesize[0],poster._pagesize[1],fill=1,stroke=0) |
|---|
| 424 | | poster.setFillColor(colors.white) |
|---|
| 425 | | except AttributeError: |
|---|
| 426 | | # now assume it is an image |
|---|
| 427 | | img = Image(background, width=poster._pagesize[0], height=poster._pagesize[1]) |
|---|
| 428 | | img.drawOn(poster,0,0) |
|---|
| 429 | | if photo_credit: |
|---|
| 430 | | poster.saveState() |
|---|
| 431 | | poster.translate(poster._pagesize[0],0) |
|---|
| 432 | | poster.rotate(90.0) |
|---|
| 433 | | poster.setFont("Helvetica-Bold", 24) |
|---|
| 434 | | poster.drawString(10,10,photo_credit) |
|---|
| 435 | | poster.restoreState() |
|---|
| 436 | | |
|---|
| | 269 | |
|---|
| | 270 | |
|---|
| | 271 | class Column(object): |
|---|
| | 272 | |
|---|
| | 273 | def __init__(self, left, width): |
|---|
| | 274 | self.left = left |
|---|
| | 275 | self.width = width |
|---|
| | 276 | self.bottom = 0 |
|---|
| | 277 | |
|---|
| | 278 | class Poster(Canvas): |
|---|
| | 279 | |
|---|
| | 280 | def __init__(self, filename, ncol, |
|---|
| | 281 | pagesize=landscape(A0), |
|---|
| | 282 | margins={'left':2*cm, 'right':2*cm, 'top':2*cm, 'bottom':2*cm}, |
|---|
| | 283 | colsep=2*cm): |
|---|
| | 284 | Canvas.__init__(self, filename, pagesize=pagesize, pageCompression=False, verbosity=2) |
|---|
| | 285 | self.margins = margins |
|---|
| | 286 | self.pagewidth = self._pagesize[0] - margins['left'] - margins['right'] |
|---|
| | 287 | self.pageheight = self._pagesize[1] - margins['bottom'] - margins['top'] |
|---|
| | 288 | self.pagetop = self._pagesize[1] - margins['top'] |
|---|
| | 289 | self.pageright = self._pagesize[0] - margins['right'] |
|---|
| | 290 | self.pagebottom = self.pagetop + self.pageheight |
|---|
| | 291 | self.setFillColor(colors.white) |
|---|
| | 292 | self.setStrokeColor(colors.white) |
|---|
| | 293 | self.ncol = ncol |
|---|
| | 294 | self.colsep = colsep |
|---|
| | 295 | self.colwidth = (self.pagewidth - (ncol-1)*self.colsep)/float(ncol) |
|---|
| | 296 | self.columns = [] |
|---|
| | 297 | for i in range(ncol): |
|---|
| | 298 | self.columns.append(Column(self.margins['left']+i*(self.colwidth+self.colsep), self.colwidth)) |
|---|
| | 299 | self.debug = False |
|---|
| | 300 | |
|---|
| | 301 | def set_metadata(self, authors, institutions, title, subject=''): |
|---|
| | 302 | self.setAuthor(", ".join([a.name for a in authors])) |
|---|
| | 303 | self.setTitle(title) |
|---|
| | 304 | self.setSubject(subject) |
|---|
| | 305 | |
|---|
| | 306 | def paint_background(self, background, credit=None): |
|---|
| | 307 | try: # first assume it is a colour |
|---|
| | 308 | self.setFillColor(getattr(colors, background)) |
|---|
| | 309 | self.rect(0, 0, self._pagesize[0], self._pagesize[1], fill=1, stroke=0) |
|---|
| | 310 | self.setFillColor(colors.white) |
|---|
| | 311 | except AttributeError: |
|---|
| | 312 | # now assume it is an image |
|---|
| | 313 | img = Image(background, width=self._pagesize[0], height=self._pagesize[1]) |
|---|
| | 314 | img.drawOn(self, 0, 0) |
|---|
| | 315 | if credit: |
|---|
| | 316 | self.saveState() |
|---|
| | 317 | self.translate(self._pagesize[0],0) |
|---|
| | 318 | self.rotate(90.0) |
|---|
| | 319 | self.setFont("Helvetica-Bold", 24) |
|---|
| | 320 | self.drawString(10, 10, credit) |
|---|
| | 321 | self.restoreState() |
|---|
| | 322 | |
|---|
| | 323 | def make_title(self, styles, title, authors, institutions, logo_left, logo_right): |
|---|
| | 324 | """Returns the y position of the bottom of the title (including bottom margin).""" |
|---|
| | 325 | title_frame = Frame(self.margins['left'], self.pageheight/2.0, self.pagewidth, 20*cm, |
|---|
| | 326 | showBoundary=True) |
|---|
| | 327 | author_str = ", ".join([str(a).replace(' ',' ') for a in authors]) |
|---|
| | 328 | inst_str = ", ".join(["<super>%d</super>%s" % (i+1,inst.replace(' ',' ')) for i, inst in enumerate(institutions)]) |
|---|
| | 329 | |
|---|
| | 330 | title_paragraph = Paragraph(title, styles['Title']) |
|---|
| | 331 | p_width = 10*cm |
|---|
| | 332 | title_paragraph.wrap(p_width, 1e12) |
|---|
| | 333 | while len(title_paragraph.breakLines([p_width,1e12]).lines) > 1: |
|---|
| | 334 | p_width *= 1.1 |
|---|
| | 335 | |
|---|
| | 336 | title_components = [title_paragraph, |
|---|
| | 337 | Paragraph(author_str, styles['Authors']), |
|---|
| | 338 | Paragraph(inst_str, styles['Affiliations']) |
|---|
| | 339 | ] |
|---|
| | 340 | f_height = 0 |
|---|
| | 341 | for p in title_components: |
|---|
| | 342 | f_height += p.wrap(p_width, self.pageheight)[1] + p.getSpaceAfter() + p.getSpaceBefore() |
|---|
| | 343 | |
|---|
| | 344 | x = self.margins['left'] + self.pagewidth/2.0 - p_width/2.0 |
|---|
| | 345 | y = self.pagetop - f_height |
|---|
| | 346 | |
|---|
| | 347 | self.roundRect(self.margins['left'], y, self.pagewidth, f_height, 2*cm, fill=1) |
|---|
| | 348 | title_frame = Frame(x, y, p_width, f_height, showBoundary=False) |
|---|
| | 349 | title_frame.addFromList(title_components, self) |
|---|
| | 350 | |
|---|
| | 351 | self.print_logo(logo_left, "left", y, f_height) |
|---|
| | 352 | self.print_logo(logo_right, "right", y, f_height) |
|---|
| | 353 | |
|---|
| | 354 | self.title_bottom = y - titlesep |
|---|
| | 355 | self.colheight = self.title_bottom - self.margins['bottom'] |
|---|
| | 356 | for column in self.columns: |
|---|
| | 357 | column.bottom = self.title_bottom |
|---|
| | 358 | |
|---|
| | 359 | return self.title_bottom |
|---|
| | 360 | |
|---|
| | 361 | def print_logo(self, filename, position, y, f_height, maxwidth=None): |
|---|
| | 362 | if maxwidth is None: |
|---|
| | 363 | maxwidth=self.pagewidth/10.0 |
|---|
| | 364 | logo = PIL.Image.open(filename) |
|---|
| | 365 | logo_height = 0.9*f_height |
|---|
| | 366 | hw_ratio = float(logo.size[1])/logo.size[0] |
|---|
| | 367 | logo_width = min(maxwidth, logo_height/hw_ratio) |
|---|
| | 368 | logo_height = logo_width*hw_ratio |
|---|
| | 369 | y = y + (f_height-logo_height)/2.0 |
|---|
| | 370 | if position == 'left': |
|---|
| | 371 | x = 2*self.margins['left'] |
|---|
| | 372 | else: |
|---|
| | 373 | x = self.pageright-logo_width-self.margins['right'] |
|---|
| | 374 | poster.drawInlineImage(logo, x, y, height=logo_height, width=logo_width) |
|---|
| | 375 | |
|---|
| | 376 | def add_frame(self, column_number, styles, content, height, _debug=False): |
|---|
| | 377 | pad = 0.5*cm |
|---|
| | 378 | column = self.columns[column_number] |
|---|
| | 379 | x = column.left |
|---|
| | 380 | y = column.bottom |
|---|
| | 381 | width = column.width |
|---|
| | 382 | frame = Frame(x, y - height, width, height, |
|---|
| | 383 | leftPadding=pad, rightPadding=pad, bottomPadding=pad, |
|---|
| | 384 | topPadding=pad, showBoundary=_debug) |
|---|
| | 385 | |
|---|
| | 386 | definition_list = content |
|---|
| | 387 | def calc_height(): |
|---|
| | 388 | paragraph_list = [Paragraph('<font color="darkgreen"><b>%s</b></font> %s' % item, styles['BodyText']) for item in definition_list] |
|---|
| | 389 | f_height = 2*pad |
|---|
| | 390 | for p in paragraph_list: |
|---|
| | 391 | f_height += p.wrap(width-2*pad, self.pageheight)[1] + p.getSpaceAfter() + p.getSpaceBefore() |
|---|
| | 392 | return f_height |
|---|
| | 393 | |
|---|
| | 394 | while calc_height() > height: |
|---|
| | 395 | scale_style(styles['BodyText'], 0.99) |
|---|
| | 396 | styles['BodyText'].alignment = TA_JUSTIFY # otherwise we get a 'bad align' error |
|---|
| | 397 | |
|---|
| | 398 | paragraph_list = [Paragraph('<font color="darkgreen"><b>%s</b></font> %s' % item, styles['BodyText']) for item in definition_list] |
|---|
| | 399 | |
|---|
| | 400 | self.roundRect(x,y-height,width,height,1*cm,fill=1) |
|---|
| | 401 | frame.addFromList(paragraph_list, self) |
|---|
| | 402 | column.bottom -= height |
|---|
| | 403 | |
|---|
| | 404 | def make_footer(self, text, fontsize=24, color=colors.white): |
|---|
| | 405 | self.setFillColor(color) # white works better with a background image |
|---|
| | 406 | self.setFont("Helvetica", fontsize) # bold works better with a background image |
|---|
| | 407 | self.drawCentredString(self.margins['left']+self.pagewidth/2.0, |
|---|
| | 408 | 0.2*self.margins['bottom'], text) |
|---|
| | 409 | |
|---|
| | 410 | def make_example(self, column_number, styles, height, _debug=False): #,x_r,y,width,height,_debug=False): |
|---|
| | 411 | """ Make a frame showing the VAbenchmarks.py script, together with figure.""" |
|---|
| | 412 | pad = 0.5*cm |
|---|
| | 413 | column = self.columns[column_number] |
|---|
| | 414 | x_r = column.left + self.colwidth |
|---|
| | 415 | y = column.bottom |
|---|
| | 416 | width = self.colwidth |
|---|
| | 417 | |
|---|
| | 418 | styles['Title'].alignment = TA_LEFT |
|---|
| | 419 | styles['Code'].leftIndent = 0 |
|---|
| | 420 | checkout_pyNN() |
|---|
| | 421 | f = open(os.path.join('pyNN_%s' % VERSION,'test','VAbenchmarks.py'),'r') |
|---|
| | 422 | example_script = f.read() |
|---|
| | 423 | f.close() |
|---|
| | 424 | lines = example_script.split('\n') |
|---|
| | 425 | nlines = len(lines) |
|---|
| | 426 | maxlength = 0 |
|---|
| | 427 | for line in lines: |
|---|
| | 428 | if len(line) > maxlength: |
|---|
| | 429 | maxlength = len(line) |
|---|
| | 430 | example_script = colourize(example_script) |
|---|
| | 431 | |
|---|
| | 432 | scale_style(styles['Code'], 10.0/styles['Code'].fontSize) |
|---|
| | 433 | |
|---|
| | 434 | code_flowable = XPreformatted(example_script, styles['Code']) |
|---|
| | 435 | |
|---|
| | 436 | paragraph_list = [Paragraph("Example", styles['Title']), |
|---|
| | 437 | code_flowable] |
|---|
| | 438 | |
|---|
| | 439 | code_height = styles['Code'].leading+styles['Code'].spaceAfter+styles['Code'].spaceBefore |
|---|
| | 440 | f_height = nlines * code_height |
|---|
| | 441 | f_height += styles['Title'].leading + styles['Title'].spaceBefore + styles['Title'].spaceAfter |
|---|
| | 442 | f_height /= 2.0 |
|---|
| | 443 | |
|---|
| | 444 | lines_in_first_frame = int(nlines - f_height/code_height) |
|---|
| | 445 | maxlength = 0 |
|---|
| | 446 | for line in lines[:lines_in_first_frame]: |
|---|
| | 447 | if len(line) > maxlength: |
|---|
| | 448 | maxlength = len(line) |
|---|
| | 449 | f_width1 = stringWidth("m"*maxlength, styles['Code'].fontName, styles['Code'].fontSize, 'UTF-8') + pad |
|---|
| | 450 | maxlength = 0 |
|---|
| | 451 | for line in lines[lines_in_first_frame:]: |
|---|
| | 452 | if len(line) > maxlength: |
|---|
| | 453 | maxlength = len(line) |
|---|
| | 454 | f_width2 = stringWidth("m"*maxlength, styles['Code'].fontName, styles['Code'].fontSize, 'UTF-8') + pad |
|---|
| | 455 | f_height += 2*pad |
|---|
| | 456 | |
|---|
| | 457 | x = x_r - f_width1 - f_width2 |
|---|
| | 458 | |
|---|
| | 459 | self.roundRect(x,y-f_height,f_width1+f_width2,f_height,1*cm,fill=1) |
|---|
| | 460 | frame1 = Frame(x, y-f_height, f_width1, f_height, showBoundary=_debug, |
|---|
| | 461 | leftPadding=pad, rightPadding=0, bottomPadding=pad, |
|---|
| | 462 | topPadding=pad) |
|---|
| | 463 | frame1.add(Paragraph("Example", styles['Title']), self) |
|---|
| | 464 | flowables = frame1.split(code_flowable, self) |
|---|
| | 465 | |
|---|
| | 466 | frame1.addFromList(flowables, self) |
|---|
| | 467 | frame2 = Frame(x + f_width1, y-f_height, f_width2, f_height, showBoundary=_debug, |
|---|
| | 468 | leftPadding=pad, rightPadding=pad, bottomPadding=pad, |
|---|
| | 469 | topPadding=pad) |
|---|
| | 470 | frame2.addFromList(flowables, self) |
|---|
| | 471 | #frame3 = Frame(x + 2*f_width, y-f_height, f_width, f_height, showBoundary=_debug, |
|---|
| | 472 | # leftPadding=0, rightPadding=pad, bottomPadding=pad, |
|---|
| | 473 | # topPadding=pad) |
|---|
| | 474 | #frame3.addFromList(flowables, self) |
|---|
| | 475 | column.bottom -= f_height |
|---|
| | 476 | return x, y-f_height, f_width1+f_width2,f_height |
|---|
| | 477 | |
|---|
| | 478 | def add_figure_frame(self, column_number, filename, caption='', _debug=False): |
|---|
| | 479 | pad = 0.5*cm |
|---|
| | 480 | column = self.columns[column_number] |
|---|
| | 481 | x = column.left |
|---|
| | 482 | y = column.bottom |
|---|
| | 483 | width = self.colwidth |
|---|
| | 484 | img = PIL.Image.open(filename) |
|---|
| | 485 | height = img.size[1]*width/img.size[0] |
|---|
| | 486 | img = Image(filename, width=width-2*pad, height=height-2*pad, kind='proportional', lazy=0) |
|---|
| | 487 | f_height = img.drawHeight + 2*pad |
|---|
| | 488 | figure_frame = Frame(x, y-f_height, |
|---|
| | 489 | width, f_height, |
|---|
| | 490 | showBoundary=_debug, |
|---|
| | 491 | leftPadding=pad, rightPadding=pad, |
|---|
| | 492 | bottomPadding=pad, topPadding=pad) |
|---|
| | 493 | poster.roundRect(x, y-f_height, width, f_height, 1*cm, fill=1) |
|---|
| | 494 | column.bottom -= height |
|---|
| | 495 | figure_frame.add(img, self) |
|---|
| | 496 | |
|---|
| 440 | | VERSION = "0.4" |
|---|
| 441 | | FILENAME = "poster_incf2008.pdf" |
|---|
| 442 | | svnpath = "https://neuralensemble.kip.uni-heidelberg.de/svn/PyNN/branches/%s" % VERSION |
|---|
| 443 | | DEBUG = False |
|---|
| 444 | | |
|---|
| 445 | | TITLE = "PyNN: Towards a universal neural simulator API in Python" |
|---|
| 446 | | |
|---|
| 447 | | INSTITUTIONS = ["UNIC, CNRS, Gif-sur-Yvette, France", |
|---|
| 448 | | "INCM, CNRS, Marseille, France", |
|---|
| 449 | | "Neurobiology and Biophysics, Albert-Ludwigs-University Freiburg, Freiburg, Germany", |
|---|
| 450 | | "Kirchhoff Institute for Physics, University of Heidelberg, Heidelberg, Germany"] |
|---|
| 451 | | |
|---|
| 452 | | AUTHORS = [Author("Andrew Davison", 1), |
|---|
| 453 | | Author("Pierre Yger", 1), |
|---|
| 454 | | Author("Jens Kremkow", 2,3), |
|---|
| 455 | | Author("Laurent Perrinet", 2), |
|---|
| 456 | | Author("Eilif Muller", 4)] |
|---|
| 457 | | |
|---|
| 458 | | SUBJECT = "Poster for CNS*2007, Toronto, Canada" |
|---|
| 459 | | |
|---|
| 460 | | ABSTRACT = """Trends in programming language development and adoption point to Python as the high-level systems |
|---|
| 461 | | integration language of choice. Python leverages a vast developer-base external to the neuroscience |
|---|
| 462 | | community, and promises leaps in simulation complexity and maintainability to any neural simulator |
|---|
| 463 | | that adopts it. PyNN [<a href="http://neuralensemble.org/PyNN">http://neuralensemble.org/PyNN</a>] strives to provide a uniform application programming |
|---|
| 464 | | interface (API) across neural simulators. Presently NEURON, NEST and PCSIM are supported, and support for |
|---|
| 465 | | other simulators, NeuroML output and neuromorphic VLSI hardware is under development. |
|---|
| 466 | | |
|---|
| 467 | | With PyNN it is possible to write a simulation script once and run it without modification on any |
|---|
| 468 | | supported simulator. It is also possible to write a script that uses capabilities specific to a single |
|---|
| 469 | | simulator. While this sacrifices simulator-independence, is adds flexibility, and can be a useful step in |
|---|
| 470 | | porting models between simulators. The design goals of PyNN include allowing access to low-level |
|---|
| 471 | | details of a simulation where necessary, while providing the capability to model at a high level of |
|---|
| 472 | | abstraction, with concomitant gains in development speed and simulation maintainability. |
|---|
| 473 | | |
|---|
| 474 | | Another of our aims with PyNN is to increase the productivity of neuroscience modeling, by making it |
|---|
| 475 | | faster to develop models <i>de novo</i>, by promoting code sharing and reuse across simulator communities, |
|---|
| 476 | | and by making it much easier to debug, test and validate simulations by running them on more than one |
|---|
| 477 | | simulator. Modelers would then become free to devote more software development effort to |
|---|
| 478 | | innovation, building on the simulator core with new tools such as network topology databases, stimulus |
|---|
| 479 | | programming, analysis and visualization tools, and simulation accounting. The resulting, community- |
|---|
| 480 | | developed 'meta-simulator' system would then represent a powerful tool for overcoming the so-called |
|---|
| 481 | | <i>complexity bottleneck</i> that is presently a major roadblock for neural modeling. |
|---|
| 482 | | """ |
|---|
| 483 | | |
|---|
| 484 | | QA = [("What is PyNN?", "A Python package that defines and implements a uniform 'application programming interface' (API) across neural simulators. In other words, you can <b>write your simulation script <i>once</i>, then run it <i>without modification</i> on any supported simulator</b> (currently NEURON, NEST and PCSIM)."), |
|---|
| 485 | | ("I use simulator <i>X</i>. Why should I switch to PyNN?", "Increased productivity. The main aim of PyNN is to increase the productivity of neuronal network modeling, by making it <b>faster to develop models</b> <i>de novo</i>, by <b>promoting code sharing and reuse</b> across simulator communities, and by making it much <b>easier to debug, test and validate simulations by running them on more than one simulator</b>. Even if you're not interested in using multiple simulators, if you don't already use Python or another dynamic, object-oriented language, and if your simulator doesn't already support programming using abstractions above the level of individual neurons and synaptic connections, you should see major gains from using a more powerful language and from more readable, shorter, more easily-maintainable code."), |
|---|
| 486 | | ("Why Python?", 'Most simulators use configuration files or have their own specific scripting language. These are inevitably less powerful and flexible than a general-purpose programming language such as Python. Furthermore, Python has a huge standard library ("batteries included"), excellent numerical and graphical packages (making it an excellent Matlab replacement), a large developer-base outside the neuroscience community, and is free software.'), |
|---|
| 487 | | ("I have a model written for simulator <i>X</i>. How do I convert it to PyNN?", "If <i>X</i> already has a Python interface (NEURON, NEST, PCSIM), first convert the code to Python (e.g. in NEURON, replace your hoc code with Python code) then gradually replace simulator-specific code with PyNN code, all the time checking that the model still runs and gives the same results. If <i>X</i> does not have a Python interface, contact us! We would be happy to work with you on adding support for <i>X</i> to PyNN, whether this is through a direct Python interface or a code-generation tool."), |
|---|
| 488 | | ("Where can I download PyNN?", "http://neuralensemble.org/PyNN"), |
|---|
| 489 | | ("What other solutions are there for developing simulator-independent models?", "<b>NeuroML</b> (http://neuroml.org) is a standard for model specification in XML. <b>NeuroConstruct</b> (http://neuroconstruct.org) is a tool for developing network models, using a graphical interface, that can then be exported as either NEURON or GENESIS code. There is also some overlap with <b>Neurospaces</b> (http://www.neurospaces.org/), a framework for modular construction of computational neuroscience simulators."), |
|---|
| 490 | | ("What are your future plans for PyNN?", "We are currently working on: (i) support for NEST version 2, which adds support for distributed (parallel) simulations (distributed simulations in NEURON and PCSIM are already supported); (ii) support for the VLSI analog spiking network hardware being developed within the FACETS project (iii) support for import/export of network models specified in NeuroML; (iv) various improvements/extensions to the API, (v) general performance improvements."), |
|---|
| 491 | | ("Why shouldn't I use PyNN?", "(i) If your work does not involve network modelling; (ii) if you prefer to use a graphical interface to develop your simulations."), |
|---|
| 492 | | ("Who is using PyNN now?", "PyNN was first developed within the FACETS project, an EC-funded consortium of fifteen or so European research groups including seven groups doing computational neuroscience, using six simulators between them. In the absence of agreement that everyone should use the same simulator, a tool to make it easier to share models between groups was needed, and PyNN was born. Within FACETS, PyNN is being used for simulations of large-scale models of primary visual cortex and of generic cortical circuits with synaptic plasticity. We would like to encourage wider use, and anyone interested in using or developing PyNN should check out http://neuralensemble.org.") |
|---|
| 493 | | ] |
|---|
| 494 | | |
|---|
| 495 | | ACKNOWLEDGEMENTS = "This work is supported by the European Commission through the FACETS project (contract number FP6-2004-IST-FETPI-15879) and by the CNRS. PY is supported by a MENRT bursary." |
|---|
| 496 | | |
|---|
| 497 | | CAPTIONS = { |
|---|
| 498 | | 'VAbenchmark_CUBA_exc.png': """<b>Results of running the CUBA benchmark script</b> (see Box: 'Example'), written in PyNN, with three different simulators (from left to right: NEURON, NEST, PCSIM). |
|---|
| 499 | | |
|---|
| 500 | | The <b><i>top row</i></b> shows the <b><i>membrane potential traces</i></b> for two of the excitatory neurons in the network. |
|---|
| 501 | | The <b><i>second row</i></b> shows the <b><i>raster plots</i></b> for the first 320 excitatory neurons in the network (total network size 3200 excitatory and 800 inhibitory neurons). Each dot represents one spike. Each row is a different cell. |
|---|
| 502 | | The <b><i>third row</i></b> shows histograms of <b><i>interspike intervals</i></b> (ISIs) for the excitatory and inhibitory sub-populations. |
|---|
| 503 | | The <b><i>bottom row</i></b> shows histograms of the <b><i>coefficient of variation (CV) of the ISI</i></b>. |
|---|
| 504 | | |
|---|
| 505 | | For NEST and NEURON, the same sequence of random numbers was used to generate the network connectivity (using a PyNN <font face="Courier">NumpyRNG</font> object. For PCSIM, the random numbers were generated within the simulator (represented in PyNN with a <font face="Courier">NativeRNG</font> object). |
|---|
| 506 | | |
|---|
| 507 | | <b>Even when using exactly the same connectivity pattern, the membrane potential trajectories diverge<b> after about the first 100 ms, although the <b>pattern of mean activity</b> across the network is <b>well preserved</b> between simulators. |
|---|
| 508 | | |
|---|
| 509 | | With a different random connectivity (but with the same connection probability), there is no similarity in detailed activity patterns, but the <b>statistical behaviour of the network</b> in terms of spike train variability <b>is the same</b>.""" |
|---|
| 510 | | } |
|---|
| 511 | | |
|---|
| | 500 | execfile("incf2008.py") |
|---|