summaryrefslogtreecommitdiff
path: root/cad/src/outtakes/undo_related_timing_tests.py
blob: e8a33826cc5454fb82ec7ec60dd481d3968b866b (plain)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""
$Id$
"""

# debug/test functions (in other submenu of debug menu) which won't be kept around.
# [moved out of undo.py by bruce 071004]

# not all of these are needed, perhaps:
from cPickle import dump, load, HIGHEST_PROTOCOL
import foundation.env as env
from utilities.debug import register_debug_menu_command, register_debug_menu_command_maker
    ###@@@ don't put all those commands in there -- use a submenu, use atom-debug,
    # or let them only show up if a related flag is set, or so...
from PyQt4.Qt import SIGNAL, QObject, QWidget #k ok to do these imports at toplevel? I hope so, since we need them in several places.
from utilities.constants import genKey, noop
from utilities import debug_flags # for atom_debug [bruce 060128, suggested by Mark;
    # if this works, we should simplify some defs which worry if it's too early for this]


from utilities.debug import call_func_with_timing_histmsg

def atpos_list(part):
    "Return a list of atom-position arrays, one per chunk in part. Warning: might include shared mutable arrays."
    res = []
    for m in part.molecules:
        res.append( m.atpos)
    return res

def save_atpos_list(part, filename):
    save_obj( atpos_list(part), filename)

def save_obj( thing, filename):
    file = open(filename, "wb")
    dump( thing, file, HIGHEST_PROTOCOL) # protocols: 0 ascii, 1 binary, 2 (new in 2.3) better for new style classes
    file.close()

def load_obj(filename):
    file = open(filename, "rb")
    res = load(file)
    file.close()
    return res

def saveposns(part, filename):
    env.history.message( "save main part atom posns to file: " + filename )
    def doit():
        save_atpos_list(part, filename)
    call_func_with_timing_histmsg( doit)

def saveposns_cmd( target): # arg is the widget that has this debug menu
    win = target.topLevelWidget()
    assy = win.assy
    part = assy.tree.part
    filename = assy.filename + "X"
    saveposns(part, filename)
    return

## register_debug_menu_command("save atom posns", saveposns_cmd) # put this command into debug menu

# ==

def loadposns( part, filename): # doesn't move atoms (just a file-reading speed test, for now)
    env.history.message( "load atom posns from file (discards them, doesn't move atoms): " + filename )
    def doit():
        return load_obj(filename)
    posns = call_func_with_timing_histmsg( doit)
    return

def loadposns_cmd( target):
    win = target.topLevelWidget()
    assy = win.assy
    part = assy.tree.part
    filename = assy.filename + "X"
    loadposns(part, filename)
    return

## register_debug_menu_command("load atom posns", loadposns_cmd)

# ==

from Numeric import concatenate, array, UnsignedInt8

def atom_array_of_part(part):
    "Return an Array of all atoms in the Part. Try to be linear time."
    res = []
    for m in part.molecules:
        res.append( array(m.atlist) )
    # concatenate might fail for chunks with exactly 1 atom -- fix later ####@@@@
    return concatenate(res) ###k

def saveelts( part, filename):
    env.history.message( "save main part element symbols -- no, atomic numbers -- to file: " + filename )
    def doit():
        atoms = atom_array_of_part(part)
        ## elts = [atm.element.symbol for atm in atoms]
        elts = [atm.element.eltnum for atm in atoms]
        env.history.message( "%d element nums, first few are %r" % (len(elts), elts[:5] ) )
        thing = array(elts)
        save_obj(thing, filename)
    call_func_with_timing_histmsg( doit)
    return

def saveelts_cmd( target):
    win = target.topLevelWidget()
    assy = win.assy
    part = assy.tree.part
    filename = assy.filename + "E" # E, not X
    saveelts(part, filename)
    return

## register_debug_menu_command("save atom element numbers", saveelts_cmd)

# ==

def savebtypes( part, filename):
    env.history.message( "save main part bond type v6 ints to file: " + filename )
    def doit():
        atoms = atom_array_of_part(part)
        res = []
        for atm in atoms:
            for b in atm.bonds:
                # don't worry for now about hitting each bond twice... well, do, and see if this makes it slower, as i guess.
                if b.atom1 is atm:
                    res.append(b.v6) # a small int
        env.history.message( "%d btype ints, first few are %r" % (len(res), res[:5] ) )
        thing = array(res, UnsignedInt8) # tell it to use a smaller type; see numpy.pdf page 14 on typecodes.
            # update, bruce 070612: we still use Numeric Python (not numarray or numpy). I am not sure what URL
            # is referred to by "numpy.pdf" above, but it is probably (and should be) about Numeric Python.
        save_obj(thing, filename)
    call_func_with_timing_histmsg( doit)
    return

def savebtypes_cmd( target):
    win = target.topLevelWidget()
    assy = win.assy
    part = assy.tree.part
    filename = assy.filename + "B" # B, not E or X
    savebtypes(part, filename)
    return

## register_debug_menu_command("save bond type v6 ints", savebtypes_cmd)

# ==

# moved to pyrex_test.py

### this depends on undotest.pyx having been compiled by Pyrex ####@@@@
##
##def count_bonds_cmd( target):
##    win = target.topLevelWidget()
##    assy = win.assy
##    part = assy.tree.part
##    mols = part.molecules
##    env.history.message( "count bonds (twice each) in %d mols:" % len(mols) )
##    from undotest import nbonds # count bonds (twice each) in a sequence of molecules
##    def doit():
##        return nbonds(mols)
##    nb = call_func_with_timing_histmsg( doit)
##    env.history.message("count was %d, half that is %d" % (nb, nb/2) )
##    return
##
##register_debug_menu_command("count bonds", count_bonds_cmd)

# ==

def blerg_try1(mols):
    dict1 = {}
    for m in mols:
        atlist = m.atlist
        atpos = m.atpos
        keys = map( lambda a: a.key, atlist )
        # now efficiently loop over both those lists. I think there's a better way in python
        # (and surely it should be done in pyrex or Numeric anyway), but try easy way first:
        for key, pos in zip(keys, atpos):
            dict1[key] = pos
    # now dict1 maps key->pos for every atom in the system
    return len(dict1), 0 # untested retval

def update_keyposdict_keyv6dict_from_mol( dict1, dictb, m):
    atlist = m.atlist
    atpos = m.atpos
    ## keys = map( lambda a: a.key, atlist )
    # now efficiently loop over both those lists. I think there's a better way in python
    # (and surely it should be done in pyrex or Numeric anyway), but try easy way first:
    for atm, pos in zip(atlist, atpos):
        ## dict1[id(atm)] = pos
        dict1[atm.key] = pos
        for b in atm.bonds:
            # use id(b) -- not ideal but good enough test for now
            # (before 080229 this code used b.bond_key, which was usually but not entirely unique,
            #  and now has frequent collisions; note, it was already in outtakes on that date)
            dictb[id(b)] = b.v6 # this runs twice per bond
    return

def diff_keyposdict_from_mol( dict1, dict2, m):
    """Like update_keyposdict_from_mol, but use dict1 as old state, and store in dict2 just the items that differ.
    Deleted atoms must be handled separately -- this won't help find them even when run on all mols.
    [Could change that by having this delete what it found from dict1, at further substantial speed cost -- not worth it.]
    """
    atlist = m.atlist
    atpos = m.atpos
    for atm, pos in zip(atlist, atpos):
        key = atm.key
        if pos != dict1.get(key): # Numeric compare -- might be slow, we'll see
            dict2[key] = pos
    return

def blerg(mols):
    dict1 = {}
    dict2 = {}
    dictb = {}
    for m in mols:
        update_keyposdict_keyv6dict_from_mol( dict1, dictb, m)
        diff_keyposdict_from_mol( dict1, dict2, m)
    # now dict1 maps key->pos for every atom in the system
    # and dictb has all v6's of all bonds (mapping from id(bond))
    return len(dict1), len(dictb)

def blerg_cmd( target):
    win = target.topLevelWidget()
    assy = win.assy
    part = assy.tree.part
    mols = part.molecules
    env.history.message( "blorg in %d mols:" % len(mols) )
    def doit():
        return blerg(mols)
    na, nb = call_func_with_timing_histmsg( doit)
    env.history.message("count was %d, %d" % (na,nb,) )
    return

## register_debug_menu_command("make key->pos dict", blerg_cmd)