Unsupported Full-Integer TensorFlow Lite models in TF 2

Describe the issue
In TF2, the full-integer quantized models produced by the TFLite Converter can only have float input and output type. This is a blocker for users who require int8 or uint8 input and/or output type.
UPDATE: We now support this workflow.
End-to-End Tutorial: https://colab.sandbox.google.com/github/google-coral/tutorials/blob/master/retrain_classification_ptq_tf2.ipynb
Only TFLite Conversion: Convert TF Models to TFLite Full-Integer models
You can refer to the code here, also given below:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
def representative_dataset_gen():
  for _ in range(num_calibration_steps):
    # Get sample input data as a numpy array in a method of your choosing.
    yield [input]
converter.representative_dataset = representative_dataset_gen
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_model = converter.convert()

Only TFLite Inference: Run inference on the TFLite model
Note that the one caveat with integer-only models is this -- you need to manually map (aka quantize) the float inputs to integer inputs during inference. To understand how this can be done -- refer to the equation provided in TensorFlow Lite 8-bit quantization specification document and it's equivalent code in python below:
import numpy as np
import tensorflow as tf

# Input to the TF model are float values in the range [0, 10] and of size (1, 100)
np.random.seed(0)
tf_input = np.random.uniform(low=0, high=10, size=(1, 100)).astype(np.float32)

# Output of the TF model.
tf_output = keras_model.predict(input)

# Output of the TFLite model.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()[0]
# Manually quantize the input from float to integer
scale, zero_point = input_details['quantization']
tflite_integer_input = tf_input / scale + zero_point
tflite_integer_input = tflite_integer_input.astype(input_details['dtype'])
interpreter.set_tensor(input_details['index'], tflite_integer_input)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
tflite_integer_output = interpreter.get_tensor(output_details['index'])
# Manually dequantize the output from integer to float
scale, zero_point = output_details['quantization']
tflite_output = tflite_integer_output.astype(np.float32)
tflite_output = (tflite_output - zero_point) * scale

# Verify that the TFLite model's output is approximately (expect some loss in
# accuracy due to quantization) the same as the TF model's output
assert np.allclose(tflite_output, tf_output, atol=1e-04) == True

I am interested in this issue. When fix is available, please let me know.
(I suppose I will get the notify when write message here, right?)
Ruey-An

@rayeh5 Yes. Whenever we post updates, you will receive emails.

@MeghnaNatraj: in the meanwhile, could you update https://www.tensorflow.org/lite/performance/post_training_quantization#full_integer_quantization_of_weights_and_activations and other documentation to reflect that the full-integer path (including inputs and outputs) is only available in TF 1.X and not TF 2.X?

expecting

Hello ,
Any updates about this issue ? Will v2 converter support input output Flags?

Yes, it is currently a work in progress. We will update this github issue
once it's completed.
On Thu, Apr 23, 2020 at 8:22 PM mahdichtourou24051994 <
<EMAIL_ADDRESS>wrote:

Hello ,
Any updates about this issue ? Will v2 converter support input output
Flags?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/tensorflow/tensorflow/issues/38285#issuecomment-618780215,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ACDCGJ2GOLCDOWIFHCDJ2VDROEAX3ANCNFSM4MCTYA6A
.

Just checking in, im also waiting for v2.

Also waiting for this issue to be resolved, thanks.

waiting for this issue to be resolved, thanks.

waiting too

I am facing this issue in TF 1.15 and TF 2.

By the way, can we use converter to create an uint8 inference tflite model rather than an int8 inference model?

Note: The following discussion is not related to the current issue of supporting full integer tensorflow lite models, including input and output, in TF 2.0
@dreamPoet No, this is not possible in TensorFlow 2. We cannot create a uint8 inference tflite model and only support int8 inference model. We've moved away from the uint8 quantization is because with int8, we're able to use asymmetrical quantization ranges without paying the same penalty. Refer to https://www.tensorflow.org/lite/performance/quantization_spec for more information
For more details:
Youtube: https://www.youtube.com/watch?v=-jBmqY_aFwE
Slides used in the Youtube Video: https://docs.google.com/presentation/d/1zGm5bqGrkAepwJZ5PABiYjrIKq1pDnzafa8ZYeaFhXY/edit?usp=sharing

@MeghnaNatraj  How about in tf1.x? I can only use toco and lite.converter without representative_dataset attributes to produce uint8 inference tflite model, while for int8 inference model, I can set representative_dataset to help quantize, is that right?

@dreamPoet Could you create a separate github issue describing your problem and assign it to me? I'd be glad to provide more information on the new issue.

@MeghnaNatraj Hi, I am working on a benchmark for the Edge TPU and this is blocking me for using it, which is really frustrating. Are you aware of any workaround? Could you provide an estimation of when the converter would be ready? Since this issue wasn't mentioned anywhere I embarked on this project but now I've reached a point where there is nothing else to do but waiting.

@MeghnaNatraj @stefano555  Excuse me, do your test the speed of the quantified model? I found there was only a slight increase in speed.

@MeghnaNatraj @stefano555 Excuse me, do your test the speed of the quantified model? I found there was only a slight increase in speed.

I would test it, if tf lite allowed me to do that :-) since I cannot get the fully quantized model, I cannot do the benchmark

Update
We now support TensorFlow Lite Full-Integer models in TF 2.0, i.e, with integer (tf.int8 and tf.uint8 types) input and output.
Exception: Support for quantize-aware trained models is still in progress
TFLite Conversion: Convert TF Models to TFLite Full-Integer models
You can refer to the code here, also given below:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
def representative_dataset_gen():
  for _ in range(num_calibration_steps):
    # Get sample input data as a numpy array in a method of your choosing.
    yield [input]
converter.representative_dataset = representative_dataset_gen
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_model = converter.convert()

TFLite Inference: Run inference on the TFLite model
Note that the one caveat with integer-only models is this -- you need to manually map (aka quantize) the float inputs to integer inputs during inference. To understand how this can be done -- refer to the equation provided in TensorFlow Lite 8-bit quantization specification document and it's equivalent code in python below:
import numpy as np
import tensorflow as tf

# Input to the TF model are float values in the range [0, 10] and of size (1, 100)
np.random.seed(0)
tf_input = np.random.uniform(low=0, high=10, size=(1, 100)).astype(np.float32)

# Output of the TF model.
tf_output = keras_model.predict(input)

# Output of the TFLite model.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()[0]
# Manually quantize the input from float to integer
scale, zero_point = input_details['quantization']
tflite_integer_input = tf_input / scale + zero_point
tflite_integer_input = tflite_integer_input.astype(input_details['dtype'])
interpreter.set_tensor(input_details['index'], tflite_integer_input)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
tflite_integer_output = interpreter.get_tensor(output_details['index'])
# Manually dequantize the output from integer to float
scale, zero_point = output_details['quantization']
tflite_output = tflite_integer_output.astype(np.float32)
tflite_output = (tflite_output - zero_point) * scale

# Verify that the TFLite model's output is approximately (expect some loss in
# accuracy due to quantization) the same as the TF model's output
self.assertTrue(np.allclose(tflite_output, tf_output, atol=1e-04))

FYI, you can try this TF2 API for full-integer post-training quantization here:
https://colab.sandbox.google.com/github/google-coral/tutorials/blob/master/retrain_classification_ptq_tf2.ipynb

@scottamain @MeghnaNatraj Hi,
I've trained a resnet50v2 model. TensorFlow nightly 2.3.0-dev20200608. The model works fine and I tried some optimization such as "simple" tf lite, tf lite dynamic range, tf lite 16float, and they all work fine.
I have updated today tf to use full-integer post-training quantization with uint8. I converted my model from SavedModel format with:
num_calibration_steps=100
def representative_dataset_gen():
for i in range(num_calibration_steps):
yield [x_train[i:i+1].astype(np.float32)]
converter = tf.lite.TFLiteConverter.from_saved_model('/path/to/my/saved_models')
This enables quantization
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.int8]
This ensures that if any ops can't be quantized, the converter throws an error
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
These set the input and output tensors to uint8 (added in r2.3)
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8
And this sets the representative dataset so we can quantize the activations
converter.representative_dataset = representative_data_gen
tflite_model = converter.convert()
with open('mobilenet_v2_1.0_224_quant.tflite', 'wb') as f:
f.write(tflite_model)
I then compiled the model for edge tpu. It works, in the sense that edge tpu allows me to run it without errors but results are gibberish. It predicts always the same value. I tried then on cpu with tf lite interpreter. Input/output tensor are correctly uint8, but again it predicts again the same value. On cpu wiht tf lite issue persists moving to int8.
Is anyone else experiencing the same issue?

Hey @stefano555
You said your input tensors are uint8, and that was my first concern. So I don't know what else it might be. But to be sure, can you verify that your results are accurate when you use float input/output (remove the inference_input_type/inference_output_type lines)?

Hi @scottamain ,
please find here a link to a google folder containing the tf trained model (validation accuracy 64.15% on 2000 test images), the dataset, the google colab file to convert from tf to tf_lite, the converted tf lite model, and the google colab file to test the tf lite model on cpu.
I was able to replicate the issue. On a subset of 100 test images (using all the images would have taken hours since tf lite on cpu is extremely slow) I got 40% but only because it kept predicting the same value and that subset there were 40 pictures corresponding to that value but it is only gibberish.
https://drive.google.com/drive/folders/11XruNeJzdIm9DTn7FnuIWYaSalqg2F0B?usp=sharing
Many thanks for your help!

Hi @MeghnaNatraj and @scottamain I have tested today DenseNet-169 and I got the identical issue with Full-Integer TensorFlow Lite.

Update: We now support TensorFlow Lite Full-Integer models in TF 2.0, i.e, with integer (tf.int8 and tf.uint8 types) input and output.
Exception: Support for quantize-aware trained models is still in progress
End-to-End Tutorial: https://colab.sandbox.google.com/github/google-coral/tutorials/blob/master/retrain_classification_ptq_tf2.ipynb
Only TFLite Conversion: Convert TF Models to TFLite Full-Integer models
You can refer to the code here, also given below:
import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
def representative_dataset_gen():
  for _ in range(num_calibration_steps):
    # Get sample input data as a numpy array in a method of your choosing.
    yield [input]
converter.representative_dataset = representative_dataset_gen
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_model = converter.convert()

Only TFLite Inference: Run inference on the TFLite model
Note that the one caveat with integer-only models is this -- you need to manually map (aka quantize) the float inputs to integer inputs during inference. To understand how this can be done -- refer to the equation provided in TensorFlow Lite 8-bit quantization specification document and it's equivalent code in python below:
import numpy as np
import tensorflow as tf

