Genetic Programming with the Mandelbrot Set - artificial-intelligence

I'm reading a chapter in this fascinating book about using genetic programming to interactively evolve images. Most of the function set is comprised of simple arithmetic and trig functions (which really operation on and return images). These functions make up the internal nodes of the parse trees that encode our images. The leaves of the tree, or the terminal values, are random numbers and x,y coordinates.
There's a section about adding iterative functions of the complex plane to the function set:
Say the genetics inserts a particular Mandelbrot set as a node somewhere in
a bushy tree. The function expects two arguments: mandel(cReal, cImag), treating
them as real and imaginary coordinates in the complex plane. If the genome
just happened to supply the pixel coordinates (x,y), and mandel() were the root
node, you would get the familiar Mset. But chances are that cReal and cImag are themselves the results of whole branches of functions, with many instances of coordinates
x,y scattered out among the leaves. Enter the iteration loop, orbit
around for a while, and finally escape with some measure of distance to the Mset
attractor, such as the number of iterations.
My question is how would you make a Mandelbrot set renderer as a function that takes the real and imaginary coordinates of a point on the complex plane as arguments and returns a rendering of the Mandelbrot set?

I'm not sure if this actually answers your question, but my understanding of the text you quoted simply says that the mandel function is just another function (like multiplication, min, max, addition, etc) that can appear in your genetic program.
The mandel function, like the multiplication function, takes two arguments (in_1 and in_2) and returns a single value. Whereas the multiplication function just returns in_1 * in_2, the mandel function might do something like this:
int mandel(int in_1, int in_2) {
x = 0
y = 0
iteration = 0
max_iteration = 1000
while( x*x + y*y <= (2*2) && iteration < max_iteration ) {
xtemp = x*x - y*y + in_1
y = 2*x*y + in_2
x = xtemp
++iteration
}
if( iteration == max_iteration ) return 0
else return iteration
}
If your whole genetic program tree consists of nothing but the mandel function with one input as x and the other input as y, then repeatedly evaluating your program for a bunch of different (x,y) values and saving the result will give you a nice picture of the Mandelbrot set.
Of course, the neat thing about genetic programming is that the inputs can be fancier than just x and y. For example, what would the result look like if one input was x and the other input was x + 2*y? Or if one input was x and the other was mandel(x,y)?

Related

recursion linked list ? I think

