Constraints related to scenario variables

This forum is for posts that specifically focus on the Windows desktop version of Ngene (i.e. all version 1.x releases).

Moderators: Andrew Collins, Michiel Bliemer, johnr

Post Reply
XIZHO5@ulaval.ca
Posts: 1
Joined: Sat Apr 18, 2026 6:13 am

Constraints related to scenario variables

Post by XIZHO5@ulaval.ca »

Hi all,

I am currently designing a travel mode choice survey and would like to include scenario variables in the utility function. However, I have encountered several problems and would really appreciate any suggestions or advice.

1. When I add constraints to the scenario variables as suggested in the manual, Ngene always returns an error saying that it cannot find feasible candidates. The error message is:

"Error: The modified Federov candidate set size of 10000 could not be achieved. The percentages of candidates that failed are: 0% due dominance, 100% due constraints, and 0% due repeated alternatives. The candidate set size has been adjusted from 10000 to 0.

[Modified Fedorov] ERROR: The candidate set of the Modified Fedorov algorithm is smaller than the number of rows specified. That is, there are not enough unique choice sets to generate the number required as specified in the ;rows property. This problem sometimes appears when there are too many reject and/or reject constraints."

My code is:

```ngene
design
? 4km-Montreal city
; alts=bus,car,ebike,ecar,metro,walk
? efficient design
; eff = (mnl,d)+ 0.5*(imbalance)
; alg = mfederov(candidates = 10000)
; rows = 12
; block = 6

;require:
walk.weather = ebike.weather,
ebike.weather = bus.weather,
bus.weather = metro.weather,
metro.weather = ecar.weather,
walk.purpose = ebike.purpose,
ebike.purpose = bus.purpose,
bus.purpose = metro.purpose,
metro.purpose = ecar.purpose,
walk.GHG = ebike.GHG,
ebike.GHG = bus.GHG,
bus.GHG = metro.GHG,
metro.GHG = ecar.GHG,
walk.MET = ebike.MET,
ebike.MET = bus.MET,
bus.MET = metro.MET,
metro.MET = ecar.MET

;reject:
walk.walk_DN + ebike.ebike_DN + bus.bus_DN + metro.metro_DN + ecar.ecar_DN + car.car_DN >=6

;model:
U(walk) = b_weather_w.dummy[-0.2|-0.3] * weather[2,3,1] + b_purpose_w.dummy[0.1|0.2] * purpose[2,3,1] + b_GHG_w.dummy[0.2] * GHG[1,0] + b_MET_w.dummy[0.1] * MET[1,0]+ b_time[-0.01] * walk_time[48,61,74] + b_cost[-0.01] * walk_cost[0] + b_gov[0.1] * walk_gov[1,0] + b_use[0.1] * walk_use[1,0] + b_DN[0.15] * walk_DN[1,0] + asc_w[0.01] /

U(ebike) = b_weather_eb.dummy[-0.1|-0.2] * weather + b_purpose_eb.dummy[0.2|0.3] * purpose + b_GHG_eb.dummy[0.08] * GHG + b_MET_eb.dummy[0.2] * MET + b_time * ebike_time[12,21,27] + b_cost * ebike_cost[4.3,5.7,7.2] + b_gov * ebike_gov[1,0] + b_use * ebike_use[1,0] + b_DN * ebike_DN[1,0] + asc_ebike[0.01] /

U(bus) = b_weather_b.dummy[-0.05|-0.1] * weather + b_purpose_b.dummy[0.1|0.1] * purpose + b_GHG_b.dummy[0.05] * GHG + b_MET_b.dummy[0.05] * MET + b_time * bus_time[14,26,43] + b_cost * bus_cost[4.0] + b_gov * bus_gov[1,0] + b_use * bus_use[1,0] + b_DN * bus_DN[1,0] + asc_bus[0.01] /

U(metro) = b_weather_m.dummy[-0.05|-0.1] * weather + b_purpose_m.dummy[0.1|0.1] * purpose + b_GHG_m.dummy[0.1] * GHG + b_MET_m.dummy[0.05] * MET + b_time * metro_time[11,22,39] + b_cost * metro_cost[4.0] + b_gov * metro_gov[1,0] + b_use * metro_use[1,0] + b_DN * metro_DN[1,0] + asc_metro[0.01] /

U(ecar) = b_weather_ec.dummy[0.1|0.3] * weather + b_purpose_ec.dummy[0.05|0.05] * purpose + b_GHG_ec.dummy[0.02] * GHG + b_MET_ec.dummy[0.05] * MET + b_time * ecar_time[9,12,15] + b_cost * ecar_cost[3.2,4.3,5.4] + b_gov * ecar_gov[1,0] + b_use * ecar_use[1,0] + b_DN * ecar_DN[1,0] + asc_ecar[0.01]/

U(car) = b_time * car_time[7,8,11] + b_cost * car_cost[1.8,2.4,3.0] + b_park[-0.01] * car_parking_cost[3,4,5](2-4,2-4,2-4)+ b_use * car_use[1,0] + b_DN * car_DN[1,0]
$
```