# Input to the TF model are float values in the range [0, 10] and of size (1, 100)
np.random.seed(0)
tf_input = np.random.uniform(low=0, high=10, size=(1, 100)).astype(np.float32)

# Output of the TF model.
tf_output = keras_model.predict(input)

# Output of the TFLite model.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()[0]
# Manually quantize the input from float to integer
scale, zero_point = input_details['quantization']
tflite_integer_input = tf_input / scale + zero_point
tflite_integer_input = tflite_integer_input.astype(input_details['dtype'])
interpreter.set_tensor(input_details['index'], tflite_integer_input)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
tflite_integer_output = interpreter.get_tensor(output_details['index'])
# Manually dequantize the output from integer to float
scale, zero_point = output_details['quantization']
tflite_output = tflite_integer_output.astype(np.float32)
tflite_output = (tflite_output - zero_point) * scale

# Verify that the TFLite model's output is approximately (expect some loss in
# accuracy due to quantization) the same as the TF model's output
assert np.allclose(tflite_output, tf_output, atol=1e-04) == True

Is it possible to manually quantize/dequantize the inputs/outputs using the TFLite for Microcontrollers C++ library? Here you're just showing how to do that using the Python library..

Quantization aware training can not convert to int8 or unint8 ?
this is my code ,thanks.
https://colab.research.google.com/drive/1qPD3Zd5qU5FpMxj_aCwIpjn7y_eR_Wxt?usp=sharing

@anferico Unfortunately, it looks like we don't have an example for TFLite Interpreter in C++ which uses the quantize/dequantize scale and zero-point to preprocess the image and process the result. We will be updating the TFLite micro examples, but until then, you can try the following suggestion:
The closest example is micro/examples/person_detection_experimental -- specifically refer to this -- a uint8 image input it converter to int8 by subtracting 128 and feeding it into the model. For this example, we noticed that the zero point is -128 and the scale is 1.0 so we directly just subtract 128 to keep the code simple.
However you should be able to access input->params->zero_point and input->params->scale and write a C++ equivalent of the python TFLite Interpreter code that we have shared.
Hope this helps!
@aa12356jm  correct, we are still working on adding support for this feature.

Following. Two of my blog posts would need to be changed when the int support for QAT models goes live.