I am very new and I have trouble understanding the macro steps I need to learn how to effectively code. These assignments feel extremely abstract and have to learn everything about recursion before I can even do it. Coding up a program is not easy, and I do really well when someone "helps me stay between the mayonnaise and mustard" so to speak. What am I doing wrong and what direction do I need to continue in?
I was thinking that I needed to sort the list first then have two seperate functions for merge sort and insertion sort per the assignment:
You are spending most of your time at home in this pandemic. It is of most importance for people to be aware of where other people, who
are infected with COVID-19 are, and who they've been near. Keeping
track of this information is known as "contact tracing."
You've heard that there might be some very high paying jobs
if you can show your contract tracing skills to the
government, so you've decided to write a little program to highlight
those skills.Your area can be modeled on the Cartesian plane. You are
located at the point (x, y). In addition, you have the Cartesian
coordinates of all people currently infected with COVID-19.
What you would like to do is write a program that sorts
these locations based on their distance from you, followed by
handling queries. The queries are of the form of a point you are
thinking of visiting. Your program should identify if someone who is
infected is at that location, and if so, what their rank is on the
sorted list of infected people. If no one is infected at
that location, you should correctly identify this.
Note: There are many important implementation restrictions for this assignment, so to make sure everyone reads these, the section on
implementation restrictions will be next, changing the order of the
sections as compared to other assignments.
Implementation Restrictions
You must use a specified combination of Merge Sort and Insertion Sort to sort the point data. Specifically, for each input
case, a threshold value, t, will be given. If the subsection of the
array to sort has t or fewer values to sort, Insertion Sort should be
used. Otherwise, Merge Sort should be used.Further details about the
comparison used for the sorting are below.
You must store your coordinates in a struct that contains two integer fields.
You must write a function compareTo which takes in two pointers, ptrPt1 and ptrPt2, to coordinate structs and returns a
negative integer if the point pointed to by ptrPt1 is closer to you
than the point pointed to by ptrPt2, 0 if the two locations pointed to
by both are identical locations, and a positive integer if the point
pointed to by ptrPt1 is farther from you than the point pointed to by
ptrPt2. Exceptions to this will be when the two pointers are pointing
to points that are the same distance from you, but are distinct
points. In these cases, if ptrPt1's x coordinate is lower
than ptrPt2's x coordinate, a negative integer must be
returned.Alternatively, if ptrPt1's x coordinate is greater than
ptrPt2's x coordinate a positive integer must be returned. Finally, if
the x coordinate of both points is the same, if ptrPt1's y coordinate
is lower than ptrPt2's y coordinate, a negative integer must be
returned. If ptrPt1's y coordinate is greater than ptrPt2's y
coordinate, a positive integer must be returned.
Since your location must be used for sorting, please make the variable that stores your x and y coordinates global. Your program
should have no other global variables.
A Binary Search function must
be used when answering queries.
Your sort function should take in
the array to be sorted,the length of the array as well as the
threshold value, t, previously mentioned. This function should NOT
be recursive. It should be a wrapper function.
The recursive sort
function (you can call this mergeSort) should take in the
array, a starting index into the array, an ending index into the
array and the threshold value t. In this function, either recursive
calls should be made OR a call to an insertion sort function should be
made.
The Problem
Given your location, and the location of each person who has COVID-19, sort the list by distance from you from shortest to
longest, breaking ties by x-coordinate (lower comes first),
and then breaking those ties by y coordinate (lower comes
first). After sorting, answer several queries about points in the
coordinate plane. Specifically, determine if a query point contains
someone who is infected or not. If so, determine that person's ranking
on the sorted list in distance from you.
The Input(to be read from standard input)-Your Program Will Be Tested on Multiple Files
The
first line of the input contains 5 integers separated by spaces. The
first two of these values are x and y (|x|, |y| ≤ 10000), representing
your location. The third integer is n (2 ≤ n ≤ 106), representing the
number of infected people. The fourth integer is s (1 ≤ s ≤ 2x105),
representing the number of points to search for. The last
integer, t (1 ≤ t≤ 30), represents the threshold to be used for
determining whether you run Merge Sort of Insertion Sort. The next
n lines of the input contain x and y coordinate values, respectively,
separated by spaces, representing the locations of infected people.
Each of these values will be integers and the points will be distinct
(and also different from your location) and the absolute value of x
and y for all of these coordinates will not exceed 10,000.Then the
next s lines of the file contains x and y coordinate values for
searching. Both values on each line will be integers with an absolute
valueless than or equal to 10,000.
The Output (to be printed to standard out)
The first n lines of output should contain the coordinates of the people infected,
sorted as previously mentioned. These lines should have the
x-coordinate, followed by a space, followed by the y-coordinate.The
last s lines of output will contain the answers to each of the s queries
in the input. The answer for a single query will be on a line by
itself. If the point queried contains an infected person, output a
line with the following format:
x y found at rank R
, where (x, y) is the
query point, and R is the one-based rank of that infected person in
the sorted list. (Thus, R will be 1 more than the array index in which
(x, y) is located, after sorting.) If the point queried does NOT
contain an infected person, output a line with the following format:
x y not found
Sample Input
(Note: Query points in blue for clarity. last five)
0 0 14 5 53
1 -6
-2 -4
3 4
-4 2
4 -1
3 2
2 0
-5 -4
-2 -6
6 4
4 -2
4 0
5 -4
6 2
-13 1
0 -5
my code so far
#include <stdio.h>
int x = 0;//global coordinates
int y = 0;
typedef struct {
int xInput, yInput;
}coordinates;
void scanPoints(coordinates[], int infectedPeople);
void scanSearchValues(coordinates[], int pointsToSearch);
void SortPoints(coordinates[], int);
int lessThan(coordinates[], int, int);
void printPoints(coordinates[], int);
void
scanPoints(coordinates pts[], int infectedPeople){
for (int i = 0; i < infectedPeople; i++){
scanf("%d %d", &pts[i].xInput, &pts[i].yInput);
}
}
void
scanSearchValues(coordinates pts[], int pointsToSearch){
for (int i = 0; i < pointsToSearch; i++){
scanf("%d %d", &pts[i].xInput, &pts[i].yInput);
}
}
void
sortPoints(coordinates pts[], int infectedPeople){
int i, start, min_index, temp;
for (start = 0; start < infectedPeople - 1; start++) {
min_index = start;
for (i = start + 1; i < infectedPeople; i++) {
if (lessThan(pts, i, min_index)) {
min_index = i;
}
}
if (min_index != start) {
coordinates temp = pts[start];
pts[start] = pts[min_index];
pts[min_index] = temp;
}
}
}
int
lessThan(coordinates pts[], int p, int q) {
if ((pts[p].xInput < pts[q].xInput) || ((pts[p].xInput == pts[q].xInput) && (pts[p].yInput < pts[q].yInput))) {
return 1;
}
}
int
main(int argc, const char * argv[]) {
int infectedPeople;
int pointsToSearch;
int threshold;
scanf("%d%d", &x, &y);
if(x > 10000 || y > 10000 )
return 0;
scanf("%d", &infectedPeople);
if(infectedPeople < 2 || infectedPeople > 1000000)
return 0;
scanf("%d", &pointsToSearch);
if(pointsToSearch < 1 || pointsToSearch > 200000)
return 0;
scanf("%d", &threshold);
if(threshold < 1 || threshold > 30)
return 0;
return 0;
}
This is a challenging exercise for someone new to programming, but the first step is to read the problem description carefully. It might help to print it out on paper, so that you can easily mark it up with highlighter and / or pen. Additionally, you may be intimidated by all the details specified in the exercise. Don't be! Although some make work for you, most make decisions for you. The latter kind save you work, and do you exactly the service you asked of us: help you stay on track.
One of the keys to programming is learning to divide a problem into smaller pieces. Sometimes those pieces will also need to be divided into even smaller pieces. Many of these pieces will correspond naturally to functions, and accordingly, a second key to programming is recognizing how to choose the pieces so that they have well-defined inputs and outputs, and, to some extent, so that pieces can be re-used. In your case, the overall problem statement gives you a starting point for performing such an analysis:
Given your location, and the location of each person who has COVID-19,
sort the list by distance from you from shortest to longest, breaking ties by x-coordinate (lower comes first), and then breaking
those ties by y coordinate (lower comes first). After sorting, answer
several queries about points in the coordinate plane. Specifically,
determine if a query point contains someone who is infected or not. If
so, determine that person's ranking on the sorted list in distance
from you.
(Emphasis added.) The three main pieces I see there are
read and store input data
sort the data
analyze the result and produce output
Reading the input
The implementation restrictions in the problem description have a lot to say about how you read and store the data. In particular,
You must store your coordinates in a struct that contains two integer fields.
You've prepared such a structure type.
Since your location must be used for sorting, please make the variable that stores your x and y coordinates global. Your program should have no other global variables.
Reading the restrictions carefully, I think the expectation is that you use the coordinates structure to represent all coordinates appearing in the program, including the (one) global variable representing your own coordinates.
Your sort function should take in the array to be sorted
You mentioned a linked list, but this indicates that you are expected to store the data in an array, not a linked list. From my more experienced vantage point, I have more reasons to believe that that is the expectation.
The detailed description of the input format gives you additional guidance on how to perform the reading, as of course the code needs to be suited to the data. So, read the first line of input to get the main program parameters, and store them appropriately. Among those is the number of infected person records to read; you'll need to store all those in memory in order to sort them and answer multiple queries about them, so allocate an array of structs large enough to hold them, then proceed to read those data.
You could similarly read and store the queries in advance, but I would suggest instead performing the required sorting first, and then processing each query immediately after reading it, without storing the whole list of queries.
Sorting the data
You write,
I was thinking that I needed to sort the list first then have two seperate functions for merge sort and insertion sort
Yes, I too read the problem description to be asking for separate merge sort and insertion sort functions -- and that's not what you seem presently to be providing. It also asks for a wrapper function that accepts the input and passes it on to the appropriate sort implementation, either (recursive) merge sort or insertion sort. Note that the wrapper function does not itself sort the list, except inasmuch as it passes the list to one of the other functions for sorting:
void sortCoordinates(coordinates coords[], int count, int threshold) {
if (/* your condition here */) {
insertionSortCoordinates(coords, count);
} else {
mergeSortCoordinates(coords, count);
}
}
(The names and most of the details of these particular functions are at your discretion.)
Additionally, the restrictions help you out again here, though you need to read between the lines a bit. Both sorting and searching require that you have a way to compare the objects in the list, and look! The restrictions tell you exactly what form that should take:
You must write a function compareTo which takes in two pointers, ptrPt1 and ptrPt2, to coordinate structs [...]
In other words,
int compareTo(coordinates *ptrPt1, coordinates *ptrPt2) {
/* your code here */
}
Your insertion and merge sort functions and also your binary search function (see below) will compare structures (when needed) by calling that function.
Do pay careful attention to the restrictions, though, as one of the decisions they make for you is the name for this function: compareTo, not lessThan. Deviating from the restrictions in this regard would likely cost you some marks.
Computing the output
Whether you read and store the query lines in advance or process them as you read them (having first sorted the input), the main computation to be performed is a binary search of the coordinates, per restriction 5. You'll wan't a function for that, maybe
int binarySearch(coordinates *target, coordinates coords[]) {
/* your code here: returns 1-based rank if found, zero if not found */
}
Again, this function will use your compareTo function to compare coordinate structures. Note in particular that if implemented correctly according to the restrictions, compareTo() will return zero if and only if the two objects being compared are equal.
in
int
lessThan(coordinates pts[], int p, int q) {
if ((pts[p].xInput < pts[q].xInput) || ((pts[p].xInput == pts[q].xInput) && (pts[p].yInput < pts[q].yInput))) {
return 1;
}
}
if ((pts[p].xInput < pts[q].xInput) || ((pts[p].xInput == pts[q].xInput) && (pts[p].yInput < pts[q].yInput))) is false the function does not return, introducing an undefined behavior in sortPoints
you wanted
int lessThan(coordinates pts[], int p, int q)
{
return ((pts[p].xInput < pts[q].xInput) || ((pts[p].xInput == pts[q].xInput) && (pts[p].yInput < pts[q].yInput)));
}
in sortPoints the variable temp in int i, start, min_index, temp; is useless, remove it
In main you only read the 5 values, nothing more, so the other functions are useless, and you do not print nor compute something
Not sure my answer is really usefull ...

find iso-cost points on a 3d grid efficiently with minimum costing of points

