ampl / amplpy

Python API for AMPL
https://amplpy.ampl.com
BSD 3-Clause "New" or "Revised" License
64 stars 19 forks source link

ampl.setData() uses depends on display_precision option #30

Closed MartinPJorge closed 3 years ago

MartinPJorge commented 4 years ago

Problem

I'm using amplpy version 0.6.11 installed with pip, and python3.

Let's take the following model.mod:

set people;
set dates ordered; # UNIX epoch-1970

param birthday {people} in dates;

var chosen {people} binary;

subject to only_three:
    sum {p in people} chosen[p] = 3;

maximize timestamps:
    sum {p in people} chosen[p] * birthday[p];

and this amplpy code to create a .DAT file:

    # Create the AMPL object
    ampl = AMPL()
    ampl.read('model.mod')

    birthdays = {
        'Ana'   :1590368301.4660506,
        'Paco'  :1590369880.8816786,
        'Luisa' :1590370343.6891866,
        'Jose'  :1590372107.3934467,
        'Lucas' :1590372547.151566 ,
        'Marta' :1590374396.673406
    }
    ampl.set['dates'] = list(birthdays.values())
    ampl.set['people'] = list(birthdays.keys())

    print('Setting birthday values to the DataFrame')
    df = DataFrame(('people'), 'birthday')
    df.setValues(birthdays)
    print(df)
    ampl.setData(df)
    print('Running display birthday')
    ampl.display('birthday')
    ampl.exportData(datfile='out.dat')

which creates the following out.dat:

set dates :=
1590368301.4660506   1590370343.6891866   1590372547.151566
1590369880.8816786   1590372107.3934467   1590374396.673406;
set people := Ana Paco Luisa Jose Lucas Marta;
param birthday [*] :=
  Ana  1590370000
 Jose  1590370000
Lucas  1590370000
Luisa  1590370000
Marta  1590370000
 Paco  1590370000
;

As you can see the birthday numbers have been rounded. Initially I thought this was because of .exportData(). But the script output was showing that although the dataframe was correct, the .setData() function did the rounding:

# Script stdout

Setting birthday values to the DataFrame
   people    |   birthday  
   'Ana'     | 1590368301.4660506
   'Paco'    | 1590369880.8816786
  'Luisa'    | 1590370343.6891866
   'Jose'    | 1590372107.3934467
  'Lucas'    | 1590372547.151566
  'Marta'    | 1590374396.6734059

Running display birthday
birthday [*] :=
  Ana  1590370000
 Jose  1590370000
Lucas  1590370000
Luisa  1590370000
Marta  1590370000
 Paco  1590370000
;

Solution

I have realised that this rounding misbehaviour is because amplpy uses the display command to write the values. Hence the problem is easy to fix by adding these lines:

    # [...]
    ampl.setOption('display_eps', 0);
    ampl.setOption('display_precision', 0);

which will yield in stdout:

Running display birthday
birthday [*] :=
  Ana  1590368301.4660506
 Jose  1590372107.3934467
Lucas  1590372547.151566
Luisa  1590370343.6891866
Marta  1590374396.673406
 Paco  1590369880.8816786
;

and out.dat:

set dates :=
1590368301.4660506   1590370343.6891866   1590372547.151566
1590369880.8816786   1590372107.3934467   1590374396.673406;
set people := Ana Paco Luisa Jose Lucas Marta;
param birthday [*] :=
  Ana  1590368301.4660506
 Jose  1590372107.3934467
Lucas  1590372547.151566
Luisa  1590370343.6891866
Marta  1590374396.673406
 Paco  1590369880.8816786
;

Request

Please do not make ampl.setData() dependant to the display precision. Values must be stored using their real value, otherwise one cannot recover them latter.

fdabrandao commented 4 years ago

Hi Martin,

Thanks for reporting this issue. However, the problem is not in setData but in exportData. If you set display_eps, and display_precision before displaying birthday you will see the values being displayed with full precision:

print(df)
ampl.setData(df)
print('Running display birthday')
ampl.setOption('display_eps', 0)
ampl.setOption('display_precision', 0)
ampl.display('birthday')
   people    |   birthday
   'Ana'     | 1590368301.4660506
   'Paco'    | 1590369880.8816786
  'Luisa'    | 1590370343.6891866
   'Jose'    | 1590372107.3934467
  'Lucas'    | 1590372547.151566
  'Marta'    | 1590374396.6734059

Running display birthday
birthday [*] :=
  Ana  1590368301.4660506
 Jose  1590372107.3934467
Lucas  1590372547.151566
Luisa  1590370343.6891866
Marta  1590374396.673406
 Paco  1590369880.8816786
;

The problem here is in exportData that indeed uses display the output the numbers to the data file. As a workaround until this issue is fixed, you will need to set display_epsand display_precision before exportData.

fdabrandao commented 3 years ago

amplpy v0.7.0 has just been released and this issue has been fixed in the internal C++ API.