@MeghnaNatraj
Can you check and mention this workaround (https://github.com/tensorflow/model-optimization/issues/431) for quantize input/output tensor when QAT is used?

Hi @scottamain and @MeghnaNatraj I just wanted to know if there are any updates and if could reproduce my same issue.

@MeghnaNatraj @alanchiao   i train a quant model with dynamic fuction, input and output are float. i test two model (float 3.6M and int8 900k) in the android phone. there are no speed accelarate.

@hahadashi could you confirm if you are using delegate when using the models from your Android phone?

@hahadashi could you confirm if you are using delegate when using the models from your Android phone?
thxs your reply.
i just follow the tensorflow/lite/g3doc/guide/inference.md
(```c++
// Load the model
std::unique_ptrtflite::FlatBufferModel model =
tflite::FlatBufferModel::BuildFromFile(filename);

// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
std::unique_ptrtflite::Interpreter interpreter;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);
// Resize input tensors, if desired.
interpreter->AllocateTensors();
float* input = interpreter->typed_input_tensor(0);
// Fill input.
interpreter->Invoke();
float* output = interpreter->typed_output_tensor(0);

develop a inference code to run the tflite on the phone.  i need do more thing?

You might want to follow this tutorial once: https://www.tensorflow.org/lite/performance/gpu.

You might want to follow this tutorial once: https://www.tensorflow.org/lite/performance/gpu. Here's the demo code snippet:

thxs, i have a question. why we need use GPU delegate to accelerate speed by gpu. the arm cpu not support quant inference?

https://www.youtube.com/watch?v=-jBmqY_aFwE

@MeghnaNatraj thanks for the great link to Pete Wardens screencast!

@stefano555 I am working on debugging your issue right now. Looks like https://drive.google.com/corp/drive/folders/11XruNeJzdIm9DTn7FnuIWYaSalqg2F0B has a ton of jupyter notebooks, and i'm not sure how to debug this. Please create a new github issue for "TFLite Converter", add me as the assignee and post detailed instructions on how I can reproduce your issue.

It looks like the issue has been solved in TF 2.4?

It looks like the issue has been solved in TF 2.4? Could we get an official confirmation?

@MeghnaNatraj is this solved in TF 2.4 (nightly)?

Hello, the issue is resolved now with TF 2.4 (nightly). Let us know if you face any issue.

Dear @MeghnaNatraj, sorry for answering you only now. The issue has not been resolved. I will create a new GitHub issue and add you as an assignee.

@MeghnaNatraj ran into: RuntimeError: Quantization not yet supported for op: 'DEQUANTIZE'. TensorFlow Version: 2.4.0-dev20200728. Let me know if you'd want me to create a separate issue.
Here's the Colab Gist.

@MeghnaNatraj I wasn't able to assign you to the new issue, you can find it here https://github.com/tensorflow/tensorflow/issues/41840#issue-667409532

sorry, I don't know if the question is appropriate to ask here:
it seems that the int8 quantization is a symmetric quantization one, will TF-Lite (or Micro vesion) has plan to support "symmetric quantization"?
thanks for all of your time

@sayakpaul Could you create a separate issue and tag me on it? I am looking into it.
@stefano555 I am looking into #41840 right now.
@rayeh5  could you explain your question in detail? Currently we support int8 quantization by default.

@MeghnaNatraj I don't think community members can assign someone to an issue (correct me if I am wrong). I referred to the following statement of yours and hence I decided to inform you about the issue here.

(This issue will remain open until we also fix all documentation, and resolve any issues that may arise in the next few days)

Please let me know if you'd still like me to open up a new issue.

@sayakpaul Yes, community members cannot assign an issue to a specific person. You can create a new issue, post the details about your issue (as in this comment and post a link to the issue here. I will continue the discussion on that thread.

@sayakpaul  I have created a separate issue here: https://github.com/tensorflow/tensorflow/issues/42082

@sayakpaul Could you create a separate issue and tag me on it? I am looking into it.
@stefano555 I am looking into #41840 right now.
@rayeh5 could you explain your question in detail? Currently we support int8 quantization by default.

As my understanding of the "TensorFlow Lite 8-bit quantization specification", Weights are symmetric, but the Activations are asymmetric. Is it possible to support both as symmetric quantization?
Thanks for your time.
ruey-an

@sayakpaul Could you create a separate issue and tag me on it? I am looking into it.
@stefano555 I am looking into #41840 right now.
@rayeh5 could you explain your question in detail? Currently we support int8 quantization by default.

As my understanding of the "TensorFlow Lite 8-bit quantization specification", Weights are symmetric, but the Activations are asymmetric. Is it possible to support both as symmetric quantization?
Thanks for your time.
ruey-an

@suharshs could you respond to this question?

@MeghnaNatraj  hi, i use post quantization as follows:
.........
converter.representative_dataset = representative_dataset_gen
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8

only input and output  of the quantization model  have the  datatype uint8,  the convolution of the middle layer is still int8.
how do I get  the model that  is  all uint8?  thanks

@aa12356jm This is currently unsupported, you can refer to this comment for more information.

Closing this issue as it has been resolved. The documentation for TensorFlow Lite has been updated. If you face an issue due to this change, either re-open this issue or create a new one and mention this issue (by adding #38285 in a comment)

Quantization aware training can not convert to int8 or unint8 ?
this is my code ,thanks.
https://colab.research.google.com/drive/1qPD3Zd5qU5FpMxj_aCwIpjn7y_eR_Wxt?usp=sharing

@MeghnaNatraj  now QAT can convert to int8 or unint8 ?  thanks

@aa12356jm We only support int8 quantization but the model input/output can be int8 or uint8. Use the latest TF version
pip install tensorflow should install the latest stable version

@MeghnaNatraj say model (it's a tf.keras.Model instance) is trained using QAT and now I would like to integer-quantize it, will the following be the right way to achieve that? I truly apologize if this is redundant:
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8
tflite_model_quant = converter.convert()

No representative dataset is required since the model is already calibrated to adjust to the activation changes.

@sayakpaul yes this is perfectly right.

@MeghnaNatraj I see a problem with the code snippet provided above. I get the following error:
ValueError: representative_dataset is required when specifying TFLITE_BUILTINS_INT8 or INT8 supported types.

Here's my Colab Notebook.

@MeghnaNatraj with the above-mentioned code snippet I get the following error, though -
ValueError: representative_dataset is required when specifying TFLITE_BUILTINS_INT8 or INT8 supported types.

Here's the Colab Notebook.

Remove the line
 converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
as for QAT you are assuming that the model can be fully quantized.

@MeghnaNatraj
This issue was resolved completely?
Even with TF 2.4.1, I still have this error:
RuntimeError: Quantization not yet supported for op: 'CUSTOM'.
Without QAT,
def representative_dataset_gen():
    for f_name in os.listdir(image_path):
       file_path = os.path.normpath(os.path.join(image_path, f_name))
        img = cv2.imread(file_path)
        img = cv2.resize(img, (320,320))
        img = img / 255.0
        img = np.reshape(img, (1, 320, 320, 3))
        image = img.astype(np.float32)
        yield [image]

converter = tf.lite.TFLiteConverter.from_saved_model(args.model)

converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset_gen

converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.target_spec.supported_types = [tf.int8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8

#converter.allow_custom_ops = True
#converter.experimental_new_converter = False

tflite_model = converter.convert()

I cannot find out what's wrong at all.

@MeghnaNatraj
This issue was resolved completely?
Even with TF 2.4.1, I still have this error:
RuntimeError: Quantization not yet supported for op: 'CUSTOM'.
Without QAT,
def representative_dataset_gen():
    for f_name in os.listdir(image_path):
       file_path = os.path.normpath(os.path.join(image_path, f_name))
        img = cv2.imread(file_path)
        img = cv2.resize(img, (320,320))
        img = img / 255.0
        img = np.reshape(img, (1, 320, 320, 3))
        image = img.astype(np.float32)
        yield [image]

converter = tf.lite.TFLiteConverter.from_saved_model(args.model)

converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset_gen

converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.target_spec.supported_types = [tf.int8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8

#converter.allow_custom_ops = True
#converter.experimental_new_converter = False

tflite_model = converter.convert()

I cannot find out what's wrong at all.

As it says in the error, one of your operations called 'CUSTOM' is not yet supported. Therefore, it's something with your original neural network (probably Keras).
You might want to check which operands are supported here.

@lheim Thanks,
I got a pb file from saved model, as this link.
The error message says 'CUSTOM' is not supported, but I cannot find where it is.

Thank you and that worked. Here's my Colab Gist for anyone else that might run into a similar problem: https://colab.research.google.com/gist/sayakpaul/d209ac353d3bcea06287be5e91628f18/scratchpad.ipynb.
Thanks for your sharing. I meet the same problem and try your script, it doesn't work now. Is it still work for you ?


Allow classpath wildcards

The recent fix for #5483 removed the ability to do all-jars-in-directory wildcards via %classpath add jar /path/to/dir/*.  I'm not sure if it was intentional or accidental that this worked, but I think it is useful.

That was accidental, thanks for pointing it out.


XY Grid - Feedback and Discussion

Now that the new XY Grid has been merged and is included in the RC lets keep all discussion surrounding this in one place, and gather as much feedback to improve it as much as possible before the full release.
You’ll find a Codepen to the new grid here:
https://codepen.io/ZURBFoundation/pen/owvqYp?editors=1000
And the docs:
http://foundation.zurb.com/sites/6.4.0-rc1/docs/xy-grid.html
For previous discussion history see:

https://github.com/zurb/foundation-sites/pull/10017
https://github.com/zurb/foundation-sites/issues/9875

Currently the plan is to rename margin/padding-gutters classes to .grid-margin/padding classes.
However feedback on how we can make this more flexible (eg allow padding to be added on all 4 sides, margin on bottom too etc) would be welcomed!

Padding to create vertical spacing in the grid - my opinion is that padding as a "gutter" might now be obsolete so combining the .grid-margin with .grid-padding might be the way to achieve this - using the $global-padding variable

Is this a bug? https://codepen.io/anon/pen/owjYyR?editors=1100

cell

cell

Seems something isn't getting overridden correctly as the grid overflows the container.
Also, would be great if this would add vertical gutters only between the cells (not above or below the grid itself). I often use such a pattern to separate teasers in a content listing where I dont want any trailing whitespace.

@oxyc can you specify browser? It looks fine in Chrome, but I'm seeing an issue in Safari that looks like this:

@brettsmason can you look into it?

In your screenshot you can see that the cells background color is not aligned with the heading, which I think it should be.

Ah, yes, I see it. The left-right negative margin from margin gutters is getting applied even though this is a vertical grid. Good catch!

@oxyc with regards to the vertical gutters not applying on the outside, I believe that actually is working but the h4 has a margin-bottom that is offsetting. Can you verify?

Another common pattern I use is a combination of grid-x and the way I described grid-y above. Basically it would add gutters on all sides of the cells, but still align the cell content with the container. Demo https://codepen.io/anon/pen/QgjGRO?editors=1100

@oxyc with regards to the vertical gutters not applying on the outside, I believe that actually is working but the h4 has a margin-bottom that is offsetting. Can you verify?

Yes, sorry, you're correct.

Another common pattern I use is a combination of grid-x and the way I described grid-y above. Basically it would add gutters on all sides of the cells, but still align the cell content with the container. Demo https://codepen.io/anon/pen/QgjGRO?editors=1100

I think this is what @brettsmason @andycochran, and I have been going back and forth about in with regards to positioning gutters on multiple sides in https://github.com/zurb/foundation-sites/pull/10017
Question for you - In this situation how would you expect the gutters to behave on the bottom of the container? Would you expect the grid to be flush with the bottom, or have a gutter beneath it?

Question for you - In this situation how would you expect the gutters to behave on the bottom of the container? Would you expect the grid to be flush with the bottom, or have a gutter beneath it?

I would not expect a gutter. For example I might want to wrap the grid in a

 where I expect the double padding to apply all around the grid. If there was a gutter below, the vertical spacing wouldn't match the horizontal. Demo https://codepen.io/anon/pen/YQyNEp?editors=1100

@oxyc Thanks so much for your valuable feedback! I'm going to take a look at some changes this evening to address this.

What are the XY grid equivalents of the Sass mixins @include flex-grid-row() and @include flex-grid-columns()?

@arjendejong12 Currently the mixins are xy-grid (https://github.com/zurb/foundation-sites/blob/develop/scss/xy-grid/_grid.scss#L23) and xy-cell (https://github.com/zurb/foundation-sites/blob/develop/scss/xy-grid/_cell.scss#L100). With the complexity of the class requirement with the new grid, the mixins have been problematic and I've struggled to find users who use mixins to get feedback.
If you use mixins I'd really appreciate if you are able to give them a try and let me know how you get on!

Thank you, it seems I was right about those mixins. I was confused by the documentation about xy-cell($size) only accepting full, auto, and shrink, while I was looking for xy-cell() to provide me with the number of columns I'd want, just like flex-grid-columns(), but it seems xy-cell() also accepts numbers. So it works now and I also changed xy-cell($gutter-type) to padding, which gave the expected results. Props for making these mixins! Maybe you could edit the documentation to include the possibility to add numbers to xy-cell($size), because for me it was confusing whether it was possible or not. I also think you should move these two mixins to top because they are the most important ones.
A personal wish for these mixins would be to add a map of breakpoint column sizes, something like this: xy-cell($breakpoint: (small: 12, medium: 6, large: 8)). This makes it easier for me to declare various column sizes in one go, instead of using the breakpoint() mixin, but I can see that this might be hard to code.

I think we need to add some docs about cell-block and cell-block container. There's information about the mixins, but it may deserve it's own section in the XY docs.

@arjendejong12 Thanks again for the feedback - I'll definitely get those docs fixes added.
I also created a PR with a new mixin: xy-cell-breakpoints based on your suggestion. I decided to add it as a new mixin for now, though I'm not sure if it should be part of the xy-cell mixin or not.
@kball what control do we have over mixin order? I'm assuming its based on the order within the partial? I agree it might be good to change the order to one that's more relevant to the end user.

@JeremyEnglert Agreed - @kball can you help me out with that one?

@brettsmason you mean in the docs? I believe that is correct, order within the partial and probably order of files in the front matter in the docs as well (I believe you can do an array not just a wildcard)
@JeremyEnglert yes will see what I can put together on cell-block and cell-block-container

Also noticed today whilst trying out mixins that xy-cell-layout is going to need the responsive treatment, just like the PR for xy-cell-breakpoints. If we can work out how to code this then it will apply to a few of the mixins. Some of the mixins may even be better merged. Don't have any time until Friday to look at anything now though unfortunately.

A few notes while I'm building a site with the new grid:

First off I love the margin grid as it's way easier to align edges without having the side gutters on the grid itself. Before I had to wrap basically every piece of content on the page in a grid to get the content to align. One consequence of having no padding on the outer sides of the grid is that once your viewport becomes smaller than $global-width (assuming you have wrapping a grid-container), the content hugs the viewport edge. My own fix was to add:
.grid-container {
  // Add a gutter between the grid and the viewport.
  @include xy-gutters($gutters: $grid-padding-gutters, $gutter-type: padding);
}

I'm still learning to use the grid but I dont imagine I will ever nest grid-containers, I'm thinking I'll only use them for the main layout of the page.
Might this be useful to have as a variable $grid-container-gutter?

I'm still struggling with proper a proper sticky footer component. That's not really related to the grid I guess. Still mentioning it as I wont be opening up a feature request.

I often want the content of a cell to take up as much space as possible, so that everything becomes equal height without resorting to equalizer. Not sure how to solve this in a good way yet, for now I'm enabling it globally on all cells to see what happens and then work from there. Some designs would obviously be hurt by this so I'll add it as a separate class after having tested it globally.
.cell {
  display: flex;
  flex-direction: column;
  // @todo other alignments will break as well...
  &.align-middle {
    flex-direction: row;
  }
}

@oxyc thanks for your continued feedback, I really appreciate it! To answer your points:

I've been wondering about how to handle this too. The trouble with adding padding around the .grid-container is the grid wont align with items that arent in a .grid-container. An example is if I wanted a full width grid of images and dont want any padding on the sides. I think adding padding could work, but it should be removed on screens larger than $global-width? Need some other opinions on this one.

What do you mean by a sticky footer - do you have an example? It might be possible to do something with .grid-frame - pretty much creating an app style layout with scrollable panels.

I see you found .align-stretch - I'm working on sorting out this issue at the moment so that will be the way to go to achieve this.

I've been wondering about how to handle this too. The trouble with adding padding around the .grid-container is the grid wont align with items that arent in a .grid-container. An example is if I wanted a full width grid of images and dont want any padding on the sides. I think adding padding could work, but it should be removed on screens larger than $global-width? Need some other opinions on this one.

True. Your suggestion would work for me but I do see the possibility for cases where user want text content to have a padding, while grids of images shouldn't. Both types of grids might still have use for the max-width of grid-container though.

What do you mean by a sticky footer - do you have an example? It might be possible to do something with .grid-frame - pretty much creating an app style layout with scrollable panels.

There are two primary use cases. First is there entire page layout where I want the header and the footer to shrink while the content area in between behaves like auto except that it will grow past the available height if the content takes up more space than is available.
Second use case would a teaser within a grid that uses align-stretch. Same deal, the title and the footer should shrink so that they're aligned with the edges of the cell, while the content takes up whatever space is left, but still affecting the height of the cell.

@kball I've been having a look at the docs and trying to update the order the mixins are listed.
I've put:
sass:
  - 'scss/xy-grid/_grid.scss'
  - 'scss/xy-grid/_cell.scss'

Into the docs partial, but its still listing cell mixins first, so I'm assuming its doing it alphabetically.
Is there any way around this?

Since XY is the new default grid, and based on Flexbox, should global-flexbox be set to 1rue`by default?

@JeremyEnglert I asked the exact same thing to @kball the other day.
I think the answer was yes - I'll do a PR later for that.

I'm playing with the new grid, and I think I found a specific case where something isn't working ok.
My goal was to create a grid with one column shrinked to the width of a thumbnail and a second column expanding to the rest of the width. It works fine with shorter titles but then the columns would stack on each other whenever the title in the expanding column would be wider than the remaining space, instead of word breaking as I was expecting. Am I doing something wrong? Adjust this example by removing the  node from under the second source heading and generally playing with the width of the content in the second column...
`

source second
This should wrap around nicely... but seems to don't when there's a longer text...

filler

`

@janslu this is a really interesting testcase, and a definite bug thank you! I've traced this to an overly generalized reset introduced in #10164. Gonna put together a PR to address it.

@oxyc @brettsmason
Following your discussion about about grid-container gutters, I've set up a simple example for myself with just
@viewport{
	max-width: 100%;
}
.grid-container {
	max-width: 95vw;
	background-color: yellow;
}

and

    ...

I can vary width of the 'grid-container' at will ... and see the results track.
What I can't figure out is how to center that grid in the viewport.
Any combination of the usual margins, padding, etc. that I've tried simply leaves the container hugging the left-edge.
What's the right method for centering the container in the viewport?

@pgnd You can use the grid-container mixin which accepts a width (uses $grid-container by default).
Your container just needs margin: 0 auto to align it centrally.
Does that help?

@brettsmason
Closer ...
I've now explicitly included the mixin (required?) and the margin spec.
My intention is to define a main grid container that's 95% viewport width.
Where, styles:
cat global.scss
	...
	@import 'variables';
	@import 'settings';
	@import 'foundation';
	@import 'foundation-icons.scss';
	$global-width: 95vw;
	...
	@include foundation-everything($flex: true);
	@include foundation-xy-grid-classes;
	.site-container {
	  @include xy-grid-container;
	  @include xy-grid;
	}
	...
	.grid-container {
		background-color: red;
		margin: 0 auto;
	}
	...

and, markup:

			test test
	{% block body %}{% endblock %}

I see a centered grid container, at width exactly = 1200px, not 95vw (currently expected == 95 x 1/100 * 2100px)
which I assume comes from '_global.scss' or '_settings.scss':
	...
	/// Global width of your site. Used by the grid to determine row width.
	/// @type Number
	$global-width: rem-calc(1200) !default;
	...

I've tried defining & mv'ing the local override
$global-width: ...

before & after each of the @include stmts - to no avail.
Given
$grid-container 	Number 	$global-width 	The maximum width of a grid container.

I've tried setting the override
$grid-container: 95vw;

Unfortunately, that gives me a grid-conatiner width of 95 px, not, 95vw.
How's the container width to be changed in xy-grid?  Atm, seems I'm doing a fine job of missing it :-/

@pgnd You have stumbled across a bug!
See here: https://github.com/zurb/foundation-sites/blob/develop/scss/xy-grid/_grid.scss#L15
Whatever value you pass to $grid-container gets parsed through rem-calc. I'll put together a PR now to remove this. Then you should just be able to set $grid-container: 95vw; and use the built in .grid-container class.

@brettsmason
Thanks for the bug ref.
You committed to 'develop' branch?  Atm, I don't see it -- likely takes a bit.
Re:

use the built in .grid-container class

I find that I must add the mixins to some element
@include foundation-xy-grid-classes;
<some element> {
	@include xy-grid-container;
 	@include xy-grid;
}

Which is not explicitly called out in the docs:
@import 'foundation';

@include xy-grid;
@include foundation-xy-grid-classes;

If I do not, i.e. just
@include foundation-xy-grid-classes;
@include xy-grid-container;
@include xy-grid;

then gulp whines
Error in plugin 'sass'
Message:
    web-src/scss/main.scss
Error: Properties are only allowed within rules, directives, mixin includes, or other properties.
        on line 1 of web-src/scss/main.scss
>> @charset "utf-8";
   ^

which is a red-herring / nothing to do with what's actually on line 1.
cref:  https://github.com/oddbird/susy/issues/626

@kball (and anyone else) do you think we need to include a class for padding a .grid-container if using margin grids? Following on from this discussion.
This should illustrate my issue - Notice the content? I dont want that flush to the edge, I want some padding, but I don't want this on the gallery.
Here's the idea I had to solve this. Basically padded gutters to the grid-container (through use of another specific class) that applies padding up to your grids max width.
What do you think? Or can anyone think of a better idea?

@pgnd Here is the pull request.
I'm a bit lost on what you are trying to do - I'll take a look later.

@brettsmason

Here is the pull request.

thx

I'm a bit lost on what you are trying to do

Pretty simple, in theory: to end up with an xy-grid contained in a grid container that encompasses 95% of viewport width, is responsive, and is centered.

Pretty simple, in theory: to end up with an xy-grid contained in a grid container that encompasses 95% of viewport width, is responsive, and is centered.

Sorry I understood that bit, just not how you were going about doing it 😄

how you were going about doing it

TBH, I don't care much -- other than as simply as possible!
IIUC that^ needs to start with grabbing your commit ... which I'll get to as soon as I get myself straightened out on the @include syntax.

@brettsmason I think your padding solution makes sense. I know you're traveling, would you like me to implement it?

@kball would you mind? Only have my phone and might be a bit difficult :)
I was thinking 2 variables - one to control the padding (defaults to the gutter map) and one to set the width where the padding applies up to? Defaults to global width.
I don't know if it would be better as a max width media queries, or just from the variable width above the left/right padding gets set to 0, and the padding is applied below that? Also don't know what a sensible class name would be...

Websites usually have spaces between columns and rows. So, shouldn't it be included by default ?
From what I understand, I need to add grid-x and grid-margin-x ( or grid-padding-x ) to every row now.  I think spacing ( gutter ) should be included by default ( like previous versions of foundation )  and there should be a class like grid-margin-0 to remove spacing between columns.
May be an argument in grid mixin to create rows with spacing enabled by default.

Is a wrapper element grid-container required now ? Can grid-container and grid-x be added to same element ?

Unlike earlier versions, custom class names cannot be supplied to mixins. grid-x, cell are hardcoded. We can use mixins, but providing custom names in mixins was helpful.

@jashwant I'm on holiday at the moment with limited access so apologies if this reply is a little vague:

We made the decision to have no spacing (gutters) by default as this allows the user to make a choice - do they want margin or padding, without having to first remove one or the other. I stand by this decision and think its the correct one.

.grid-container is for wrapping your grid to a max-width. If your grid doesn't need to be contained then you don't need it, otherwise you will. .grid-container can be applied directly to a grid if using padding gutters. If you are using margin gutters you need a wrapper around the grid (margin gutters apply a negative margin to the grid, so centering with auto wont work). In my opinion .row should never of had the grid-container behaviour and should always of been an extra container around your grid.

Padding example:

Margin example:

I would also like to add the ability to customise the class names, but this wont be in 6.4. Hopefully we can include this in 6.4.1 (@kball would that be allowed in a minor version?) I have an idea of how to implement this, but I chose to allow inclusion of individual grid parts, rather than class names as code bloat was higher on my priority list.

Is there something from float/flex grid?

Content

With the xy-grid i would write something like this:

Content

Would be cool if i could save the two extra divs.

Hi @marvinhuebner - for a padding grid, you can actually combine the container with the grid... so something like

but for margin it needs to be separate, which was one of the primary reasons for separating that out into its' own class.

For full width cells, not sure exactly what the recommendation would be there... @brettsmason any thoughts?

This is enough i guess for me

@kball sorry for the late reply.
Personally I never understood the need for combining a row and column - they are 2 separate elements and should stay like that in my opinion.
However what @IamManchanda pointed out will of course work. The grid should be modular enough to be able to combine classes for the desired behavior :)
Alternatively if using Sass you could change the grid container padding max width and have it output padding on all sizes, and use that (then no need for cell). However this isn't really a grid, it's just a contained element with padding.
Maybe we need a gutter utility class that can be used to add responsive padding to any element?