I have a 3d grid where in each point (x,y,z) on the grid is associated with a cost value. The cost of any point (x,y,z) is not known in advance. To know the cost, we need to make a complex query which is really expensive. One thing we know about the object is that cost is monotonically non-decreasing in all 3 dimensions.
Now given a cost C, I need to find the points (x,y,z) on the surface which have cost C. This has to be done by costing only bare minimum. How to solve my problem?
When I searched online, I am getting contour identification related techniques but all these techniques assume all point's cost is known in advance like Marching cubes method etc. In my case major metric is the number of points costed should be minimum.
It would be helpful if some one can suggest a way to get approximate locations at least if not exact.
Rewritten explanation:
(original text, in case it might clarify the idea to someone, is kept unchanged below the line)
We have some function f(x,y,z) in three dimensions, and we wish to find the surface f(x,y,z) = c. Since the function yields a single number, it defines a scalar field, and the surface we are looking for is the isosurface c.
In our case, evaluating the function f(x,y,z) is very costly, so we wish to minimize the number of times we use it. Unfortunately, most isosurface algorithms assume the opposite.
My suggestion is to use a similar isosurface walk as Fractint could use for two-dimensional fractals. Code-wise, it is complicated, but it should minimize the amount of function evaluations needed -- that was exactly the purpose it was implemented in Fractint.
Background / History:
In the late 1980s and early 1990s, I encoutered a fractal drawing suite Fractint. Computers were much slower then, and evaluating each point was painfully slow. A lot of effort was made in Fractint to make it display the fractals as fast as possible, but still accurately. (Some of you might remember the color-cycling it could do, by rotating the colors in the palette used. It was hypnotic; here is a Youtube clip from the 1995 documentary "Colors of Infinity", which both color-cycles and zooms in. Calculating a full-screen fractal could take hours (at high zoom factors, close to the actual fractal set), but then you could (save it as an image and) use the color-cycling to "animate" it.)
Some of those fractals were, or had regions, where the number of iterations needed was monotonically non-decreasing toward the actual fractal set fractal -- that is, no "islands" sticking out, just steady occasional increase in iteration steps --, one fast evaluation mode used edge tracing to locate the boundary where the number of iterations changed: in other words, the regions filled with a single color. After closing a region, it then traced towards the center of that region to find the next iteration edge; after that was closed too, it could just fill the donut- or C-shaped region between those boundaries with the correct constant color, without evaluating the function for those pixels!
Here, we have a very similar situation, except in three dimensions instead of two. Each isosurface is also two-dimensional by definition, so really, all that changes, is how we walk the boundary.
The walk itself is similar to flood fill algorithms, except that we walk in three dimensions, and our boundary is the isosurface we're tracing.
We sample the original function in a regular grid, say an N×N×N grid. (This is not the only possibility, but it is the easiest and most useful case, and what the OP is doing.)
In general, the isosurfaces will not pass through the grid points exactly, but between the grid points. Therefore, our task is to find the grid cells the isosurface passes through.
In an N×N×N regular grid, there are (N-1)×(N-1)×(N-1) cubic cells:
Each cell has eight corners at (x,y,z), (x+1,y,z), (x,y+1,z), (x+1,y+1,z), (x,y,z+1), (x+1,y,z+1), (x,y+1,z+1), and (x+1,y+1,z+1), where x,y,Z ∈ ℕ are the integer grid coordinates, 0 ≤ x,y,z ≤ N-2 are the integer grid coordinates.
Carefully note the integer grid coordinate limits. If you think about it, you'll realize that an N×N×N grid has only (N-1)×(N-1)×(N-1) cells, and since we use the grid coordinates for the corner closest to origin, the valid coordinate range for that corner is 0 to N-2, inclusive.
If f(x,y,z) increases monotonically in each dimension, then isosurface c passes through cell (x,y,z) if
f(x,y,z) ≤ c
and at least one of
f(x+1, y, z) > c
f(x, y+1, z) > c
f(x+1, y+1, z) > c
f(x, y, z+1) > c
f(x+1, y, z+1) > c
f(x, y+1, z+1) > c
f(x+1, y+1, z+1) > c
If f(x,y,z) is monotonically non-decreasing -- that is, its partial derivatives are either zero or positive at all points --, then the above locates two-dimensional isosurfaces, and the outer surface for isovolumes (volumes where f(x,y,z) is constant). The inner surface for isovolumes c are then those cells (x,y,z) for which
f(x,y,z) < c
and at least one of
f(x+1, y, z) ≥ c
f(x, y+1, z) ≥ c
f(x+1, y+1, z) ≥ c
f(x, y, z+1) ≥ c
f(x+1, y, z+1) ≥ c
f(x, y+1, z+1) ≥ c
f(x+1, y+1, z+1) ≥ c
Extension to any scalar function:
The approach shown here actually works for any f(x,y,z) that has only one maximum within the sampled region, say at (xMAX,yMAX,zMAX); and only one minimum, say at (xMIN,yMIN,zMIN); with no local maxima or minima within the sampled region.
In that case, the rule is that at least one of f(x,y,z), f(x+1,y,z), f(x,y+1,z), f(x+1,y+1,z), f(x,y,z), f(x+1,y,z), f(x,y+1,z), f(x+1,y+1,z) must be below or equal to c, and at least one above or equal to c, and not all equal to c.
Also, an initial cell an isosurface c passes through can then always be found using a binary search between (xMAX,yMAX,zMAX) and (xMIN,yMIN,zMIN), limiting the coordinates to 0 ≤ xMAX,yMAX,zMAX,xMIN,yMIN,zMIN ≤ N-2 (to only consider valid cells, in other words).
If the function is not monotonic, locating an initial cell the isosurface c passes through is more complicated. In that case, you need a different approach. (If you can find the grid coordinates for all local maxima and minima, then you can do binary searches from global minimum to local maxima above c, and from local minima below c to global maximum.)
Because we sample the function f(x,y,z) at intervals, we implicitly assume it to be continous. If that is not true -- and you need to show also the discontinuities -- you can augment the grid with discontinuity information at each point (seven boolean flags or bits per grid point, for "discontinuity from (x,y,z) to (x+,y+,z+)"). The surface walking then must also respect (not cross) such discontinuities.
In practice, I would use two arrays to describe the grid: one for cached samples, and one for two flags per grid point. One flag would describe that the cached value exists, and another that the walking routine has already walked the grid cell at that point. The structure I'd use/need for walking and constructing isosurfaces (for a monotonically non-decreasing function sampled in a regular grid) would be
typedef struct {
size_t xsize;
size_t ysize;
size_t zsize;
size_t size; /* xsize * ysize * zsize */
size_t xstride; /* [z][y][x] array = 1 */
size_t ystride; /* [z][y][x] array = xsize */
size_t zstride; /* [z][y][x] array = xsize * ysize */
double xorigin; /* Function x for grid coordinate x = 0 */
double yorigin; /* Function y for grid coordinate y = 0 */
double zorigin; /* Function z for grid coordinate z = 0 */
double xunit; /* Function x for grid coordinate x = 1 */
double yunit; /* Function y for grid coordinate y = 1 */
double zunit; /* Function z for grid coordinate z = 1 */
/* Function to obtain a new sample */
void *data;
double *sample(void *data, double x, double y, double z);
/* Walking stack */
size_t stack_size;
size_t stack_used;
size_t *stack;
unsigned char *cell; /* CELL_ flags */
double *cache; /* Cached samples */
} grid;
#define CELL_UNKNOWN (0U)
#define CELL_SAMPLED (1U)
#define CELL_STACKED (2U)
#define CELL_WALKED (4U)
double grid_sample(const grid *const g, const size_t gx, const size_t gy, const size_t gz)
{
const size_t i = gx * g->xstride + gy * g->ystride + gz * g->zstride;
if (!(g->cell[i] & CELL_SAMPLED)) {
g->cell[i] |= CELL_SAMPLED;
g->cache[i] = g->sample(g->data, g->xorigin + (double)gx * g->xunit,
g->yorigin + (double)gy * g->yunit,
g->zorigin + (double)gz * g->zunit);
}
return g->cache[i];
}
and the function to find the cell to start the walk on, using a binary search along the grid diagonal (assuming non-decreasing monotonic function, so all isosurfaces must cross the diagonal):
size_t grid_find(const grid *const g, const double c)
{
const size_t none = g->size;
size_t xmin = 0;
size_t ymin = 0;
size_t zmin = 0;
size_t xmax = g->xsize - 2;
size_t ymax = g->ysize - 2;
size_t zmax = g->zsize - 2;
double s;
s = grid_sample(g, xmin, ymin, zmin);
if (s > c) {
return none;
}
if (s == c)
return xmin*g->xstride + ymin*g->ystride + zmin*g->zstride;
s = grid_sample(g, xmax, ymax, zmax);
if (s < c)
return none;
if (s == c)
return xmax*g->xstride + ymax*g->ystride + zmax*g->zstride;
while (1) {
const size_t x = xmin + (xmax - xmin) / 2;
const size_t y = ymin + (ymax - ymin) / 2;
const size_t z = zmin + (zmax - zmin) / 2;
if (x == xmin && y == ymin && z == zmin)
return x*g->xstride + y*g->ystride + z*g->zstride;
s = grid_sample(g, x, y, z);
if (s < c) {
xmin = x;
ymin = y;
zmin = z;
} else
if (s > c) {
xmax = x;
ymax = y;
zmax = z;
} else
return x*g->xstride + y*g->ystride + z*g->zstride;
}
}
#define GRID_X(grid, index) (((index) / (grid)->xstride)) % (grid)->xsize)
#define GRID_Y(grid, index) (((index) / (grid)->ystride)) % (grid)->ysize)
#define GRID_Z(grid, index) (((index) / (grid)->zstride)) % (grid)->zsize)
The three macros above show how to convert the grid index back to grid coordinates.
To walk the isosurface, we cannot rely on recursion; the call chains would be too long. Instead, we have a walk stack for cell indexes we should examine:
static void grid_push(grid *const g, const size_t cell_index)
{
/* If the stack is full, remove cells already walked. */
if (g->stack_used >= g->stack_size) {
const size_t n = g->stack_used;
size_t *const s = g->stack;
unsigned char *const c = g->cell;
size_t i = 0;
size_t o = 0;
while (i < n)
if (c[s[i]] & CELL_WALKED)
i++;
else
s[o++] = s[i++];
g->stack_used = o;
}
/* Grow stack if still necessary. */
if (g->stack_used >= g->stack_size) {
size_t *new_stack;
size_t new_size;
if (g->stack_used < 1024)
new_size = 1024;
else
if (g->stack_used < 1048576)
new_size = g->stack_used * 2;
else
new_size = (g->stack_used | 1048575) + 1048448;
new_stack = realloc(g->stack, new_size * sizeof g->stack[0]);
if (new_stack == NULL) {
/* FATAL ERROR, out of memory */
}
g->stack = new_stack;
g->stack_size = new_size;
}
/* Unnecessary check.. */
if (!(g->cell[cell_index] & (CELL_STACKED | CELL_WALKED)))
g->stack[g->stack_used++] = cell_index;
}
static size_t grid_pop(grid *const g)
{
while (g->stack_used > 0 &&
g->cell[g->stack[g->stack_used - 1]] & CELL_WALKED)
g->stack_used--;
if (g->stack_used > 0)
return g->stack[--g->stack_used];
return g->size; /* "none" */
}
The function that verifies that the isosurface passes through the current cell, reports those to a callback function, and walks the isosurface, would be something like
int isosurface(grid *const g, const double c,
int (*report)(grid *const g,
const size_t x, const size_t y, const size_t z,
const double c,
const double x0y0z0,
const double x1y0z0,
const double x0y1z0,
const double x1y1z0,
const double x0y0z1,
const double x1y0z1,
const double x0y1z1,
const double x1y1z1))
{
const size_t xend = g->xsize - 2; /* Since we examine x+1, too */
const size_t yend = g->ysize - 2; /* Since we examine y+1, too */
const size_t zend = g->zsize - 2; /* Since we examine z+1, too */
const size_t xstride = g->xstride;
const size_t ystride = g->ystride;
const size_t zstride = g->zstride;
unsigned char *const cell = g->cell;
double x0y0z0, x1y0z0, x0y1z0, x1y1z0,
x0y0z1, x1y0z1, x0y1z1, x1y1z1; /* Cell corner samples */
size_t x, y, z, i;
int r;
/* Clear walk stack. */
g->stack_used = 0;
/* Clear walked and stacked flags from the grid cell map. */
i = g->size;
while (i-->0)
g->cell[i] &= ~(CELL_WALKED | CELL_STACKED);
i = grid_find(g, c);
if (i >= g->size)
return errno = ENOENT; /* No isosurface c */
x = (i / g->xstride) % g->xsize;
y = (i / g->ystride) % g->ysize;
z = (i / g->zstride) % g->zsize;
/* We need to limit x,y,z to the valid *cell* coordinates. */
if (x > xend) x = xend;
if (y > yend) y = yend;
if (z > zend) z = zend;
i = x*g->xstride + y*g->ystride + z*g->zstride;
if (x > xend || y > yend || z > zend)
return errno = ENOENT; /* grid_find() returned an edge cell */
grid_push(g, i);
while ((i = grid_pop) < g->size) {
x = (i / g->xstride) % g->xsize;
y = (i / g->ystride) % g->ysize;
z = (i / g->zstride) % g->zsize;
cell[i] |= CELL_WALKED;
x0y0z0 = grid_sample(g, x, y, z);
if (x0y0z0 > c)
continue;
x1y0z0 = grid_sample(g, 1+x, y, z);
x0y1z0 = grid_sample(g, x, 1+y, z);
x1y1z0 = grid_sample(g, 1+x, 1+y, z);
x0y0z1 = grid_sample(g, x, y, 1+z);
x1y0z1 = grid_sample(g, 1+x, y, 1+z);
x0y1z1 = grid_sample(g, x, 1+y, 1+z);
x1y1z1 = grid_sample(g, 1+x, 1+y, 1+z);
/* Isosurface does not pass through this cell?!
* (Note: I think this check is unnecessary.) */
if (x1y0z0 < c && x0y1z0 < c && x1y1z0 < c &&
x0y0z1 < c && x1y0z1 < c && x0y1z1 < c &&
x1y1z1 < c)
continue;
/* Report the cell. */
if (report) {
r = report(g, x, y, z, c, x0y0z0, x1y0z0,
x0y1z0, x1y1z0, x0y0z1, x1y0z1,
x0y1z1, x1y1z1);
if (r) {
errno = 0;
return r;
}
}
/* Could the surface extend to -x? */
if (x > 0 &&
!(cell[i - xstride] & (CELL_WALKED | CELL_STACKED)) &&
( x0y1z0 >= c || x0y0z1 >= c ))
grid_push(g, i - xstride);
/* Could the surface extend to -y? */
if (y > 0 &&
!(cell[i - ystride] & (CELL_WALKED | CELL_STACKED)) &&
( x0y0z1 >= c || x1y0z0 >= c ))
grid_push(g, i - ystride);
/* Could the surface extend to -z? */
if (z > 0 &&
!(cell[i - zstride] & (CELL_WALKED | CELL_STACKED)) &&
( x1y0z0 >= c || x0y1z0 >= c ))
grid_push(g, i - zstride);
/* Could the surface extend to +x? */
if (x < xend &&
!(cell[i + xstride] & (CELL_WALKED | CELL_STACKED)) &&
( x0y1z0 >= c || x0y0z1 >= c ))
grid_push(g, i + xstride);
/* Could the surface extend to +y? */
if (y < xend &&
!(cell[i + ystride] & (CELL_WALKED | CELL_STACKED)) &&
( x1y0z0 >= c || x0y0z1 >= c ))
grid_push(g, i + ystride);
/* Could the surface extend to +z? */
if (z < xend &&
!(cell[i + zstride] & (CELL_WALKED | CELL_STACKED)) &&
( x1y0z0 >= c || x0y1z0 >= c ))
grid_push(g, i + zstride);
}
/* All done. */
errno = 0;
return 0;
}
In this particular case, I do believe the isosurfaces are best visualized/described using a polygon mesh, with samples within a cell linearly interpolated. Then, each report() call produces one polygon (or one or more flat triangles).
Note that the cell has 12 edges, and the isosurface must cross at least three of these. Let's assume we have two samples at corners c0 and c1, spanned by an edges, with the two corners having coordinates p0=(x0,y0,z0) and p1=(x1,y1,z1) respectively:
if (c0 == c && c1 == c)
/* Entire edge is on the isosurface */
else
if (c0 == c)
/* Isosurface intersects edge at p0 */
else
if (c1 == c)
/* Isosurface intersects edge at p1 */
else
if (c0 < c && c1 > c)
/* Isosurface intersects edge at p0 + (p1-p0)*(c-c0)/(c1-c0) */
else
if (c0 > c && c1 < c)
/* Isosurface intersects edge at p1 + (p0-p1)*(c-c1)/(c0-c1) */
else
/* Isosurface does not intersect the edge */
The above check is valid for any kind of continuous function f(x,y,z); for non-monotonic functions the problem is just finding the relevant cells. The isosurface() function needs some changes (the checks wrt. x0y0z0..x1y1z1), according to the rules outlined earlier in this post, but it too can be made to work for any continuous function f(x,y,z) with little effort.
Constructing the polygon/triangle(s) when the samples at the cell corners are known, especially using linear interpolation, is very simple as you can see.
Note that there is usually no reason to worry about the order in which the edges of a cell are checked, as you will almost certainly use vector calculus and cross product in particular to orient the points and polygons. Or, if you like, you can do Delaunay triangulation on the points (3 to 12 for any function, although more than 6 points indicates there are two separate surfaces, I believe) to construct flat polygons.
Questions? Comments?
We have a scalar field f(x,y,z) in three dimensions. The field is costly to sample/evaluate, and we do so only at integer coordinates 0 ≤ x,y,z ∈ ℕ. To visualize the scalar field, we wish to locate one or more isosurfaces (surfaces with a specific f(x,y,z) value), using the minimum number of samples/evaluations.
The approach I'll try to describe here is a variant of the algorithm used in fractint, to minimize the number of iterations needed to draw certain fractals. Some fractals have large areas with the same "value", so instead of sampling every point within the area, certain drawing mode traced the edges of those areas.
In other words, instead of locating individual points of the isosurface c, f(x,y,z) = c, you can locate just one point, and then walk the isosurface. The walk part is a bit complicated to visualize, but it really is just a 3D variant of the flood fill algorithm used in simple computer graphics. (Actually, given the field is monotonically non-decreasing along each dimension, it'll actually be a mostly 2D walk, with typically just a few grid points other than those relevant to the isosurface c sampled. This should be really efficient.)
I'm pretty sure there are good peer-reviewed papers describing this very technique (probably in more than one problem domain), but since I'm too lazy to do a better search than a couple of minutes of Google searches, I leave it to others to find good references. Apologies.
For simplicity, for now, let's assume that the field is continuous and monotonically increasing along each dimension. Within an axis-oriented box of size N×N×N, the field will have a minimum at one corner at origin (0,0,0), a maximum at the far corner from origin, at (N,N,N), with all possible values between the minimum and maximum found along the diagonal from (0,0,0) to (N,N,N). In other words, that every possible isosurface exists and is a continuous 2D surface, excluding points (0,0,0) and (N,N,N), and every such surface intersects the diagonal.
If the field is actually non-continuous, we won't be able to tell, because of our sampling method. In practice, our sampling means we implicitly assume the scalar field is continuous; we will treat is as continuous, whether or not it really is!
If the function is actually monotonically increasing along each dimension, then it is possible to map f(x,y,z)=c to X(y,z)=x, Y(x,z)=y, Z(x,y)=z, although any one of the three is sufficient to define the isosurface c. This is because the isosurface can only cross any line spanning the box in at most one point.
If the function is monotonically non-decreasing instead, the isosurface can intersect any line spanning the box still only once, but the intersection can be wider (than a point) along the line. In practice, you can handle this by considering only the lower or upper surfaces of the isovolumes (volumes with a static field); i.e. only the transition from-lower-than-c-to-c-or-greater, or the transition from-c-or-lower-to-greater-than-c.
In all cases, you're not really looking for the isosurface value c, but trying to locate where a pair of the field samples crosses c.
Because we sample the field at regular grid points, and the isosurface rarely (if ever) intersects those grid points exactly, we divide the original box into N×N×N unit-sized cubes, and try to find the cubes the desired isosurface intersects.
Here is a simple illustration of one such cube, at (x,y,z) to (x+1,y+1,z+1):
When the isosurface intersects a cube, it intersects at least one of the edges marked X, Y, or Z, and/or the diagonal marked D. In particular, we'll have f(x,y,z) ≤ c, and one or more of:
f(x+1,y,z) > c (isosurface c crosses the cube edge marked with X)
(Note: In this case, we wish to walk along the y and z dimensions)
f(x,y+1,z) > c (isosurface c crosses the cube edge marked with Y)
(Note: In this case, we wish to walk along the x and z dimensions)
f(x,y,z+1) > c (isosurface c crosses the cube edge marked with Z)
(Note: In this case, we wish to walk along the x and y dimensions)
f(x+1,y+1,z+1) > c (isosurface c crosses the cube diagonal, marked with D)
(Note: In this case, we may need to examine all directly connected grid points, to see which direction we need to walk to.)
Instead of doing a complete search of the original volume, we can just find one such cube, and walk along the cubes to discover the cubes the isosurface intersects.
Since all isosurfaces have to intersect the diagonal from (0,0,0) to (N,N,N), we can find such a cube using just 2+ceil(log2(N)) samples, using a binary search over the cubes on the diagonal. The target cube (i,i,i) is the one for which f(i,i,i) ≤ c and f(i+1,i+1,i+1) > c. (For monotonically non-decreasing fields with isovolumes, this shows the isovolume surface closer to origin as the isosurface.)
When we know that the isosurface c intersects a cube, we can use basically three approaches to convert that knowledge to a point (that we consider the isosurface to intersect):
The cube has eight corners, each at a grid point. We can pick the corner/grid point with the field value closest to c.
We can interpolate -- choose an approximate point -- where the isosurface c intersects the edge/diagonal. We can do linear interpolation without any extra samples, since we already know the samples at the ends of the crossed edge/diagonal.
If u = f(x,y,z) < c, and v > c is the sample at the other end, the linearly interpolated intersection point along that line occurs at (c-u)/(v-u), with 0 being at (x,y,z), and 1 being at the other end of the edge/diagonal (at (x+1,y,z), (x,y+1,z), (x,y,z+1), or (x+1,y+1,z+1)).
You can use a binary search along the edge/diagonal, to find the intersection point.
This needs n extra samples per edge/diagonal, to get the intersection point at n-bit accuracy along the edge/diagonal. (As the original grid cannot be too coarse compared to the details in the field, or the details will not be visible anyway, you normally use something like n=2, n=3, n=4, or n=5 at most.)
The intersection points for the isosurface c thus obtained can be used for fitting some surface function, but I have not seen that in real life. Typically, Delaunay triangulation is used to convert the point set to a polygon mesh, which is then easy to visualize.
Another option is to remember which cube ((x,y,z)) and edge/diagonal (X, Y, or Z edge, or D for diagonal) each point is related to. Then, you can form a polygon mesh directly. Voxel techniques can also be used to quickly draw partially transparent isosurfaces; each view ray examines each cube once, and if the isosurface is present, the isosurface intersection points can be used to interpolate a surface normal vector, producing very smooth and accurate-looking isosurfaces with raycasting/raytracing methods (without creating any polygon mesh).
It seems to me I this answer is in need of editing -- at minimum, some sleep and further thought, and clarifications. Questions, suggestions, and even edits are welcome!
If there is interest from more than just the OP, I could try and see if I can cobble together a simple example C program for this. I've toyed with visualizing simulated electronic structures, and those fields are not even monotonic (although sampling is cheap).
You should look into this article which talks about the 2-dimensional case and gives you a great insight into the different methodologies:
http://leetcode.com/2010/10/searching-2d-sorted-matrix.html
In my opinion, the step-wise linear search (in part II there) would be a great first step for you because it's very easy to apply to the 3-d case and it really doesn't require a lot of experience to understand.
Because this is so straightforward and still very efficient, I would go with this and see if it fits your needs for the kind of data you're working with in 3-d.
However, if your only goal is performance, then you should apply the binary partition to 3-d. This gets a little bit more complex because the 'binary partition' he talks about essentially becomes a 'binary plane partition'.
So you don't have a line partitioning your matrix into 2 possible smaller matrices.
Instead you have a plane partitioning your cube into 2 possible smaller cubes.
To make the search in that plane (or matrix) efficient, you would first have to implement one of his methods :). Then you repeat everything with the smaller cubes.
Keep in mind that implementing this in a very efficient way (i.e. keeping memory access in mind) is not trivial.
I'll give this answer in an effort to try to minimize the number of costs calculated. Matt Ko links to a good solution, but it assumes a cheap cost function and a matrix-based data, which you don't seem to have either of. The approach I give requires much closer to O(log N + k) calls to the cost function, where k is the number of points with the desired cost. Note that this algorithm with some performance optimiztions could be made to be O(N) on a 3D matrix with little chance to performance cost function call wise, though it's a fair bit more complicated.
The psudeocode, which is based on techniques used in quickselect looks like this:
While there are still points under considerations:
Find the ideal pivot point and calculate it's cost
Remove the pivot from the point set
If the cost is the desired cost then:
Add the pivot to the solution set
Else:
Separate the points into 3 groups:
G1. Those that are in in the pivot's octant `VII`
G2. Those have the same x, y, or z of the pivot
G3. Those that are not in the pivot's octant `VII`
# Note this can be done in O(N)
If all points are in group 2:
Use 1D binary searches in each dimension to find points with the desired cost
Else:
Compute the cost of the pivot
Keep all points in group 2
If the pivot cost is greater than desired:
Keep only the points in group 1
Else:
Keep only the points in group 3
The pivot selected based on the points inside and outside of octant VII from that line. Points on the any of the 3 lines that form the octants are dealt with later if needed (G2).
The ideal pivot point is the such that the number of points in group 1 (G1) and group 3 (G3) are as close to equal as possible. To look at it mathematically would be along the lines of maximizing the larger of the two over the smaller of the two, or maximize(max(|G1|,|G3|) / min(|G1|,|G3|) ). Even a fairly naive algorithm looking for the ideal pivot point can find it in O(N^2) (an O(N log N) algorithm likely exists), but it takes O(N^3) to compute the cost of the ideal pivot after it's found.
After the ideal pivot is found and it's cost computed, each iteration should see on average roughly half the remaining points discarded, which again, results in only O(log N + k) calls to the cost function.
Final Note:
In retrospect, I'm not sure special consideration for group 2 is actually required as it's probably in group 3, but I'm not 100% sure. However, separating it out doesn't seem to change the Big O, so I didn't see a need to change it, though doing so would simplify the algorithm slightly.
This is not an answer per se, just slightly generalized example C code.
(The code was too long to include verbatim.)
The basic implementation is in grid.h (pastebin link).
In it, I've tried to make a distinction between grid coordinates (0 ≤ x, y, z ≤ size-1) and cell coordinates (0 ≤ x, y, z ≤ size-2). In particular, note the span type. Each cell spans a range of values: either interpolated, or the discrete set of the samples at the eight corners of the cell. Because this example uses linear interpolation to determine where within each cell the isosurface intersects the edges or a diagonal, I assume continuous spans.
I didn't realize how important cells spanning values is for edge cases, before I implemented this example code. That is why the OP and I discussed the edge cases in the comments to my other answer, and why the logic outlined in my other answer alone does not handle the edge cases correctly.
Since OP's particular case is not that common/interesting, this example is much more generic (and therefore quite unoptimized for the OP's case). In fact, this example only requires that the function has no local minima or maxima (saddle points and constant regions are allowed); just one minimum and one maximum within the gridded region. Minimum and maximum do not need to be point-like; they can be continous regions.
As such, at grid generation time, we do not know which cells contain the minimum and maximum. (In OP's case, the scalar field is monotonically non-decreasing and limited to the positive octant, so the minimum is at 0,0,0 and maximum at size-1,size-1,size-1.)
To find the minimum and maximum, I implemented two functions, that start from the best corner in the grid (having the smallest or greatest sample value). grid_maximum_cell() walks non-decreasing cells, and grid_minimum_cell() walks non-increasing cells. Since the scalar field is sampled, we implicitly assume it is continuous. As long as there are no local maxima or minima where the walk might stop, the walk will reach the correct cell in relatively few samples. (This search could be optimized much further, though. Consider these two functions just starting points for your own implementation. The OP does not need these at all, of course.)
(Actually, the requirement for the sampled scalar field is that each isosurface is continous, and that all isosurfaces intersect the line drawn from the minimum and maximum cells found using the above two functions.)
The function grid_isosurface() can be used to locate the cells the desired isosurface (field value) passes through. The last parameter is a function pointer. That function is called once for each cell the isosurface passes through. (Note the indexing order for the corner samples, [x][y][z].)
grid_isosurface() locates an initial cell the desired isosurface passes through using a binary search (on the line from the cell containing the minimum sample, to the cell containg the maximum sample). It then traces the surface, using the flood-fill-like algorithm outlined in my answer.
For an example, grid.c (pastebin link) uses the above include file, to evaluate the scalar field
f(x, y, z) = x3 + y3 + z3 + x + y - 0.125·(x·y + x·z + y·z + x·y·z).
On my Linux machine, I compiled and ran the example using
gcc -Wall -std=c99 -Wno-unused -O2 grid.c -o isosurface
./isosurface 50 -1.0 1.0 0.0 > out-0.0
./isosurface 50 -1.0 1.0 0.5 > out-0.5
./isosurface 50 -1.0 1.0 1.0 > out-1.0
and used Gnuplot to plot out the three isosurfaces:
splot "out-0.0" u 1:2:3 notitle w dots, "out-0.5" u 1:2:3 notitle w dots, "out-1.0" u notitle w dots
which leads to this pretty nice point cloud (rotatable in Gnuplot):
When the grid is initially generated, 14 samples are taken to locate the maximum and minimum cells. Tracing the isosurfaces required additional 18024, 18199, and 16953 samples, respectively; note that much fewer samples are needed for the second and further isosurfaces, if you do them consecutively on the same grid.
The total grid above contains 51×51×51 = 132651 samples, so tracing one isosurface required about 13% of the grid points to be sampled. For a 101×101×101 grid, the samples needed drops down to about 7%; for a 201×201×201 grid, down to 3.5%; for a 501x501x501 grid, to 1.4% (1.7M out of 125.75M samples).
None of this code is optimized for OP's case, nor optimized in general. A sample cache is used to minimize the number of samples needed in general, but the grid_isosurface() isosurface walking function, and the initial grid_minimum_cell() and grid_maximum_cell() functions can be modified to require slightly fewer samples. For larger grids, I don't expect the optimizations to make much of a difference, but for very small grids and very slow functions to evaluate, it might be worthwhile.
If the intent is to generate a polygon mesh for each isosurface, I recommend generating each polygon in the callback function, not from the overall generated point cloud. Using the edge/diagonal intersections like in the above example program, you get all the vertices for the polygon spanning that cell (no caches or such are needed). All you need is to order the edge intersection points correctly.
Questions? Comments? Bug fixes? Suggestions?

Arithmetic in complex numbers isn't working properly

The context is simply a function that solves input quadratic equations. Here's the section of the code which malfunctions:
case NEGATIVE:
printf("\n\n beforehand sqrt(discriminant) is %f%+fi",creal(csqrt(eqn->discriminant)), cimag(csqrt(eqn->discriminant)));
eqn->complex_root = (-(eqn->b)+csqrt(eqn->discriminant))/(2*eqn->a);
printf("\n\n result after full formula is %f%+fi", creal(eqn->complex_root),cimag(eqn->complex_root));
break;
And the output text I get with x^2+5 = 0 as the trial equation. The middle three lines are debugging text, where type simply refers to what kind of solutions the program should expect for the quadratic (0 means 2 complex solutions of course):
Please enter the coefficients of the quadratic separated by spaces: 1 0 5
The coefficients entered are a=1, b=0 and c=5.
TYPE RETURNED: 0
beforehand sqrt(discriminant) is 0.000000+4.472136i
result after full formula is 0.000000+0.000000i
The equation defined by 1x^2 +0x +5=0 has two complex solutions, x = 0+0i and x = 0-0i.
I simply have no idea why the results reduce to 0. What's going on?
Forgot to assign the variable holding these values as complex. i.e. used float instead of float _Complex.

Matlab: how to use an array in dsolve function?

I have an ODE system of two equations, but want to minimize it with using just one equation with the result of the other.
1)
t=linspace(0,2,3);
syms x(t) y(t);
inits='x(0)=2,y(0)=0';
[x,y]=dsolve('Dx=y','Dy=(y*2)-x', inits)
x = 2*exp(t) - 2*t*exp(t);
y = -2*t*exp(t)
xx=eval(vectorize(x));
xx = 2.0000; 0; -14.7781
yy=eval(vectorize(y));
yy = 0; -5.4366; -29.5562
After I had got the results, I tried to solve it just with one equation and use xx array in Dy equation.
2)
inits='y(0)=0';
[y]=dsolve('Dy=(y*2)-xx', inits);
y = xx/2 - (xx*exp(2*t))/2
yy=eval(vectorize(y));
yy = 0; 0; 396.0397
The values are not the same as it was in the first example. How to get the same result using array?
One problem seems to be that variable xx is not symbolic, so the symbolic solver appears to be considering it as a constant.
A bigger problem is that you really haven't identified how exactly you want matlab to treat the xx values as a continuous function, when it's merely a vector of three points! The fact that you are even expecting the output to be the same for the second case indicates some kind of misunderstanding to me.
But to make this definite, lets assume that you want it to treat xx as a ZOH (zero order held) continuous signal. To handle this symbolically I believe you would need to construct the ZOH signal explicitly using Heaviside functions.
Alternative you could solve it numerically using ode45 for example
t = [0,1,2];
xx = [2, 0, -14.7781];
dydy = #(t,y) 2*y - xx(1+trunc(t));
y = ode45(dydt, [0,2], 0);
This will return yy values of [0, -6.39, -47.21] at the t values of [0, 1, 2] respectively.
This corresponds well with the theoretical values (calculated by hand) of [0, 1-e^2, e^2-e^4] for the ZOH system.
As you can see the above answer is much more in line with your original solution of yy = [0, -5.4366, -29.5562]. Naturally however the two systems differ, as the first one was fed with a continuous time exponential signal whereas the second system was fed with a very coarsely sampled approximation!
You make the two more similar by sampling at a faster rate (finer time interval), and also by interpolating the intersample points with something better than a ZOH.
Update:
Thank you. Maybe can you help me with creating ZOH continuous signal? How to do that?
In the above example I created a ZOH in my derivative function (dydx) by using the three given points in the xx vector and accessing these using "xx(1+trunc(t))". This uses trunc (truncate) to explicitly hold the input constant during the inter-sample (non integer) times.
Seeing as your ODE is linear, you could also use the matlab function "lsim()" which allows you to directly specify the time vector and input vector, and also to directly specify the type of input interpolation (including ZOH, which is actually the default).
For example:
t=[0,1,2]
x=[2,0,-2*e^2]
num=-1
den=[1,-2]
mytf = tf(num,den)
y = lsim(mytf,x,t,0,'zoh');
As with my previous ode45 numerical solution, this gives the identical solution of,
y = [0.00000; -6.38906; -47.20909]
Update (again)
Thank you. Maybe can you help me with creating ZOH continuous signal? How to do that?
Re the symbolic solver. I don't have access to the Matlab symbolic library, but if you really want to use the symbolic solver, then as I explained previous, you can construct a continuous time ZOH signal using the heaviside (unit step) function. Something like the following should do it:
syms xzoh(t)
xzoh = xx(1)*heaviside(t) + (xx(2)-xx(1))*heaviside(t-1) + (xx(3)-xx(2))*heaviside(t-2)

