Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

r""" 

Parallel Interface to the Sage interpreter 

 

This is an expect interface to \emph{multiple} copy of the \sage 

interpreter, which can all run simultaneous calculations. A PSage 

object does not work as well as the usual Sage object, but does have 

the great property that when you construct an object in a PSage you 

get back a prompt immediately. All objects constructed for that 

PSage print <<currently executing code>> until code execution 

completes, when they print as normal. 

 

\note{BUG -- currently non-idle PSage subprocesses do not stop when 

\sage exits. I would very much like to fix this but don't know how.} 

 

EXAMPLES: 

 

We illustrate how to factor 3 integers in parallel. 

First start up 3 parallel Sage interfaces:: 

 

sage: v = [PSage() for _ in range(3)] 

 

Next, request factorization of one random integer in each copy. :: 

 

sage: w = [x('factor(2^%s-1)'% randint(250,310)) for x in v] # long time (5s on sage.math, 2011) 

 

Print the status:: 

 

sage: w # long time, random output (depends on timing) 

[3 * 11 * 31^2 * 311 * 11161 * 11471 * 73471 * 715827883 * 2147483647 * 4649919401 * 18158209813151 * 5947603221397891 * 29126056043168521, 

<<currently executing code>>, 

9623 * 68492481833 * 23579543011798993222850893929565870383844167873851502677311057483194673] 

 

Note that at the point when we printed two of the factorizations had 

finished but a third one hadn't. A few seconds later all three have 

finished:: 

 

sage: w # long time, random output 

[3 * 11 * 31^2 * 311 * 11161 * 11471 * 73471 * 715827883 * 2147483647 * 4649919401 * 18158209813151 * 5947603221397891 * 29126056043168521, 

23^2 * 47 * 89 * 178481 * 4103188409 * 199957736328435366769577 * 44667711762797798403039426178361, 

9623 * 68492481833 * 23579543011798993222850893929565870383844167873851502677311057483194673] 

""" 

from __future__ import print_function 

from __future__ import absolute_import 

 

import os 

import time 

 

from .sage0 import Sage, SageElement 

from pexpect import ExceptionPexpect 

 

number = 0 

 

 

class PSage(Sage): 

def __init__(self, **kwds): 

if 'server' in kwds: 

raise NotImplementedError("PSage doesn't work on remote server yet.") 

Sage.__init__(self, **kwds) 

import sage.misc.misc 

T = sage.misc.temporary_file.tmp_dir('sage_smp') 

self.__tmp_dir = T 

self.__tmp = '%s/lock'%T 

self._unlock() 

self._unlock_code = "open('%s','w').write('__unlocked__')"%self.__tmp 

 

global number 

self._number = number 

number += 1 

 

def _repr_(self): 

""" 

TESTS:: 

 

sage: from sage.interfaces.psage import PSage 

sage: PSage() # indirect doctest 

A running non-blocking (parallel) instance of Sage (number ...) 

 

""" 

return 'A running non-blocking (parallel) instance of Sage (number %s)'%(self._number) 

 

def _unlock(self): 

self._locked = False 

open(self.__tmp, 'w').write('__unlocked__') 

 

def _lock(self): 

self._locked = True 

open(self.__tmp, 'w').write('__locked__') 

 

def _start(self): 

Sage._start(self) 

self.expect().timeout = 0.25 

self.expect().delaybeforesend = 0.01 

 

def is_locked(self): 

if open(self.__tmp).read() == '__locked__': 

try: 

self.expect().expect(self._prompt) 

self.expect().expect(self._prompt) 

except ExceptionPexpect: 

pass 

return open(self.__tmp).read() == '__locked__' 

 

def __del__(self): 

print("deleting") 

for x in os.listdir(self.__tmp_dir): 

os.remove('%s/%s'%(self.__tmp_dir, x)) 

os.removedirs(self.__tmp_dir) 

if not (self._expect is None): 

cmd = 'kill -9 %s'%self._expect.pid 

print(cmd) 

os.system(cmd) 

Sage.__del__(self) 

 

def eval(self, x, strip=True, **kwds): 

""" 

x -- code 

strip --ignored 

""" 

if self.is_locked(): 

return "<<currently executing code>>" 

if self._locked: 

self._locked = False 

#self._expect.expect('__unlocked__') 

self.expect().send('\n') 

self.expect().expect(self._prompt) 

self.expect().expect(self._prompt) 

try: 

return Sage.eval(self, x, **kwds) 

except ExceptionPexpect: 

return "<<currently executing code>>" 

 

 

def get(self, var): 

""" 

Get the value of the variable var. 

""" 

try: 

return self.eval('print(%s)' % var) 

except ExceptionPexpect: 

return "<<currently executing code>>" 

 

def set(self, var, value): 

""" 

Set the variable var to the given value. 

""" 

cmd = '%s=%s'%(var,value) 

self._send_nowait(cmd) 

time.sleep(0.02) 

 

def _send_nowait(self, x): 

if x.find('\n') != -1: 

raise ValueError("x must not have any newlines") 

# Now we want the client Python process to execute two things. 

# The first is x and the second is c. The effect of c 

# will be to unlock the lock. 

if self.is_locked(): 

return "<<currently executing code>>" 

E = self.expect() 

self._lock() 

E.write(self.preparse(x) + '\n') 

try: 

E.expect(self._prompt) 

except ExceptionPexpect: 

pass 

E.write(self._unlock_code + '\n\n') 

 

def _object_class(self): 

return PSageElement 

 

class PSageElement(SageElement): 

def is_locked(self): 

return self.parent().is_locked()