Maybe we need a gutter utility class that can be used to add responsive padding to any element?

I used to have that in my projects but in the end it caused more harm because i was using it as a prototyping utility and when the project changed we easily ignored making it into a real grid and gutters would be applied twice.
Nowadays (already outdated though) I use always wrap it as a grid if it could potentially become a grid in the future. For components where I need gutters to space out content from the container (usually containers with a background color or an image), I use named placeholders to add spacing. https://github.com/generoi/sage/blob/foundation-64/resources/assets/styles/utils/_spacings.scss

There's an issue with .grid-frame and .cell-block-y which don't get height:100% (they only have the max-height:100% set), which kinda defeats the point of a grid frame/app frame. The issue is that if the content within .cell-block-y doesn't overflow then in never hits the 100% height, which, if you have background applied results in columns that are too short and gives weird look.
The issue can be demonstrated if you delete some paragraphs from the official docs grid frame example. I'm attaching the photo for you to visualize what I mean:

@brgrz Thanks for reporting this. Is there any way you could open that as a new issue? I think this thread should be closed now as the grid is live and its a little hard to keep track of 😄

sure

Serious question. But why has the XY Grid been included by default considering it literally breaks everything?
Every template, every building-block, most components. Everything. Broken.

XY Grid + the equalizer doesn't seem to work around anymore. Is there a built in feature of XY Grid that can solve the issue of multiple items to have equal heights?

A simple question but I seem to struggle on this, why is there no mixin to just change the xy-cell size ?
For example I have this :
li {     @include xy-cell(6, $gutter-type: padding, $gutter-output: false);     margin-bottom: rem-calc(5);     text-align: left; }
And I just want to change the xy-cell($size), to 4, instead of 6 for large breakpoint so I'm using this :
@include breakpoint(large) {   @include xy-cell(4); }
But this override the parameters I have passed above, why is there no specific mixin to just target the cell-size ? These is a function but I can't find any specific usability ^^
Thanks!

I'm having some problems making a grid "semantically" using mixins. I've posted this in Foundation forums and found a workoaround already.
http://foundation.zurb.com/forum/posts/54127-i-cant-reproduce-simple-xy-grid-with-mixins
Could someone explain if this could be made using only "official" mixins. Im I doing something wrong? Thanks!

@sveggiani are you able to link to a live link of your code? Not entirely sure from looking at the forum thread what you are trying to achieve. Everything will be possible with mixins, I just need a better idea of what you need to do in order to help.

@brettsmason Hi, thanks for your response.
What I'm trying to achieve is to create a simple grid like this:

A

B

I would like reproduce that exact grid layout without using Foundation classes in markup but mixins:

A

B

.item-row {
  @include xy-grid;
}

.item-row__cell {
  @include xy-cell(6);
}

But I'm not being able to express with mixins what the  .grid-margin-x does. The only way I've found to achieve this is by extending that class into the row container styles:
.item-row {
  @include xy-grid;
  @extend .grid-margin-x; // <---- this!
}

I'm sorry I can't point you to a live example because I'm not being able to include Foundation SASS mixins in Codepen or similar tools.
Thank you very much.

@sveggiani sorry for the delay.
So it looks like there might be a small bug with your use case - the base flex properties aren't being applied if you don't use xy-cell with full width.
Therefore for now, you would need to do this:
.item-row {
  @include xy-grid;
}