The variables weather, purpose, GHG, and MET are scenario variables that should be the same across alternatives. In addition, the number of DN attributes should be less than 6.

I have tried many different methods to solve this problem, but none of them worked.

2. When I run other versions of the code with fewer constraints, the design can be generated successfully. However, the process takes a very long time, and the levels of some attributes are highly imbalanced.

These are the main problems I am facing at the moment. If anyone has experience with this kind of issue or any suggestions, I would really appreciate your help.

Thank you very much!
Michiel Bliemer
Posts: 2087
Joined: Tue Mar 31, 2009 4:13 pm

Re: Constraints related to scenario variables

Post by Michiel Bliemer »

I can confirm that the issue is that Ngene is unable to find choice tasks that satisfy all criteria. Since this experiment has many alternatives and attributes, Ngene is unable to generate the full factorial and then check each choice task against the criteria. Instead, Ngene generates millions of random choice tasks and checks against the criteria. Since the require criteria are extremely strict, it is not possible to determine a candidate set in this way. In the presence of a lot of constraints, it is best to work with an external candidate set as I outline below.

Step 1: Generate a random candidate set that satisfies your reject constraint. In the script below, I have used 2,000 rows as 10,000 makes the modified Federov algorithm very slow and such a large candidate set is usually not necessary.

Code: Select all

?4km-Montreal city
;alts=walk,ebike,bus,metro,ecar,car
?random candidate set
;fact
;rows = 2000

;reject:
walk.walk_DN + ebike.ebike_DN + bus.bus_DN + metro.metro_DN + ecar.ecar_DN + car.car_DN >=6

;model:
U(walk) = b_weather_w.dummy[-0.2|-0.3] * weather[2,3,1] 
        + b_purpose_w.dummy[0.1|0.2]   * purpose[2,3,1] 
        + b_GHG_w.dummy[0.2]           * GHG[1,0] 
        + b_MET_w.dummy[0.1]           * MET[1,0]
        + b_time[-0.01]                * walk_time[48,61,74] 
        + b_cost[-0.01]                * walk_cost[0] 
        + b_gov[0.1]                   * walk_gov[1,0]  
        + b_use[0.1]                   * walk_use[1,0] 
        + b_DN[0.15]                   * walk_DN[1,0] 
        + asc_w[0.01] 
        /

U(ebike) = b_weather_eb.dummy[-0.1|-0.2] * weather 
         + b_purpose_eb.dummy[0.2|0.3]   * purpose 
         + b_GHG_eb.dummy[0.08]          * GHG    
         + b_MET_eb.dummy[0.2]           * MET 
         + b_time                        * ebike_time[12,21,27] 
         + b_cost                        * ebike_cost[4.3,5.7,7.2] 
         + b_gov                         * ebike_gov[1,0]        
         + b_use                         * ebike_use[1,0] 
         + b_DN                          * ebike_DN[1,0] 
         + asc_ebike[0.01] 
         /

