Working in ampl with sets containing the same number various times - indexing

I am doing an optimization problem. For that, some param have to take different values. I am using a Smolyak reduced grid. I have 9 realizations. I have constructed a code which includes in each set the values that the param has to take in each realization. Unfortunately, these param take the same values in various realizations, therefore making my code useless. I need a way to make ampl accept said sets.
I haven't found anything useful in ampl's book in the chapters dedicated to the sets
https://ampl.com/BOOK/CHAPTERS/08-sets1.pdf
https://ampl.com/BOOK/CHAPTERS/09-sets2.pdf
However I found this question, AMPL error, duplicate number for set, in which something similar happens. However, if I copy the code, the error remains.
This is where I define the sets, and the error occurs
param Level=9;
set slM1Set ordered := {45.4236, 45.3191, 45.5438,45.4236, 45.4236, 45.4236, 45.4236, 45.4236, 45.4236};
set slM2Set ordered := {3.8222, 3.8222, 3.8222, 3.8134, 3.8324, 3.8222, 3.8222, 3.8222, 3.8222};
set slP1Set ordered := {-37040,-37040,-37040,-37040,-37040,-47040,-27040, 37040,-37040};
set slP2Set ordered := {0, 0, 0, 0, 0, 0, 0, -577.350, 577.350};
param w_x;
param w_y;
param x1M;
param x2M;
and this is where I use them
for {a in 1..Level}
{
let w_x := member(a, slM1Set);
let w_y := member(a, slM2Set);
let x1M := member(a, slP1Set);
let x2M := member(a, slP2Set);
solve;
I need to know if I can change my code to work as intended, meaning that the loop will be executed 9 times, the first, the variable w_x will take the first value from slM1Set, w_y from slM2Set, x1M from slP1Set, x2M from slP2Set, in the second execution the variable w_x will take the second value from slM1Set, w_y from slM2Set, x1M from slP1Set, x2M from slP2Set and so on.

Unless there is some reason that you specifically need these to be sets, you're almost certainly better off handling them as indexed params.
param Level=9;
param slM1{1..Level} := ...
param slM2{1..Level} := ...
param slP1{1..Level} := ...
param slP2{1..Level} := ...
...
for {a in 1..Level}
{
let w_x := slM1[a];
let w_y := slM2[a];
let x1m := slP1[a];
let x2m := slP2[a];
solve;
}

Related

I couldn't find the error in my binary search function

I don't know why this function I made in pascal only gives the index of the number I'm searching for (n) and doesn't give -1 when it doesnt find it..
(i checked theres no problem with other functions the only problem is that is doesnt print the message 'num is not here' when it doesnt exist) i would also appreciate it if someone points out where my code could have been more efficient.
`
Function binary_search(L : Array Of Integer; n : Integer) : Integer;
Var
i, p, middle, first, last : Integer;
Begin
first := 0;
binary_search := -1;
last := Sizeof(L) Div Sizeof(L[0]);
While (first <= last) Do
Begin
middle := (first + last) Div 2;
If (middle = n) Then
Begin
binary_search := middle;
break;
End;
If (middle < n) Then first := middle +1;
If (middle > n) Then last := middle -1;
End;
End;
Begin
Write('num of elements in array : ');
read(m);
fillup(arr, m);
For i :=0 To m-1 Do
Begin
permutarr[i] := arr[i];
End;
Write('the num youre looking for : ');
read(A);
sort(arr, 1, m);
If (binary_search(arr, A)= -1) Then Writeln('the number isnt here') //this doesnt work
Else
Begin
For i:=0 To m-1 Do
Begin
If (permutarr[i] = binary_search(arr, A)) Then
Begin
Writeln('index : ', i);
break;
End;
End;
End;
End.
`
I am not familiar with your dialect of Pascal (it's been 15 years since I did any serious programming), but I'm quite sure that the chief problem is that
Your code is not looking at the entries of the array L at all!
All the comparisons in the function binary_search only involve the indices, middle, first, last. The first thing I would try is to edit all the comparisons with middle to use L[middle] instead (on lines 11, 16 and 17). Then your code will, at least, actually be looking at the array :-)
Other things:
I'm not 100 per cent sure that your code handles arrays of all lengths correctly (can't test right now). It might happen that first and last never meet. I'm probably wrong about this, because then your code would get stuck in an endless loop.
When I was coding I took care never to assign a value to the function_name, here binary_search prematurely. In other words, I would not be surprised to learn that your function always returns $-1$, because I fully expect the execution of a function to end the instant anything is assigned to it, so here at the line binary_search:=-1;. As I said, my dialect was different, and the recollection is "dated" at best.
Anyway, the problem in bold fits your description of the problematic behavior. You can test my theory by giving A a value that exceeds the length of the array. Then it should be unable to find it with your current code.