.item-row__cell {
  @include xy-cell-base;
  @include xy-cell(6);
}

@kball looks like an issue here: https://github.com/zurb/foundation-sites/blob/develop/scss/xy-grid/_cell.scss#L123 however xy-cell-base isn't setup to work with a number: https://github.com/zurb/foundation-sites/blob/develop/scss/xy-grid/_cell.scss#L48

@brettsmason I am using this code to achieve grid-margin-x :
.item-row {
  @include xy-grid;
  @include xy-gutters($negative: true);
}

.item-row__cell {
  @include xy-cell(6);
}

What about my issue above with the lack of xy-cell-size mixin ?

@brettsmason I've made some tests and the solution from @heyflo is the right for my case. Using  @include xy-cell-base doens't solve the problem with margins in the container.
Thank you both!

@sveggiani no problem 👍
In my case if you have any idea why there is no mixin such as grid-column-size for the XY Grid to just redefine the size of a cell depending on the breakpoint (see : https://github.com/zurb/foundation-sites/issues/10141#issuecomment-326993008), or if you have any idea on how to achieve this without redefining the whole xy-cell, I'll be thanksful !

@heyflo Sorry for missing your question - I'm taking a small break from contributing to Foundation at the moment so am not very active.
So to answer your question - I started off with having a xy-cell-size mixin to do what you mentioned. However with the introduction of the margin option things got a lot more complicated, as the gutter is then included in the size so it would have to be passed to the mixin too.
Question - are you using just padding gutters? If so maybe an option to change the default for the mixin, or a mixin to wrap code to set some defaults so you dont have to keep setting them might be a good idea.
With your example, you woud currently need to write:
li {
    @include xy-cell(6, $gutter-type: padding, $gutter-output: false);
    margin-bottom: rem-calc(5);
    text-align: left;

        @include breakpoint(large) {
            @include xy-cell(4, $gutter-type: padding, $gutter-output: false)
        }
}

To be honest the mixins do need a lot of work and probably a rework. They are fairly biased towards using margin gutters at the moment because at the time its what people seemed to be vocal about.
@kball Do you have any thoughts on how to make this scenario easier? I think we really need to completely redo the mixins at some point...

@brettsmason Thanks for answering. That is what I am already using, I figured out there was no other solution. To point out what I wanted to do intially so it does make sense, I wanted to have some cells to have the full width (and not the calc(% - gutters)), so in order to do that I had to set the $gutter-type to padding and turn the output to false.
With some tests, I figured out that only having $gutter-output: false, doesn't remove the calc(% - gutters) for the width size, something I don't really understand. If you don't want gutters then I think the width should be a clear % and not a calc(% - gutters).

@brettsmason @heyflo @sveggiani I tried to accomplish the same: A grid-margin-x grid and thought the xy-grid-layout mixin should be exactly what is needed for that. So I used the following code:
.item-row{
  @include xy-grid();
    @include xy-gutters($negative: true);

    @include xy-grid-layout(1, '.gallery-item', $gutter-type: margin);

    @include breakpoint(medium) {
        @include xy-grid-layout(2, '.item-row__cell', $gutter-type: margin);
    }
}

It wasn´t clear to me how to remove the padding gutter to archive the margin-gutter. Instead of using     @include xy-gutters($negative: true); it would make more sense to me to do something like @include xy-grid($gutter-type: margin);

@jmartsch I think you are right, using a parameter on the xy-grid just like like xy-grid($gutter-type: margin); which could be null by default, would actually remove the need to use another mixin to apply negative margin on that element when there is an xy-container as a parent.
Maybe they thought about more usages of that mixin (xy-gutters) that we don't think of. @kball or @brettsmason if you can shed some lights on this, I would be thanksful 👍

I don't have anything to contribute at the moment but have run into the same issue.
Very much appreciate the solutions provided but a bit surprised this doesn't work better out of the box in regards to building semantically.

Hi there, a question (and perhaps a feature request?) — many of the designs I work with don't have square gutters. What do I mean by this? For example, gutters might be 20px wide and 18px tall. I'm not sure if the grid supports this currently?
My suggestion would be to support the following, for example:
$grid-margin-gutters: (
  small: 1.125rem 1rem,
  large: 1.25rem 1.125rem
);

Where I'm setting these in rem instead of pixels.
Is this possible / planned? I couldn't find an issue or much about this except for this question on stack overflow

Thanks everyobne for the helpful feedback.
We are closing this very long issue as separate issues and questions should be reported as new single issues.


Magento Downloader Fatal Error

PHP message: PHP Fatal error:  Call to a member function getSessionId() on null in *****/.modman/magneto-debug/code/Debug/Model/RequestInfo.php on line 505" while reading response header from upstream, client: *********, server: *********, request: "POST /downloader/index.php?A=cleanCache
Looks like Sheep injects even to downloader :) need to exclude this I guess

Hi @devzorg,
Thanks for reporting this. I've committed some fixes that will resolve fatal errors. This is going to be included in our weekly release.
There is another ticket that will address on-demand capturing and probably whitelisting/blacklisting requests.
Cheers,
Mario


v12 Add info: Use the StorageSharedKeyCredential class to create the credential that is used to sign the SAS."

The v12 version of this page contains the line:  "Use the StorageSharedKeyCredential class to create the credential that is used to sign the SAS.", but then offers no hint on how this class is to be used.  If I follow the instructions as written, the line:
Uri sasUri = containerClient.GenerateSasUri(sasBuilder);
results in an exception:
System.ArgumentNullException
  Message=Value cannot be null. (Parameter 'sharedKeyCredential')
  Source=Azure.Storage.Blobs

An hour into StackOverflow and MS docs and I still don't have a clue how to fix this.

Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

ID: 4ba8dd43-ef02-5c5c-382d-f8d653721280
Version Independent ID: 4127d388-f82a-b182-c912-879cf854c37a
Content: Create a service SAS for a container or blob - Azure Storage
Content Source: articles/storage/blobs/sas-service-create.md
Service: storage
Sub-service: blobs
GitHub Login: @tamram
Microsoft Alias: tamram

@jaybo This sample might be helpful to you.
I have assigned the issue to the content author to investigate further and update the document as appropriate.

@jaybo - The StorageSharedKeyCredential constructor takes the account name and account key. You can then use the initialized instance to create a new BlobContainerClient object. For example:
BlobContainerClient blobContainerClient = new BlobContainerClient(new Uri(containerEndpoint),                                                                               new StorageSharedKeyCredential(AccountName1, AccountKey1));
I'll make sure that we have an example for this specifically in the docs and report back here when it's ready. We need a separate article to address Shared Key credentials.

@jaybo - The StorageSharedKeyCredential constructor takes the account name and account key. You can then use the initialized instance to create a new BlobContainerClient object. For example:
BlobContainerClient blobContainerClient = new BlobContainerClient(new Uri(containerEndpoint),                                                                               new StorageSharedKeyCredential(AccountName1, AccountKey1));
I'll make sure that we have an example for this specifically in the docs and report back here when it's ready. We need a separate article to address Shared Key credentials.

We've added an additional example showing how to instantiate the StorageSharedKeyCredential class.
#please-close


[PROPOSAL] Add linter check and prettier to dashboards-desktop repo

@CCongWang had successfully introduced  linter and prettier to FTRepo to improve the code quality and over all productivity.
Let's bring good practice to this repo
https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/195
https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/186

Add prettier:
checkout the description in this PR, like how to add prettier and config prettier rules
Add ESLint:
checkout this blog on how to integrate prettier and ESLint
Add Husky precommit hook:

// install husky
npm install husky -D

// install hook
yarn husky install

// add precommit hook
yarn husky add .husky/pre-commit "yarn lint(or your specific lint command defined in package.json)"
git add .husky/pre-commit

Add postinstall script in package.json to automatically install hook
checkout example PR


Add fix for #114

This PR does several things:
1. Add a fix for #114 - Add ConstrainWiderOrEqual
2. Adds the ability for Instructions to have constraints, and add a ConstrainWiderOrEqual to ireduce,{s,u}extend and f{promote,demote}
3. Reduce code duplication in TypeConstraint subclasses
4. Modify runtime constraint checks to reject match when they encounter overflow
5. Fix a small bug in emitting constraint edges in TypeEnv.dot();

One more question: In the current implementation i32 is considered "WiderOrEqual" compared to b8. Should I limit it to require that the two lane types are of the same kind as well?

No need, I think comparing lane bits is good enough


Update raml specification to be compatible with go-raml generation tool

Geems go-raml still doesn't support inline type declaration
in request and response body, so I created the missing type

@LeeSmet can you have a look at this and check if it makes sense ?

Problem of generation comes from this issue on go-raml: https://github.com/Jumpscale/go-raml/issues/96


AttributeError: 'JPEG' object has no attribute 'decompressor'

WARNING:tensorflow:Can't decode with jpeg4py (libjpeg-turbo): Could not load libjpeg-turbo library. Will use OpenCV. Exception ignored in: <bound method JPEG.__del__ of <jpeg4py._py.JPEG object at 0x7f0e8962d710>> Traceback (most recent call last):   File "/home/lfy/anaconda3/lib/python3.6/site-packages/jpeg4py/_py.py", line 215, in __del__     if self.decompressor is not None: AttributeError: 'JPEG' object has no attribute 'decompressor'