U(bus) = b_weather_b.dummy[-0.05|-0.1] * weather 
       + b_purpose_b.dummy[0.1|0.1]    * purpose 
       + b_GHG_b.dummy[0.05]           * GHG 
       + b_MET_b.dummy[0.05]           * MET 
       + b_time                        * bus_time[14,26,43] 
       + b_cost                        * bus_cost[4.0] 
       + b_gov                         * bus_gov[1,0]          
       + b_use                         * bus_use[1,0] 
       + b_DN                          * bus_DN[1,0] 
       + asc_bus[0.01] 
       /
 
U(metro) = b_weather_m.dummy[-0.05|-0.1] * weather 
         + b_purpose_m.dummy[0.1|0.1]    * purpose 
         + b_GHG_m.dummy[0.1]            * GHG 
         + b_MET_m.dummy[0.05]           * MET 
         + b_time                        * metro_time[11,22,39] 
         + b_cost                        * metro_cost[4.0] 
         + b_gov                         * metro_gov[1,0] 
         + b_use                         * metro_use[1,0] 
         + b_DN                          * metro_DN[1,0] 
         + asc_metro[0.01] 
         /

U(ecar) = b_weather_ec.dummy[0.1|0.3]   * weather 
        + b_purpose_ec.dummy[0.05|0.05] * purpose 
        + b_GHG_ec.dummy[0.02]          * GHG 
        + b_MET_ec.dummy[0.05]          * MET 
        + b_time                        * ecar_time[9,12,15] 
        + b_cost                        * ecar_cost[3.2,4.3,5.4] 
        + b_gov                         * ecar_gov[1,0] 
        + b_use                         * ecar_use[1,0] 
        + b_DN                          * ecar_DN[1,0] 
        + asc_ecar[0.01]
        /

U(car) = b_time        * car_time[7,8,11] 
       + b_cost        * car_cost[1.8,2.4,3.0] 
       + b_park[-0.01] * car_parking_cost[3,4,5]     ?(2-4,2-4,2-4)
       + b_use         * car_use[1,0] 
       + b_DN          * car_DN[1,0]
$
Step 2: Copy and paste the generated candidate set to Excel, and manually apply your require constraints. Simply copy the weather, purpose, GHG, and MET columns from walk to the other alternatives. Import the design back into Ngene (for Ngene v1.x you need to add a column with all ones, see the manual).
(note: I encountered some issues in opening the candidate set in Ngene. After several error messages. I could copy it to Excel. The new version of Ngene does not have the same issue. If you encounter the same issue, simply save the design as NGD file, open with a text editor, and copy and paste the design to Excel).

Step 3: Remove all your require and reject constraints from the script, and add the following:
;alg = mfederov(candidates = spreadsheet.xlsx) ? where spreadsheet is the name of your spreadsheet

This worked for me in Ngene Online.

A few things to note:

1) Using 12 rows is very minimal; you have more than 40 parameters to estimate in your model. You may want to consider using 24 rows.

2) The most efficient design with near-zero priors is a design where mostly the extreme values for the numerical attributes appear. So that means that for most of your time attributes, the middle level will not appear much. The modified Federov algorithm cannot guarantee attribute level balance unless you apply attribute level frequency constraints, which will make it more difficult to find a feasible design. Since your priors seem noninformative, a trick is to dummy code all attributes (including numerical attributes such as the time attribute). This automatically ensures a high degree of level balance because imbalance means that one of the levels cannot be estimated as precise. To achieve this, you can use:
b_time.dummy[0|0] * walk_time[1,2,3],
where levels 1,2,3 are merely placeholders and are irrelevant if you have (near) zero priors. After the design has been generated, you can replace 1,2,3 with 48,61,74 for walk, 12,21,27 for ebike, etc. If you want instead to use informative priors, you can consider alternative-specific dummies, namely
b_walk_time.dummy[...] * walk_time[48,61,74],
which is perhaps preferred because travel time walking, in the car, or in public transport is not perceived the same (e.g. walking takes effort, in public transport you can read a book, in the car you need to drive).

Michiel
Post Reply