Adressing multiple, similar variables/objects in Delphi

I am writing a program that uses many shapes and I need to create a procedure to turn them all white. The shapes in question are named SectorBorder1 to SectorBorder20.
Is there a way to adress the shapes like this or similarly?
SectorBorder[X].brush.color := ClWhite;
Inc(X);
...where X is the number (obviously), instead of having to do:
SectorBorder1.brush.color := ClWhite;
SectorBorder2.brush.color := ClWhite;
...
SectorBorder20.brush.color := ClWhite;
So basically being able to differentiate names through a variable. This is the only way I could think of describing it. (Sorry, could someone also maybe include a better discription?) Any advice would be greatly apreciated.
Use an array
private
SectorBorders: array[1..20] of TShape;
procedure TMyForm.FormCreate(Sender: TObject):
begin
SectorBorders[1] := SectorBorder1;
..
SectorBorders[20] := SectorBorder20;
end;
procedure TMyForm.SetAllToWhite;
var
X: Integer;
begin
for X := Low(SectorBorders) to High(SectorBorders) do
SectorBorders[X].Brush.Color := clWhite;
end;
As an alternative to an array you can use a List. If you use the TList from System.Generics.Containers you can easily pass through all elements of the list.
You use it like this:
interface
uses System.Generics.Collections; // you need this to get the TList, you also need your other refereces of course
...
protected
pShapes: TList<TShape>;
procedure TMyForm.FormCreate(Sender: TObject):
var
nLoop: Integer;
begin
Self.pShapes:=TList<TShape>.Create;
nLoop:=0;
while(nLoop<Self.ComponentCount) do
begin
if(Self.Components[nLoop] is TShape) then
pList.Add(TShape(Self.Components[nLoop]));
Inc(nLoop);
end;
end;
procedure TMyForm.SetAllToWhite;
var
pShape: TShape;
begin
for pShape in Self.pShapes do
pShape.Brush.Color := clWhite;
end;
Choosing whether you want to use an array or a TList will be partially down to preference, but also down to what else you may want to do with the collection of TShapes and how they are managed within the object.
You can use the FindComponent method of the form.
shape:= FindComponent('SectorBorder' + IntToStr(i)) as TShape;
if shape <> nil then
pShape.Brush.Color := clWhite;
See http://docwiki.embarcadero.com/CodeExamples/Sydney/en/FindComponent_(Delphi)

Delphi ClientDataSet Sorting by changing IndexName

