A quick tour

First steps

In the interactive mode, commands are typed at the prompt. For example,

>>> 3 + 3**2
12

or

>>> print "Hello world"
Hello world

Variables can be defined:

>>> name = "Mick"
>>> surname = "Dundee"
>>> print name,surname
Mick Dundee

>>> x = 4
>>> y = 2.5
>>> x * y
10.0

Iteration is possible:

>>> for i in range(10):
...    print i, 2.5 * i
...
0 0.0
1 2.5
2 5.0
3 7.5
4 10.0
5 12.5
6 15.0
7 17.5
8 20.0
9 22.5

Note

  • The prompt changes from >>> to ... when a command is incomplete, allowing multi-line statements to be entered, like the for statement above.
  • Indentation delimits a block of instructions. In the for statement above, the second line is indented, making it part of the for-loop body. Any number of lines can be indented to form a block of code.

Conditional statements also use blocks:

>>> data = 15
>>> if data > 10:
...    print "big"
... else:
...    print "small"
...
big

Uncertain numbers

GTC uses an uncertain number to represent a value that is not precisely known.

Different types of uncertain number are used for real and complex values.

Uncertain real numbers

At least two pieces of information are needed to define an uncertain real number:

  • a value (of the estimate)
  • a standard uncertainty (of the value as an estimate of the quantity of interest).

For example, suppose the current flowing in an electrical circuit \(I\) and the voltage across a circuit element \(V\) have been measured.

The estimates (measured values) are \(V \approx 0.1\, \mathrm{V}\) and \(I \approx 15\,\mathrm{mA}\), with standard uncertainties \(u(V) = 1\, \mathrm{mV}\) and \(u(I) = 0.5\,\mathrm{mA}\), respectively.

Uncertain numbers for \(V\) and \(I\) are defined using ureal

>>> V = ureal(0.1,1E-3)
>>> I = ureal(15E-3,0.5E-3)

The resistance can be calculated using Ohm’s law

>>> R = V/I

Uncertain number attributes

Information about the value (estimate of the quantity of interest) and the associated uncertainty can be obtained from an uncertain number in different ways.

  • Typing the name of an uncertain number at the prompt displays its Python representation:

    >>> R
    ureal(6.666666666666667, 0.23200681130912335, inf)
    

    The value is 6.666666666666667 and the standard uncertainty 0.23200681130912335 (inf signifies infinite degrees-of-freedom, see note below).

  • Attributes, or corresponding functions, can be used to obtain the value, or the uncertainty, etc,

    >>> R.x
    6.666666666666667
    >>> value(R)
    6.666666666666667
    
    >>> R.u
    0.23200681130912335
    >>> uncertainty(R)
    0.23200681130912335
    
  • Alternatively, a summary string can be generated showing the value, the uncertainty and the degrees of freedom. Numbers in the string are formatted to show only significant digits:

    >>> print R.s
    6.67, u=0.23, df=inf
    >>> summary(R)
    '6.67, u=0.23, df=inf'
    

Note

By default, the number of degrees of freedom is infinity (inf): this implies that the standard uncertainty is known exactly.

Uncertain complex numbers

To define an uncertain number for a complex quantity, at least two pieces of information are needed:

  • a value (of the estimate)
  • an uncertainty (of the value as an estimate of the quantity of interest - different formats may be used, see below)

The same functions, or attributes, mentioned above, can be used to obtain information about uncertain complex numbers.

For example, suppose the alternating current flowing in an electrical circuit \(i\), the voltage across a circuit element \(v\) and the phase of the voltage with respect to the current \(\phi\) have been measured.

The measured values (estimates) are: \(v \approx 4.999\, \mathrm{V}\), \(i \approx 19.661\,\mathrm{mA}\) and \(\phi \approx 1.04446\,\mathrm{rad}\), with standard uncertainties \(u(v) = 0.0032\, \mathrm{V}\), \(u(i) = 0.0095\,\mathrm{mA}\) and \(u(\phi) = 0.00075\,\mathrm{rad}\).

Uncertain numbers for \(v\), \(i\) and \(\phi\) can be defined using ucomplex:

>>> v = ucomplex(complex(4.999,0),(0.0032,0))
>>> i = ucomplex(complex(19.661E-3,0),(0.0095E-3,0))
>>> phi = ucomplex(complex(0,1.04446),(0,0.00075))

Note that in these definitions the second argument is a pair of numbers [1]. These are the standard uncertainties associated with estimates of the real and imaginary components.

The complex impedance is

>>> z = v * exp(phi) / i

The value, uncertainty and degrees-of-freedom of z are (in summary form)

>>> print z.s
(127.73-219.85j), u=[0.19,0.20], r=0.058, df=inf

In this format, the complex value of the estimate is followed by the standard uncertainty in the real and imaginary components. The term r=0.058 is the correlation coefficient between the real and imaginary component estimates.

