Esempio n. 1
0
    def pymol_text_string(self):
        counter = 0
        s = ''
        pa_s = 'cmd.set("label_size", 20)\n'
        uids = []

        for (p, n, color, width, text, key) in self.segments:
            if len(text) == 0:
                continue

            # generate a unique identifier for every object so that other
            # scripts can add others that don't clash
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            s += "cgox_%s = []" % (uid) + '\n'
          
            if np.all(n==p):
                pos = n
                axes = [ [2,0,0], [0,2,0], [0,0,2] ]
            else:
                comp1 = cuv.normalize(n - p)

                ncl = cuv.get_non_colinear_unit_vector(comp1)

                comp2 = cuv.normalize(np.cross(ncl, comp1))
                comp3 = cuv.normalize(np.cross(ncl, comp2))

                pos = (p + n) / 2.0 + 3 * comp2

                axes = [list(comp1 * 2), list(comp2 * 2), list(comp3 * 2)]

            text = "%s: %.1f" % (text, cuv.magnitude(n - p))

            s += "cyl_text(cgox_%s, plain, %s, " % (uid, str(list(pos)))
            s += "\"%s\", 0.20, axes=%s)" % (text, str(axes)) + '\n'
            pa_s += "pa_%s = cmd.pseudoatom(pos=%s," % (uid, str(list(pos)))
            pa_s += "b=1.0, label=\"%s\")\n" % (text)
            counter += 1

        '''
        for (text, pos) in self.labels:
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            pa_s += "pa_%s = cmd.pseudoatom(pos=%s," % (uid, str(list(pos)))
            pa_s += "b=1.0, label=\"%s\")\n" % (text)
        '''

        s += "cmd.set(\"cgo_line_radius\",0.03)" + '\n'
        for i in range(counter):
            s += "cmd.load_cgo(cgox_%s, " % (uids[i])
            s += "\'cgox%s\')" % (uids[i]) + '\n'
        s += "cmd.zoom(\"all\", 2.0)" + '\n'

        return pa_s
Esempio n. 2
0
    def pymol_text_string(self):
        counter = 0
        s = ''
        pa_s = 'cmd.set("label_size", 20)\n'
        uids = []

        for (p, n, color, width, text) in self.segments:
            if len(text) == 0:
                continue

            # generate a unique identifier for every object so that other
            # scripts can add others that don't clash
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            s += "cgox_%s = []" % (uid) + '\n'

            comp1 = cuv.normalize(n - p)

            ncl = cuv.get_non_colinear_unit_vector(comp1)

            comp2 = cuv.normalize(np.cross(ncl, comp1))
            comp3 = cuv.normalize(np.cross(ncl, comp2))

            pos = (p + n) / 2.0 + 3 * comp2
            #pos = p + (n - p) / 4.0 + 3 * comp2
            axes = [list(comp1 * 2), list(comp2 * 2), list(comp3 * 2)]

            text = "%s: %.1f" % (text, cuv.magnitude(n - p))
            #text = "%s" % (text)

            s += "cyl_text(cgox_%s, plain, %s, " % (uid, str(list(pos)))
            s += "\"%s\", 0.20, axes=%s)" % (text, str(axes)) + '\n'
            pa_s += "pa_%s = cmd.pseudoatom(pos=%s," % (uid, str(list(pos)))
            pa_s += "b=1.0, label=\"%s\")\n" % (text)
            counter += 1

        '''
        for (text, pos) in self.labels:
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            pa_s += "pa_%s = cmd.pseudoatom(pos=%s," % (uid, str(list(pos)))
            pa_s += "b=1.0, label=\"%s\")\n" % (text)
        '''

        s += "cmd.set(\"cgo_line_radius\",0.03)" + '\n'
        for i in range(counter):
            s += "cmd.load_cgo(cgox_%s, " % (uids[i])
            s += "\'cgox%s\')" % (uids[i]) + '\n'
        s += "cmd.zoom(\"all\", 2.0)" + '\n'

        return pa_s
