Delphi Array Initializations
Asked Answered
G

3

6

This is somewhat a continuation of my previous question, found here. Essentially, I'm trying to test the dll/functions with a basic example, but I'm getting 'E2010 - incompatible types: AInteger/ADouble and Set' and 'E1012 - constant expression violates subrange bounds' errors on my arrays. I get (somewhat) what it's trying to say, but can't figure out what I should be fixing. For example:

var
  n: Integer; 
  Ap, Ai: AInteger;
  Ax, b: ADouble;

begin
  // Initializations
  n := 5;
  Ap := [0, 2, 5, 9, 10, 12]; <- E2010
  Ai := [0, 1, 0, 2, 4, 1, 2, 3, 4, 2, 1, 4]; <- E2010
  Ax := [2, 3, 3, -1, 4, 4, -3, 1, 2, 2, 6, 1]; <- E2010 and E1012
  b := [8, 45, -3, 3, 19]; <- E1012

where the AInteger and ADouble types are my arrays:

ADouble = array[0..High(Integer) div SizeOf(Double) - 1] of Double;
AInteger = array[0..High(Integer) div SizeOf(Integer) - 1] of Integer;

and should be initialized this way (according to Rudy's Delphi page and other C-to-Pascal sources) since they were written as double Ax[] in C. I'm sure there's something simple I'm doing wrong or can change for the sake of testing my dll, but maybe I'm googling wrong because I can't find an example/solution. So, in question form:

Q1: Is E1012 referring to

"And if you do things like these [AInteger and ADouble], be sure not to come too close to High(Integer), since the compiler might complain that the data structure would be too large." (quoted from Rudy's page)

Q2: How should I change this code?

Thanks in advance for any help.

Guillema answered 17/6, 2013 at 17:58 Comment(3)
It might help if you describe exactly what you're trying to do with your arrays. The code you've written to initialize your arrays has never worked in any version of Delphi/Pascal, and Rudy's pages don't say they should be AFAICT, because it's invalid syntax and Rudy knows better (if it in fact says that, please provide a link). Delphi only allows initialization of constant arrays (not with that syntax) or dynamic arrays using the pseudo-constructor syntax. It's hard to tell you how you should change it because you've not made clear what you're trying to do.Electroballistics
This looks like a sparse matrix solver. Which one are you using out of interest. I like Tim Davis's CSparse which I've wrapped by compiling the C code to .obj and then linking in the exact same way that you are doing.Achlorhydria
Ken - sorry, the link I'm referring to involves C-to-pascal conversions. Rudy's page mentions it [here](rvelthuis.de/articles/articles-convert.html here) under the "Array Parameters" subsection. @David That is exactly the solver I'm working with, glad to know that the way I've decided to go is done by someone else haha.Guillema
M
11

You can do this with such a syntax.

Defining your array as such:

ADouble = array[0..High(Integer) div SizeOf(Double) - 1] of Double;

will initialize an array of integer of the size of the whole 32 bit RAM! You will never be able to allocate such a variable (only on Win64, but you will use 4 GB of RAM for storing only 6 integers)! :)

Your array needs to be dynamic, i.e. to have a changing size at runtime. So you have to define it as such:

type
  AInteger =  array of integer;

Such arrays can't be assigned directly, in the current state of the language, AFAIR.

So you need to write such a function:

procedure SetArray(var dest: AInteger; const values: array of integer);
begin
  SetLength(dest,Length(values));
  move(values[0],dest[0],length(values)*sizeof(integer));
end;

And you can use either a constant array as source:

const
  C: array[0..5] of Integer = (0, 2, 5, 9, 10, 12);
var
  Ap: AInteger;
begin
  SetArray(Ap,C);

Or use an open-array parameter:

var
  Ai: AInteger;
begin
  SetArray(Ai,[0, 2, 5, 9, 10, 12]);

Of course, the 2nd solution sounds closer to what you expect.

Update: for newer versions, you can of course use a dynamic array constructor, as such:

var
  Ai: AInteger;
begin
  Ai := AInteger.Create(0,2,5,9,10,12);

Update 2: since XE7, you can use another cleaner syntax:

var
  Ai: AInteger;
begin
  Ai := [0,2,5,9,10,12];
Marathi answered 18/6, 2013 at 5:37 Comment(5)
You can create a dynamic arrays directly, Ai := AInteger.Create(0,2,5,9,10,12);Padegs
Thanks! I was initially working with dynamic arrays as you've defined them, but it was suggested I change my code to the way I defined it above. Makes me feel good to know I initially had the right idea.Guillema
Or simply Ap := [0, 2, 5, 9, 10, 12];, since Delphi XE7. That is, what the OP was initially trying to do.Ridgepole
@Jean-ClaudeArbaut Indeed. But the OP is using XE2.Marathi
But now the question may be useful to other users as well (I believe it's the goal of SO). Thanks for the update.Ridgepole
A
3

It looks to me as though you have sparse solver code written in C and are trying to link that to your Delphi program. I think you have a fundamental problem in the way you have declared your external imports. Rather than answer the question you asked directly, I'll show you what I believe to be the right way to declare and call such external imports.

The first thing I would say is that the large static array types that you have declared are not what you need here. Those array types can sometimes be useful, but only really when you cast another array to PADouble = ^ADouble. In your situation you don't need those arrays at all and I do suggest that you remove them.

I'm going to assume that you are calling a function called solve that takes n, nz, Ap, Ai, Ax and b as input parameters and returns x as an output parameter. The function returns x such that A*x=b where A is the n dimensional square sparse matrix specified by Ap, Ai and Ax. The nz parameter specifies the number of non-zero elements. No doubt the actual function will vary in details, but the concepts will be just the same. For example, it's common to infer nz from Ap[n], but those details are for you to resolve.

I suggest that you declare the function to receive the parameters as pointers to the first element. So the function declarations looks like this:

function solve(
  n: Integer; 
  nz: Integer;
  Ap: PInteger;
  Ai: PInteger;
  Ax: PDouble;
  b: PDouble;
  x: PDouble
): Integer; cdecl; external;

Then you need to populate your sparse matrix arrays. Declare these as dynamic arrays:

var
  Ap: TArray<Integer>;
  Ai: TArray<Integer>;
  Ax: TArray<Double>;
....
SetLength(Ap, n);
Ap[0] := ...;
....
SetLength(Ai, nz);
Ap[0] := ...;
....
SetLength(Ax, nz);
Ax[0] := ...;
....

I expect that you will only know the value of n, nz etc. at runtime and that the content of the matrix will be filled out using loops and so on. The code in the question is presumably test code to try and test out the external code. Arnaud's answer gives you sound advice on how to populate a dynamic array.

You will also need to initialise b and x:

var
  b: TArray<Double>;
  x: TArray<Double>;
....
SetLength(b, n);
b[0] := ...;
....
SetLength(x, n);
// no need to initialise values of x[i] since it is the output

Now you can call the function:

var
  retval: Integer;
....
retval := solve(n, nz, PInteger(Ap), PInteger(Ai), PDouble(Ax), 
  PDouble(b), PDouble(x));

One final point, converning the use of generic arrays. Since you are using a modern Delphi I do suggest that you use generic dynamic arrays. So, instead of array of ... you should use TArray<...>. The reason for this is that generic types have different type compatibility rules from old-style dynamic arrays. In the code above, for example, b and x are assignment compatible. But if they were declared like this:

var
  b: array of Double;
  x: array of Double;

then they would not be assignment compatible. You can get around that issue by declaring a type, TDoubleArray = array of Double. However, if you use the generic array then you can use generic container classes such as TList<T> than return values of type TArray<T> which you can readily consume.

I know this isn't quite the question that you asked but I have a feeling that it may be useful to you.

Achlorhydria answered 18/6, 2013 at 8:15 Comment(7)
+1 I am using TDavis's Sparse package, thanks so much for the tips! I wasn't aware of TArray<T> at all, glad I learned about it before I spent hours debugging compatibility issues. This may not answer the exact questions posted, but it gives a lot of advice (and will more than likely save me loads of time overall). I really appreciate it.Guillema
Which routines in CSparse are you planning to call. I'm doing LU solve. I also wonder how you are planning on creating your matrix. I collect triplets and then convert them to sparse column storage. That conversion is quite tricky. I actually did it by porting some code of Tim's to Delphi. If this is going in a commercial product I hope you remember to pay Tim for the licence. His code is really superb.Achlorhydria
I'll also be using LU solve, and in the future an A matrix will be provided and I'll have to "create" the required Ap, Ai, etc. vectors. The matrices themselves will be, on average, quite large (n=10^5). I really do like his code, but then again I love numerical analysis/integration, linear algebra, and programming.Guillema
Sounds like I already have exactly what you need! Getting from triplets to sparse format is a little tricky. That's what is liable to stump you I think.Achlorhydria
Cool! I'm currently working on debugging some of my code using his demo code (since he gives the answers) but I haven't worked on the future initialization and data conditioning.Guillema
the arrays aren't being passed to the c function at all. I did a "trace in" to the c function (the .c file), and they come up as Ap/Ai ...[0] 0 (0x00000000) (only one element) under the list of local variables. Whether I use PInteger and PInteger(Ap) or just AInteger and Ap (delclaration and function parameter, respectively). I'm not sure what happened, any suggestions?Guillema
I made a new question since it required a bit of code.Guillema
R
0

This question has nothing to do with open arrays, so please remove that tag from your question.

The bracket syntax you are using declares a Set of values, which is basically a special type of bitmask where each bit corresponds to the value in that position. In other words, [0, 2, 5, 9, 10, 12] is a Set of Integer containing 6 elements, where bit 0 refers to value 0, bit 1 refers to value 2, bit 2 refers to value 5, and so on. A Set cannot be assigned directly to an array like you are attempting to do.

Revolute answered 17/6, 2013 at 22:3 Comment(2)
I won't be so rude. AFAIK [0, 2, 5, 9, 10, 12] can also be an open array parameter containing arrays, so can be affected, via a function, to an array of integer. See my answer.Marathi
Was introduced in Delphi XE7.Ridgepole

© 2022 - 2024 — McMap. All rights reserved.