MrTLane
MrTLane

Reputation: 21

Python - Generate an array where elements are a function of a varying parameter, without building the array each time

TLDR : How can I generate an array whose elements depend on some arbitrary (float) value, k, without having to go through the extremely time-expensive process of constructing the array from scratch every time I change the value of k.

What I want to achieve would look like:

Intended structure of the code

I am generating a huge Hamiltonian in the atomic basis of a 2D lattice (N x N numpy array). Filling this array requires comparing positions (xyz) of the atomic sites multiple times for each different coupling types that I want to include, becoming very time-expensive as the system size grows. (typically N > 16,000 sites).

Elements of this array have a dependence upon some other float type variable, k (in the physical context of the program this is a quantum counting number that I want to iterate over). I need to calculate this array many times for a range of 1000 k-values.

i.e generate the 256,000,000 element array 1000 times... eurgh

At present I have to create the array every time I change to a new k-value which is obviously very inefficient. The basic structure of this looks (very generally) like:

class Device():

    def __init__(self, xyz, energy, ... other input parameters ...):

        self.xyz = xyz           # N x 3 array of positions
        self.energy = energy     # Length N list of energies
        # + A range of other parameters that define the device


     # -------- OTHER OPERATIONS ON THE DEVICE -------- #


     def get_H(self, k):
        """ Get the Hamiltonian for a given k - value """

        # Initialise the N x N array
        h = np.zeros((len(self.xyz), len(self.xyz)))

        # - Fill THE HAMILTONIAN BY COMPARING ALL ATOMIC POSITIONS IN self.xyz - #

        return h

which requires that I call the entire construction process each time.

I want to know if there is a way to generate this array once, with k having been left as a free parameter which can then be filled in later. i.e return an array which is a function of k. The priority is to only have to construct the array once since testing indicates that this takes a significant portion of my total run-time.

Below is a minimal (non-working) example of what I want to achieve by acting on a test array. I want to make the Hamiltonian an object variable rather than a method that has to be made each time, but with some dependence on k (I realise that this will be syntactically disastrous but hopefully will be a good start for an answer).

class Test_device():

def __init__():

    self.get_H = self.make_H()

def make_H(self):

    h = np.linspace(1,9,9).reshape((3,3)) # Test array

    # The below clearly will not work because k is not defined, but this is
    # where I want to achieve this

    h[1,1] += k # Give some k-dependence to the middle element of the array

    def ham(k, h = h):

        # Somehow set the value of k in h

        return h

    return ham

Which I would then access via

device = Test_device()
device.get_H(k = k_value)

Thanks in advance!

Upvotes: 2

Views: 618

Answers (2)

jtlz2
jtlz2

Reputation: 8407

On reflection I do not think np.fromfunction() is what you want. Rather try:

import numpy as np

class Test_device(object):

    def __init__(self, h_shape):
        self.h_shape = h_shape

        # Create array once
        self.h = np.linspace(1, h_shape[0] ** 2, h_shape[1] ** 2).reshape(self.h_shape)

    def get_H(self, k, locn=(1, 1)):
        self.h[locn] += k  # Give some k-dependence to the middle element of the array

        # Somehow set the value of k in h
        pass

In what follows, initialize device once (given an intended shape for h). Then choose a k (and a location locn, if you want). Then call the get_H method of device. k is never attributed to device, but could be if you like for reference with self.k=k (as stated by eatmeinadanish).

Then you can access device.h whenever you like.

h_shape = (3, 3)
device = Test_device(h_shape)
k = 1.3
locn = (1, 1)
device.get_H(k, locn=locn)
print device.h

I'm not sure this helps you much in the end, whether it's what you actually intended, and note that it doesn't really add much to eatmeinadanish's answer.

Upvotes: 0

eatmeimadanish
eatmeimadanish

Reputation: 3907

This isn't a working example but...

class Test_device():

    def __init__(self, h):
        self.k = 0
        self.h = h

    def get_H(self):

        self.h = np.linspace(1,9,9).reshape((3,3)) # Test array

        # The below clearly will not work because k is not defined, but this is
        # where I want to achieve this

        self.h[1,1] += self.k # Give some k-dependence to the middle element of the array

    def ham(self):

        # Somehow set the value of k in h
        return self.h

You can do something like this:

device = Test_Device(10)
device.get_H()
device.h = 12
device.get_H()
h = device.ham()

You can change h or k at any time by just changing the value inside the class like device.h = 14. The same with k.

Upvotes: 0

Related Questions