Esempio n. 3
0
def consensus(coords, structs):
    #structure 1 as reference:
    directions =[]
    for i in range(len(coords)):
        directions.append(structs[i].coords_to_directions())
    directions = np.array(directions)
    new_directions = []

    for i in range(len(directions[0])):
        av_dir = np.zeros(3)
        av_len = 0
        for j in range(len(coords)):
            av_dir+=directions[j,i]
            av_len+=ftuv.magnitude(directions[j,i])
        av_dir=ftuv.normalize(av_dir)
        av_len/=len(coords)
        new_directions.append(av_dir*av_len)
    
    flexibilities = []
    for i in range(len(directions[0])):
        flex = np.zeros(3)
        for j in range(len(coords)):
            flex+=(new_directions[i]-directions[j,i])**2
        flex/=len(coords)
        flexibilities.append(flex)
    sorted_defines = sorted(structs[0].defines.keys())
    for i,d in enumerate(sorted_defines):
        print(d, new_directions[i], "+-", flexibilities[i])
    
    consensus = copy.copy(structs[0])
    consensus.coords_from_directions(new_directions)
    consensus.to_cg_file("consensus.coord")
    print("File consensus.coord written") 
    return consensus
Esempio n. 4
0
def consensus(coords, structs):
    #structure 1 as reference:
    directions = []
    for i in range(len(coords)):
        directions.append(structs[i].coords_to_directions())
    directions = np.array(directions)
    new_directions = []

    for i in range(len(directions[0])):
        av_dir = np.zeros(3)
        av_len = 0
        for j in range(len(coords)):
            av_dir += directions[j, i]
            av_len += ftuv.magnitude(directions[j, i])
        av_dir = ftuv.normalize(av_dir)
        av_len /= len(coords)
        new_directions.append(av_dir * av_len)

    flexibilities = []
    for i in range(len(directions[0])):
        flex = np.zeros(3)
        for j in range(len(coords)):
            flex += (new_directions[i] - directions[j, i])**2
        flex /= len(coords)
        flexibilities.append(flex)
    sorted_defines = sorted(structs[0].defines.keys())
    for i, d in enumerate(sorted_defines):
        print(d, new_directions[i], "+-", flexibilities[i])

    consensus = copy.copy(structs[0])
    consensus.coords_from_directions(new_directions)
    consensus.to_cg_file("consensus.coord")
    print("File consensus.coord written")
    return consensus
Esempio n. 5
0
 def test_fixed_angle(self):
     a = (np.array([0., 0, 0]), np.array([0, 0, 1]))
     b = (np.array([0., 0, 1]), np.array(
         [0., 0, 1]) + ftuv.normalize([1, 1, 1.]))
     x = np.linspace(0.01, 4, 500)
     for f in x:
         col = ftuv.line_segments_collinearity(a, (b[0] * f, b[1] * f))
         self.assertLess(col, 0.95)
         self.assertGreater(col, 0.6)
Esempio n. 6
0
 def test_fixed_angle(self):
     a = (np.array([0., 0, 0]), np.array([0, 0, 1]))
     b = (np.array([0., 0,
                    1]), np.array([0., 0, 1]) + ftuv.normalize([1, 1, 1.]))
     x = np.linspace(0.01, 4, 500)
     for f in x:
         col = ftuv.line_segments_collinearity(a, (b[0] * f, b[1] * f))
         self.assertLess(col, 0.95)
         self.assertGreater(col, 0.6)
Esempio n. 7
0
    def add_cone(self, p, n, color='white', width=2.4, text=''):
        if self.override_color is not None:
            color = self.override_color

        cone_extension = 2.
        cyl_vec = cuv.normalize(n-p)
        cyl_len = cuv.magnitude(n-p)

        new_width = width * (cyl_len + cone_extension) / cyl_len

        self.new_cones += [(np.array(p) - cone_extension * cyl_vec, np.array(n), color, width, text)]
        self.new_cones += [(np.array(n) + cone_extension * cyl_vec, np.array(p), color, width, text)]
Esempio n. 8
0
    def add_cone(self, p, n, color='white', width=2.4, text=''):

        cone_extension = 2.
        cyl_vec = cuv.normalize(n - p)
        cyl_len = cuv.magnitude(n - p)

        new_width = width * (cyl_len + cone_extension) / cyl_len

        self.cones += [(np.array(p) - cone_extension * cyl_vec, np.array(n),
                        color, width, text)]
        self.cones += [(np.array(n) + cone_extension * cyl_vec, np.array(p),
                        color, width, text)]