I've been learning about the ClientDataSet in delphi and how it can help sort my SQL database. The data is showing fine in my TDBGrid and i've enabled sorting by clicking on a header by changing the IndexField of the ClientDataset. I want to make it go descending on sorts sometimes though so have been trying to use 2 IndexNames outlined here https://stackoverflow.com/a/13130816/4075632
However, when I swap the IndexName from DEFAULT_ORDER to CHANGEINDEX, all data in my DBGrid disappears. I'm pretty new to all of this, and I know it will depend on my situation, but what are some of the ways this happens and I will try to troubleshoot them.
I have 1 TSQLConnection connected to TSQLQuery, and that connected to TDataSetProvider, and that connected to my ClientDataSet which leads to a TDataSource to the TDBGrid. Why might the ClientDataSet which is usually fine cause problems when I change its name? Please bear in mind that most of the settings are default because I'm not too sure about these components. Thanks, I hope you can provide some useful help and I'm sorry it may be difficult to se my situation.
Toby
I use the following code to build indexes for a clientdataset:
Procedure BuildIndices (cds: TClientDataSet);
var
i, j: integer;
alist: tstrings;
begin
with cds do
begin
open;
logchanges:= false;
for i:= 0 to FieldCount - 1 do
if fields[i].fieldkind <> fkCalculated then
begin
j:= i * 2;
addindex ('idx' + inttostr (j), fieldlist.strings[i], [], '', '', 0);
addindex ('idx' + inttostr (j+1), fieldlist.strings[i], [ixDescending], '', '', 0);
end;
alist:= tstringlist.create;
getindexnames (alist);
alist.free;
close;
end;
end;
As a result, there is an index 'idx0' for sorting column 0 ascending and 'idx1' for sorting column 0 descending; 'idx2' and 'idx3' for column 1, etc.
Then, in the grid's OnTitleClick event, I have the following
procedure Txxx.DBGrid1TitleClick(Column: TColumn);
var
n, ex: word;
begin
n:= column.Index;
try
dbgrid1.columns[prevcol].title.font.color:= clNavy
except
end;
dbgrid1.columns[n].title.font.color:= clRed;
prevcol:= n;
directions[n]:= not directions[n];
ex:= n * 2;
if directions[n] then inc (ex);
with clientdataset do
try
disablecontrols;
indexname:= 'idx' + inttostr (ex);
finally
first;
enablecontrols
end;
end;
In each form, I define an array of booleans ('directions'), one element per grid column. These elements track whether a column should be sorted ascending or descending.
The ClientDataSet comes with two predefined indexes: DEFAULT_ORDER and CHANGEINDEX, which are of no real use for your task, because you cannot tweak them to your needs. So you have to create your own indexes. A comprehensive description by Cary Jensen can be found in this article as well as in his highly recommended book about ClientDataSets.

SQL Query fails on empty result