INFO:tensorflow:Images without annotation: 24 INFO:tensorflow:[226111, 458790, 461275, 370999, 98497, 228771, 240767, 560371, 330554, 550939, 502910, 127135, 320706, 64574, 198915, 101022, 25593, 528977, 542073, 261796, 312549, 267946, 49091, 447789] INFO:tensorflow:Using config: {'_model_dir': '/home/lfy/work/training_toolbox_tensorflow-develop/training_toolbox/ssd_detector/coco/model', '_tf_random_seed': 666, '_save_summary_steps': 100, '_save_checkpoints_steps': 1000, '_save_checkpoints_secs': None, '_session_config': intra_op_parallelism_threads: 2 inter_op_parallelism_threads: 8 gpu_options {   per_process_gpu_memory_fraction: 0.9   allow_growth: true } allow_soft_placement: true , '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 0.1, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_service': None, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f38a676b0b8>, '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1} INFO:tensorflow:Calling model_fn. INFO:tensorflow:Done calling model_fn. INFO:tensorflow:Create CheckpointSaverHook. INFO:tensorflow:Graph was finalized. 2019-02-28 17:55:23.655413: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 2019-02-28 17:55:23.735004: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:897] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero 2019-02-28 17:55:23.735324: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties:  name: GeForce GTX 1060 6GB major: 6 minor: 1 memoryClockRate(GHz): 1.759 pciBusID: 0000:01:00.0 totalMemory: 5.93GiB freeMemory: 5.46GiB 2019-02-28 17:55:23.735338: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0 2019-02-28 17:55:23.903616: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-02-28 17:55:23.903644: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971]      0  2019-02-28 17:55:23.903649: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0:   N  2019-02-28 17:55:23.903773: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 5462 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 6GB, pci bus id: 0000:01:00.0, compute capability: 6.1) INFO:tensorflow:Restoring parameters from /home/lfy/work/training_toolbox_tensorflow-develop/training_toolbox/ssd_detector/coco/model/model.ckpt-0 INFO:tensorflow:Running local_init_op. INFO:tensorflow:Done running local_init_op. INFO:tensorflow:Saving checkpoints for 0 into /home/lfy/work/training_toolbox_tensorflow-develop/training_toolbox/ssd_detector/coco/model/model.ckpt. WARNING:tensorflow:Can't decode with jpeg4py (libjpeg-turbo): Could not load libjpeg-turbo library. Will use OpenCV. Exception ignored in: <bound method JPEG.__del__ of <jpeg4py._py.JPEG object at 0x7f3861ef3320>> Traceback (most recent call last):   File "/home/lfy/anaconda3/lib/python3.6/site-packages/jpeg4py/_py.py", line 215, in __del__     if self.decompressor is not None: AttributeError: 'JPEG' object has no attribute 'decompressor'

check the environment
each libraries' version is compatible following the prerequest.txt

check the environment
each libraries' version is compatible following the prerequest.txt


Questions about using multiple GPUs to train Llama-2-13B

Great job! However, I encountered a problem when training Llama-2-13B with multiple GPUs. The program would stall in the validation process without continuing or interrupting. Have you ever encountered this in multi-GPU training? I only added and modified two places: " os.environ['CUDA_VISIBLE_DEVICES'] = '4,5,6,7' " and "devices=4".

Hello junzhang-zj.
We apologize for the delayed response. All our code was developed based on a single GPU implementation, and our experiments were conducted in that environment. We have acknowledged several issues when running in a multi-GPU setting. Currently, we are exploring the expected effects of applying DropBP in a multi-GPU environment and plan to implement these findings in our code for a future update. Thank you for bringing this significant issue to our attention. We will notify you as soon as the DropBP update for multi-GPU support is available.

@WooSunghyeon　Thanks for your reply, it helps me a lot! Is it also running on a single GPU for LLaMA-2-70B? The reason why I use multiple GPUs for testing is that when I test LLaMA-2-13B, when I set droprate of DropBP to 0, a single GPU (A100) will report OOM. Have you encountered this problem? In addition, I have a question about the calculation of FLOPs. I would like to ask how the x-axis FloPs is calculated in the convergence speed analysis in the article.

4.12 Update: I think I have solved the memory problem of LLaMA-2-13B by setting the precision of bf16-true; but I am still stuck on training on 70B. In this scenario, I set bf16-true and set quantize for bnb.nf4, but the run stops at load_state_checkpoint and does not proceed. Do I need to wait longer, or are there other necessary settings that I have overlooked? Also, I still don't know how to calculate FLOPs.

I recommend setting micro_batch_size = 1 in finetune/lora.py with --quantize bnb.nf4.
To calculate FLOP, we added the FLOPs of all linear and attention layers (for example, when passing through nn.Linear(h, h) with an input size of [b, s, h], the FLOPs for that layer are calculated as $2bsh^2$). If you find this process somewhat cumbersome, you can refer to the approximations calculated in the paper by Kaplan et al. Specifically, the FLOPs consumed by the model in one iteration, given the number of parameters ($N$), batch size ($b$), sequence length ($s$), and hidden dimension ($h$), can be represented as $6NBS(1+\frac{s}{h})$. This approximation is very close to the actual FLOPs.
We apologize once again for the late response and thank you again for your excellent question :)

@WooSunghyeon Thank you for your answer! I understand this approximation, but it seems that this calculation does not distinguish between training parameters and frozen parameters. I will rearrange this part and discuss it with you if I have any questions.
Also, I seem to have missed the deployment of learning rate amplification for skip layers (layers with fewer updates) in the code. Could you please help me point out this part? After all, this comparison under the same learning rate setting is extremely important, because we found that 'Full LoRA (drop_rate=0)' still converges at the 1e-3 learning rate.

Thank you for your detailed analysis!

That's a good point. First, the $N$ in the equation refers to parameters not considered by LoRA (i.e. frozen parameters). Note that the increase in computation due to LoRA is negligible, so it can be ignored. However, when LoRA is applied, the computational effort required for weight updates is significantly reduced, which can be calculated as $4NBS(1+\frac{s}{h})$. When DropBP is also applied, the FLOPs decrease to $2NBS(1+\frac{s}{h}) +2NBS(1+\frac{s}{h})(1-p)$, as the backward propagation FLOPs reduce depending on the drop rate $p$. The details can be found in Appendix B.

We apologize for any confusion caused. Our actual implementation did not consider fewer updates. We have compared experimental results with and without fewer updates in our DropBP, and found almost no difference in accuracy. Therefore, we did not apply fewer updates in the code for simplicity, and the results in Table 2 also reflect experiments without fewer updates.
We plan to make an update to remove the content related to fewer updates from the paper accordingly.

Thank you!

@WooSunghyeon Thanks again for your thorough reply!
There is still a bit of doubt about the experimental setup, which seems to unfairly favor certain learning rates. In my testing, 'dropbp (0.85)' achieved optimal performance at lr=3e-4. However, increasing the learning rate disrupts convergence, leading to a significant increase in loss after around 10k iterations. On the other hand, 'full lora' can converge successfully at lr=1e-3 and 2e-3, with notably faster convergence compared to lr=3e-4. Have you taken these observations into account?

Interesting observation indeed! We didn't experiment with that high learning rate setting. Your point is valid, and we'll conduct experiments with higher learning rates as suggested. However, as you can see in Appendix A, in most cases, the convergence speed was fastest when the drop rate was 0.5. Additionally, empirically, we observed that training was stable similar to full LoRA when the drop rate was 0.5, while it became slightly unstable with a drop rate of 0.875. Therefore, we expect that training will remain stable with a drop rate of 0.5 even in high learning rate settings, and furthermore, we anticipate achieving faster convergence speeds compared to the baseline. This perspective will be incorporated into future experiments and reflected in the paper, and we'll make sure to keep you informed. Once again, thank you for your insightful advice!

@WooSunghyeon I encountered another problem, because I want to run a Qwen1.5 to test the performance, but litgpt does not support it, so I try to use hf in the package to use dropbp. Is this code executable? I seem to have encountered some obstacles.

Sorry about that. We have been working on modifying the code to run dropbp on huggingface as well, and we have already succeeded in fine-tuning mistral 7b locally. However, it was my mistake that I didn't upload it to GitHub. I apologize for that. I have now updated the code on GitHub. Thank you.
Please note that the code to apply QLoRA and DropBP together on huggingface has not yet been implemented. We will update it as soon as possible.

Thank you for your prompt reply! I'll try it right away!

I’m sorry for the late response. I didn’t notice that the original question had been edited, so I’ve only just become aware of the changes.
Our previous version of DropBP had several issues, which we have now resolved by creating a custom HuggingFace library. With this updated approach, you can easily use DropBP by installing our custom HuggingFace library and simply adding the drop_rate parameter to the Trainer. For more details, please see dropbp/huggingface/README.md.
If you have any further questions or need assistance with the new library, please open a new issue. Thanks!


Add back rollback functionality

Resolves #2
I would like to be able to get the FunctionName via the apigateway. Then the user will only have to pass in the stage_name. Any ideas?

Is it actually necessary to rollback the API gateway? I thought you could just rollback the Lambda function and have that work? Or are you saying that those are separate functionalities?

But this is looking good!

I have never used lambda before, so I am not sure. My assumption was (and could very well be wrong), that you need to update the function, and then redeploy the stage with the updated function. I will investigate this further over the weekend.

I'm pretty sure that the code and the APIGW are totally separate, and that rolling back the function can be done totally separately from the APIGW. You can check this manually in the console.

@Miserlou Ah, ok. So in the original issue:

python manage.py rollback dev

that dev was the function name, not the stage name?
If that is the case, then rollback_lambda_function_version method is all that is needed to resolve this issue.

Right, so you set your different production environments in your ZAPPA_SETTINGS, (ex - dev / staging / production), and then rollback would just rollback for the env. Does that make sense?
If you just add one more commit to remove the API GW rollback, I'll merge this in?
Thanks very much for your contribution!!

Done. Thanks for the project :-)

Sweeeet!
Do you want to make a Django management command for this feature as well?

Will do.


Implement writeToOutputStream for JavalinGson

This implements writing JSON directly to an output stream from a Stream<T> using the Gson mapper.

Thanks @bseib !


One tuple may meet two events.

One tuple may meet two events, but now we only consider whether the tuple meets one of the events.

@liuchangshiye What does this mean? Is it related to #119. A event should be considered for both birth and cohort selection?


Bad performance for prefix exact matches