Esempio n. 9
0
    def pymol_text_string(self, rna_number):
        counter = 0
        s = ''
        pa_s = 'cmd.set("label_size", 20)\n'
        uids = []
        names = []
        for (p, n, color, width, text, key) in self.segments:
            if len(text) == 0:
                continue

            # generate a unique identifier for every object so that other
            # scripts can add others that don't clash
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            if np.all(n == p):
                pos = n
                axes = [[2, 0, 0], [0, 2, 0], [0, 0, 2]]
            else:
                comp1 = cuv.normalize(n - p)
                ncl = cuv.get_non_colinear_unit_vector(comp1)
                comp2 = cuv.normalize(np.cross(ncl, comp1))
                comp3 = cuv.normalize(np.cross(ncl, comp2))
                pos = (p + n) / 2.0 + 3 * comp2
                axes = [list(comp1 * 2), list(comp2 * 2), list(comp3 * 2)]

            #text = "%s: %.1f" % (text, cuv.magnitude(n - p))

            name = "label{}_{}_{}".format(len(uids), rna_number, self.name)
            names.append(name)
            pa_s += 'pa_{} = cmd.pseudoatom("{}", pos={},'.format(uid,
                                                                  name, str(list(pos)))
            pa_s += 'b=1.0, label="{}")\n'.format(text)
            counter += 1

        all_patoms = ["pa_{}".format(ui) for ui in uids]
        pa_s += "cmd.group('{}', '{}')\n".format(
            "labels_{}_{}".format(rna_number, self.name), " ".join(names))
        return pa_s
Esempio n. 10
0
def make_random_chain(n=12):
    """
    Return a list of random vectors, each with distance
    3.8 from the previous vector.
    """
    v=array([0,0,0])
    l=[v]
    for i in range(0, n-1):
        nv=normalize(random(3))
        nv=v + 3.8 * nv
        l.append(nv)
        v=nv
    return l
Esempio n. 11
0
 def plot_fixed_angle(self):
     a = (np.array([0., 0, 0]), np.array([0, 0, 1]))
     b = (np.array([0., 0,
                    1]), np.array([0., 0, 1]) + ftuv.normalize([1, 1, 1.]))
     x = np.linspace(0.01, 4, 5000)
     y = []
     for f in x:
         y.append(ftuv.line_segments_collinearity(a, (b[0] * f, b[1] * f)))
     import matplotlib.pyplot as plt
     plt.title("Fixed angle")
     plt.plot(x, y)
     plt.show()
     assert False
Esempio n. 12
0
def make_random_chain(n=12):
    """
    Return a list of random vectors, each with distance
    3.8 from the previous vector.
    """
    v = array([0, 0, 0])
    l = [v]
    for i in range(0, n - 1):
        nv = normalize(random(3))
        nv = v + 3.8 * nv
        l.append(nv)
        v = nv
    return l
Esempio n. 13
0
    def pymol_text_string(self, rna_number):
        counter = 0
        s = ''
        pa_s = 'cmd.set("label_size", 20)\n'
        uids = []
        names = []
        for (p, n, color, width, text, key) in self.segments:
            if len(text) == 0:
                continue

            # generate a unique identifier for every object so that other
            # scripts can add others that don't clash
            uid = str(uuid.uuid4()).replace('-', 'x')
            uids += [uid]

            if np.all(n == p):
                pos = n
                axes = [[2, 0, 0], [0, 2, 0], [0, 0, 2]]
            else:
                comp1 = cuv.normalize(n - p)
                ncl = cuv.get_non_colinear_unit_vector(comp1)
                comp2 = cuv.normalize(np.cross(ncl, comp1))
                comp3 = cuv.normalize(np.cross(ncl, comp2))
                pos = (p + n) / 2.0 + 3 * comp2
                axes = [list(comp1 * 2), list(comp2 * 2), list(comp3 * 2)]

            #text = "%s: %.1f" % (text, cuv.magnitude(n - p))

            name = "label{}_{}_{}".format(len(uids), rna_number, self.name)
            names.append(name)
            pa_s += 'pa_{} = cmd.pseudoatom("{}", pos={},'.format(
                uid, name, str(list(pos)))
            pa_s += 'b=1.0, label="{}")\n'.format(text)
            counter += 1

        all_patoms = ["pa_{}".format(ui) for ui in uids]
        pa_s += "cmd.group('{}', '{}')\n".format(
            "labels_{}_{}".format(rna_number, self.name), " ".join(names))
        return pa_s