If the magnitude and phase of \(z\) are of interest

>>> print magnitude(z).s
254.26, u=0.20, df=inf

>>> print phase(z).s
1.04446, u=0.00075, df=inf

Different formats for the uncertainty of uncertain complex numbers

When defining an uncertain complex number, it is possible to specify the uncertainty of a complex value in different ways:

  • A single number will specify the standard uncertainty of real and imaginary components to the same value,

  • A 2-element sequence of numbers specifies the standard uncertainties of the real and imaginary components

  • A 4-element sequence of numbers specifies the elements of the variance-covariance matrix

    If v is such a 4-element sequence then

    • v[0] is the standard variance associated with the real component (the standard uncertainty is math.sqrt(v[0]))
    • v[3] is the standard variance associated with the imaginary component (the standard uncertainty is math.sqrt(v[3]))
    • v[1] and v[2] must be equal, they represent the covariance between the real and imaginary components (the correlation coefficient is v[1]/math.sqrt(v[0]*v[3]))

For example,

>>> z = ucomplex(1+1j,1)
>>> uncertainty(z)
standard_uncertainty(real=1.0, imag=1.0)
>>> variance(z)
variance_covariance(rr=1.0, ri=0.0, ir=0.0, ii=1.0)

>>> z = ucomplex(1+1j,(.5,.5))
>>> uncertainty(z)
standard_uncertainty(real=0.5, imag=0.5)
>>> variance(z)
variance_covariance(rr=0.25, ri=0.0, ir=0.0, ii=0.25)

>>> z = ucomplex(1+1j,(.5,0.1,0.1,.5))
>>> uncertainty(z)
standard_uncertainty(real=0.70710678118654757, imag=0.70710678118654757)
>>> z = ucomplex(1+1j,(.5,0.1,0.1,.5))
>>> variance(z)
variance_covariance(rr=0.50000000000000011, ri=0.099999999999999992,
    ir=0.099999999999999992, ii=0.50000000000000011)

Note

A namedtuple is returned by the functions uncertainty and variance.

The elements of a namedtuple can be accessed by index, or by name. For example,

>>> cv = variance(z)
>>> cv
variance_covariance(rr=0.5000000000000001, ri=0.09999999999999999,
    ir=0.09999999999999999, ii=0.5000000000000001)
>>> cv[0]
0.50000000000000011
>>> cv.rr
0.50000000000000011

Programming

GTC uses the Python programming language. This section gives a very brief introduction to some aspects of the language (see references [2] and [3] for more comprehensive Python tutorials).

Sequences

A sequence is a collection of objects. The two main types of sequence are list and tuple. Tuples cannot be altered (they are read-only) whereas elements may be inserted, changed, sorted, etc, in lists.

For example, a tuple containing the numbers 1 and 2 is

>>> tup = (1,2)

The elements can be accessed by index (sequences are always base-0)

>>> print tup[0]
1

but cannot not changed

>>> tup[1] = 3
Traceback (most recent call last):

  File "<console>", line 1, in <module>

TypeError: 'tuple' object does not support item assignment

Lists are created using square brackets

>>> l = [-8,6,9]
>>> print l[1]
6

The elements of both tuples and lists can be iterated over using for loops

>>> data = (1.1,3.2,6.7)
>>> for d in data:
...     print d**2
...
1.21
10.24
44.89

The function range creates a list of integers, which can be useful for iteration

>>> for i in range(4):
...     print i
...
0
1
2
3

>>> print range(4)
[0, 1, 2, 3]

A useful feature allows sequences to be packed, and unpacked, by matching the number of elements on either side of an = sign. For example,

>>> a = 1,2,3
>>> a
(1,2,3)

>>> x,y,z = a
>>> y
2

Functions

A function is defined by a name followed by a list of arguments in parentheses and a colon. An indented block of code defines the function body. A return statement sends a result back to the calling context.

Here is a function that calculates the surface area of a box, given the length, width and height of the sides

def surface(l,w,h):
    end_area = 2 * w * h
    side_area = 2 * l * h
    top_bottom = 2 * w * l

    area = end_area + side_area + top_bottom

    return area

Note

This code snippet would be stored in a file, so the interactive prompt >>> does not appear.

Note

The arguments l, w and h can be ordinary numbers or uncertain numbers.

Strings and printing

When a print statement is used to display an uncertain number, the value will be displayed between question marks. The number of significant digits is determined by the uncertainty

>>> x = ureal(1.11111,0.1)
>>> print x
?1.11?

However, typing the name of the uncertain number (without using print) produces a more detailed description, called the Python representation:

>>> x
ureal(1.111, 0.10000000000000001, inf)

There are two ways of formatting data as strings (one described in detail under % formatting in the Python documentation, the other under format str). The % formatting method uses a template string containing format specifiers to determine how data will appear in a resulting string.

For example, the %s format specifier is a placeholder for a string in the output