Restricted 3 body simulation not working correctly

I am currently trying to do an assignment where i have to write a simulation for the restricted 3 body gravitational problem, with two fixed masses and one test mass. The information i have been given on the problem is: Check out this link and here is my program so far:
#include<stdlib.h>
#include<stdio.h>
#include <math.h>
int main (int argc, char* argv[])
{
double dt=0.005, x[20000],y[20000],xv,yv,ax,ay,mneg,mpos,time,radius=0.01;
int n,validation=0;
FILE* output=fopen("proj1.out", "w");
printf("\n");
if((argv[1]==NULL) || (argv[2]==NULL) || (argv[3]==NULL) || (argv[4]==NULL) || (argv[5]==NULL) || (argv[6]==NULL))
{
printf("************************ ERROR! ***********************\n");
printf("** Not enough comand line arguments input. **\n");
printf("** Please run again with the correct amount (6). **\n");
printf("*******************************************************\n");
validation=1;
goto VALIDATIONFAIL;
}
if((sscanf(argv[1], "%lf", &mneg)==NULL) || (sscanf(argv[2], "%lf", &mpos)==NULL) || (sscanf(argv[3], "%lf", &x[0])==NULL) ||
(sscanf(argv[4], "%lf", &y[0])==NULL) || (sscanf(argv[5], "%lf", &xv)==NULL) || (sscanf(argv[6], "%lf", &yv)==NULL) )
{
printf("************************* ERROR! ************************\n");
printf("** Input values must be numbers. Please run again with **\n");
printf("** with numerical inputs (6). **\n");
printf("*********************************************************\n");
validation=1;
goto VALIDATIONFAIL;
}
sscanf(argv[1], "%lf", &mneg);
sscanf(argv[2], "%lf", &mpos);
sscanf(argv[3], "%lf", &x[0]);
sscanf(argv[4], "%lf", &y[0]);
sscanf(argv[5], "%lf", &xv);
sscanf(argv[6], "%lf", &yv);
x[1]=x[0]+(xv*dt);
y[1]=y[0]+(yv*dt);
for(n=1;n<10000;n++)
{
if(x[n-1]>=(1-radius) && x[n-1]<=(1+radius) && y[n-1]>=(0-radius) && y[n-1]<=(0+radius))
{
printf("Test mass has collided with M+ at (1,0), Exiting...\n");
goto EXIT;
}
else if(x[n-1]>=(-1-radius) && x[n-1]<=(-1+radius) && y[n-1]>=(0-radius) && y[n-1]<=(0+radius))
{
printf("Test mass has collided with M- at (-1,0), Exiting...\n");
goto EXIT;
}
else
{
double dxn = x[n] + 1;
double dxp = x[n] - 1;
double mnegdist = pow((dxn*dxn + (y[n]*y[n])), -1.5);
double mposdist = pow((dxp*dxp + (y[n]*y[n])), -1.5);
ax = -(mpos*dxp*mposdist+mneg*dxn*mnegdist);
ay = -(mpos*y[n]*mposdist+mneg*y[n]*mnegdist);
x[n+1]=((2*x[n])-x[n-1] +(dt*dt*ax));
y[n+1]=((2*y[n])-y[n-1]+(dt*dt*ay));
fprintf(output, "%lf %lf\n",x[n-1], y[n-1]);
}
}
VALIDATIONFAIL:
printf("\n");
return(EXIT_FAILURE);
EXIT:
return(EXIT_SUCCESS);
}
My program is working to certain extent but i am getting some weird problems that i hope someone can help me with.
The main issue is that when the test mass gets to a point in its trajectory when it should go off and start to orbit about the other mass it instead just shoots off on a straight line to infinity! at first i thought it was that the masses were colliding so i put in the radius check, but in some cases this does work, in some cases it doesn't, and in some cases the masses collide earlier on before the trajectory goes wrong anyway so this clearly isn't the issue. I am not sure if i have explained that all too well so here is a picture to show you what i mean. (the simulation on the right is from here)
However, this is not always the case, sometimes instead of going in a straight line, the trajectory just goes crazy when it should go over to the other mass, like this:
I really have absolutely no idea whats going on i have spent days trying to figure this out but just cant seem to get anywhere, so any help in identifying where my problem is would be very much appreciated.
This is just too long to fit in a comment and I also might be of use to future visitors.
The proper choice of timestep for a given computation is not an easy task. The family of Verlet integrators are symplectic, which means that they preserve the phase space volume and hence should preserve the total energy of the system given infinite precision and an infinitely small timestep, but unfortunately real computers operate with finite precision and the human life is too short in order for us to wait for an infinite number of timesteps.
The Verlet integrator, like the one that you have implemented and the velocity Verlet scheme, have global error which is O(Δt2). It means that the algorithm is only quadratically sensitive to the timestep and in order to greatly improve the precision, one has to decrease the timestep accordingly by as many times as the square root of the desired precision improvement factor. Click on the into button of the Flash applet that you compare your trajectories with and you'll see that it uses a completely different integrator - the Euler-Cromer algorithm (also known as semi-implicit Euler method). It has different precision given the same timestep (actually it is worse than that of the Verlet scheme given the same timestep) and hence you cannot and should not directly compare both trajectories, rather only their statistical properties (e.g. mean energy, mean velocity, etc.)
My point was that you have to decrease the timestep, because it is too large to handle the cases when the test body comes too close to one of the gravitational centres. There is another problem hidden here and it is the finite numerical precision. Observe this term:
double dxp = x[n] - 1;
double mposdist = pow((dxp*dxp + (y[n]*y[n])), -1.5);
Whenever you subtract two closely valued floating point numbers (x[n] and 1.0), a very unfortunate event happens, known as precision loss as most of the higher significant bits in their mantissas cancel each other and in the end, after the normalisation step, you get a number with much less significant bits than the original two numbers. This precision loss gets even bigger as the result is then squared and used as a denominator. Note that this mostly happens near the axis of symmetry of the system where y[n] comes close to 0. Otherwise y[n] might be big enough so that dxp*dxp is only a tiny correction to the value of y[n]*y[n]. The net result is that the force would come out totally wrong near each fixed mass and would usually be greater in magnitude than the actual force. This is prevented in your case as you test for the point being outside the prescribed radius.
Greater forces lead to greater displacements given a fixed timestep. This also leads to an artificial increase in the total energy of the system, i.e. the test mass would tend to move faster than in a finer simulation. It also might happen that the test body ends up so close to the gravitational centre, that the huge force times the square of the timestep might give so huge a displacement, that your test mass would end up much far away, but this time with the increased total energy it would result in high kinetic energy and the body would practically be ejected from the simulation volume. This could also happen even if you compute the force with infinite precision - simply the displacement between two timesteps might be so large (because of the large timestep) that the system would make an unrealistic jump in the phase space to a completely different energy isosurface. And with gravity (as well as with electrostatics) it is so easy to get to such a case as the force increases as 1/r^2 and near the radius it is many orders of magnitude stronger than in the initial state.
One might come up with different rules of a thumb to estimate the size of the timestep given the largest expected force value, but in general the higher the maximum force, the lower the timestep should be. These kind of things can usually be roughly estimated given the initial conditions, which saves a lot of failed simulations due to "ejection" effects.
Now as the Verlet schemes are symplectic, the best way to control the correctness of the simulation is to observe the total energy of the system. Note that the velocity Verlet integrator is a bit better as it is numerically more stable (but still it has the same dependence of the accuracy on the square of the timestep). With the standard Verlet scheme you can get an approximation of the speed v[i] by taking (x[i+1] - x[i-1])/(2*dt). With the velocity Verlet, the speed is included explicitly in the equations.
Either way, it would make sense to take the speed and to compute the total energy of the system at each timestep and to observe the value. If the timestep is Just Right (tm), then the total should be almost conserved with relatively small oscillations around the mean value. If it goes crazy upwards, then your timestep is too big and should be decreased.
Decreasing the timestep increases the run-time of the simulation accordingly. One could also observe that in the far field the forces are small, the point moves slowly and a long timestep is just fine. Shorter timestep would not improve the solution there but would only increase the run-time. That's why people have invented the multi-timestep algorithms and also the adaptive timestep algorithms that automatically refine the solutions in the near field. Also a different method to compute the forces might be applied there by transforming the equations so as to not include subtraction of closely valued variables.
(well, this came out way larger than even several comments)
I found it difficult to understand your code, so I will try to be as helpful as I can and apologize, if I am telling you things you already know.
The best way I have found to calculate the physics for simulations involving gravity is to use Newtons Law of Universal Gravitation. This is given by the formula:
F = ( ( -G * M1 * M2 ) / ( R * R ) ) * r_unit_vector;
Where:
G ~= 6.67e-11,
M1 is the mass of the first object,
M2 is the mass of the second object,
R is the distance between the two objects: sqrt(pow(X2 - X1, 2) + pow(Y2 - Y1, 2))
X1 is the X-coordinate of object 1,
X2 is the X-coordinate of object 2,
Y1 is the Y-coordinate of object 1,
Y2 is the Y-coordinate of object 2.
r_unit_vector is a unit vector pointing from object 2 to object 1
struct r_unit_vector_struct{
double x, y;
}r_unit_vector;
r_unit_vector has a x component which is object 2's x-coordinate - object 1's x-coordinate,
r_unit_vector has a y component which is object 2's y-coordinate - object 1's y-coordinate.
To make r_unit_vector a unit vector, you must divide (both the x aand y components separately) by its length, which is given by sqrt(pow(r_unit_vector.x, 2) + pow(r_unit_vector.y - Y1, 2))
And you should all be ready to go! Hopefully this makes sense. If not, I will write you a class to do this stuff or explain it further if I can!

Resources