Esempio n. 14
0
 def plot_fixed_angle(self):
     a = (np.array([0., 0, 0]), np.array([0, 0, 1]))
     b = (np.array([0., 0, 1]), np.array(
         [0., 0, 1]) + ftuv.normalize([1, 1, 1.]))
     x = np.linspace(0.01, 4, 5000)
     y = []
     for f in x:
         y.append(ftuv.line_segments_collinearity(a, (b[0] * f, b[1] * f)))
     import matplotlib.pyplot as plt
     plt.title("Fixed angle")
     plt.plot(x, y)
     plt.show()
     assert False
Esempio n. 15
0
    def add_dashed(self, point1, point2, width=0.3):
        '''
        Add a dashed line from point1 to point2.
        '''
        dash_length = 0.6
        gap_length = dash_length * 2
        direction = ftuv.normalize(point2 - point1)

        num_dashes = ftuv.magnitude(point2 - point1) / (dash_length + gap_length)

        for i in range(int(num_dashes)):
            self.add_segment(point1 + i * (dash_length + gap_length) * direction, 
                             point1 + (i * (dash_length + gap_length) + dash_length) * direction, "purple",
                             width, "", key=key)
Esempio n. 16
0
    def add_dashed(self, point1, point2, width=0.3):
        '''
        Add a dashed line from point1 to point2.
        '''
        dash_length = 0.6
        gap_length = dash_length * 2
        direction = ftuv.normalize(point2 - point1)

        num_dashes = ftuv.magnitude(point2 - point1) / (dash_length + gap_length)

        for i in range(int(num_dashes)):
            self.add_segment(point1 + i * (dash_length + gap_length) * direction, 
                             point1 + (i * (dash_length + gap_length) + dash_length) * direction, "purple",
                             width, "", key=key)
Esempio n. 17
0
    def get_twists(self, node):
        ''' 
        Get the array of twists for this node. If the node is a stem,
        then the twists will simply those stored in the array. 
        If the node is an interior loop or a junction segment, 
        then the twists will be the ones that are adjacent to it. 
        If the node is a hairpin loop or a free end, then the same twist
        will be duplicated and returned twice.

        @param node: The name of the node
        '''                                                                                                           
        if node[0] == 's':
            return self.twists[node]                                                                                  

        connections = list(self.edges[node])
        (s1b, s1e) = self.get_sides(connections[0], node)

        if len(connections) == 1:
            vec = ftuv.normalize(ftuv.vector_rejection( 
                                  self.twists[connections[0]][s1b],
                                  self.coords[connections[0]][1] -  
                                  self.coords[connections[0]][0]))
            return (vec,vec)                                                  

        if len(connections) == 2: 
            # interior loop or junction segment                                                                  
            (s2b, s2e) = self.get_sides(connections[1], node) 
            bulge_vec = (self.coords[connections[0]][s1b] - 
                         self.coords[connections[1]][s2b])                                                            
            return (ftuv.normalize(ftuv.vector_rejection( 
                    self.twists[connections[0]][s1b], bulge_vec)),
                    ftuv.normalize(ftuv.vector_rejection(self.twists[connections[1]][s2b], bulge_vec)))  

        # uh oh, this shouldn't happen since every node                 
        # should have either one or two edges 
        return None                                                                                                   
Esempio n. 18
0
def point_on_line(P, A, D):
    '''
    Point on axis
    http://stackoverflow.com/questions/5227373/minimal-perpendicular-vector-between-a-point-and-a-line
    
    @param P: A point anywhere
    @param A: A point on the axis
    @param D: The direction of the axis
    '''

    D = normalize(D)
    X = A + dot((P - A), D) * D

    #print "dot((P-A), D) * D", dot((P-A), D) * D

    #print "X:", X, dot(X-P, X-A)

    return X
