Evaluating code in q

There are three ways to evaluate code in q via PyKX:

1. By calling pykx.q, e.g. pykx.q('10 {x,sum -2#x}/ 0 1')
2. By dropping into the interactive console
3. Over IPC

The first two methods evaluate the code locally within the Python process, and are not available without a q license. The third method evaluates the code in a separate q process, and can be used with or without a q license.

Warning

Functions pulled in over IPC are executed locally in PyKX, see the IPC documentation for more information on how to ensure q code is executed on the server and not locally.

K Objects

Calling a q instance or a connection to a q instance will return what is commonly referred to as a K object. A K object is an instance of the pykx.K class, or one of its subclasses. These classes are documented on the PyKX wrappers API doc page.

K objects are wrappers around objects in q's memory space within the Python process that PyKX (and your program that uses PyKX) runs in. These wrappers are cheap to make as they do not require copying any data out of q's memory space.

These K objects support a variety of Python features (e.g. iteration, slicing, calling, etc.), and so oftentimes converting them to other types (e.g. a pykx.Vector to a numpy.ndarray) is unnecessary.

Calling q functions

Consider the following q function that checks if a given number is prime:

{$[x in 2 3;1;x<2;0;{min x mod 2_til 1+floor sqrt x}x]} We can evaluate it through q to obtain a pykx.Lambda object. This object can then be called as a Python function: import pykx as kx is_prime = kx.q('{$[x in 2 3;1;x<2;0;{min x mod 2_til 1+floor sqrt x}x]}')
assert is_prime(2)
assert is_prime(127)
assert not is_prime(128)

Arguments to the function are converted to pykx.K objects via the pykx.toq module, and so the arguments can be anything supported by that module, i.e. any Python type X for which a pykx.toq.from_X function exists (barring some caveats - see the pykx.toq documentation).

For instance, we can apply the each adverb to is_prime and then provide it a range of numbers to check like so:

>>> is_prime.each(range(10))
pykx.LongVector(q('0 0 1 1 0 1 0 1 0 0'))

Then we could pass that into pykx.q.where

>>> kx.q.where(is_prime.each(range(10)))
pykx.LongVector(pykx.q('2 3 5 7'))

Context is persisted between embedded calls to q, but not calls over IPC.

>>> kx.q('\d .abc') # change to the .abc context
pykx.Identity(pykx.q('::'))
>>> kx.q('xyz: 1 2 3') # set variable xyz within the .abc context
pykx.Identity(pykx.q('::'))
>>> kx.q('.abc.xyz')
pykx.LongVector(pykx.q('1 2 3'))
>>> kx.q('\d .') # change back to the default . global context
pykx.Identity(pykx.q('::'))
>>> kx.q('xyz: 4 5 6') # set variable xyz within the . global context
pykx.Identity(pykx.q('::'))
>>> kx.q('.abc.xyz')
pykx.LongVector(pykx.q('1 2 3'))
>>> kx.q('xyz')
pykx.LongVector(pykx.q('4 5 6'))
>>> q = kx.QConnection('localhost', 5001)
>>> q('\d .abc')
pykx.Identity(pykx.q('::'))
>>> q('xyz: 1 2 3')
pykx.Identity(pykx.q('::'))
>>> q('.abc.xyz')
pykx.exceptions.QError: .abc.xyz # xyz was not set within the .abc context.
>>> q('xyz')
pykx.LongVector(pykx.q('1 2 3')) # It got set within the global context