Skip to content
William Wood edited this page Mar 25, 2021 · 3 revisions

Ranges

Often you will want to generate a "list" of numbers. You can do this JISA by using the static methods within the Range class. These will reach return a Range<...> object that you can then iterate over. The available methods (thus types of range are):

// Equally-spaced range with a specified number of elements
Range<Double>  a = Range.linear(double start, double stop, int numSteps);

// Range of whole numbers from start to stop inclusive represented as floating-point numbers
Range<Double>  b = Range.linear(int start, int stop);

// Equally-spaced range with a specified step-size
Range<Double>  c = Range.step(double start, double stop, double stepSize);

// Range of whole numbers from start to stop inclusive, represented as integers
Range<Integer> d = Range.count(int start, int stop);

// Geometric sequence from start to stop in specified number of steps
Range<Double>  e = Range.exponential(double start, double stop, int numSteps);

// Geometric sequence from start until stop with specified geometric/multiplicative factor
Range<Double>  f = Range.geometric(double start, double stop, double factor);

// The same number, a given number of times
Range<Double>  g = Range.repeat(double value, int numTimes);

All ranges generated this way are done so by using Java's BigDecimal arithmetic which is designed to avoid rounding errors.

Range Objects

When generating a range, you will be given a Range object. There a slightly different types of these, for instance Range<Integer> objects are ranges containing integer values whereas Range<Double> objects contains floating-point values. For example, if you make a count range then you will get an integer range but if you make a linear range then they will be double values:

// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
Range<Integer> count  = Range.count(1, 10);

// [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 ]
Range<Double>  linear = Range.linear(1, 10);

These objects are iterable meaning you can stick them straight into a for-loop:

Java

for (double v : Range.linear(1, 5)) {
    System.out.println(v);
}

Kotlin

for (v in Range.linear(1, 5)) {
    println(v)
}

Python

for v in Range.linear(1, 5):
    print(v)

which will result in the output:

1.0
2.0
3.0
4.0
5.0

Linear Ranges

You can create sequences of equally-spaced numbers where

by use of linear(...) or step(...) depending on whether you want to define the range by the number of elements within it or by the step size, .

By Number of Steps

To create a linear range of numbers you have a few options. The one you'll likely use most often is linear(...):

Range.linear(double start, double stop, int numSteps);

This allows you to create a range starting a value and ending at another with a specified number of steps. For instance, if we wanted to go from 10 to 20 in 11 steps:

Range.linear(10, 20, 11);

The result of this would be a step-size of 1.0. In this case then we are actually creating the range of integer values from 10 to 20 inclusive. For ranges such as these we can just specify the start and end values (so-long as they are integers):

Range.linear(int start, int stop);

In our case we could write:

Range.linear(10, 20);
// is the same as
Range.linear(10, 20, 11);

Despite all of the values in such a range being integers mathematically, they are not represented as integers in code. They are still double values (ie with a decimal point). For instance, 10 is actually 10.0. This is useful as things like SMU objects require double values to be supplied for setting voltage/current values even if they are to supply an integer amount of volts/amps.

However, if you need a range of integers (ie a Range<Integer> object) then you can use count(...) instead:

Range<Integer> range = Range.count(int start, int stop);

// as opposed to
Range<Double>  range = Range.linear(int start, int stop);

Ranges don't have to always "count up" they can also "count down". For example to start at 20 and go down to 10 in integer steps:

Range.linear(20, 10, 11);
Range.linear(20, 10);
Range.count(20, 10);

By Step Size

Instead of defining a range based on the number of steps and letting JISA calculate the required step-size, you can specify the step-size yourself. This is done by using step(...) instead of linear(...):

Range.step(double start, double stop, double stepSize);

For instance, to go from 10.0 to 20.0 in steps of 0.5:

Range.step(10, 20, 0.5);

Like with linear(...) you can go down in value insted of up. You do not need to negate the step-size although doing so will have no effect:

Range.step(20, 10, 0.5);
// produces the same range as
Range.step(20, 10, -0.5);

Geomatric Ranges

Ranges where the mapping from one element to the next is by a multiplicative factor, ie:

can be achieved by use of the exponential(...) and geometric(...) methods depending on whether you want to specify the number of elements or the multiplicative factor, $A$.

By Number of Steps

To produce a geometric series with a defined number of elements:

Range.exponential(double start, double stop, int numSteps);

For instance:

Range.exponential(1, 100, 3);

will produce [1.0, 10.0, 100.0].

By Multiplicative Factor

To instead define the range by the value of the multiplicative factor, use geometric(...) like so:

Range.geometric(double start, double stop, double factor);

For instance:

Range.geometric(1, 32, 2);

will give us: [1.0, 2.0, 4.0, 8.0, 16.0, 32.0]

Transforming Ranges

After creating a range, you can:

  • Reverse the order of values
  • Append the reverse of the range onto the end
  • Make the range repeat itself n times
  • Cyclically shift elements in the range by n spaces (right: +ve, left: -ve)
  • Randomly shuffle the order of elements in the range

These are achieved by use of the following, respective, methods:

Range<T> reversed   = range.reverse();
Range<T> mirrored   = range.mirror();
Range<T> repeated   = range.repeat(int n);
Range<T> shifted    = range.shift(int n);
Range<T> shuffled   = range.shuffle();

These are inteded to be called in a chained-fashion (most likely within a for loop), for example (in Kotlin):

for (x in Range.linear(1, 3).mirror()) {
    println(x)
}

will output:

1.0
2.0
3.0
3.0
2.0
1.0

An example of an application would be sweeping the voltage on an SMU both ways:

for (voltage in Range.linear(0, 60).mirror()) {

    smu.setVoltage(voltage)
    
    // Measure something...

}

Matrix Conversion

You can convert a Range object into a RealMatrix by use of reshape(...), column() and row() like so:

RealMatrix matrix    = range.reshape(int rows, int cols);
RealMatrix colMatrix = range.column();
RealMatrix rowMatrix = range.row();

For instance, writing the following:

RealMatrix matrix = Range.linear(1, 9).reshape(3, 3);

will produce the following matrix:

whereas

RealMatrix matrix = Range.linear(1, 9).row();

gives:

and

RealMatrix matrix = Range.linear(1, 9).column();

gives:

Clone this wiki locally