Esempio n. 19
0
def point_on_line(P, A, D):
    '''
    Point on axis
    http://stackoverflow.com/questions/5227373/minimal-perpendicular-vector-between-a-point-and-a-line
    
    @param P: A point anywhere
    @param A: A point on the axis
    @param D: The direction of the axis
    '''

    D = normalize(D)
    X = A + dot((P - A), D) * D

    #print "dot((P-A), D) * D", dot((P-A), D) * D

    #print "X:", X, dot(X-P, X-A)

    return X
Esempio n. 20
0
    def coordinates_to_pymol(self, cg):
        loops = list(cg.hloop_iterator())

        for key in cg.coords.keys():
            if self.constraints is not None:
                if key not in self.constraints:
                    continue

            (p, n) = cg.coords[key]
            color = self.get_element_color(key)

            if key[0] == 's':
                self.add_stem_like(cg, key)
                self.draw_bounding_boxes(cg, key)
            else:
                if key[0] == 'h':
                    if self.add_loops:
                        if key in loops:
                            self.add_segment(p, n, color, 1.0,
                                             key + " " + str(cg.get_length(key)))
                elif key[0] == 'm':
                    twists = cg.get_twists(key)

                    # check if the multiloop is longer than one. If it's not, then
                    # it has an empty define and we its length will be 1
                    if len(cg.defines[key]) == 0:
                        self.add_segment(p, n, color, 1.0,
                                         key + " 1")
                    else:
                        self.add_segment(p, n, color, 1.0,
                                         key + " " +
                                         str(cg.defines[key][1] -
                                         cg.defines[key][0] + 1))

                    self.add_segment(p, p+ 7 * twists[0], 'light gray', 0.3)
                    self.add_segment(n, n+ 7 * twists[1], 'light gray', 0.3)

                    x = (p + n) / 2
                    t = ftuv.normalize((twists[0] + twists[1]) / 2.)
                    self.add_segment(x, x + 7 * t, 'middle gray', 0.3)
                elif key[0] == 'f':
                    if self.visualize_three_and_five_prime:
                        self.add_segment(p, n, color, 1.0,
                                         key + " " +
                                         str(cg.defines[key][1] -
                                         cg.defines[key][0] + 1) + "")

                elif key[0] == 't':
                    if self.visualize_three_and_five_prime:
                        self.add_segment(p, n, color, 1.0,
                                         key + " " +
                                         str(cg.defines[key][1] -
                                         cg.defines[key][0]) + "")
                else:
                    #self.add_stem_like(cg, key, "yellow", 1.0)
                    self.add_segment(p, n, color, 1.0, key)

        if self.add_longrange:
            for key1 in cg.longrange.keys():
                for key2 in cg.longrange[key1]:
                    try:

                        p = cuv.line_segment_distance(cg.coords[key1][0],
                                                      cg.coords[key1][1],
                                                      cg.coords[key2][0],
                                                      cg.coords[key2][1])
                        (point1, point2) = p

                        #point1 = cg.get_point(key1)
                        #point2 = cg.get_point(key2)

                        dash_length = 0.6
                        gap_length = dash_length * 2
                        direction = ftuv.normalize(point2 - point1)

                        num_dashes = ftuv.magnitude(point2 - point1) / (dash_length + gap_length)
                        fud.pv('num_dashes')

                        for i in range(int(num_dashes)):
                            self.add_segment(point1 + i * (dash_length + gap_length) * direction, 
                                             point1 + (i * (dash_length + gap_length) + dash_length) * direction, "purple",
                                             0.3, "")

                            '''
                            self.add_segment(point1, point2, "purple",
                                             0.3, key1 + " " + key2)
                            
                            '''
                    except:
                        continue

        if self.encompassing_stems:
            self.add_encompassing_cylinders(cg, 7.)

        if self.max_stem_distances > 0:
            for (s1, s2) in it.permutations(cg.stem_iterator(), r=2):
                (i1, i2) = cuv.line_segment_distance(cg.coords[s1][0],
                                                     cg.coords[s1][1],
                                                     cg.coords[s2][0],
                                                     cg.coords[s2][1])
                if cuv.magnitude(i2 - i1) < self.max_stem_distances:
                    #self.add_segment(i1, i2, 'cyan', 0.3, s1 + " " + s2)
                    self.add_segment(i1, i2, 'cyan', 0.3)

        if self.virtual_atoms:
            va = ftug.virtual_atoms(cg, sidechain=False)

            atom_width = 0.5
            for i,r in enumerate(sorted(va.keys())):
                for a in va[r].keys():
                    if self.rainbow:
                        import matplotlib
                        matplotlib.use('Agg')
                        import matplotlib.pyplot as plt
                        cmap = plt.get_cmap('gist_rainbow')
                        self.add_sphere(va[r][a], 
                                        color_rgb = cmap(i / float(len(va.keys()))), 
                                        width=atom_width)
                    else:
                        d = cg.get_node_from_residue_num(r)
                        if d[0] == 's':
                            self.add_sphere(va[r][a], 'green', width=atom_width)
                        elif d[0] == 'i':
                            self.add_sphere(va[r][a], 'yellow', width=atom_width)
                        elif d[0] == 'm':
                            self.add_sphere(va[r][a], 'red', width=atom_width)
                        elif d[0] == 'h':
                            self.add_sphere(va[r][a], 'blue', width=atom_width)

        if self.basis:
            for d in cg.defines.keys():
                origin, basis = ftug.element_coord_system(cg, d)

                self.add_segment(origin, origin + 7. * basis[1], 'purple', 2.)

        print >>sys.stderr, "energy_function:", self.energy_function
        # print the contributions of the energy function, if one is specified
        if self.energy_function is not None:
            print >>sys.stderr, "key"
            sum_energy = 0.

            e_func = self.energy_function
            e_func_iter = e_func.interaction_energy_iter(cg, background=False)
            int_energies = list(e_func_iter)
            max_energy = max(int_energies, key=lambda x: x[1])
            print >>sys.stderr, "max_energy:", max_energy

            for (interaction, energy) in int_energies:
                (p, n) = (cg.get_point(interaction[0]),
                          cg.get_point(interaction[1]))
                scaled_energy = - max_energy[1] + energy

                self.add_segment(p, n, 'purple', 3 * np.exp(scaled_energy))

                sum_energy += energy

        if self.stem_stem_orientations is not None:
            for (s1, s2) in it.permutations(cg.stem_iterator(), 2):
                '''
                if cg.are_adjacent_stems(s1, s2):
                    continue
                '''

                if s1 != 's65':
                    if s2 != 's65':
                        continue

                s1_vec = cg.coords[s1][1] - cg.coords[s1][0]
                s2_vec = cg.coords[s2][1] - cg.coords[s2][0]
                (i1, i2) = cuv.line_segment_distance(cg.coords[s1][0],
                                                     cg.coords[s1][1],
                                                     cg.coords[s2][0],
                                                     cg.coords[s2][1])
                i_vec = i2 - i1

                #i_rej will be orthogonal to s1_vec in the direction
                #of s2
                i_rej = cuv.vector_rejection(i_vec, s1_vec)

                #plane_vec will be orthogonal to s1_vec and to the direction
                # of s2
                plane_vec = np.cross(i_rej, s1_vec)

                # s2_proj is in the intersection plane
                s2_proj_in = cuv.vector_rejection(s2_vec, plane_vec)
                # s2 proj_out is out of the intersection plane
                #s2_proj_out = cuv.vector_rejection(s2_vec, i_rej)

                start_point = cg.coords[s1][0] + 5 * cg.twists[s1][0]
                ortho_offset = cuv.magnitude(i_rej)
                dist = cuv.magnitude(i_vec) + 0.0001

                lateral_offset = m.sqrt(dist ** 2 - ortho_offset ** 2)

                if lateral_offset > 10:
                    continue

                '''
                #self.add_segment(start_point,
                                  start_point + 10 * cuv.normalize(s2_vec),
                                  'white', 0.5)
                #self.add_segment(start_point,
                                  start_point + 5 * cuv.normalize(plane_vec),
                                  'magenta', 0.5)
                #self.add_segment(start_point,
                                  start_point + 5 * cuv.normalize(i_vec),
                                  'cyan', 0.5)
                #self.add_segment(i1, i1 + i_rej,  'cyan', 0.5)
                '''
                self.add_segment(start_point,
                                 start_point + 7 * cuv.normalize(s2_proj_in),
                                 'white', 1.5)
                '''