is_explicit, false
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = ""
function=query duration=20.673µs
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "i"
1/1, active matching set, 1, threshold, 1
function=query duration=71.169µs
Key(110)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "in"
1/2, active matching set, 1, threshold, 1
2/2, active matching set, 2, threshold, 1
function=query duration=111.748µs
Key(99)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inc"
1/3, active matching set, 1, threshold, 1
2/3, active matching set, 2, threshold, 1
3/3, active matching set, 3, threshold, 1
function=query duration=115.519µs
Key(111)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inco"
1/4, active matching set, 1, threshold, 1
2/4, active matching set, 2, threshold, 1
3/4, active matching set, 3, threshold, 1
4/4, active matching set, 4, threshold, 1
function=query duration=147.717µs
Key(110)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "incon"
1/5, active matching set, 1, threshold, 1
2/5, active matching set, 2, threshold, 1
3/5, active matching set, 3, threshold, 1
4/5, active matching set, 4, threshold, 1
5/5, active matching set, 5, threshold, 1
function=query duration=169.858µs
Key(99)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconc"
1/6, active matching set, 1, threshold, 1
2/6, active matching set, 2, threshold, 1
3/6, active matching set, 3, threshold, 1
4/6, active matching set, 4, threshold, 1
5/6, active matching set, 5, threshold, 1
6/6, active matching set, 6, threshold, 1
function=query duration=211.553µs
Key(101)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconce"
1/7, active matching set, 1, threshold, 1
2/7, active matching set, 2, threshold, 1
3/7, active matching set, 3, threshold, 1
4/7, active matching set, 4, threshold, 1
5/7, active matching set, 5, threshold, 1
6/7, active matching set, 6, threshold, 1
7/7, active matching set, 7, threshold, 1
function=query duration=332.172µs
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconcei"
1/8, active matching set, 1, threshold, 1
2/8, active matching set, 2, threshold, 1
3/8, active matching set, 3, threshold, 1
4/8, active matching set, 4, threshold, 1
5/8, active matching set, 5, threshold, 1
6/8, active matching set, 6, threshold, 1
7/8, active matching set, 7, threshold, 1
8/8, active matching set, 8, threshold, 1
function=query duration=2.433599ms
Key(118)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceiv"
1/9, active matching set, 1, threshold, 1
2/9, active matching set, 2, threshold, 1
3/9, active matching set, 3, threshold, 1
4/9, active matching set, 4, threshold, 1
5/9, active matching set, 5, threshold, 1
6/9, active matching set, 6, threshold, 1
7/9, active matching set, 7, threshold, 1
8/9, active matching set, 8, threshold, 1
9/9, active matching set, 304, threshold, 1
function=query duration=4.46679ms
Key(97)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceiva"
1/10, active matching set, 1, threshold, 1
2/10, active matching set, 2, threshold, 1
3/10, active matching set, 3, threshold, 1
4/10, active matching set, 4, threshold, 1
5/10, active matching set, 5, threshold, 1
6/10, active matching set, 6, threshold, 1
7/10, active matching set, 7, threshold, 1
8/10, active matching set, 8, threshold, 1
9/10, active matching set, 304, threshold, 1
10/10, active matching set, 306, threshold, 2
function=query duration=23.148242ms
Key(98)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivab"
1/11, active matching set, 1, threshold, 1
2/11, active matching set, 2, threshold, 1
3/11, active matching set, 3, threshold, 1
4/11, active matching set, 4, threshold, 1
5/11, active matching set, 5, threshold, 1
6/11, active matching set, 6, threshold, 1
7/11, active matching set, 7, threshold, 1
8/11, active matching set, 8, threshold, 1
9/11, active matching set, 304, threshold, 1
10/11, active matching set, 306, threshold, 2
11/11, active matching set, 3690, threshold, 2
function=query duration=37.083325ms
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabi"
1/12, active matching set, 1, threshold, 1
2/12, active matching set, 2, threshold, 1
3/12, active matching set, 3, threshold, 1
4/12, active matching set, 4, threshold, 1
5/12, active matching set, 5, threshold, 1
6/12, active matching set, 6, threshold, 1
7/12, active matching set, 7, threshold, 1
8/12, active matching set, 8, threshold, 1
9/12, active matching set, 304, threshold, 1
10/12, active matching set, 306, threshold, 2
11/12, active matching set, 3690, threshold, 2
12/12, active matching set, 3693, threshold, 2
function=query duration=51.86169ms
Key(108)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabil"
1/13, active matching set, 1, threshold, 1
2/13, active matching set, 2, threshold, 1
3/13, active matching set, 3, threshold, 1
4/13, active matching set, 4, threshold, 1
5/13, active matching set, 5, threshold, 1
6/13, active matching set, 6, threshold, 1
7/13, active matching set, 7, threshold, 1
8/13, active matching set, 8, threshold, 1
9/13, active matching set, 304, threshold, 1
10/13, active matching set, 306, threshold, 2
11/13, active matching set, 3690, threshold, 2
12/13, active matching set, 3693, threshold, 2
13/13, active matching set, 3696, threshold, 2
function=query duration=67.725688ms
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabili"
1/14, active matching set, 1, threshold, 1
2/14, active matching set, 2, threshold, 1
3/14, active matching set, 3, threshold, 1
4/14, active matching set, 4, threshold, 1
5/14, active matching set, 5, threshold, 1
6/14, active matching set, 6, threshold, 1
7/14, active matching set, 7, threshold, 1
8/14, active matching set, 8, threshold, 1
9/14, active matching set, 304, threshold, 1
10/14, active matching set, 306, threshold, 2
11/14, active matching set, 3690, threshold, 2
12/14, active matching set, 3693, threshold, 2
13/14, active matching set, 3696, threshold, 2
14/14, active matching set, 3700, threshold, 2
function=query duration=99.955436ms
Key(116)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabilit"
1/15, active matching set, 1, threshold, 1
2/15, active matching set, 2, threshold, 1
3/15, active matching set, 3, threshold, 1
4/15, active matching set, 4, threshold, 1
5/15, active matching set, 5, threshold, 1
6/15, active matching set, 6, threshold, 1
7/15, active matching set, 7, threshold, 1
8/15, active matching set, 8, threshold, 1
9/15, active matching set, 304, threshold, 1
10/15, active matching set, 306, threshold, 2
11/15, active matching set, 3690, threshold, 2
12/15, active matching set, 3693, threshold, 2
13/15, active matching set, 3696, threshold, 2
14/15, active matching set, 3700, threshold, 2
15/15, active matching set, 3703, threshold, 3
function=query duration=171.135194ms
Key(121)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivability"
1/16, active matching set, 1, threshold, 1
2/16, active matching set, 2, threshold, 1
3/16, active matching set, 3, threshold, 1
4/16, active matching set, 4, threshold, 1
5/16, active matching set, 5, threshold, 1
6/16, active matching set, 6, threshold, 1
7/16, active matching set, 7, threshold, 1
8/16, active matching set, 8, threshold, 1
9/16, active matching set, 304, threshold, 1
10/16, active matching set, 306, threshold, 2
11/16, active matching set, 3690, threshold, 2
12/16, active matching set, 3693, threshold, 2
13/16, active matching set, 3696, threshold, 2
14/16, active matching set, 3700, threshold, 2
15/16, active matching set, 3703, threshold, 3
16/16, active matching set, 21686, threshold, 3
function=query duration=319.873793ms
Key(65513)

See this log, all inputs prefix-exact-matches a word in the index, but it has to iterate over that many elements.
        for query_prefix_len in 1..=query_chars.len() {
            if threshold > MAX_DISTANCE && active_matching_set.matchings.len() > MAX_SET {
                println!("query cancelled");
                return vec![];
            }
            debug_println!(
                "{}/{}, active matching set, {}, threshold, {}",
                query_prefix_len,
                query_chars.len(),
                active_matching_set.matchings.len(),
                threshold
            );
            result.clear();
            threshold = self.autocomplete_step(
                &mut active_matching_set,
                &query_chars,
                query_prefix_len,
                threshold,
                requested,
                &mut result,
            );
        }

Later I tried to keep threshold at 1
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "i"
1/1, active matching set, 1, threshold, 1
function=query duration=74.941µs
Key(110)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "in"
1/2, active matching set, 1, threshold, 1
2/2, active matching set, 2, threshold, 1
function=query duration=117.056µs
Key(99)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inc"
1/3, active matching set, 1, threshold, 1
2/3, active matching set, 2, threshold, 1
3/3, active matching set, 3, threshold, 1
function=query duration=121.806µs
Key(111)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inco"
1/4, active matching set, 1, threshold, 1
2/4, active matching set, 2, threshold, 1
3/4, active matching set, 3, threshold, 1
4/4, active matching set, 4, threshold, 1
function=query duration=153.305µs
Key(110)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "incon"
1/5, active matching set, 1, threshold, 1
2/5, active matching set, 2, threshold, 1
3/5, active matching set, 3, threshold, 1
4/5, active matching set, 4, threshold, 1
5/5, active matching set, 5, threshold, 1
function=query duration=228.944µs
Key(99)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconc"
1/6, active matching set, 1, threshold, 1
2/6, active matching set, 2, threshold, 1
3/6, active matching set, 3, threshold, 1
4/6, active matching set, 4, threshold, 1
5/6, active matching set, 5, threshold, 1
6/6, active matching set, 6, threshold, 1
function=query duration=262.329µs
Key(101)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconce"
1/7, active matching set, 1, threshold, 1
2/7, active matching set, 2, threshold, 1
3/7, active matching set, 3, threshold, 1
4/7, active matching set, 4, threshold, 1
5/7, active matching set, 5, threshold, 1
6/7, active matching set, 6, threshold, 1
7/7, active matching set, 7, threshold, 1
function=query duration=413.399µs
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconcei"
1/8, active matching set, 1, threshold, 1
2/8, active matching set, 2, threshold, 1
3/8, active matching set, 3, threshold, 1
4/8, active matching set, 4, threshold, 1
5/8, active matching set, 5, threshold, 1
6/8, active matching set, 6, threshold, 1
7/8, active matching set, 7, threshold, 1
8/8, active matching set, 8, threshold, 1
function=query duration=2.56162ms
Key(118)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceiv"
1/9, active matching set, 1, threshold, 1
2/9, active matching set, 2, threshold, 1
3/9, active matching set, 3, threshold, 1
4/9, active matching set, 4, threshold, 1
5/9, active matching set, 5, threshold, 1
6/9, active matching set, 6, threshold, 1
7/9, active matching set, 7, threshold, 1
8/9, active matching set, 8, threshold, 1
9/9, active matching set, 304, threshold, 1
function=query duration=4.558143ms
Key(97)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceiva"
1/10, active matching set, 1, threshold, 1
2/10, active matching set, 2, threshold, 1
3/10, active matching set, 3, threshold, 1
4/10, active matching set, 4, threshold, 1
5/10, active matching set, 5, threshold, 1
6/10, active matching set, 6, threshold, 1
7/10, active matching set, 7, threshold, 1
8/10, active matching set, 8, threshold, 1
9/10, active matching set, 304, threshold, 1
10/10, active matching set, 306, threshold, 1
function=query duration=24.966806ms
Key(98)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivab"
1/11, active matching set, 1, threshold, 1
2/11, active matching set, 2, threshold, 1
3/11, active matching set, 3, threshold, 1
4/11, active matching set, 4, threshold, 1
5/11, active matching set, 5, threshold, 1
6/11, active matching set, 6, threshold, 1
7/11, active matching set, 7, threshold, 1
8/11, active matching set, 8, threshold, 1
9/11, active matching set, 304, threshold, 1
10/11, active matching set, 306, threshold, 1
11/11, active matching set, 3690, threshold, 1
function=query duration=38.473195ms
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabi"
1/12, active matching set, 1, threshold, 1
2/12, active matching set, 2, threshold, 1
3/12, active matching set, 3, threshold, 1
4/12, active matching set, 4, threshold, 1
5/12, active matching set, 5, threshold, 1
6/12, active matching set, 6, threshold, 1
7/12, active matching set, 7, threshold, 1
8/12, active matching set, 8, threshold, 1
9/12, active matching set, 304, threshold, 1
10/12, active matching set, 306, threshold, 1
11/12, active matching set, 3690, threshold, 1
12/12, active matching set, 3693, threshold, 1
function=query duration=59.101932ms
Key(108)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabil"
1/13, active matching set, 1, threshold, 1
2/13, active matching set, 2, threshold, 1
3/13, active matching set, 3, threshold, 1
4/13, active matching set, 4, threshold, 1
5/13, active matching set, 5, threshold, 1
6/13, active matching set, 6, threshold, 1
7/13, active matching set, 7, threshold, 1
8/13, active matching set, 8, threshold, 1
9/13, active matching set, 304, threshold, 1
10/13, active matching set, 306, threshold, 1
11/13, active matching set, 3690, threshold, 1
12/13, active matching set, 3693, threshold, 1
13/13, active matching set, 3696, threshold, 1
function=query duration=75.17944ms
Key(105)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabili"
1/14, active matching set, 1, threshold, 1
2/14, active matching set, 2, threshold, 1
3/14, active matching set, 3, threshold, 1
4/14, active matching set, 4, threshold, 1
5/14, active matching set, 5, threshold, 1
6/14, active matching set, 6, threshold, 1
7/14, active matching set, 7, threshold, 1
8/14, active matching set, 8, threshold, 1
9/14, active matching set, 304, threshold, 1
10/14, active matching set, 306, threshold, 1
11/14, active matching set, 3690, threshold, 1
12/14, active matching set, 3693, threshold, 1
13/14, active matching set, 3696, threshold, 1
14/14, active matching set, 3700, threshold, 1
function=query duration=90.591975ms
Key(116)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivabilit"
1/15, active matching set, 1, threshold, 1
2/15, active matching set, 2, threshold, 1
3/15, active matching set, 3, threshold, 1
4/15, active matching set, 4, threshold, 1
5/15, active matching set, 5, threshold, 1
6/15, active matching set, 6, threshold, 1
7/15, active matching set, 7, threshold, 1
8/15, active matching set, 8, threshold, 1
9/15, active matching set, 304, threshold, 1
10/15, active matching set, 306, threshold, 1
11/15, active matching set, 3690, threshold, 1
12/15, active matching set, 3693, threshold, 1
13/15, active matching set, 3696, threshold, 1
14/15, active matching set, 3700, threshold, 1
15/15, active matching set, 3703, threshold, 1
function=query duration=89.729627ms
Key(121)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconceivability"
1/16, active matching set, 1, threshold, 1
2/16, active matching set, 2, threshold, 1
3/16, active matching set, 3, threshold, 1
4/16, active matching set, 4, threshold, 1
5/16, active matching set, 5, threshold, 1
6/16, active matching set, 6, threshold, 1
7/16, active matching set, 7, threshold, 1
8/16, active matching set, 8, threshold, 1
9/16, active matching set, 304, threshold, 1
10/16, active matching set, 306, threshold, 1
11/16, active matching set, 3690, threshold, 1
12/16, active matching set, 3693, threshold, 1
13/16, active matching set, 3696, threshold, 1
14/16, active matching set, 3700, threshold, 1
15/16, active matching set, 3703, threshold, 1
16/16, active matching set, 3705, threshold, 1
function=query duration=106.70657ms