>>> name = 'Jim'
>>> "Hello %s" % name
'Hello Jim'
The format str approach uses the format method of the string object. ::
>>> name = 'Jim'
>>> "Hello {}".format(name)
'Hello Jim'

Note

Strings are delimited by apostrophes ' or quotation marks " and multi-line strings begin and end with triple delimiters of either kind.

There are format specifiers for display floating point numbers (e.g., %f, %E, %G)

>>> p = math.pi
>>> "pi = %f" % p
'pi = 3.141593'
>>> "pi = %E" % p
'pi = 3.141593E+00'
>>> "pi = %G" % p
'pi = 3.14159'

alternatively

>>> p = math.pi
>>> "pi = {:f}".format(p)
'pi = 3.141593'
>>> "pi = {:E}".format(p)
'pi = 3.141593E+00'
>>> "pi = {:G}".format(p)
'pi = 3.14159'

The G format specifier switches automatically to an exponential scientific format when the number becomes too big, or too small (use E to always get the scientific format)

>>> bigger = p * 1E6
>>> "pi = %G" % bigger
'pi = 3.14159E+06'

Several arguments can inserted in a string using format specifiers. The corresponding data must be contained in a tuple to the right of the % operator. For example,

>>> quantity = 'voltage'
>>> value = 1.4
>>> unit = 'V'
>>> "%s = %f (%s)" % (quantity,value,unit)
'voltage = 1.400000 (V)'
>>> "{!s} = {:f} ({!s})".format(quantity,value,unit)
'voltage = 1.400000 (V)'

Operators

A few Python operators may be unfamiliar. For example, raising one number to the power of another uses a double asterisk operator

>>> 2 ** 4
16

Testing for equality uses a double equals operator

>>> 2.0 == 2
True

Note that a single ‘=’ is used to assign one thing to another, so the following is an error

>>> 2.0 = 2
  File "<console>", line 1
SyntaxError: can't assign to literal

There are quite a few different operators in Python, so it is best to consult the Python Help file, or a Python reference, for further details [4].

Modules

Libraries of functions and classes can be defined in files called modules. Many standard Python modules are included with GTC. For example, math and cmath define basic mathematical operations for real and complex numbers.

GTC has a module structure of its own. The different GTC modules are all accessible by name (or through a shorter alias). For example, the function reporting.budget, which is part of the reporting module (alias rp) is useful for obtaining an uncertainty budget, as this short example shows:

x1 = ureal(1,0.1,label='x1')
x2 = ureal(1,0.1,label='x2')
x3 = ureal(1,0.1,label='x3')

x4 = x1 + x2
x5 = x2 + x3

x6 = x4 + x5

print "u(x6) = %G" % uncertainty(x6)
for u_cpt in reporting.budget(x6):
    print " %s: %G" % u_cpt

The results are

u(x6) = 0.244949
 x2: 0.2
 x1: 0.1
 x3: 0.1

Errors

When programming errors occur, an exception is raised. This halts execution and generates an error message called a traceback.

Example: RuntimeError

An exception is raised when an attempt is made to define an uncertain number with a negative uncertainty

>>> x = ureal(1,-1)
Traceback (most recent call last):

  File "<console>", line 1, in <module>

RuntimeError: invalid uncertainty: -1

The error message is invalid uncertainty: -1 and the exception is an instance of a RuntimeError. The problem occurred while executing the "<console>" file, which is actually the GTC command window.

If a file containing the same faulty command had been executed, the error message would look slightly different. Here, executing a file called simple_error.py, containing x = ureal(1,-1) in line 1, we see:

C:\>gtc simple_error.py
Traceback (most recent call last):

  File "simple_error.py", line 1, in <module>
    x = ureal(1,-1)

RuntimeError: invalid uncertainty: -1

The first line now refers to the file by name and identifies the code that caused the problem. The RuntimeError error message invalid uncertainty: -1 is given at the bottom, as before.

Other types of error

A RuntimeError is just one of a number of built-in Python exceptions. However, the basic structure of traceback messages remain the same.

Here is an example where a ValueError is raised while performing a mathematical operation:

>>> x = ureal(-1,1)
>>> sqrt(x)
Traceback (most recent call last):

  File "<console>", line 1, in <module>

ValueError: math domain error

The exception was raised while executing sqrt(x) and the problem is signaled as a math domain error.

Footnotes

[1]The parentheses around these numbers are important: they define a type of Python sequence called a tuple.
[2]“A Non-Programmer’s Tutorial” <http://en.wikibooks.org/wiki/Non-Programmer’s_Tutorial_for_Python_2.6>
[3]A list of beginner guides for non-programmers is available here: http://wiki.python.org/moin/BeginnersGuide/NonProgrammers
[4]The Python Help file distributed with GTC can be used to quickly check if a symbol is in fact a Python operator. Open the Help file and type the symbol (or symbols) into the index line. If the symbol is part of Python, the Help file will provide some reference information.