I have a function that performs a query on a SQL database with an ADO connection, it is simply designed to provide a single result for a database entry that can only have one match for a SELECT type of query (i.e. get me the x value from ID 45, where there is and can only be one ID 45 entry).
The function works fine, until I hit a query that returns no results. The query just hangs, and the application cannot continue. Here is an example query string:
'SELECT Cost FROM MaterialCost ' +
'WHERE MaterialType = ''' + 'MS' +
''' AND Thickness = ''' + '0.250' + '''';
Again this exact string will work fine until I maybe query for something that I know before hand doesn't exist, which should return null or an empty string. Here is the function:
function SelectOneQuery(AQueryString : String; AADOConnectionString : String) : String;
var
ADOQuery: TADOQuery;
begin
//Create empty ADO Query, then connect to connection string
ADOQuery := TADOQuery.Create(nil);
ADOQuery.ConnectionString:=AADOConnectionString;
ADOQuery.SQL.Clear;
ADOQuery.SQL.Add(AQueryString);
ADOQuery.ExecSQL;
ADOQuery.Open;
//Set first query result and return first result
ADOQuery.First;
if(ADOQuery.Fields.Count > 0) then begin
result:=ADOQuery.Fields[0].Value;
end
else begin
result := '';
end;
end;
I added the fields count thing, but I'm not sure if that's helping at all. Basically, if there are no result, i want result := ''
You have a few problems in your code snippet:
The main problem is that you are checking FieldCount. FieldCount will always be nonzero because it contains the number of columns your query returns, regardless of the fact your query returned records or not. One option is to check RecordCount which represents the number of rows returned but a better option is to check EOF flag.
you are leaking ADOQuery. Always use try/finally blocks to create and cleanup objects.
ExecSQL is used for queries that don't return recordsets (like INSERT and DELETE),
use Open instead
No need to use First after Open
If you use the same query over and over you are better off using parameters, as a bonus your code will be more readable.
Example:
ADOQuery.SQL.Text := 'SELECT Cost FROM MaterialCost WHERE MaterialType = :MaterialType AND Thickness = :Thickness';
ADOQuery.Parameters.ParamByname('MaterialType').Value := 'MS';
ADOQuery.Parameters.ParamByname('Thickness').Value := 0.25;
Your function code should be something like this:
function SelectOneQuery(const AQueryString, AADOConnectionString: string): string;
var
ADOQuery: TADOQuery;
begin
Result := '';
ADOQuery := TADOQuery.Create(nil);
try
ADOQuery.ConnectionString := AADOConnectionString;
ADOQuery.SQL.Text := AQueryString;
ADOQuery.Open;
if not ADOQuery.EOF then
Result := ADOQuery.Fields[0].AsString;
finally
ADOQuery.Free;
end;
end;

Variable structure for database results

A lot of times when we query the database, we just need one column with varchar.
So I've made a nice function for querying the database and putting the results in a stringlist:
function Getdatatostringlist(sqlcomponent, sqlquery: string): TStringlist;
What I'm looking for now is basically the same function but for results with multiple columns where you don't know in advance what type the data is, be it varchar, int, datetime.
What kind of datastructure would be good to use here.
The reason I want this is that I try not to work on open datasets. I like much more to fetch all results into a temporary structure, close the dataset and work on the results.
After Kobiks reply about using in Memory datasets I came up with the following, it's fast put together to test the concept:
procedure TForm1.Button2Click(Sender: TObject);
var
MyDataSet : TAdoDataSet;
begin
MyDataSet := GetDataToDataSet('SELECT naam FROM user WHERE userid = 1', ADOConnection1);
try
Form1.Caption := MyDataSet.FieldByName('naam').AsString;
finally
MyDataSet.free;
end;
end;
function TForm1.GetDataToDataSet(sSql: string; AdoConnection: TADOConnection): TAdoDataSet;
begin
Result := TAdoDataSet.Create(nil);
Result.LockType := ltBatchOptimistic;
Result.Connection := AdoConnection;
Result.CommandText := sSql;
Result.Open;
Result.Connection := nil;
end;
I think this is something to build on.
You should use any disconnected in-memory TDataSet descendant, such as TClientDataSet.
Do not attempt to re-invent the wheel by storing a record-set in some new "Variant" structure. A TClientDataSet already contains all features you need to manipulate a "temporary" data structure.
Here is how you create a TClientDataSet structure:
cds.FieldDefs.Add('id', ftInteger);
cds.FieldDefs.Add('name', ftString, 100);
// ...
// create it
cds.CreateDataSet;
// add some data records
cds.AppendRecord([1, 'Foo']);
cds.AppendRecord([2, 'Bar']);
Many TDataSets has an ability to be used as an in-memory (client) datasets depending on the provider and LockType, for example a TADODataSet with LockType=ltBatchOptimistic could fetch results-set from the server, and then remain disconnected.
For exchanging Data with Excel this structure is usefull, might be useful for other purposes.
Function GetDatasetasDynArray(Ads: TDataset; WithHeader: Boolean = true): Variant;
// 20130118 by Thomas Wassermann
var
i, x, y: Integer;
Fields: Array of Integer;
begin
x := 0;
y := Ads.RecordCount;
if WithHeader then
inc(y);
SetLength(Fields, Ads.FieldCount);
for i := 0 to Ads.FieldCount - 1 do
if Ads.Fields[i].Visible then
begin
Fields[x] := i;
inc(x);
end;
SetLength(Fields, x);
Result := VarArrayCreate([0, y - 1 , 0, length(Fields) - 1], VarVariant);
y := 0;
if WithHeader then
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].DisplayLabel;
end;
inc(y);
end;
try
Ads.DisableControls;
Ads.First;
while not Ads.EOF do
begin
for i := Low(Fields) to High(Fields) do
begin
Result[y, i] := Ads.Fields[Fields[i]].Value;
end;
Ads.Next;
inc(y);
end;
finally
Ads.EnableControls;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
DynArray:Variant;
begin
DynArray := GetDatasetasDynArray(Adodataset1,true);
//DynArray[0,x] Header or First row
//DynArray[1,x] First row or SecondRow
Excel.Range.Value := DynArray;
end;
Why don't you like working with open datasets? They usually do not block the server. Copying the data from the dataset to whatever you want is extra overhead which is most likely not necessary.
A dataset provides exactly the functionality you want: A matrix with variable columns and rows.
EDIT: However, if you have iterate through the dataset often, you should consider creating a class holding the relevant information and then copy the data into a generic list, dictionary, tree or whatever you need as fast lookup structure.
Of course you could think of building something smart which can be as flexible as a dataset but: The more general things get, the poorer the performance (usually).