If I ask for only one result, it works well
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconvenie"
1/10, active matching set, 1, threshold, 1
2/10, active matching set, 2, threshold, 1
3/10, active matching set, 3, threshold, 1
4/10, active matching set, 4, threshold, 1
5/10, active matching set, 5, threshold, 1
6/10, active matching set, 6, threshold, 1
7/10, active matching set, 7, threshold, 1
8/10, active matching set, 8, threshold, 1
9/10, active matching set, 9, threshold, 1
10/10, active matching set, 10, threshold, 1
function=query duration=278.323µs
Key(110)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconvenien"
1/11, active matching set, 1, threshold, 1
2/11, active matching set, 2, threshold, 1
3/11, active matching set, 3, threshold, 1
4/11, active matching set, 4, threshold, 1
5/11, active matching set, 5, threshold, 1
6/11, active matching set, 6, threshold, 1
7/11, active matching set, 7, threshold, 1
8/11, active matching set, 8, threshold, 1
9/11, active matching set, 9, threshold, 1
10/11, active matching set, 10, threshold, 1
11/11, active matching set, 11, threshold, 1
function=query duration=346.839µs
Key(99)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconvenienc"
1/12, active matching set, 1, threshold, 1
2/12, active matching set, 2, threshold, 1
3/12, active matching set, 3, threshold, 1
4/12, active matching set, 4, threshold, 1
5/12, active matching set, 5, threshold, 1
6/12, active matching set, 6, threshold, 1
7/12, active matching set, 7, threshold, 1
8/12, active matching set, 8, threshold, 1
9/12, active matching set, 9, threshold, 1
10/12, active matching set, 10, threshold, 1
11/12, active matching set, 11, threshold, 1
12/12, active matching set, 12, threshold, 1
function=query duration=377.5µs
Key(101)
[offdict-tauri/src-tauri/src/main.rs:269:9] e.text() = "inconvenience"
1/13, active matching set, 1, threshold, 1
2/13, active matching set, 2, threshold, 1
3/13, active matching set, 3, threshold, 1
4/13, active matching set, 4, threshold, 1
5/13, active matching set, 5, threshold, 1
6/13, active matching set, 6, threshold, 1
7/13, active matching set, 7, threshold, 1
8/13, active matching set, 8, threshold, 1
9/13, active matching set, 9, threshold, 1
10/13, active matching set, 10, threshold, 1
11/13, active matching set, 11, threshold, 1
12/13, active matching set, 12, threshold, 1
13/13, active matching set, 13, threshold, 1
function=query duration=369.956µs


How to generate license from an installer

Hi,
First thanks for this great and easy to use library.
The application I'm trying to release does not have any websites, and the trial edition is supposed to expire in 30 days after installation.
So I guess the Windows installer is supposed to generate the license file (any alternatives?). To do that I suppose I need to create a console application that generates the license and run it after installation is over. However that program will contain private key and pass phrase embedded into it, which is something I'm trying to avoid. Is there any advice or best practices on how to generate a license as part of Windows installer for apps that don't have a Web API?

Look at using custom actions.

Generating the License file on the client is not an option, because it would require to have also your private key on the client.
You could:

you have a small website/service that accepts name and email address and creates a 30-day trial license and send it to the client (download, email, etc.) This would also help to check that the same person (by name / email) is not generating every 30 days a new trial license.
you automatically generate the setup for your product on your server every days and also generate a 30 day trial license with it. But the use could download then every few days a new build with a new 30-day license

Thanks


docs: fix introduction section links

📚 Description

The links on the Introduction page were all the same and didn't link to the correct place.
🔗 Linked Issue

🧪 Test Plan

📄 Documentation to Update

 Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.You have signed the CLA already but the status is still pending? Let us recheck it.

cc @mohab-sameh

@all-contributors add @stramel for doc


updateTimeDimension

I forked your repo and tried to re-create a radar layer for the US. I ran into an issue when I added the updateTimeDimensions: true   to get realtime updates...
The error I have is in this function this._availableTimes is empty. I tried increasing the loadingTimeout, but that didn't seem to help :
_newTimeIndexLoaded: function() {
if (this._loadingTimeIndex == -1) {
return;
}
var time = this._availableTimes[this._loadingTimeIndex];  // <---- Time is undefined because this._availableTimes is empty [].
Not sure if you have any insight, but here is my code:
https://github.com/eflowbeach/MobileWeather/blob/master/js/mobileweather.js#L51

Hi!
I've checked the GetCapabilities response of the WMS you are using. Layer '1' doesn't have a Extent[name="time"] element. But its parent yes (it seems to be a nested layers structure).
I've changed the code, and now it gets the available times from the layer or its parent.
Take into account that you would need to setup a proxy to make the request (example https://github.com/socib/Leaflet.TimeDimension/blob/master/examples/server/proxy.php).
I will add a page in this repository to collect Leaflet.TimeDimension examples of usage. Feel free to add your case.

Hi again,
So, I added your changes from commit 9ad9ab7 and I added a proxy call with the proxy.php script you politely provided:
https://github.com/eflowbeach/MobileWeather/commit/36c9355458e61722519902748c4b321f998bdddb
and I still get time=undefined here:
_newTimeIndexLoaded: function() {
if (this._loadingTimeIndex == -1) {
return;
}
var time = this._availableTimes[this._loadingTimeIndex]; // <----still available times is empty
Thank you for your help!

Hi,
I have uploaded a new example with this layer (see http://apps.socib.es/Leaflet.TimeDimension/examples/example14.html ).
There is a conflict between times setted in your timeDimensionOptions and the times retrieved from getCapabilities. With this configuration, the program does an intersection of both. And this intersection is null.
If you active updateTimeDimension option, after retrieving available times from the server, the timedimension component is update according to the updateTimeDimensionMode parameter. By default this mode is 'intersect', and you might want to change it to 'replace' (see https://github.com/socib/Leaflet.TimeDimension#-options-2 ).
I would recommend to not set timeInterval and period options, as you will see in the example 14. Therefore, the available times will be the ones retrieved from the server.

Thanks Biel! It is working now. :+1:
So, since I removed the timeInterval and period options, is there a way to limit the amount of frames to say 1 hour worth of data (maybe the cache options do this?) ? If not, I could probably provide you with a pull request that does this.
Btw, this is a really cool library!

Thanks! :)
It might be useful to add maxTime and minTime optional parameters to the layer, and modify the list of times retrieved from the server (probably at setAvailableTimes).
But you can filter the list with the default list of times. But this default list must match the times on the server.
NOAA's layer is composed by layers each 5 minutes. You need to make sure that you create the default list of times taking a date with minutes finishing in 0 or 5 (and 0 seconds).
var endDate = new Date();
endDate.setUTCSeconds(0, 0);
var minutes = endDate.getUTCMinutes();
endDate.setUTCMinutes(minutes - (minutes % 5));
[...]
    timeDimensionOptions: {
        timeInterval: "PT1H/" + endDate.toISOString(),
        period: "PT5M", // or  "PT10M"...
    },

updateTimeDimensions option is still recommended to remove the last occurrence, if it is still not available on the server.